Before we move on, I want to
point out that it’s possible to reap most of the
class-based component benefits previously mentioned by creating
standalone classes not derived from tkinterFrames
or other widgets. For instance, the
class in
Example 7-24
generates the window shown in
Figure 7-23
.
Example 7-24. PP4E\Gui\Intro\gui7.py
from tkinter import *
class HelloPackage: # not a widget subbclass
def __init__(self, parent=None):
self.top = Frame(parent) # embed a Frame
self.top.pack()
self.data = 0
self.make_widgets() # attach widgets to self.top
def make_widgets(self):
Button(self.top, text='Bye', command=self.top.quit).pack(side=LEFT)
Button(self.top, text='Hye', command=self.message).pack(side=RIGHT)
def message(self):
self.data += 1
print('Hello number', self.data)
if __name__ == '__main__': HelloPackage().top.mainloop()
Figure 7-23. A standalone class package in action
When run, the Hye button here prints tostdout
and the Bye button closes and exits the
GUI, much as before:
C:\...\PP4E\Gui\Intro>python gui7.py
Hello number 1
Hello number 2
Hello number 3
Hello number 4
Also as before,self.data
retains state between events, and callbacks are routed to theself.message
method within this class. Unlike
before, theHelloPackage
class is not
itself a kind ofFrame
widget. In
fact, it’s not a kind of anything—it serves only as a generator of
namespaces for storing away real widget objects and state. Because of
that, widgets are attached to aself.top
(an embeddedFrame
), not toself
. Moreover, all references to the object
as a widget must descend to the embedded frame, as in thetop.mainloop
call to start the GUI at the end
of the script.
This makes for a bit more coding within the class, but it avoids
potential name clashes with both attributes added toself
by the tkinter framework and existing
tkinter widget methods. For instance, if you define aconfig
method in your class, it will hide theconfig
call exported by tkinter. With
the standalone class package in this example, you get only the methods
and instance attributes that your class defines.
In practice, tkinter doesn’t use very many names,
so this is not generally a big concern.
[
31
]
It can happen, of course; but frankly, I’ve never seen a
real tkinter name clash in widget subclasses in some 18 years of Python
coding. Moreover, using standalone classes is not without other
downsides. Although they can generally be attached and subclassed as
before, they are not quite plug-and-play compatible with real widget
objects. For instance, the configuration calls made in
Example 7-21
for theFrame
subclass fail in
Example 7-25
.
Example 7-25. PP4E\Gui\Intro\gui7b.py
from tkinter import *
from gui7 import HelloPackage # or get from gui7c--__getattr__ added
frm = Frame()
frm.pack()
Label(frm, text='hello').pack()
part = HelloPackage(frm)
part.pack(side=RIGHT) # FAILS!--need part.top.pack(side=RIGHT)
frm.mainloop()
This won’t quite work, becausepart
isn’t really a widget. To treat it as
such, you must descend topart.top
before making GUI configurations and hope that the nametop
is never changed by the class’s developer.
In other words, it exposes some of the class’s internals. The class
could make this better by defining a method that always routes unknown
attribute fetches to the embeddedFrame
, as in
Example 7-26
.
Example 7-26. PP4E\Gui\Intro\gui7c.py
import gui7
from tkinter import *
class HelloPackage(gui7.HelloPackage):
def __getattr__(self, name):
return getattr(self.top, name) # pass off to a real widget
if __name__ == '__main__': HelloPackage().mainloop() # invokes __getattr__!
As is, this script simply creates
Figure 7-23
again; changing
Example 7-25
to import this
extendedHelloPackage
fromgui7c
, though, produces the correctly-working
window in
Figure 7-24
.
Figure 7-24. A standalone class package in action
Routing attribute fetches to nested widgets works this way, but
that then requires even more extra coding in standalone package classes.
As usual, though, the significance of all these trade-offs varies per
application.
[
31
]
If you study thetkinter
module’s source code (today, mostly in file__init__.py
inLib\tkinter
), you’ll notice that many of
the attribute names it creates start with a single underscore to
make them unique from yours; others do not because they are
potentially useful outside of the tkinter implementation (e.g.,self.master
,self.children
). Curiously, at this writing
most of tkinter still does not use the Python “pseudoprivate
attributes” trick of prefixing attribute names with two leading
underscores to automatically add the enclosing class’s name and thus
localize them to the creating class. If tkinter is ever rewritten to
employ this feature, name clashes will be much less likely in widget
subclasses. Most of the attributes of widget classes, though, are
methods intended for use in client scripts; the single underscore
names are accessible too, but are less likely to clash with most
names of your own.
In this chapter, we learned the core concepts of Python/tkinter
programming and met a handful of simple widget objects along the way—e.g.,
labels, buttons, frames, and the packer geometry manager. We’ve seen
enough to construct simple interfaces, but we have really only scratched
the surface of the tkinter widget set.
In the next two chapters, we will apply what we’ve learned here to
study the rest of the tkinter library, and we’ll learn how to use it to
generate the kinds of interfaces you expect to see in realistic GUI
programs. As a preview and roadmap,
Table 7-1
lists the kinds of widgets we’ll meet
there in roughly their order of appearance. Note that this table lists
only widget classes; along the way, we will also
meet a few additional widget-related topics that don’t
appear in this table.
Table 7-1. tkinter widget classes
Widget | Description |
---|---|
Label | A simple |
Button | A simple |
Frame | A container for |
| A new |
Message | A |
Entry | A simple |
Checkbutton | A two-state |
Radiobutton | A two-state button |
Scale | A slider |
PhotoImage | An image |
BitmapImage | An image |
Menu | A set of |
Menubutton | A button |
Scrollbar | A control |
Listbox | A list of |
Text | A multiline |
Canvas | A graphic |
We’ve already metLabel
,Button
, andFrame
in this chapter’s tutorial. To make the
remaining topics easier to absorb, they are split over the next two
chapters:
Chapter 8
covers the first
widgets in this table up to but not includingMenu
, and
Chapter 9
presents widgets that are lower
in this table.
Besides the widget classes in this table, there are additional
classes and tools in the tkinter library, many of which we’ll explore in
the following two chapters as well:
pack
,grid
,place
StringVar
,IntVar
,DoubleVar
,BooleanVar
Spinbox
,LabelFrame
,PanedWindow
Dialog
,ScrolledText
,OptionMenu
Widgetafter
,wait
, andupdate
methods
Standard dialogs, clipboard,bind
andEvent
, widget configuration options,
custom and modal dialogs, animation techniques
Most tkinter widgets are familiar user interface devices. Some are
remarkably rich in functionality. For instance, theText
class implements a sophisticated multiline
text widget that supports fonts, colors, and special effects and is
powerful enough to implement a web browser’s page display. The similarly
feature-richCanvas
class provides
extensive drawing tools powerful enough for visualization and other
image-processing applications. Beyond this, tkinter extensions such as the
Pmw, Tix, and ttk packages described at the start of this chapter add even
richer widgets to a GUI programmer’s toolbox.
At the start
of this chapter, I mentioned that tkinter is Python’s
interface to the
Tk GUI library, originally written for the Tcl language. To
help readers migrating from Tcl to Python and to summarize some of the
main topics we met in this chapter, this section contrasts Python’s Tk
interface with Tcl’s. This mapping also helps make Tk references written
for other languages more useful to Python developers.
In general terms, Tcl’s command-string view of the world differs
widely from Python’s object-based approach to programming. In terms of Tk
programming, though, the syntactic differences are fairly small. Here are
some of the main distinctions in Python’s tkinter interface:
Widgets are created as class instance objects by calling a
widget class.
Parents are previously created objects that are passed to
widget-class constructors.
Options are constructor orconfig
keyword arguments or indexed
keys.
Widget operations (actions) become tkinter widget class object
methods.
Callback handlers are any callable objects: function, method,
lambda, and so on.
Widgets are extended using Python class inheritance
mechanisms.
Interfaces are constructed by attaching objects, not by
concatenating names.
Variables associated with widgets are tkinter class objects
with methods.
In Python, widget creation commands (e.g.,button
) are Python class names that start with
an uppercase letter (e.g.,Button
),
two-word widget operations (e.g.,add
command
) become a single method name with
an underscore (e.g.,add_command
), and
the “configure” method can be abbreviated as “config,” as in Tcl. In
Chapter 8
, we will also see that tkinter
“variables” associated with widgets take the form of class instance
objects (e.g.,StringVar
,IntVar
) withget
andset
methods, not simple Python or Tcl variable names.
Table 7-2
shows some of the primary language
mappings in more concrete terms.
Table 7-2. Tk-to-tkinter mappings
Operation | Tcl/Tk | Python/tkinter |
---|---|---|
Creation | Frame .panel | panel = |
Masters | button | quit = |
Options | button .panel.go -fg | go = Button(panel, |
Configure | .panel.go config -bg | go.config(bg='red') |
Actions | .popup | popup.invoke() |
Packing | pack .panel -side left | panel.pack(side=LEFT, |
Some of these differences are more than just syntactic, of course.
For instance, Python builds an internal widget object tree based on parent
arguments passed to widget constructors, without ever requiring
concatenated widget pathname strings. Once you’ve made a widget object,
you can use it directly by object reference. Tcl coders can hide some
dotted pathnames by manually storing them in variables, but that’s not
quite the same as Python’s purely object-based model.
Once you’ve written a few Python/tkinter scripts, though, the coding
distinctions in the Python object world will probably seem trivial. At the
same time, Python’s support for object-oriented techniques adds an
entirely new component to Tk development; you get the same widgets, plus
Python’s support for code structure and reuse.