blob: bd62c6d2a21bbab1a3f648386b8ab283025d9f08 [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 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 Rossumc4570481998-03-20 20:45:49 +0000110 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)
119 self.set_selection(default)
120 self.filter_command()
121 self.selection.focus_set()
122 self.top.grab_set()
123 self.how = None
124 self.master.mainloop() # Exited by self.quit(how)
Fred Drake073b8291998-05-06 17:28:23 +0000125 if key:
126 directory, pattern = self.get_filter()
127 if self.how:
128 directory = os.path.dirname(self.how)
129 dialogstates[key] = directory, pattern
Guido van Rossumc4570481998-03-20 20:45:49 +0000130 self.top.destroy()
131 return self.how
Guido van Rossum0978f991996-05-28 23:14:36 +0000132
133 def quit(self, how=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000134 self.how = how
135 self.master.quit() # Exit mainloop()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000136
137 def dirs_double_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000138 self.filter_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000139
140 def dirs_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000141 dir, pat = self.get_filter()
142 subdir = self.dirs.get('active')
143 dir = os.path.normpath(os.path.join(self.directory, subdir))
144 self.set_filter(dir, pat)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000145
146 def files_double_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000147 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000148
149 def files_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000150 file = self.files.get('active')
151 self.set_selection(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000152
153 def ok_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000154 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000155
156 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000157 self.quit(self.get_selection())
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000158
159 def filter_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000160 dir, pat = self.get_filter()
161 try:
162 names = os.listdir(dir)
163 except os.error:
164 self.master.bell()
165 return
166 self.directory = dir
167 self.set_filter(dir, pat)
168 names.sort()
169 subdirs = [os.pardir]
170 matchingfiles = []
171 for name in names:
172 fullname = os.path.join(dir, name)
173 if os.path.isdir(fullname):
174 subdirs.append(name)
175 elif fnmatch.fnmatch(name, pat):
176 matchingfiles.append(name)
177 self.dirs.delete(0, END)
178 for name in subdirs:
179 self.dirs.insert(END, name)
180 self.files.delete(0, END)
181 for name in matchingfiles:
182 self.files.insert(END, name)
183 head, tail = os.path.split(self.get_selection())
184 if tail == os.curdir: tail = ''
185 self.set_selection(tail)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000186
187 def get_filter(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000188 filter = self.filter.get()
189 filter = os.path.expanduser(filter)
190 if filter[-1:] == os.sep or os.path.isdir(filter):
191 filter = os.path.join(filter, "*")
192 return os.path.split(filter)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000193
Guido van Rossum0978f991996-05-28 23:14:36 +0000194 def get_selection(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000195 file = self.selection.get()
196 file = os.path.expanduser(file)
197 return file
Guido van Rossum0978f991996-05-28 23:14:36 +0000198
199 def cancel_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000200 self.quit()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000201
202 def set_filter(self, dir, pat):
Guido van Rossumc4570481998-03-20 20:45:49 +0000203 if not os.path.isabs(dir):
204 try:
205 pwd = os.getcwd()
206 except os.error:
207 pwd = None
208 if pwd:
209 dir = os.path.join(pwd, dir)
210 dir = os.path.normpath(dir)
211 self.filter.delete(0, END)
212 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000213
214 def set_selection(self, file):
Guido van Rossumc4570481998-03-20 20:45:49 +0000215 self.selection.delete(0, END)
216 self.selection.insert(END, os.path.join(self.directory, file))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000217
218
219class LoadFileDialog(FileDialog):
220
221 """File selection dialog which checks that the file exists."""
222
223 title = "Load File Selection Dialog"
224
225 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000226 file = self.get_selection()
227 if not os.path.isfile(file):
228 self.master.bell()
229 else:
230 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000231
232
233class SaveFileDialog(FileDialog):
234
235 """File selection dialog which checks that the file may be created."""
236
237 title = "Save File Selection Dialog"
238
239 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000240 file = self.get_selection()
241 if os.path.exists(file):
242 if os.path.isdir(file):
243 self.master.bell()
244 return
245 d = Dialog(self.top,
246 title="Overwrite Existing File Question",
247 text="Overwrite existing file %s?" % `file`,
248 bitmap='questhead',
249 default=1,
250 strings=("Yes", "Cancel"))
251 if d.num != 0:
252 return
253 else:
254 head, tail = os.path.split(file)
255 if not os.path.isdir(head):
256 self.master.bell()
257 return
258 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000259
260
261def test():
262 """Simple test program."""
263 root = Tk()
264 root.withdraw()
265 fd = LoadFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000266 loadfile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000267 fd = SaveFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000268 savefile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000269 print loadfile, savefile
270
271
272if __name__ == '__main__':
273 test()