blob: 76271af5d50ed858feadc225d95c3a955024f69a [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
14ANCHOR = 'anchor'
15
16import os
17import fnmatch
18
19
Guido van Rossum0978f991996-05-28 23:14:36 +000020dialogstates = {}
21
22
Guido van Rossumac4f8d31995-08-29 23:46:35 +000023class FileDialog:
24
25 """Standard file selection dialog -- no checks on selected file.
26
27 Usage:
28
29 d = FileDialog(master)
Guido van Rossum0978f991996-05-28 23:14:36 +000030 file = d.go(dir_or_file, pattern, default, key)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000031 if file is None: ...canceled...
Guido van Rossum0978f991996-05-28 23:14:36 +000032 else: ...open file...
33
34 All arguments to go() are optional.
35
36 The 'key' argument specifies a key in the global dictionary
37 'dialogstates', which keeps track of the values for the directory
38 and pattern arguments, overriding the values passed in (it does
39 not keep track of the default argument!). If no key is specified,
40 the dialog keeps no memory of previous state. Note that memory is
41 kept even when the dialog is cancelled. (All this emulates the
42 behavior of the Macintosh file selection dialogs.)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000043
44 """
45
46 title = "File Selection Dialog"
47
Guido van Rossum0978f991996-05-28 23:14:36 +000048 def __init__(self, master, title=None):
49 if title is None: title = self.title
Guido van Rossumac4f8d31995-08-29 23:46:35 +000050 self.master = master
51 self.directory = None
Guido van Rossum0978f991996-05-28 23:14:36 +000052
Guido van Rossumac4f8d31995-08-29 23:46:35 +000053 self.top = Toplevel(master)
Guido van Rossum0978f991996-05-28 23:14:36 +000054 self.top.title(title)
55 self.top.iconname(title)
56
57 self.botframe = Frame(self.top)
58 self.botframe.pack(side=BOTTOM, fill=X)
59
60 self.selection = Entry(self.top)
61 self.selection.pack(side=BOTTOM, fill=X)
62 self.selection.bind('<Return>', self.ok_event)
63
Guido van Rossumac4f8d31995-08-29 23:46:35 +000064 self.filter = Entry(self.top)
Guido van Rossum0978f991996-05-28 23:14:36 +000065 self.filter.pack(side=TOP, fill=X)
Guido van Rossumac4f8d31995-08-29 23:46:35 +000066 self.filter.bind('<Return>', self.filter_command)
Guido van Rossum0978f991996-05-28 23:14:36 +000067
Guido van Rossumac4f8d31995-08-29 23:46:35 +000068 self.midframe = Frame(self.top)
69 self.midframe.pack(expand=YES, fill=BOTH)
Guido van Rossum0978f991996-05-28 23:14:36 +000070
71 self.filesbar = Scrollbar(self.midframe)
72 self.filesbar.pack(side=RIGHT, fill=Y)
73 self.files = Listbox(self.midframe, exportselection=0,
74 yscrollcommand=(self.filesbar, 'set'))
Guido van Rossumac4f8d31995-08-29 23:46:35 +000075 self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
76 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'))
86 self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
87 self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
88
Guido van Rossumac4f8d31995-08-29 23:46:35 +000089 self.ok_button = Button(self.botframe,
90 text="OK",
91 command=self.ok_command)
92 self.ok_button.pack(side=LEFT)
93 self.filter_button = Button(self.botframe,
94 text="Filter",
95 command=self.filter_command)
96 self.filter_button.pack(side=LEFT, expand=YES)
97 self.cancel_button = Button(self.botframe,
98 text="Cancel",
99 command=self.cancel_command)
100 self.cancel_button.pack(side=RIGHT)
101
Guido van Rossum0978f991996-05-28 23:14:36 +0000102 self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
103 # XXX Are the following okay for a general audience?
104 self.top.bind('<Alt-w>', self.cancel_command)
105 self.top.bind('<Alt-W>', self.cancel_command)
106
107 def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
108 if key and dialogstates.has_key(key):
109 self.directory, pattern = dialogstates[key]
110 else:
111 dir_or_file = os.path.expanduser(dir_or_file)
112 if os.path.isdir(dir_or_file):
113 self.directory = dir_or_file
114 else:
115 self.directory, default = os.path.split(dir_or_file)
116 self.set_filter(self.directory, pattern)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000117 self.set_selection(default)
Guido van Rossum0978f991996-05-28 23:14:36 +0000118 self.filter_command()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000119 self.selection.focus_set()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000120 self.top.grab_set()
Guido van Rossum0978f991996-05-28 23:14:36 +0000121 self.how = None
122 self.master.mainloop() # Exited by self.quit(how)
123 if key: dialogstates[key] = self.get_filter()
124 self.top.destroy()
125 return self.how
126
127 def quit(self, how=None):
128 self.how = how
129 self.master.quit() # Exit mainloop()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000130
131 def dirs_double_event(self, event):
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000132 self.filter_command()
133
134 def dirs_select_event(self, event):
135 dir, pat = self.get_filter()
136 subdir = self.dirs.get(ANCHOR)
137 dir = os.path.normpath(os.path.join(self.directory, subdir))
138 self.set_filter(dir, pat)
139
140 def files_double_event(self, event):
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000141 self.ok_command()
142
143 def files_select_event(self, event):
144 file = self.files.get(ANCHOR)
145 self.set_selection(file)
146
147 def ok_event(self, event):
148 self.ok_command()
149
150 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000151 self.quit(self.get_selection())
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000152
153 def filter_command(self, event=None):
154 dir, pat = self.get_filter()
155 try:
156 names = os.listdir(dir)
157 except os.error:
158 self.master.bell()
159 return
160 self.directory = dir
161 self.set_filter(dir, pat)
162 names.sort()
163 subdirs = [os.pardir]
164 matchingfiles = []
165 for name in names:
166 fullname = os.path.join(dir, name)
167 if os.path.isdir(fullname):
168 subdirs.append(name)
169 elif fnmatch.fnmatch(name, pat):
170 matchingfiles.append(name)
171 self.dirs.delete(0, END)
172 for name in subdirs:
173 self.dirs.insert(END, name)
174 self.files.delete(0, END)
175 for name in matchingfiles:
176 self.files.insert(END, name)
Guido van Rossum0978f991996-05-28 23:14:36 +0000177 head, tail = os.path.split(self.get_selection())
Guido van Rossumb9e39c81995-09-01 20:36:47 +0000178 if tail == os.curdir: tail = ''
179 self.set_selection(tail)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000180
181 def get_filter(self):
182 filter = self.filter.get()
Guido van Rossum0978f991996-05-28 23:14:36 +0000183 filter = os.path.expanduser(filter)
184 if filter[-1:] == os.sep or os.path.isdir(filter):
185 filter = os.path.join(filter, "*")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000186 return os.path.split(filter)
187
Guido van Rossum0978f991996-05-28 23:14:36 +0000188 def get_selection(self):
189 file = self.selection.get()
190 file = os.path.expanduser(file)
191 return file
192
193 def cancel_command(self, event=None):
194 self.quit()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000195
196 def set_filter(self, dir, pat):
Guido van Rossum0978f991996-05-28 23:14:36 +0000197 if not os.path.isabs(dir):
198 try:
199 pwd = os.getcwd()
200 except os.error:
201 pwd = None
202 if pwd:
203 dir = os.path.join(pwd, dir)
204 dir = os.path.normpath(dir)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000205 self.filter.delete(0, END)
206 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
207
208 def set_selection(self, file):
209 self.selection.delete(0, END)
210 self.selection.insert(END, os.path.join(self.directory, file))
211
212
213class LoadFileDialog(FileDialog):
214
215 """File selection dialog which checks that the file exists."""
216
217 title = "Load File Selection Dialog"
218
219 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000220 file = self.get_selection()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000221 if not os.path.isfile(file):
222 self.master.bell()
223 else:
Guido van Rossum0978f991996-05-28 23:14:36 +0000224 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000225
226
227class SaveFileDialog(FileDialog):
228
229 """File selection dialog which checks that the file may be created."""
230
231 title = "Save File Selection Dialog"
232
233 def ok_command(self):
Guido van Rossum0978f991996-05-28 23:14:36 +0000234 file = self.get_selection()
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000235 if os.path.exists(file):
236 if os.path.isdir(file):
237 self.master.bell()
238 return
Guido van Rossum0978f991996-05-28 23:14:36 +0000239 d = Dialog(self.top,
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000240 title="Overwrite Existing File Question",
241 text="Overwrite existing file %s?" % `file`,
242 bitmap='questhead',
Guido van Rossum0978f991996-05-28 23:14:36 +0000243 default=1,
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000244 strings=("Yes", "Cancel"))
Guido van Rossum0978f991996-05-28 23:14:36 +0000245 if d.num != 0:
246 return
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000247 else:
248 head, tail = os.path.split(file)
249 if not os.path.isdir(head):
250 self.master.bell()
251 return
Guido van Rossum0978f991996-05-28 23:14:36 +0000252 self.quit(file)
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000253
254
255def test():
256 """Simple test program."""
257 root = Tk()
258 root.withdraw()
259 fd = LoadFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000260 loadfile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000261 fd = SaveFileDialog(root)
Guido van Rossum0978f991996-05-28 23:14:36 +0000262 savefile = fd.go(key="test")
Guido van Rossumac4f8d31995-08-29 23:46:35 +0000263 print loadfile, savefile
264
265
266if __name__ == '__main__':
267 test()