[Python-checkins] r67254 - sandbox/trunk/tkinter-polo/src/FileDialog.py

guilherme.polo python-checkins at python.org
Tue Nov 18 04:24:49 CET 2008


Author: guilherme.polo
Date: Tue Nov 18 04:24:48 2008
New Revision: 67254

Log:
* Removed star import;
* FileDialog is a subclass of Toplevel now;
* Renamed class attribute 'title' to 'dlg_title' so it doesn't conflict with
  Tkinter.Wm.title
* Making clear that the person should /try/ opening the file (if one wants to)
  instead of saying just to open it (FileDialog docstring);
* Changed its layout a bit: the filter button is next to the filter entry now,
  added some spacing between widgets, changed scrollbar positions, added a
  panedwindow between the listboxes, the bottom buttons are placed on right;
* Uses the tab order that one would naturally expect;
* Renamed self.how to self.result (makes more sense to me);
* Renamed FileDialog.quit to FileDialog.close, the former has been taken by
  Tkinter.Misc;
* dirs_select_event and files_select_event use listbox curselection now,
  this eliminates the need to deal with bindtags and it also checks for no
  selection, so creating an empty FileDialog and clicking on it doesn't give
  some tracebacks;


Modified:
   sandbox/trunk/tkinter-polo/src/FileDialog.py

Modified: sandbox/trunk/tkinter-polo/src/FileDialog.py
==============================================================================
--- sandbox/trunk/tkinter-polo/src/FileDialog.py	(original)
+++ sandbox/trunk/tkinter-polo/src/FileDialog.py	Tue Nov 18 04:24:48 2008
@@ -1,33 +1,30 @@
-"""File selection dialog classes.
+"""File selection dialog classes."""
 
-Classes:
-
-- FileDialog
-- LoadFileDialog
-- SaveFileDialog
-
-"""
-
-from Tkinter import *
-from Dialog import Dialog
+__all__ = ['FileDialog', 'LoadFileDialog', 'SaveFileDialog']
 
 import os
 import fnmatch
 
+from Tkinter import Toplevel, Frame, PanedWindow
+from Tkinter import Button, Entry, Listbox, Scrollbar
+from Tkconstants import TOP, BOTTOM, X, Y, BOTH, RIGHT, LEFT, END, E
+from Dialog import Dialog
 
 dialogstates = {}
 
-
-class FileDialog:
-
+class FileDialog(Toplevel):
     """Standard file selection dialog -- no checks on selected file.
 
     Usage:
 
         d = FileDialog(master)
         fname = d.go(dir_or_file, pattern, default, key)
-        if fname is None: ...canceled...
-        else: ...open file...
+        if fname is None:
+            # canceled
+            ...
+        else:
+            # try opening it
+            ...
 
     All arguments to go() are optional.
 
@@ -38,76 +35,84 @@
     the dialog keeps no memory of previous state.  Note that memory is
     kept even when the dialog is canceled.  (All this emulates the
     behavior of the Macintosh file selection dialogs.)
-
     """
 
-    title = "File Selection Dialog"
+    dlg_title = "File Selection Dialog"
 
     def __init__(self, master, title=None):
-        if title is None: title = self.title
-        self.master = master
-        self.directory = None
+        if title is None:
+            title = self.dlg_title
 
-        self.top = Toplevel(master)
-        self.top.title(title)
-        self.top.iconname(title)
+        Toplevel.__init__(self, master)
+        self.wm_title(title)
+        self.iconname(title)
 
-        self.botframe = Frame(self.top)
-        self.botframe.pack(side=BOTTOM, fill=X)
-
-        self.selection = Entry(self.top)
-        self.selection.pack(side=BOTTOM, fill=X)
-        self.selection.bind('<Return>', self.ok_event)
+        self.directory = None
 
-        self.filter = Entry(self.top)
-        self.filter.pack(side=TOP, fill=X)
-        self.filter.bind('<Return>', self.filter_command)
+        filterframe = Frame(self)
+        self.filter = Entry(filterframe)
+        filter_button = Button(filterframe, text="Filter",
+                               command=self.filter_command)
+
+        # Middle frame, holds the listboxes
+        midframe = Frame(self)
+        paned = PanedWindow(midframe, orient='horizontal')
+        # Directory listbox
+        dirsframe = Frame(paned)
+        self.dirs = Listbox(dirsframe, exportselection=0)
+        dirsbar = Scrollbar(dirsframe, command=self.dirs.yview)
+        self.dirs['yscrollcommand'] = dirsbar.set
+        # Files listbox
+        filesframe = Frame(paned)
+        self.files = Listbox(filesframe, exportselection=False)
+        filesbar = Scrollbar(filesframe, command=self.files.yview)
+        self.files['yscrollcommand'] = filesbar.set
+        # Paned
+        paned.add(dirsframe)
+        paned.add(filesframe)
 
