Fix an amazing number of typos & malformed sentences reported by Detlef
[python/dscho.git] / Lib / lib-tk / FileDialog.py
blob74e9741251311257c1d6ed9d5edf7637e6e1f6ce
1 """File selection dialog classes.
3 Classes:
5 - FileDialog
6 - LoadFileDialog
7 - SaveFileDialog
9 """
11 from Tkinter import *
12 from Dialog import Dialog
14 import os
15 import fnmatch
18 dialogstates = {}
21 class FileDialog:
23 """Standard file selection dialog -- no checks on selected file.
25 Usage:
27 d = FileDialog(master)
28 file = d.go(dir_or_file, pattern, default, key)
29 if file is None: ...canceled...
30 else: ...open file...
32 All arguments to go() are optional.
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.)
42 """
44 title = "File Selection Dialog"
46 def __init__(self, master, title=None):
47 if title is None: title = self.title
48 self.master = master
49 self.directory = None
51 self.top = Toplevel(master)
52 self.top.title(title)
53 self.top.iconname(title)
55 self.botframe = Frame(self.top)
56 self.botframe.pack(side=BOTTOM, fill=X)
58 self.selection = Entry(self.top)
59 self.selection.pack(side=BOTTOM, fill=X)
60 self.selection.bind('<Return>', self.ok_event)
62 self.filter = Entry(self.top)
63 self.filter.pack(side=TOP, fill=X)
64 self.filter.bind('<Return>', self.filter_command)
66 self.midframe = Frame(self.top)
67 self.midframe.pack(expand=YES, fill=BOTH)
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'))
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'))
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 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)
91 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)
104 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)
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)
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:
126 directory, pattern = self.get_filter()
127 if self.how:
128 directory = os.path.dirname(self.how)
129 dialogstates[key] = directory, pattern
130 self.top.destroy()
131 return self.how
133 def quit(self, how=None):
134 self.how = how
135 self.master.quit() # Exit mainloop()
137 def dirs_double_event(self, event):
138 self.filter_command()
140 def dirs_select_event(self, event):
141 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)
146 def files_double_event(self, event):
147 self.ok_command()
149 def files_select_event(self, event):
150 file = self.files.get('active')
151 self.set_selection(file)
153 def ok_event(self, event):
154 self.ok_command()
156 def ok_command(self):
157 self.quit(self.get_selection())
159 def filter_command(self, event=None):
160 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)
187 def get_filter(self):
188 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)
194 def get_selection(self):
195 file = self.selection.get()
196 file = os.path.expanduser(file)
197 return file
199 def cancel_command(self, event=None):
200 self.quit()
202 def set_filter(self, dir, pat):
203 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 "*"))
214 def set_selection(self, file):
215 self.selection.delete(0, END)
216 self.selection.insert(END, os.path.join(self.directory, file))
219 class LoadFileDialog(FileDialog):
221 """File selection dialog which checks that the file exists."""
223 title = "Load File Selection Dialog"
225 def ok_command(self):
226 file = self.get_selection()
227 if not os.path.isfile(file):
228 self.master.bell()
229 else:
230 self.quit(file)
233 class SaveFileDialog(FileDialog):
235 """File selection dialog which checks that the file may be created."""
237 title = "Save File Selection Dialog"
239 def ok_command(self):
240 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)
261 def test():
262 """Simple test program."""
263 root = Tk()
264 root.withdraw()
265 fd = LoadFileDialog(root)
266 loadfile = fd.go(key="test")
267 fd = SaveFileDialog(root)
268 savefile = fd.go(key="test")
269 print loadfile, savefile
272 if __name__ == '__main__':
273 test()