Section 11.2. GuiMixin: Common Tool Mixin Classes


11.2. GuiMixin: Common Tool Mixin Classes

If you read the last three chapters, you probably noticed that the code used to construct nontrivial GUIs can become long if we make each widget by hand. Not only do we have to link up all the widgets manually, but we also need to remember and then set dozens of options. If we stick to this strategy, GUI programming often becomes an exercise in typing, or at least in cut-and-paste text editor operations.

Instead of performing each step by hand, a better idea is to wrap or automate as much of the GUI construction process as possible. One approach is to code functions that provide typical widget configurations; for instance, we could define a button function to handle configuration details and support most of the buttons we draw.

Alternatively, we can implement common methods in a class and inherit them everywhere they are needed. Such classes are commonly called mixin classes because their methods are "mixed in" with other classes. Mixins serve to package generally useful tools as methods. The concept is almost like importing a module, but mixin classes can access the subject instance, self, to utilize per-instance state and inherited methods. The script in Example 11-1 shows how.

Example 11-1. PP3E\Gui\Tools\guimixin.py

 ############################################################################## # a "mixin" class for other frames: common methods for canned dialogs, # spawning programs, simple text viewers, etc; this class must be mixed # with a Frame (or a subclass derived from Frame) for its quit method ############################################################################## from Tkinter import * from tkMessageBox import * from tkFileDialog import * from ScrolledText import ScrolledText from PP3E.launchmodes import PortableLauncher, System class GuiMixin:     def infobox(self, title, text, *args):              # use standard dialogs         return showinfo(title, text)                    # *args for bkwd compat     def errorbox(self, text):         showerror('Error!', text)     def question(self, title, text, *args):         return askyesno(title, text)     def notdone(self):         showerror('Not implemented', 'Option not available')     def quit(self):         ans = self.question('Verify quit', 'Are you sure you want to quit?')         if ans == 1:             Frame.quit(self)                            # quit not recursive!     def help(self):         self.infobox('RTFM', 'See figure 1...')         # override this better     def selectOpenFile(self, file="", dir="."):         # use standard dialogs         return askopenfilename(initialdir=dir, initialfile=file)     def selectSaveFile(self, file="", dir="."):         return asksaveasfilename(initialfile=file, initialdir=dir)     def clone(self):         new = Toplevel( )                   # make a new version of me         myclass = self._ _class_ _            # instance's (lowest) class object         myclass(new)                       # attach/run instance to new window     def spawn(self, pycmdline, wait=0):         if not wait:             PortableLauncher(pycmdline, pycmdline)( )     # run Python progam         else:             System(pycmdline, pycmdline)( )               # wait for it to exit     def browser(self, filename):         new  = Toplevel( )                                # make new window         text = ScrolledText(new, height=30, width=90)      # Text with scrollbar         text.config(font=('courier', 10, 'normal'))        # use fixed-width font         text.pack( )         new.title("Text Viewer")                          # set window mgr attrs         new.iconname("browser")         text.insert('0.0', open(filename, 'r').read( ) )  # insert file's text if _ _name_ _ == '_ _main_ _':     class TestMixin(GuiMixin, Frame):      # standalone test         def _ _init_ _(self, parent=None):             Frame._ _init_ _(self, parent)             self.pack( )             Button(self, text='quit',  command=self.quit).pack(fill=X)             Button(self, text='help',  command=self.help).pack(fill=X)             Button(self, text='clone', command=self.clone).pack(fill=X)     TestMixin().mainloop( ) 

Although Example 11-1 is geared toward GUIs, it's really about design concepts. The GuiMixin class implements common operations with standard interfaces that are immune to changes in implementation. In fact, the implementations of some of this class's methods did changebetween the first and second editions of this book, old-style Dialog calls were replaced with the new Tk standard dialog calls. Because this class's interface hides such details, its clients did not have to be changed to use the new dialog techniques.

As is, GuiMixin provides methods for common dialogs, window cloning, program spawning, text file browsing, and so on. We can add more methods to such a mixin later if we find ourselves coding the same methods repeatedly; they will all become available immediately everywhere this class is imported and mixed. Moreover, GuiMixin's methods can be inherited and used as is, or they can be redefined in subclasses.

There are a few things to notice here:

  • The quit method serves some of the same purpose as the reusable Quitter button we used in earlier chapters. Because mixin classes can define a large library of reusable methods, they can be a more powerful way to package reusable components than individual classes. If the mixin is packaged well, we can get a lot more from it than a single button's callback.

  • The clone method makes a new copy, in a new top-level window, of the most specific class that mixes in a GuiMixin (self._ _class_ _ is the class object that the instance was created from). This opens a new independent copy of the window.

  • The browser method opens the standard library's ScrolledText object in a new window and fills it with the text of a file to be viewed. We wrote our own ScrolledText in the previous chapter; you might need to use it here instead, if the standard library's class ever becomes deprecated (please, no wagering).

  • The spawn method launches a Python program command line as a new process and waits for it to end or not (depending on the wait argument). This method is simple, though, because we wrapped launching details in the launchmodes module presented at the end of Chapter 5. GuiMixin both fosters and practices good code reuse habits.

The GuiMixin class is meant to be a library of reusable tool methods and is essentially useless by itself. In fact, it must generally be mixed with a Frame-based class to be used: quit assumes it's mixed with a Frame, and clone assumes it's mixed with a widget class. To satisfy such constraints, this module's self-test code at the bottom combines GuiMixin with a Frame widget. Figure 11-1 shows the scene created by the self-test after pressing "clone" twice, and then "help" in one of the three copies.

Figure 11-1. GuiMixin self-test code in action


We'll see this class show up again as a mixin in later examplesthat's the whole point of code reuse, after all.




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