-        self.midframe = Frame(self.top)
-        self.midframe.pack(expand=YES, fill=BOTH)
+        self.selection = Entry(self)
+        self.selection.bind('<Return>', self.ok_event)
 
-        self.filesbar = Scrollbar(self.midframe)
-        self.filesbar.pack(side=RIGHT, fill=Y)
-        self.files = Listbox(self.midframe, exportselection=0,
-                             yscrollcommand=(self.filesbar, 'set'))
-        self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
-        btags = self.files.bindtags()
-        self.files.bindtags(btags[1:] + btags[:1])
-        self.files.bind('<ButtonRelease-1>', self.files_select_event)
-        self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
-        self.filesbar.config(command=(self.files, 'yview'))
+        # Bottom frame, holds dialog buttons
+        btframe = Frame(self)
+        ok_button = Button(btframe, text="OK", command=self.ok_command)
+        cancel_button = Button(btframe, text="Cancel",
+                               command=self.cancel_command)
 
-        self.dirsbar = Scrollbar(self.midframe)
-        self.dirsbar.pack(side=LEFT, fill=Y)
-        self.dirs = Listbox(self.midframe, exportselection=0,
-                            yscrollcommand=(self.dirsbar, 'set'))
-        self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
-        self.dirsbar.config(command=(self.dirs, 'yview'))
-        btags = self.dirs.bindtags()
-        self.dirs.bindtags(btags[1:] + btags[:1])
+        # setup bindings
+        self.filter.bind('<Return>', self.filter_command)
         self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
         self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
+        self.files.bind('<ButtonRelease-1>', self.files_select_event)
+        self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
+        self.wm_protocol('WM_DELETE_WINDOW', self.cancel_command)
+        # XXX Are the following okay for a general audience?
+        self.bind('<Alt-w>', self.cancel_command)
+        self.bind('<Alt-W>', self.cancel_command)
 
-        self.ok_button = Button(self.botframe,
-                                 text="OK",
-                                 command=self.ok_command)
-        self.ok_button.pack(side=LEFT)
-        self.filter_button = Button(self.botframe,
-                                    text="Filter",
-                                    command=self.filter_command)
-        self.filter_button.pack(side=LEFT, expand=YES)
-        self.cancel_button = Button(self.botframe,
-                                    text="Cancel",
-                                    command=self.cancel_command)
-        self.cancel_button.pack(side=RIGHT)
+        # pack widgets
+        spacing = {'padx': 6, 'pady': 6}
 
-        self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
-        # XXX Are the following okay for a general audience?
-        self.top.bind('<Alt-w>', self.cancel_command)
-        self.top.bind('<Alt-W>', self.cancel_command)
+        filterframe.pack(fill=X)
+        self.filter.pack(side=LEFT, fill=X, expand=True, **spacing)
+        filter_button.pack(side=RIGHT, **spacing)
+
+        midframe.pack(expand=True, fill=BOTH, **spacing)
+        paned.pack(expand=True, fill=BOTH)
+        self.dirs.pack(side=LEFT, fill=BOTH, expand=True)
+        dirsbar.pack(side=LEFT, fill=Y)
+        self.files.pack(side=LEFT, fill=BOTH, expand=True)
+        filesbar.pack(side=LEFT, fill=Y)
+
+        self.selection.pack(fill=X, **spacing)
+
+        btframe.pack(side=BOTTOM, anchor=E)
+        cancel_button.pack(side=LEFT, **spacing)
+        ok_button.pack(side=LEFT, **spacing)
 
     def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
-        if key and dialogstates.has_key(key):
+        if key and key in dialogstates:
             self.directory, pattern = dialogstates[key]
         else:
             dir_or_file = os.path.expanduser(dir_or_file)
@@ -119,28 +124,35 @@
         self.set_selection(default)
         self.filter_command()
         self.selection.focus_set()
-        self.top.wait_visibility() # window needs to be visible for the grab
-        self.top.grab_set()
-        self.how = None
-        self.master.mainloop()          # Exited by self.quit(how)
+
+        self.result = None
+        self.wait_visibility() # window needs to be visible for the grab
+        self.grab_set()
+        self.mainloop()
+
         if key:
             directory, pattern = self.get_filter()
-            if self.how:
-                directory = os.path.dirname(self.how)
+            if self.result:
+                directory = os.path.dirname(self.result)
             dialogstates[key] = directory, pattern
-        self.top.destroy()
-        return self.how
 
-    def quit(self, how=None):
-        self.how = how
-        self.master.quit()              # Exit mainloop()
+        self.destroy()
+        return self.result
+
+    def close(self, result=None):
+        self.result = result
+        self.quit()
 
     def dirs_double_event(self, event):
         self.filter_command()
 
     def dirs_select_event(self, event):
