Recipe11.12.Copying Geometry Methods and Options Between Tkinter Widgets


Recipe 11.12. Copying Geometry Methods and Options Between Tkinter Widgets

Credit: Pedro Werneck

Problem

You want to create new Tkinter compound widgets, not by inheriting from Frame and packing other widgets inside, but rather by setting geometry methods and options from other widget to another.

Solution

Here is an example of a compound widget built by this approach:

from Tkinter import * class LabeledEntry(Entry):     """ An Entry widget with an attached Label """     def _ _init_ _(self, master=None, **kw):         ekw = {  }                               # Entry options dictionary         fkw = {  }                               # Frame options dictionary         lkw = {'name':'label'}                 # Label options dictionary         skw = {'padx':0, 'pady':0, 'fill':'x', # Geometry manager opts dict                 'side':'left'}         fmove = ('name',)                      # Opts to move to the Frame dict         lmove = ('text', 'textvariable',                  'anchor','bitmap', 'image')   # Opts to move to the Label dict         smove = ('side', 'padx', 'pady',       # Opts to move to the Geometry                  'fill')                       # manager dictionary         # dispatch each option towards the appropriate component         for k, v in kw:             if k in fmove: fkw[k] = v             elif k in lmove: lkw[k] = v             elif k in smove: skw[k] = v             else: ekw[k] = v         # make all components with the accumulated options         self.body = Frame(master, **fkw)         self.label = Label(self.body, **lkw)         self.label.pack(side='left', fill=skw['fill'],                         padx=skw['padx'], pady=skw['pady'])         Entry._ _init_ _(self, self.body, **ekw)         self.pack(side=skw['side'], fill=skw['fill'],                   padx=skw['padx'], pady=skw['pady'])         methods = (Pack._ _dict_ _.keys( ) +  # Set Frame geometry methods to self                    Grid._ _dict_ _.keys( ) +                    Place._ _dict_ _.keys( ))         for m in methods:             if m[0] != '_' and m != 'config' and m != 'configure':                 setattr(self, m, getattr(self.body, m))

Discussion

Here is an example of use of this LabeledEntry widget, presented, as usual, with a guard of if _ _name_ _ == '_ _main_ _' so we can make it part of the module containing the class and have it run when the module is executed as a "main script":

if _ _name_ _ == '_ _main_ _':     root = Tk( )     le1 = LabeledEntry(root, name='label1', text='Label 1: ',                        width=5, relief=SUNKEN, bg='white', padx=3)     le2 = LabeledEntry(root, name='label2', text='Label 2: ',                        relief=SUNKEN, bg='red', padx=3)     le3 = LabeledEntry(root, name='label3', text='Label 3: ',                        width=40, relief=SUNKEN, bg='yellow', padx=3)     le1.pack(expand=1, fill=X)     le2.pack(expand=1, fill=X)     le3.pack(expand=1, fill=X)     root.mainloop( )

The usual approach to defining new compound Tkinter widgets is to inherit from Frame and pack your component widgets inside. While simple and habitual, that approach has a few problems. In particular, you need to invent, design, document, and implement additional methods or options to access the component widgets' attributes from outside of the compound widget class. Using another alternative (which I've often seen done, but it's still a practice that is not advisable at all!), you can violate encapsulation and Demeter's Law by having other code access the component widgets directly. If you do violate encapsulation, you'll pay for it in the not-so-long run, when you find a need to tweak your compound widget and discover that you can't do it without breaking lots of code that depends on the compound widget's internal structure. Those consequences are bad enough when you own all of the code in question, but it's worse if you have "published" your widget and other people's code depends on it.

This recipe shows it doesn't have to be that bad, by elaborating upon an idea I first saw used in the ScrolledText widget, which deserves to be more widely exposed. Instead of inheriting from Frame, you inherit from the "main" widget of your new compound widget. Then, you create a Frame widget to be used as a body, pretty much like in the more usual approach. Then, and here comes the interesting novelty, you create dicts for each component widget you contain and move to those dictionaries the respective options that pertain to component widgets.

The novelty continues after you've packed the "main" widget: at that point, you can reset said widget's geometry methods to the base Frame attributes (meaning, in this case, methods), so that accessing the object methods will in fact access the inner base Frame geometry methods. This transparent, seamless delegation by juggling bound methods is uniquely Pythonic and is part of what makes this recipe so novel and interesting!

The main advantage of this recipe's approach is that you can create your widget with options to all slave widgets inside it in a single line, just like any other widget, instead of doing any further w.configure or w['option'] calls or accesses to set all details exactly the way you want them. To be honest, there is a potential disadvantage, too: in this recipe's approach, it's hard to handle options with the same name on different component widgets. However, sometimes you can handle them by renaming options: if two separate widgets need a 'foo' option that's also of interest to the "main" widget, for example, use, 'upper_foo' and 'lower_foo' variants and rename them appropriately (with yet another auxiliary dictionary) at the same time you're dispatching them to the appropriate dictionary of component-widget options. You can't sensibly keep doing that "forever", as the number of component widgets competing for the same option grows without bounds: if that happens, revert to the good old tried-and-true approach. But for nine out of ten compound widgets you find yourself programming, you'll find this recipe's approach to be an interesting alternative to the usual, traditional approach to compound-widget programming.

See Also

Information about Tkinter can be obtained from a variety of sources, such as Fredrik Lundh, An Introduction to Tkinter (PythonWare: http://www.pythonware.com/library), New Mexico Tech's Tkinter Reference (http://www.nmt.edu/tcc/help/lang/python/docs.html), Python in a Nutshell, and various other books.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

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