0001 """File selection dialog classes. 0002 0003 Classes: 0004 0005 - FileDialog 0006 - LoadFileDialog 0007 - SaveFileDialog 0008 0009 """ 0010 0011 from Tkinter import * 0012 from Dialog import Dialog 0013 0014 import os 0015 import fnmatch 0016 0017 0018 dialogstates = {} 0019 0020 0021 class FileDialog: 0022 0023 """Standard file selection dialog -- no checks on selected file. 0024 0025 Usage: 0026 0027 d = FileDialog(master) 0028 fname = d.go(dir_or_file, pattern, default, key) 0029 if fname is None: ...canceled... 0030 else: ...open file... 0031 0032 All arguments to go() are optional. 0033 0034 The 'key' argument specifies a key in the global dictionary 0035 'dialogstates', which keeps track of the values for the directory 0036 and pattern arguments, overriding the values passed in (it does 0037 not keep track of the default argument!). If no key is specified, 0038 the dialog keeps no memory of previous state. Note that memory is 0039 kept even when the dialog is canceled. (All this emulates the 0040 behavior of the Macintosh file selection dialogs.) 0041 0042 """ 0043 0044 title = "File Selection Dialog" 0045 0046 def __init__(self, master, title=None): 0047 if title is None: title = self.title 0048 self.master = master 0049 self.directory = None 0050 0051 self.top = Toplevel(master) 0052 self.top.title(title) 0053 self.top.iconname(title) 0054 0055 self.botframe = Frame(self.top) 0056 self.botframe.pack(side=BOTTOM, fill=X) 0057 0058 self.selection = Entry(self.top) 0059 self.selection.pack(side=BOTTOM, fill=X) 0060 self.selection.bind('<Return>', self.ok_event) 0061 0062 self.filter = Entry(self.top) 0063 self.filter.pack(side=TOP, fill=X) 0064 self.filter.bind('<Return>', self.filter_command) 0065 0066 self.midframe = Frame(self.top) 0067 self.midframe.pack(expand=YES, fill=BOTH) 0068 0069 self.filesbar = Scrollbar(self.midframe) 0070 self.filesbar.pack(side=RIGHT, fill=Y) 0071 self.files = Listbox(self.midframe, exportselection=0, 0072 yscrollcommand=(self.filesbar, 'set')) 0073 self.files.pack(side=RIGHT, expand=YES, fill=BOTH) 0074 btags = self.files.bindtags() 0075 self.files.bindtags(btags[1:] + btags[:1]) 0076 self.files.bind('<ButtonRelease-1>', self.files_select_event) 0077 self.files.bind('<Double-ButtonRelease-1>', self.files_double_event) 0078 self.filesbar.config(command=(self.files, 'yview')) 0079 0080 self.dirsbar = Scrollbar(self.midframe) 0081 self.dirsbar.pack(side=LEFT, fill=Y) 0082 self.dirs = Listbox(self.midframe, exportselection=0, 0083 yscrollcommand=(self.dirsbar, 'set')) 0084 self.dirs.pack(side=LEFT, expand=YES, fill=BOTH) 0085 self.dirsbar.config(command=(self.dirs, 'yview')) 0086 btags = self.dirs.bindtags() 0087 self.dirs.bindtags(btags[1:] + btags[:1]) 0088 self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event) 0089 self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event) 0090 0091 self.ok_button = Button(self.botframe, 0092 text="OK", 0093 command=self.ok_command) 0094 self.ok_button.pack(side=LEFT) 0095 self.filter_button = Button(self.botframe, 0096 text="Filter", 0097 command=self.filter_command) 0098 self.filter_button.pack(side=LEFT, expand=YES) 0099 self.cancel_button = Button(self.botframe, 0100 text="Cancel", 0101 command=self.cancel_command) 0102 self.cancel_button.pack(side=RIGHT) 0103 0104 self.top.protocol('WM_DELETE_WINDOW', self.cancel_command) 0105 # XXX Are the following okay for a general audience? 0106 self.top.bind('<Alt-w>', self.cancel_command) 0107 self.top.bind('<Alt-W>', self.cancel_command) 0108 0109 def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None): 0110 if key and dialogstates.has_key(key): 0111 self.directory, pattern = dialogstates[key] 0112 else: 0113 dir_or_file = os.path.expanduser(dir_or_file) 0114 if os.path.isdir(dir_or_file): 0115 self.directory = dir_or_file 0116 else: 0117 self.directory, default = os.path.split(dir_or_file) 0118 self.set_filter(self.directory, pattern) 0119 self.set_selection(default) 0120 self.filter_command() 0121 self.selection.focus_set() 0122 self.top.wait_visibility() # window needs to be visible for the grab 0123 self.top.grab_set() 0124 self.how = None 0125 self.master.mainloop() # Exited by self.quit(how) 0126 if key: 0127 directory, pattern = self.get_filter() 0128 if self.how: 0129 directory = os.path.dirname(self.how) 0130 dialogstates[key] = directory, pattern 0131 self.top.destroy() 0132 return self.how 0133 0134 def quit(self, how=None): 0135 self.how = how 0136 self.master.quit() # Exit mainloop() 0137 0138 def dirs_double_event(self, event): 0139 self.filter_command() 0140 0141 def dirs_select_event(self, event): 0142 dir, pat = self.get_filter() 0143 subdir = self.dirs.get('active') 0144 dir = os.path.normpath(os.path.join(self.directory, subdir)) 0145 self.set_filter(dir, pat) 0146 0147 def files_double_event(self, event): 0148 self.ok_command() 0149 0150 def files_select_event(self, event): 0151 file = self.files.get('active') 0152 self.set_selection(file) 0153 0154 def ok_event(self, event): 0155 self.ok_command() 0156 0157 def ok_command(self): 0158 self.quit(self.get_selection()) 0159 0160 def filter_command(self, event=None): 0161 dir, pat = self.get_filter() 0162 try: 0163 names = os.listdir(dir) 0164 except os.error: 0165 self.master.bell() 0166 return 0167 self.directory = dir 0168 self.set_filter(dir, pat) 0169 names.sort() 0170 subdirs = [os.pardir] 0171 matchingfiles = [] 0172 for name in names: 0173 fullname = os.path.join(dir, name) 0174 if os.path.isdir(fullname): 0175 subdirs.append(name) 0176 elif fnmatch.fnmatch(name, pat): 0177 matchingfiles.append(name) 0178 self.dirs.delete(0, END) 0179 for name in subdirs: 0180 self.dirs.insert(END, name) 0181 self.files.delete(0, END) 0182 for name in matchingfiles: 0183 self.files.insert(END, name) 0184 head, tail = os.path.split(self.get_selection()) 0185 if tail == os.curdir: tail = '' 0186 self.set_selection(tail) 0187 0188 def get_filter(self): 0189 filter = self.filter.get() 0190 filter = os.path.expanduser(filter) 0191 if filter[-1:] == os.sep or os.path.isdir(filter): 0192 filter = os.path.join(filter, "*") 0193 return os.path.split(filter) 0194 0195 def get_selection(self): 0196 file = self.selection.get() 0197 file = os.path.expanduser(file) 0198 return file 0199 0200 def cancel_command(self, event=None): 0201 self.quit() 0202 0203 def set_filter(self, dir, pat): 0204 if not os.path.isabs(dir): 0205 try: 0206 pwd = os.getcwd() 0207 except os.error: 0208 pwd = None 0209 if pwd: 0210 dir = os.path.join(pwd, dir) 0211 dir = os.path.normpath(dir) 0212 self.filter.delete(0, END) 0213 self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*")) 0214 0215 def set_selection(self, file): 0216 self.selection.delete(0, END) 0217 self.selection.insert(END, os.path.join(self.directory, file)) 0218 0219 0220 class LoadFileDialog(FileDialog): 0221 0222 """File selection dialog which checks that the file exists.""" 0223 0224 title = "Load File Selection Dialog" 0225 0226 def ok_command(self): 0227 file = self.get_selection() 0228 if not os.path.isfile(file): 0229 self.master.bell() 0230 else: 0231 self.quit(file) 0232 0233 0234 class SaveFileDialog(FileDialog): 0235 0236 """File selection dialog which checks that the file may be created.""" 0237 0238 title = "Save File Selection Dialog" 0239 0240 def ok_command(self): 0241 file = self.get_selection() 0242 if os.path.exists(file): 0243 if os.path.isdir(file): 0244 self.master.bell() 0245 return 0246 d = Dialog(self.top, 0247 title="Overwrite Existing File Question", 0248 text="Overwrite existing file %r?" % (file,), 0249 bitmap='questhead', 0250 default=1, 0251 strings=("Yes", "Cancel")) 0252 if d.num != 0: 0253 return 0254 else: 0255 head, tail = os.path.split(file) 0256 if not os.path.isdir(head): 0257 self.master.bell() 0258 return 0259 self.quit(file) 0260 0261 0262 def test(): 0263 """Simple test program.""" 0264 root = Tk() 0265 root.withdraw() 0266 fd = LoadFileDialog(root) 0267 loadfile = fd.go(key="test") 0268 fd = SaveFileDialog(root) 0269 savefile = fd.go(key="test") 0270 print loadfile, savefile 0271 0272 0273 if __name__ == '__main__': 0274 test() 0275
Generated by PyXR 0.9.4