Hello, Again

When you write larger programs, it is usually a good idea to
wrap your code up in one or more classes. The following example is
adapted from the “hello world” program in Matt
Conway’s A Tkinter
Life Preserver (dead link).

Running the Example

If you click the right button, the text “hi there,
everyone!” is printed to the console. If you click the
left button, the program stops.

Note: Some Python development environments have problems
running Tkinter examples like this one. The problem is usually that
the enviroment uses Tkinter itself, and the mainloop call and
the quit calls interact with the environment’s expectations. Other environments may misbehave if you leave out the explicit destroy call. If the example doesn’t behave as expected, check for Tkinter-specific documentation for your development environment.

Details

This sample application is written as a class. The constructor
(the __init__ method) is called with a parent widget (the
master), to which it adds a number of child widgets. The
constructor starts by creating a Frame widget. A frame is
a simple container, and is in this case only used to hold the other
two widgets.

This time, we pass a number of options to the
constructor, as keyword arguments. The first button is labelled
“QUIT”, and is made red (fg is short for foreground). The second is labelled “Hello”. Both
buttons also take a command option. This option specifies
a function, or (as in this case) a bound method, which will be
called when the button is clicked.

The button instances are stored in instance attributes. They are
both packed, but this time with the side=LEFT argument.
This means that they will be placed as far left as possible in the
frame; the first button is placed at the frame’s left edge, and the
second is placed just to the right of the first one (at the left
edge of the remaining space in the frame, that is). By
default, widgets are packed relative to their parent (which is
master for the frame widget, and the frame itself for the
buttons). If the side is not given, it defaults to TOP.

The “hello” button callback is given next. It
simply prints a message to the console everytime the button is
pressed:

def say_hi(self):
print "hi there, everyone!"

Finally, we provide some script level code that creates a Tk
root widget, and one instance of the App class using the root
widget as its parent:

root = Tk()
app = App(root)
root.mainloop()
root.destroy()

The mainloop call enters the Tk event loop, in which the application will stay until the quit method is called (just click the QUIT button), or the window is closed.

The destroy call is only required if you run this example under certain development environments; it explicitly destroys the main window when the event loop is terminated. Some development environments won’t terminate the Python process unless this is done.

More on widget references

In the second example, the frame widget is stored in a local
variable named frame, while the button widgets are stored
in two instance attributes. Isn’t there a serious problem hidden in
here: what happens when the __init__ function returns and
the frame variable goes out of scope?

Just relax; there’s actually no need to keep a reference to the
widget instance. Tkinter automatically maintains a widget tree (via
the master and children attributes of each widget
instance), so a widget won’t disappear when the application’s last
reference goes away; it must be explicitly destroyed before this
happens (using the destroy method). But if you wish to do
something with the widget after it has been created, you better
keep a reference to the widget instance yourself.

Note that if you don’t need to keep a reference to a widget, it
might be tempting to create and pack it on a single line:

Button(frame, text="Hello", command=self.hello).pack(side=LEFT)

Don’t store the result from this operation; you’ll only get
disappointed when you try to use that value (the pack
method returns None). To be on the safe side, it might be
better to always separate construction from packing:

w = Button(frame, text="Hello", command=self.hello)
w.pack(side=LEFT)

More on widget names

Another source of confusion, especially for those who have some
experience of programming Tk using Tcl, is Tkinter’s notion of the
widget name. In Tcl, you must explicitly name each widget.
For example, the following Tcl command creates a Button
named “ok”, as a child to a widget named
“dialog” (which in turn is a child of the root window,
“.”).

button .dialog.ok

The corresponding Tkinter call would look like:

ok = Button(dialog)

However, in the Tkinter case, ok and dialog
are references to widget instances, not the actual names of the
widgets. Since Tk itself needs the names, Tkinter automatically
assigns a unique name to each new widget. In the above case, the
dialog name is probably something like “.1428748,” and
the button could be named “.1428748.1432920”. If you
wish to get the full name of a Tkinter widget, simply use the
str function on the widget instance:

>>> print str(ok)
.1428748.1432920

(if you print something, Python automatically uses the
str function to find out what to print. But obviously, an
operation like “name = ok” won’t do the that, so make
sure always to explicitly use str if you need the
name).

If you really need to specify the name of a widget, you can use
the name option when you create the widget. One (and most
likely the only) reason for this is if you need to interface with
code written in Tcl.

In the following example, the resulting widget is named
“.dialog.ok” (or, if you forgot to name the dialog,
something like “.1428748.ok”):

ok = Button(dialog, name="ok")

To avoid conflicts with Tkinter’s naming scheme, don’t use names
which only contain digits. Also note that name is a
“creation only” option; you cannot change the name once
you’ve created the widget.