+        sel = self.dirs.curselection()
+        if not sel:
+            return
+
         dir, pat = self.get_filter()
-        subdir = self.dirs.get('active')
+        subdir = self.dirs.get(sel[0])
         dir = os.path.normpath(os.path.join(self.directory, subdir))
         self.set_filter(dir, pat)
 
@@ -148,24 +160,32 @@
         self.ok_command()
 
     def files_select_event(self, event):
-        file = self.files.get('active')
-        self.set_selection(file)
+        sel = self.files.curselection()
+        if not sel:
+            return
+
+        self.set_selection(self.files.get(sel[0]))
 
     def ok_event(self, event):
         self.ok_command()
 
     def ok_command(self):
-        self.quit(self.get_selection())
+        self.close(self.get_selection())
+
+    def cancel_command(self, event=None):
+        self.close()
 
     def filter_command(self, event=None):
         dir, pat = self.get_filter()
         try:
             names = os.listdir(dir)
         except os.error:
-            self.master.bell()
+            self.bell()
             return
+
         self.directory = dir
         self.set_filter(dir, pat)
+
         names.sort()
         subdirs = [os.pardir]
         matchingfiles = []
@@ -175,14 +195,18 @@
                 subdirs.append(name)
             elif fnmatch.fnmatch(name, pat):
                 matchingfiles.append(name)
+
         self.dirs.delete(0, END)
         for name in subdirs:
             self.dirs.insert(END, name)
+
         self.files.delete(0, END)
         for name in matchingfiles:
             self.files.insert(END, name)
+
         head, tail = os.path.split(self.get_selection())
-        if tail == os.curdir: tail = ''
+        if tail == os.curdir:
+            tail = ''
         self.set_selection(tail)
 
     def get_filter(self):
@@ -193,12 +217,8 @@
         return os.path.split(filter)
 
     def get_selection(self):
-        file = self.selection.get()
-        file = os.path.expanduser(file)
-        return file
-
-    def cancel_command(self, event=None):
-        self.quit()
+        sel = self.selection.get()
+        return os.path.expanduser(sel)
 
     def set_filter(self, dir, pat):
         if not os.path.isabs(dir):
@@ -206,7 +226,7 @@
                 pwd = os.getcwd()
             except os.error:
                 pwd = None
-            if pwd:
+            else:
                 dir = os.path.join(pwd, dir)
                 dir = os.path.normpath(dir)
         self.filter.delete(0, END)
@@ -218,57 +238,65 @@
 
 
 class LoadFileDialog(FileDialog):
-
     """File selection dialog which checks that the file exists."""
 
-    title = "Load File Selection Dialog"
+    dlg_title = "Load File Selection Dialog"
 
     def ok_command(self):
-        file = self.get_selection()
-        if not os.path.isfile(file):
+        sel = self.get_selection()
+        if not os.path.isfile(sel):
             self.master.bell()
         else:
-            self.quit(file)
+            self.close(sel)
 
 
 class SaveFileDialog(FileDialog):
-
     """File selection dialog which checks that the file may be created."""
 
-    title = "Save File Selection Dialog"
+    dlg_title = "Save File Selection Dialog"
 
     def ok_command(self):
-        file = self.get_selection()
-        if os.path.exists(file):
-            if os.path.isdir(file):
-                self.master.bell()
+        sel = self.get_selection()
+
+        if os.path.exists(sel):
+            if os.path.isdir(sel):
+                # selection is not a file, keep the FileDialog running
+                self.bell()
                 return
-            d = Dialog(self.top,
+
+            dlg = Dialog(self,
                        title="Overwrite Existing File Question",
-                       text="Overwrite existing file %r?" % (file,),
+                       text=("Overwrite existing file %r ?" % sel),
                        bitmap='questhead',
                        default=1,
                        strings=("Yes", "Cancel"))
-            if d.num != 0:
+
+            if dlg.num == 1:
+                # cancelled, keep the FileDialog running
                 return
+
         else:
-            head, tail = os.path.split(file)
+            head, tail = os.path.split(sel)
             if not os.path.isdir(head):
-                self.master.bell()
+                # selection is not a file, keep the FileDialog running
+                self.bell()
                 return
-        self.quit(file)
+
+        self.close(sel)
 
 
-def test():
-    """Simple test program."""
+def example():
+    """Example program."""
+    from Tkinter import Tk
     root = Tk()
     root.withdraw()
+
     fd = LoadFileDialog(root)
     loadfile = fd.go(key="test")
     fd = SaveFileDialog(root)
     savefile = fd.go(key="test")
-    print loadfile, savefile
 
+    print loadfile, savefile
 
-if __name__ == '__main__':
-    test()
+if __name__ == "__main__":
+    example()


More information about the Python-checkins mailing list