Section 11.7. Wrapping Up Top-Level Window Interfaces


11.7. Wrapping Up Top-Level Window Interfaces

Top-level window interfaces were introduced in Chapter 9. This section picks up where that introduction left off and wraps up those interfaces in classes that automate some of the work of building top-level windowssetting titles, finding and displaying window icons, issuing proper close actions based on a window's role, intercepting window manager close button clicks, and so on.

Example 11-13 provides wrapper classes for the most common window typesa main application window, a transient pop-up window, and an embedded GUI component window. These window types vary slightly in terms of their close operations, but most inherit common functionality related to window borders: icons, titles, and close buttons. By creating, mixing in, or subclassing the class for the type of window you wish to make, you'll get all the setup logic for free.

Example 11-13. PP3E\Gui\Tools\windows.py

 ############################################################################### # classes that encapsulate top-level interfaces; # allows same GUI to be main, pop-up, or attached;  content classes may inherit # from these directly, or be mixed together with them per usage mode;  may also # be called directly without a subclass;  designed to be mixed in after (further # to the right than) app-specific classes: else, subclass gets methods here # (destroy, okayToQuit), instead of from app-specific classes--can't redefine. ############################################################################### import os, glob from Tkinter      import Tk, Toplevel, Frame, YES, BOTH, RIDGE from tkMessageBox import showinfo, askyesno class _window:     """     mixin shared by main and pop-up windows     """     foundicon = None                                       # shared by all inst     iconpatt  = '*.ico'                                    # may be reset     iconmine  = 'py.ico'     def configBorders(self, app, kind, iconfile):         if not iconfile:                                    # no icon passed?             iconfile = self.findIcon( )                     # try curr,tool dirs         title = app         if kind: title += ' - ' + kind         self.title(title)                                  # on window border         self.iconname(app)                                 # when minimized         if iconfile:             try:                 self.iconbitmap(iconfile)                  # window icon image             except:                                        # bad py or platform                 pass         self.protocol('WM_DELETE_WINDOW', self.quit)       # don't close silent     def findIcon(self):         if _window.foundicon:                              # already found one?             return _window.foundicon         iconfile  = None                                   # try curr dir auto         iconshere = glob.glob(self.iconpatt)               # assume just one         if iconshere:                                      # del icon for red Tk             iconfile = iconshere[0]         else:                                              # try tools dir icon             mymod  = _ _import_ _(_ _name_ _)                    # import self for dir             path   = _ _name_ _.split('.')                   # poss a package path             for mod in path[1:]:                           # follow path to end                 mymod = getattr(mymod, mod)             mydir  = os.path.dirname(mymod._ _file_ _)             myicon = os.path.join(mydir, self.iconmine)    # use myicon, not tk             if os.path.exists(myicon): iconfile = myicon         _window.foundicon = iconfile                       # don't search again         return iconfile class MainWindow(Tk, _window):     """     when run in main top-level window     """     def _ _init_ _(self, app, kind='', iconfile=None):         Tk._ _init_ _(self)         self._ _app = app         self.configBorders(app, kind, iconfile)     def quit(self):         if self.okayToQuit( ):                                # threads running?             if askyesno(self._ _app, 'Verify Quit Program?'):                 self.destroy( )                               # quit whole app         else:             showinfo(self._ _app, 'Quit not allowed')         # or in okayToQuit?     def destroy(self):                                       # exit app silently         Tk.quit(self)                                        # redef if exit ops     def okayToQuit(self):                                    # redef me if used         return True                                          # e.g., thread busy class PopupWindow(Toplevel, _window):     """     when run in secondary pop-up window     """     def _ _init_ _(self, app, kind='', iconfile=None):         Toplevel._ _init_ _(self)         self._ _app = app         self.configBorders(app, kind, iconfile)     def quit(self):                                          # redef me to change         if askyesno(self._ _app, 'Verify Quit Window?'):    # or call destroy             self.destroy( )                                 # quit this window     def destroy(self):                                     # close win silently         Toplevel.destroy(self)                             # redef for close ops class QuietPopupWindow(PopupWindow):     def quit(self):         self.destroy( )                                     # don't verify close class ComponentWindow(Frame):     """     when attached to another display     """     def _ _init_ _(self, parent):                            # if not a frame         Frame._ _init_ _(self, parent)                       # provide container         self.pack(expand=YES, fill=BOTH)         self.config(relief=RIDGE, border=2)                # reconfig to change     def quit(self):         showinfo('Quit', 'Not supported in attachment mode')     # destroy from Frame: erase frame silent               # redef for close ops 

