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 2.1 - 31 Dec 2020 - Home