 
This document briefly describes how to design and code Pmw megawidgets by inheriting from the Pmw base classes. It shows step by step how to build a simple example megawidget. This megawidget allows the user to select one of a range of numbers and it also indicates if the selected number is greater than a given threshold.
The megawidget will be built using a Tkinter.Scale widget to allow the user to select a number in a range, and a Tkinter.Frame widget to act as an indicator, displaying red (say) if the selected number exceeds the threshold. It will look something like this:
   
The programmer using this megawidget will need access to the scale widget, since they will need to set the scale's range. Therefore the scale will be made a component of the megawidget. The programmer will probably not need access to the indicator frame, but, just in case the need arises to change the borderwidth or relief of the indicator, we will make it a component too. This illustrates a convention about components - for maximum configurability, make all sub-widgets components.
Apart from the component options now available through the scale and indicator components, the megawidget will need a few options of its own. It will need a threshold option to set the threshold. It may also need options to set the colors of the indicator when the selected value is both above and below the threshold. Other options could be orient or indicatorpos to specify the relative position of components and margin, padx or pady to specify spacing between and around the components. For this example, we will define three options - threshold, colors and value. The colors option will be a 2-element sequence specifying two colors (below threshold, above threshold). The value option will be the initial value of the scale.
The first things to do are to decide on a name for the new megawidget, decide which base class to inherit from and to begin to write the constructor. Most Pmw megawidgets are derived from either Pmw.MegaWidget, Pmw.MegaToplevel or Pmw.Dialog. In this case, since the widget is not to be contained within its own toplevel window, we will inherit from Pmw.MegaWidget. The constructors of megawidgets take one argument (the widget to use as the parent of the megawidget's hull, defaulting to the root window) and any number of keyword arguments.
class ThresholdScale(Pmw.MegaWidget):
    """ Megawidget containing a scale and an indicator.
    """
 
    def __init__(self, parent = None, **kw):
  Next, we need to define the options supplied by this megawidget. 
  Each option is specified by a 3-element sequence.  The first element
  is the option's name.  The second element is the default value.  The
  third element is either a callback function,
  Pmw.INITOPT or None.  In the first
  case, the function is called at the end of construction (during the 
  call to self.inialiseoptions) and also
  whenever the option is set by a call to
  configure.  Pmw.INITOPT indicates that
  the option is an initialisation option - it cannot be set by calling
  configure.  None indicates that the
  option can be set by calling configure, but that there
  is no callback function.
  The call to self.defineoptions also includes the
  keyword arguments passed in to the constructor.  The value given to
  any option specified in the keywords will override the default
  value.
        # Define the megawidget options.
        optiondefs = (
            ('colors',    ('green', 'red'), None),
            ('threshold', 50,               None),
            ('value',     None,             Pmw.INITOPT),
        )
        self.defineoptions(kw, optiondefs)
After defining the options, the constructor of the base class should be called. The options need to be defined first so that a derived class can redefine the default value of an option defined in a base class. This is because the value specified by the derived class must be made available before the base class constructor is called. The keyword arguments should not be passed into the base class constructor since they have already been dealt with in the previous step.
        # Initialise base class (after defining options).
        Pmw.MegaWidget.__init__(self, parent)
Now we should create the components. The components are created as children (or grandchildren ...) of the megawidget's interior.
        # Create the components.
        interior = self.interior()
  The first component to create is the indicator.  The
  createcomponent method creates the sub-widget and
  registers the widget as a component of this megawidget.  It takes
  five arguments plus any number of keyword arguments.  The arguments
  are name, aliases, group, class and constructor arguments.  See the
  Pmw.MegaArchetype reference manual)
  for full details.
        # Create the indicator component.
        self.indicator = self.createcomponent('indicator',
                (), None,
                Tkinter.Frame, (interior,),
                        width = 16,
                        height = 16,
                        borderwidth = 2,
                        relief = 'raised')
        self.indicator.grid()
The scale component is created in a similar way. In this case, the initial value of the scale is also set to the value of the value initialisation option.
        # Create the scale component.
        self.scale = self.createcomponent('scale',
                (), None,
                Tkinter.Scale, (interior,),
                        command = self._doCommand,
                        tickinterval = 20,
                        length = 200,
                        from_ = 100,
                        to = 0,
                        showvalue = 0)
        self.scale.grid()
 
        value = self['value']
        if value is not None:
            self.scale.set(value)
  At the end of the constructor, the initialiseoptions
  method is called to check that all keyword arguments have been used
  (that is, the caller did not specify any unknown or misspelled
  options) and to call the option callback functions.
        # Check keywords and initialise options.
        self.initialiseoptions()
All other methods must now be defined. In this case, only one method is required - a method called whenever the scale changes and which sets the indicator color according to the threshold.
    def _doCommand(self, valueStr):
        if self.scale.get() > self['threshold']:
            color = self['colors'][1]
        else:
            color = self['colors'][0]
        self.indicator.configure(background = color)
  To complete the megawidget, methods from other classes can be
  copied into this class.  In this case, all Tkinter.Scale methods
  not already defined by the megawidget are made available as methods
  of this class and are forwarded to the scale component.  Note that
  the third argument to Pmw.forwardmethods is the name of
  the instance variable referring to the Tkinter.Scale widget and not
  the name of the component.  This function is called outside of and
  after the class definition.
Pmw.forwardmethods(ThresholdScale, Tkinter.Scale, 'scale')
    Important note: If a megawidget defines options
    using defineoptions(), then this method must be
    called in the megawidget constructor before the call to the base
    class constructor and a matching call to
    initialiseoptions() must made at the end of the
    constructor.  For example:
    def __init__(self, parent = None, **kw):
	optionDefs = ...
	self.defineoptions(kw, optionDefs)
	BaseClass.__init__(self, parent)
	...
	self.initialiseoptions()
The code below creates two of our example megawidgets. The first is created with default values for all options. The second is created with new values for the options. It also redefines some of the options of the components.
# Create and pack two ThresholdScale megawidgets.
mega1 = ThresholdScale()
mega1.pack(side = 'left', padx = 10, pady = 10)
mega2 = ThresholdScale(
        colors = ('green', 'yellow'),
        threshold = 75,
        value = 80,
        indicator_width = 32,
        scale_width = 25)
mega2.pack(side = 'left', padx = 10, pady = 10)
   
The complete code for this example can be seen here.
These exercises build on the example presented so far.
mega1 so that the scale
    widget displays the current value next to the slider.  (You may
    need to look at the Tk scale manual page to find which option to
    the scale component to set.)  You will be able to
    do this without modifying the ThresholdScale class code.
  _doCommand method so that it
    displays the current value of the scale in this label.
  createlabel() method in
    the Pmw.MegaArchetype reference
    manual and add labelpos and
    labelmargin initialisation options which allow
    the creation of a label for the megawidget.
  An example of how these changes can be made can be seen here.
If you have completed a megawidget that may be useful to others, you may like to consider contributing it to Pmw. See Contributions welcome for how to contribute.
As a final note, the Pmw code makes an attempt to follow these coding conventions.
= with spaces when used with keyword
    parameters in function calls.
  
     
    
Pmw 1.2 - 5 Aug 2003 - Home