So why not just set an application's icon and title by calling protocol methods directly? For one thing, those are the sorts of details that are easy to forget (you will probably wind up cutting and pasting code much of the time). For another, these classes add higher-level functionality that we might otherwise have to code redundantly. Among other things, the classes arrange for automatic quit verification dialog pop ups and icon file searching. For instance, the window classes always search the current working directory and the directory containing this module for a window icon file, once per process.

By using classes that encapsulatethat is, hidesuch details, we inherit powerful tools without even having to think about their implementation again in the future. Moreover, by using such classes, we'll give our applications a standard look-and-feel. And if we ever need to change that appearance, we have to change code in only one place, not in every window we generate.

To test this utility module, Example 11-14 exercises its classes in a variety of modesas mix-in classes, as superclasses, and as calls from nonclass code.

Example 11-14. PP3E\Gui\Tools\windows-test.py

 # must import windows to test, # else _ _name_ _ is _ _main_ _ in findIcon from Tkinter import Toplevel, Tk, Button, mainloop from windows import MainWindow, PopupWindow, ComponentWindow def _selftest( ):     from Tkinter import Button, mainloop     # mixin usage     class content:         "same code used as a Tk, Toplevel, and Frame"         def _ _init_ _(self):             Button(self, text='Larch', command=self.quit).pack( )             Button(self, text='Sing ', command=self.destroy).pack( )     class contentmix(MainWindow, content):         def _ _init_ _(self):             MainWindow._ _init_ _(self, 'mixin', 'Main')             content._ _init_ _(self)     contentmix( )     class contentmix(PopupWindow, content):         def _ _init_ _(self):             PopupWindow._ _init_ _(self, 'mixin', 'Popup')             content._ _init_ _(self)     prev = contentmix( )     class contentmix(ComponentWindow, content):         def _ _init_ _(self):                               # nested frame             ComponentWindow._ _init_ _(self, prev)          # on prior window             content._ _init_ _(self)                        # Sing erases frame     contentmix( )     # subclass usage     class contentsub(PopupWindow):         def _ _init_ _(self):             PopupWindow._ _init_ _(self, 'popup', 'subclass')             Button(self, text='Pine', command=self.quit).pack( )             Button(self, text='Sing', command=self.destroy).pack( )     contentsub( )     # non-class usage     win = PopupWindow('popup', 'attachment')     Button(win, text='Redwood', command=win.quit).pack( )     Button(win, text='Sing   ', command=win.destroy).pack( )     mainloop( ) if _ _name_ _ == '_ _main_ _':     _selftest( ) 

When run, the test generates the window in Figure 11-10. All generated windows get a blue "PY" icon automatically, thanks to the search and configuration logic they inherit from the window module's classes. Some of the buttons on the test windows close just the enclosing window, some close the entire applications, some erase an attached window, and others pop up a quit verification dialog. Run this on your own to see what the examples' buttons do.[*]

[*] Caveat: in Python 2.4, when setting window iconbitmask images, there is a slight pause when opening the window on Windows (during which an empty window flashes briefly for a fraction of a second). No workaround could be found for this, and it may be improved in the future (window icons were first supported on Windows very recently, in Python 2.3). If this startup delay is undesirable, simply delete the .ico icon files to force the compiled-in red "Tk" icon to be used; icons can also be set in the C API; see Tk documentation for details.

Figure 11-10. windows-test display


We'll use these window protocol wrappers in the next chapter's PyClock example, and then again later in Chapter 15 where they'll come in handy to reduce the complexity of the PyMailGUI program. Part of the benefit of doing OOP in Python now is that we can forget the details later.




Programming Python
Programming Python
ISBN: 0596009259
EAN: 2147483647
Year: 2004
Pages: 270
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net