4 <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
5 <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
6 <title>How to build Pmw megawidgets</title>
9 <body bgcolor="#ffffff" text="#000000" link="#0000ee"
10 vlink="551a8b" alink="ff0000">
12 <h1 ALIGN="CENTER">How to build Pmw megawidgets</h1>
14 <center><P ALIGN="CENTER">
15 <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
19 <dt> <h3>Introduction</h3></dt><dd>
21 This document briefly describes how to design and code Pmw
22 megawidgets by inheriting from the Pmw base classes. It shows step
23 by step how to build a simple example megawidget. This megawidget
24 allows the user to select one of a range of numbers and it also
25 indicates if the selected number is greater than a given threshold.
30 <dt> <h3>Choosing the components</h3></dt><dd>
33 The megawidget will be built using a Tkinter.Scale widget to allow
34 the user to select a number in a range, and a Tkinter.Frame widget
35 to act as an indicator, displaying red (say) if the selected number
36 exceeds the threshold. It will look something like this:
40 <center><P ALIGN="CENTER">
41 <IMG SRC = scale1.gif ALT = "Scale 2" WIDTH=70 HEIGHT=244>
45 The programmer using this megawidget will need access to the scale
46 widget, since they will need to set the scale's range. Therefore
47 the scale will be made a component of the megawidget. The
48 programmer will probably not need access to the indicator frame,
49 but, just in case the need arises to change the borderwidth or
50 relief of the indicator, we will make it a component too. This
51 illustrates a convention about components - for maximum
52 configurability, make all sub-widgets components.
57 <dt> <h3>Choosing the options</h3></dt><dd>
60 Apart from the component options now available through the scale and indicator
61 components, the megawidget will need a few options of its own. It
62 will need a <strong>threshold</strong> option to set the threshold.
63 It may also need options to set the colors of the indicator when the
64 selected value is both above and below the threshold. Other options
65 could be <strong>orient</strong> or <strong>indicatorpos</strong> to
66 specify the relative position of components and
67 <strong>margin</strong>, <strong>padx</strong> or
68 <strong>pady</strong> to specify spacing between and around the
69 components. For this example, we will define three options -
70 <strong>threshold</strong>, <strong>colors</strong> and
71 <strong>value</strong>. The <strong>colors</strong> option will be
72 a 2-element sequence specifying two colors (below threshold, above
73 threshold). The <strong>value</strong> option will be the initial
79 <dt> <h3>Coding the megawidget</h3></dt><dd>
82 The first things to do are to decide on a name for the new
83 megawidget, decide which base class to inherit from and to begin to
84 write the constructor. Most Pmw megawidgets are derived from either
85 Pmw.MegaWidget, Pmw.MegaToplevel or Pmw.Dialog. In this case, since
86 the widget is not to be contained within its own toplevel window, we
87 will inherit from Pmw.MegaWidget. The constructors of megawidgets
88 take one argument (the widget to use as the parent of the
89 megawidget's hull, defaulting to the root window) and any number of
95 class ThresholdScale(Pmw.MegaWidget):
96 """ Megawidget containing a scale and an indicator.
99 def __init__(self, parent = None, **kw):
103 Next, we need to define the options supplied by this megawidget.
104 Each option is specified by a 3-element sequence. The first element
105 is the option's name. The second element is the default value. The
106 third element is either a callback function,
107 <strong>Pmw.INITOPT</strong> or <strong>None</strong>. In the first
108 case, the function is called at the end of construction (during the
109 call to <code>self.inialiseoptions</code>) and also
110 whenever the option is set by a call to
111 <code>configure</code>. <strong>Pmw.INITOPT</strong> indicates that
112 the option is an initialisation option - it cannot be set by calling
113 <code>configure</code>. <strong>None</strong> indicates that the
114 option can be set by calling <code>configure</code>, but that there
115 is no callback function.
120 The call to <code>self.defineoptions</code> also includes the
121 keyword arguments passed in to the constructor. The value given to
122 any option specified in the keywords will override the default
128 # Define the megawidget options.
130 ('colors', ('green', 'red'), None),
131 ('threshold', 50, None),
132 ('value', None, Pmw.INITOPT),
134 self.defineoptions(kw, optiondefs)
138 After defining the options, the constructor of the base class should
139 be called. The options need to be defined first so that a derived
140 class can redefine the default value of an option defined in a base
141 class. This is because the value specified by the derived class
142 must be made available before the base class constructor is called.
144 arguments should not be passed into the base class constructor since
145 they have already been dealt with in the previous step.
150 # Initialise base class (after defining options).
151 Pmw.MegaWidget.__init__(self, parent)
155 Now we should create the components. The components are created as
156 children (or grandchildren ...) of the megawidget's interior.
161 # Create the components.
162 interior = self.interior()
166 The first component to create is the indicator. The
167 <code>createcomponent</code> method creates the sub-widget and
168 registers the widget as a component of this megawidget. It takes
169 five arguments plus any number of keyword arguments. The arguments
170 are name, aliases, group, class and constructor arguments. See the
171 <a href="MegaArchetype.html">Pmw.MegaArchetype reference manual</a>)
177 # Create the indicator component.
178 self.indicator = self.createcomponent('indicator',
180 Tkinter.Frame, (interior,),
185 self.indicator.grid()
189 The scale component is created in a similar way. In this case, the
190 initial value of the scale is also set to the value of the
191 <strong>value</strong> initialisation option.
196 # Create the scale component.
197 self.scale = self.createcomponent('scale',
199 Tkinter.Scale, (interior,),
200 command = self._doCommand,
208 value = self['value']
209 if value is not None:
210 self.scale.set(value)
214 At the end of the constructor, the <code>initialiseoptions</code>
215 method is called to check that all keyword arguments have been used
216 (that is, the caller did not specify any unknown or misspelled
217 options) and to call the option callback functions.
222 # Check keywords and initialise options.
223 self.initialiseoptions()
227 All other methods must now be defined. In this case, only one
228 method is required - a method called whenever the scale changes and
229 which sets the indicator color according to the threshold.
234 def _doCommand(self, valueStr):
235 if self.scale.get() > self['threshold']:
236 color = self['colors'][1]
238 color = self['colors'][0]
239 self.indicator.configure(background = color)
243 To complete the megawidget, methods from other classes can be
244 copied into this class. In this case, all Tkinter.Scale methods
245 not already defined by the megawidget are made available as methods
246 of this class and are forwarded to the scale component. Note that
247 the third argument to <code>Pmw.forwardmethods</code> is the name of
248 the instance variable referring to the Tkinter.Scale widget and not
249 the name of the component. This function is called outside of and
250 after the class definition.
255 Pmw.forwardmethods(ThresholdScale, Tkinter.Scale, 'scale')
259 <strong>Important note:</strong> If a megawidget defines options
260 using <code>defineoptions()</code>, then this method must be
261 called in the megawidget constructor before the call to the base
262 class constructor and a matching call to
263 <code>initialiseoptions()</code> must made at the end of the
264 constructor. For example:
268 def __init__(self, parent = None, **kw):
270 self.defineoptions(kw, optionDefs)
271 BaseClass.__init__(self, parent)
273 self.initialiseoptions()
277 <dt> <h3>Creating instances of the megawidget</h3></dt><dd>
280 The code below creates two of our example megawidgets. The first is
281 created with default values for all options. The second is created
282 with new values for the options. It also redefines some of the
283 options of the components.
290 # Create and pack two ThresholdScale megawidgets.
291 mega1 = ThresholdScale()
292 mega1.pack(side = 'left', padx = 10, pady = 10)
294 mega2 = ThresholdScale(
295 colors = ('green', 'yellow'),
298 indicator_width = 32,
300 mega2.pack(side = 'left', padx = 10, pady = 10)
305 <center><P ALIGN="CENTER">
306 <IMG SRC = scale2.gif ALT = "Scale 1" WIDTH=150 HEIGHT=244>
310 <dt> <h3>The complete code</h3></dt><dd>
313 The complete code for this example can be seen
314 <a href="example.py">here</a>.
319 <dt> <h3>Exercises</h3></dt><dd>
322 These exercises build on the example presented so far.
328 Change the call to create <code>mega1</code> so that the scale
329 widget displays the current value next to the slider. (You may
330 need to look at the Tk scale manual page to find which option to
331 the <strong>scale</strong> component to set.) You will be able to
332 do this without modifying the ThresholdScale class code.
336 Add a Tkinter.Label component between the indicator and scale
337 components. Modify the <code>_doCommand</code> method so that it
338 displays the current value of the scale in this label.
342 Modify the <strong>colors</strong> and <strong>threshold</strong>
343 options so that they both accept a tuple. Now implement multiple
344 thresholds, so that the indicator displays one of several colors,
345 depending on the value of the scale.
349 Add an <strong>orient</strong> initialisation option and lay out
350 the components horizontally or vertically depending on its value.
354 Read the description of the <code>createlabel()</code> method in
355 the <a href="MegaArchetype.html">Pmw.MegaArchetype reference
356 manual</a> and add <strong>labelpos</strong> and
357 <strong>labelmargin</strong> initialisation options which allow
358 the creation of a label for the megawidget.
364 An example of how these changes can be made can be seen
365 <a href="exercises.py">here</a>.
370 <dt> <h3>Contributing your megawidgets to Pmw</h3></dt><dd>
373 If you have completed a megawidget that may be useful to others, you
374 may like to consider contributing it to Pmw. See
375 <a href="starting.html#contributions">Contributions welcome</a> for
381 <dt> <h3>Pmw coding conventions</h3></dt><dd>
384 As a final note, the Pmw code makes an attempt to follow these coding
390 Class names: initial of each word is upper case (including first word).
394 Public method and function names: all in lower case.
398 Megawidget options: all in lower case.
402 Megawidget component names: all in lower case.
406 Function arguments: initial of each word is upper case (except first word).
410 Private names: initial of each word is upper case (except first
415 Underscores as word separators are only used when overriding
416 Tkinter methods of same name.
420 Indent is four spaces.
424 Continuation lines are indented by eight spaces, so that they
425 won't be confused with a following nested code block.
426 Continuation lines should be used when a statement, which would
427 normally be written on one line, is longer than 80 characters.
428 Examples are "if" statements which contain many conditions and
429 function calls with many arguments.
434 Surround <code>=</code> with spaces when used with keyword
435 parameters in function calls.
440 Multi-line function calls should have one keyword parameter per
449 <center><P ALIGN="CENTER">
450 <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
455 <center><P ALIGN="CENTER">
458 - <a href="index.html">Home</a>