blob: d3c96b8a00fd8f5ac1016f9f543e57b0c855c5c7 [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
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):
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)
125 if key: dialogstates[key] = self.get_filter()
126 self.top.destroy()
127 return self.how
Guido van Rossum0978f991996-05-28 23:14:36 +0000128
129 def quit(self, how=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000130 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 Rossumc4570481998-03-20 20:45:49 +0000134 self.filter_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000135
136 def dirs_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000137 dir, pat = self.get_filter()
138 subdir = self.dirs.get('active')
139 dir = os.path.normpath(os.path.join(self.directory, subdir))
140 self.set_filter(dir, pat)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000141
142 def files_double_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000143 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000144
145 def files_select_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000146 file = self.files.get('active')
147 self.set_selection(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000148
149 def ok_event(self, event):
Guido van Rossumc4570481998-03-20 20:45:49 +0000150 self.ok_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000151
152 def ok_command(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000153 self.quit(self.get_selection())
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000154
155 def filter_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000156 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)
179 head, tail = os.path.split(self.get_selection())
180 if tail == os.curdir: tail = ''
181 self.set_selection(tail)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000182
183 def get_filter(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000184 filter = self.filter.get()
185 filter = os.path.expanduser(filter)
186 if filter[-1:] == os.sep or os.path.isdir(filter):
187 filter = os.path.join(filter, "*")
188 return os.path.split(filter)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000189
Guido van Rossum0978f991996-05-28 23:14:36 +0000190 def get_selection(self):
Guido van Rossumc4570481998-03-20 20:45:49 +0000191 file = self.selection.get()
192 file = os.path.expanduser(file)
193 return file
Guido van Rossum0978f991996-05-28 23:14:36 +0000194
195 def cancel_command(self, event=None):
Guido van Rossumc4570481998-03-20 20:45:49 +0000196 self.quit()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000197
198 def set_filter(self, dir, pat):
Guido van Rossumc4570481998-03-20 20:45:49 +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)
207 self.filter.delete(0, END)
208 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000209
210 def set_selection(self, file):
Guido van Rossumc4570481998-03-20 20:45:49 +0000211 self.selection.delete(0, END)
212 self.selection.insert(END, os.path.join(self.directory, file))
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000213
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 Rossumc4570481998-03-20 20:45:49 +0000222 file = self.get_selection()
223 if not os.path.isfile(file):
224 self.master.bell()
225 else:
226 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 Rossumc4570481998-03-20 20:45:49 +0000236 file = self.get_selection()
237 if os.path.exists(file):
238 if os.path.isdir(file):
239 self.master.bell()
240 return
241 d = Dialog(self.top,
242 title="Overwrite Existing File Question",
243 text="Overwrite existing file %s?" % `file`,
244 bitmap='questhead',
245 default=1,
246 strings=("Yes", "Cancel"))
247 if d.num != 0:
248 return
249 else:
250 head, tail = os.path.split(file)
251 if not os.path.isdir(head):
252 self.master.bell()
253 return
254 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()