blob: fe98b6f3447ee98fdfa27926e8a7fe8f3a8327b3 [file] [log] [blame]
Guido van Rossumac4f8d31995-08-29 23:46:35 +00001"""File selection dialog classes.
2
3Classes:
4
5- FileDialog
6- LoadFileDialog
7- SaveFileDialog
8
Guido van Rossumac4f8d31995-08-29 23:46:35 +00009"""
10
11from Tkinter import *
12from Dialog import Dialog
13
Guido van Rossumac4f8d31995-08-29 23:46:35 +000014import os
15import fnmatch
16
17
Guido van Rossum0978f991996-05-28 23:14:36 +000018dialogstates = {}
19
20
Guido van Rossumac4f8d31995-08-29 23:46:35 +000021class FileDialog:
22
23 """Standard file selection dialog -- no checks on selected file.
24
25 Usage:
26
27 d = FileDialog(master)
Guido van Rossum0978f991996-05-28 23:14:36 +000028 file = d.go(dir_or_file, pattern, default, key)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000029 if file is None: ...canceled...
Guido van Rossum0978f991996-05-28 23:14:36 +000030 else: ...open file...
31
32 All arguments to go() are optional.
33
34 The 'key' argument specifies a key in the global dictionary
35 'dialogstates', which keeps track of the values for the directory
36 and pattern arguments, overriding the values passed in (it does
37 not keep track of the default argument!). If no key is specified,
38 the dialog keeps no memory of previous state. Note that memory is
39 kept even when the dialog is cancelled. (All this emulates the
40 behavior of the Macintosh file selection dialogs.)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000041
42 """
43
44 title = "File Selection Dialog"
45
Guido van Rossum0978f991996-05-28 23:14:36 +000046 def __init__(self, master, title=None):
47 if title is None: title = self.title
Guido van Rossumac4f8d31995-08-29 23:46:35 +000048 self.master = master
49 self.directory = None
Guido van Rossum0978f991996-05-28 23:14:36 +000050
Guido van Rossumac4f8d31995-08-29 23:46:35 +000051 self.top = Toplevel(master)
Guido van Rossum0978f991996-05-28 23:14:36 +000052 self.top.title(title)
53 self.top.iconname(title)
54
55 self.botframe = Frame(self.top)
56 self.botframe.pack(side=BOTTOM, fill=X)
57
58 self.selection = Entry(self.top)
59 self.selection.pack(side=BOTTOM, fill=X)
60 self.selection.bind('<Return>', self.ok_event)
61
Guido van Rossumac4f8d31995-08-29 23:46:35 +000062 self.filter = Entry(self.top)
Guido van Rossum0978f991996-05-28 23:14:36 +000063 self.filter.pack(side=TOP, fill=X)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000064 self.filter.bind('<Return>', self.filter_command)
Guido van Rossum0978f991996-05-28 23:14:36 +000065
Guido van Rossumac4f8d31995-08-29 23:46:35 +000066 self.midframe = Frame(self.top)
67 self.midframe.pack(expand=YES, fill=BOTH)
Guido van Rossum0978f991996-05-28 23:14:36 +000068
69 self.filesbar = Scrollbar(self.midframe)
70 self.filesbar.pack(side=RIGHT, fill=Y)
71 self.files = Listbox(self.midframe, exportselection=0,
72 yscrollcommand=(self.filesbar, 'set'))
Guido van Rossumac4f8d31995-08-29 23:46:35 +000073 self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
Guido van Rossum7fc0bf81997-01-03 23:39:26 +000074 btags = self.files.bindtags()
75 self.files.bindtags(btags[1:] + btags[:1])
Guido van Rossumac4f8d31995-08-29 23:46:35 +000076 self.files.bind('<ButtonRelease-1>', self.files_select_event)
77 self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
Guido van Rossum0978f991996-05-28 23:14:36 +000078 self.filesbar.config(command=(self.files, 'yview'))
79
80 self.dirsbar = Scrollbar(self.midframe)
81 self.dirsbar.pack(side=LEFT, fill=Y)
82 self.dirs = Listbox(self.midframe, exportselection=0,
83 yscrollcommand=(self.dirsbar, 'set'))
84 self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
85 self.dirsbar.config(command=(self.dirs, 'yview'))
Guido van Rossum7fc0bf81997-01-03 23:39:26 +000086 btags = self.dirs.bindtags()
87 self.dirs.bindtags(btags[1:] + btags[:1])
Guido van Rossum0978f991996-05-28 23:14:36 +000088 self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
89 self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
90
Guido van Rossumac4f8d31995-08-29 23:46:35 +000091 self.ok_button = Button(self.botframe,
92 text="OK",
93 command=self.ok_command)
94 self.ok_button.pack(side=LEFT)
95 self.filter_button = Button(self.botframe,
96 text="Filter",
97 command=self.filter_command)
98 self.filter_button.pack(side=LEFT, expand=YES)
99 self.cancel_button = Button(self.botframe,
100 text="Cancel",
101 command=self.cancel_command)
102 self.cancel_button.pack(side=RIGHT)
103
Guido van Rossum0978f991996-05-28 23:14:36 +0000104 self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
105 # XXX Are the following okay for a general audience?
106 self.top.bind('<Alt-w>', self.cancel_command)
107 self.top.bind('<Alt-W>', self.cancel_command)
108
109 def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
110 if key and dialogstates.has_key(key):
111 self.directory, pattern = dialogstates[key]
112 else:
113 dir_or_file = os.path.expanduser(dir_or_file)
114 if os.path.isdir(dir_or_file):
115 self.directory = dir_or_file
116 else:
117 self.directory, default = os.path.split(dir_or_file)
118 self.set_filter(self.directory, pattern)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000119 self.set_selection(default)
Guido van Rossum0978f991996-05-28 23:14:36 +0000120 self.filter_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000121 self.selection.focus_set()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000122 self.top.grab_set()
Guido van Rossum0978f991996-05-28 23:14:36 +0000123 self.how = None
124 self.master.mainloop() # Exited by self.quit(how)
125 if key: dialogstates[key] = self.get_filter()
126 self.top.destroy()
127 return self.how
128
129 def quit(self, how=None):
130 self.how = how
131 self.master.quit() # Exit mainloop()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000132
133 def dirs_double_event(self, event):
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000134 self.filter_command()
135
136 def dirs_select_event(self, event):
137 dir, pat = self.get_filter()
Guido van Rossum7fc0bf81997-01-03 23:39:26 +0000138 subdir = self.dirs.get('active')
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000139 dir = os.path.normpath(os.path.join(self.directory, subdir))
140 self.set_filter(dir, pat)
141
142 def files_double_event(self, event):
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000143 self.ok_command()
144
145 def files_select_event(self, event):
Guido van Rossum7fc0bf81997-01-03 23:39:26 +0000146 file = self.files.get('active')
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000147 self.set_selection(file)
148
149 def ok_event(self, event):
150 self.ok_command()
151
152 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000153 self.quit(self.get_selection())
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000154
155 def filter_command(self, event=None):
156 dir, pat = self.get_filter()
157 try:
158 names = os.listdir(dir)
159 except os.error:
160 self.master.bell()
161 return
162 self.directory = dir
163 self.set_filter(dir, pat)
164 names.sort()
165 subdirs = [os.pardir]
166 matchingfiles = []
167 for name in names:
168 fullname = os.path.join(dir, name)
169 if os.path.isdir(fullname):
170 subdirs.append(name)
171 elif fnmatch.fnmatch(name, pat):
172 matchingfiles.append(name)
173 self.dirs.delete(0, END)
174 for name in subdirs:
175 self.dirs.insert(END, name)
176 self.files.delete(0, END)
177 for name in matchingfiles:
178 self.files.insert(END, name)
Guido van Rossum0978f991996-05-28 23:14:36 +0000179 head, tail = os.path.split(self.get_selection())
Guido van Rossumb9e39c81995-09-01 20:36:47 +0000180 if tail == os.curdir: tail = ''
181 self.set_selection(tail)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000182
183 def get_filter(self):
184 filter = self.filter.get()
Guido van Rossum0978f991996-05-28 23:14:36 +0000185 filter = os.path.expanduser(filter)
186 if filter[-1:] == os.sep or os.path.isdir(filter):
187 filter = os.path.join(filter, "*")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000188 return os.path.split(filter)
189
Guido van Rossum0978f991996-05-28 23:14:36 +0000190 def get_selection(self):
191 file = self.selection.get()
192 file = os.path.expanduser(file)
193 return file
194
195 def cancel_command(self, event=None):
196 self.quit()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000197
198 def set_filter(self, dir, pat):
Guido van Rossum0978f991996-05-28 23:14:36 +0000199 if not os.path.isabs(dir):
200 try:
201 pwd = os.getcwd()
202 except os.error:
203 pwd = None
204 if pwd:
205 dir = os.path.join(pwd, dir)
206 dir = os.path.normpath(dir)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000207 self.filter.delete(0, END)
208 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
209
210 def set_selection(self, file):
211 self.selection.delete(0, END)
212 self.selection.insert(END, os.path.join(self.directory, file))
213
214
215class LoadFileDialog(FileDialog):
216
217 """File selection dialog which checks that the file exists."""
218
219 title = "Load File Selection Dialog"
220
221 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000222 file = self.get_selection()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000223 if not os.path.isfile(file):
224 self.master.bell()
225 else:
Guido van Rossum0978f991996-05-28 23:14:36 +0000226 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000227
228
229class SaveFileDialog(FileDialog):
230
231 """File selection dialog which checks that the file may be created."""
232
233 title = "Save File Selection Dialog"
234
235 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000236 file = self.get_selection()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000237 if os.path.exists(file):
238 if os.path.isdir(file):
239 self.master.bell()
240 return
Guido van Rossum0978f991996-05-28 23:14:36 +0000241 d = Dialog(self.top,
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000242 title="Overwrite Existing File Question",
243 text="Overwrite existing file %s?" % `file`,
244 bitmap='questhead',
Guido van Rossum0978f991996-05-28 23:14:36 +0000245 default=1,
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000246 strings=("Yes", "Cancel"))
Guido van Rossum0978f991996-05-28 23:14:36 +0000247 if d.num != 0:
248 return
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000249 else:
250 head, tail = os.path.split(file)
251 if not os.path.isdir(head):
252 self.master.bell()
253 return
Guido van Rossum0978f991996-05-28 23:14:36 +0000254 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000255
256
257def test():
258 """Simple test program."""
259 root = Tk()
260 root.withdraw()
261 fd = LoadFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000262 loadfile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000263 fd = SaveFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000264 savefile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000265 print loadfile, savefile
266
267
268if __name__ == '__main__':
269 test()