blob: 9ded88b553e0074b777c47ce4e53505fa8063e4a [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)
Andrew M. Kuchling38752812003-01-21 14:19:21 +000028 fname = d.go(dir_or_file, pattern, default, key)
29 if fname is None: ...canceled...
Guido van Rossumc4570481998-03-20 20:45:49 +000030 else: ...open file...
Guido van Rossum0978f991996-05-28 23:14:36 +000031
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
Thomas Wouters7e474022000-07-16 12:04:32 +000039 kept even when the dialog is canceled. (All this emulates the
Guido van Rossum0978f991996-05-28 23:14:36 +000040 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):
Guido van Rossumc4570481998-03-20 20:45:49 +000047 if title is None: title = self.title
48 self.master = master
49 self.directory = None
Guido van Rossum0978f991996-05-28 23:14:36 +000050
Guido van Rossumc4570481998-03-20 20:45:49 +000051 self.top = Toplevel(master)
52 self.top.title(title)
53 self.top.iconname(title)
Guido van Rossum0978f991996-05-28 23:14:36 +000054
Guido van Rossumc4570481998-03-20 20:45:49 +000055 self.botframe = Frame(self.top)
56 self.botframe.pack(side=BOTTOM, fill=X)
Guido van Rossum0978f991996-05-28 23:14:36 +000057
Guido van Rossumc4570481998-03-20 20:45:49 +000058 self.selection = Entry(self.top)
59 self.selection.pack(side=BOTTOM, fill=X)
60 self.selection.bind('<Return>', self.ok_event)
Guido van Rossum0978f991996-05-28 23:14:36 +000061
Guido van Rossumc4570481998-03-20 20:45:49 +000062 self.filter = Entry(self.top)
63 self.filter.pack(side=TOP, fill=X)
64 self.filter.bind('<Return>', self.filter_command)
Guido van Rossum0978f991996-05-28 23:14:36 +000065
Guido van Rossumc4570481998-03-20 20:45:49 +000066 self.midframe = Frame(self.top)
67 self.midframe.pack(expand=YES, fill=BOTH)
Guido van Rossum0978f991996-05-28 23:14:36 +000068
Guido van Rossumc4570481998-03-20 20:45:49 +000069 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'))
73 self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
74 btags = self.files.bindtags()
75 self.files.bindtags(btags[1:] + btags[:1])
76 self.files.bind('<ButtonRelease-1>', self.files_select_event)
77 self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
78 self.filesbar.config(command=(self.files, 'yview'))
Guido van Rossum0978f991996-05-28 23:14:36 +000079
Guido van Rossumc4570481998-03-20 20:45:49 +000080 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'))
86 btags = self.dirs.bindtags()
87 self.dirs.bindtags(btags[1:] + btags[:1])
88 self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
89 self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
Guido van Rossum0978f991996-05-28 23:14:36 +000090
Guido van Rossumc4570481998-03-20 20:45:49 +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)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000103
Guido van Rossumc4570481998-03-20 20:45:49 +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)
Guido van Rossum0978f991996-05-28 23:14:36 +0000108
109 def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
Guido van Rossume014a132006-08-19 16:53:45 +0000110 if key and key in dialogstates:
Guido van Rossumc4570481998-03-20 20:45:49 +0000111 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)
119 self.set_selection(default)
120 self.filter_command()
121 self.selection.focus_set()
Martin v. Löwisb217cd82004-08-03 18:36:25 +0000122 self.top.wait_visibility() # window needs to be visible for the grab
Guido van Rossumc4570481998-03-20 20:45:49 +0000123 self.top.grab_set()
124 self.how = None
125 self.master.mainloop() # Exited by self.quit(how)
Fred Drake073b8291998-05-06 17:28:23 +0000126 if key:
127 directory, pattern = self.get_filter()
128 if self.how:
129 directory = os.path.dirname(self.how)
130 dialogstates[key] = directory, pattern
Guido van Rossumc4570481998-03-20 20:45:49 +0000131 self.top.destroy()
132 return self.how
Guido van Rossum0978f991996-05-28 23:14:36 +0000133
134 def quit(self, how=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000135 self.how = how
136 self.master.quit() # Exit mainloop()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000137
138 def dirs_double_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000139 self.filter_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000140
141 def dirs_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000142 dir, pat = self.get_filter()
143 subdir = self.dirs.get('active')
144 dir = os.path.normpath(os.path.join(self.directory, subdir))
145 self.set_filter(dir, pat)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000146
147 def files_double_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000148 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000149
150 def files_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000151 file = self.files.get('active')
152 self.set_selection(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000153
154 def ok_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000155 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000156
157 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000158 self.quit(self.get_selection())
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000159
160 def filter_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000161 dir, pat = self.get_filter()
162 try:
163 names = os.listdir(dir)
164 except os.error:
165 self.master.bell()
166 return
167 self.directory = dir
168 self.set_filter(dir, pat)
169 names.sort()
170 subdirs = [os.pardir]
171 matchingfiles = []
172 for name in names:
173 fullname = os.path.join(dir, name)
174 if os.path.isdir(fullname):
175 subdirs.append(name)
176 elif fnmatch.fnmatch(name, pat):
177 matchingfiles.append(name)
178 self.dirs.delete(0, END)
179 for name in subdirs:
180 self.dirs.insert(END, name)
181 self.files.delete(0, END)
182 for name in matchingfiles:
183 self.files.insert(END, name)
184 head, tail = os.path.split(self.get_selection())
185 if tail == os.curdir: tail = ''
186 self.set_selection(tail)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000187
188 def get_filter(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000189 filter = self.filter.get()
190 filter = os.path.expanduser(filter)
191 if filter[-1:] == os.sep or os.path.isdir(filter):
192 filter = os.path.join(filter, "*")
193 return os.path.split(filter)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000194
Guido van Rossum0978f991996-05-28 23:14:36 +0000195 def get_selection(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000196 file = self.selection.get()
197 file = os.path.expanduser(file)
198 return file
Guido van Rossum0978f991996-05-28 23:14:36 +0000199
200 def cancel_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000201 self.quit()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000202
203 def set_filter(self, dir, pat):
Guido van Rossumc4570481998-03-20 20:45:49 +0000204 if not os.path.isabs(dir):
205 try:
206 pwd = os.getcwd()
207 except os.error:
208 pwd = None
209 if pwd:
210 dir = os.path.join(pwd, dir)
211 dir = os.path.normpath(dir)
212 self.filter.delete(0, END)
213 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000214
215 def set_selection(self, file):
Guido van Rossumc4570481998-03-20 20:45:49 +0000216 self.selection.delete(0, END)
217 self.selection.insert(END, os.path.join(self.directory, file))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000218
219
220class LoadFileDialog(FileDialog):
221
222 """File selection dialog which checks that the file exists."""
223
224 title = "Load File Selection Dialog"
225
226 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000227 file = self.get_selection()
228 if not os.path.isfile(file):
229 self.master.bell()
230 else:
231 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000232
233
234class SaveFileDialog(FileDialog):
235
236 """File selection dialog which checks that the file may be created."""
237
238 title = "Save File Selection Dialog"
239
240 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000241 file = self.get_selection()
242 if os.path.exists(file):
243 if os.path.isdir(file):
244 self.master.bell()
245 return
246 d = Dialog(self.top,
247 title="Overwrite Existing File Question",
Walter Dörwald70a6b492004-02-12 17:35:32 +0000248 text="Overwrite existing file %r?" % (file,),
Guido van Rossumc4570481998-03-20 20:45:49 +0000249 bitmap='questhead',
250 default=1,
251 strings=("Yes", "Cancel"))
252 if d.num != 0:
253 return
254 else:
255 head, tail = os.path.split(file)
256 if not os.path.isdir(head):
257 self.master.bell()
258 return
259 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000260
261
262def test():
263 """Simple test program."""
264 root = Tk()
265 root.withdraw()
266 fd = LoadFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000267 loadfile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000268 fd = SaveFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000269 savefile = fd.go(key="test")
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000270 print(loadfile, savefile)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000271
272
273if __name__ == '__main__':
274 test()