tkinter GUIs always
have an application root window, whether you get it by
default or create it explicitly by calling theTk
object constructor. This main root window is
the one that opens when your program runs, and it is where you generally
pack your most important and long-lived widgets. In addition, tkinter
scripts can create any number of independent windows, generated and popped
up on demand, by creatingToplevel
widget objects.
EachToplevel
object created
produces a new window on the display and automatically adds it to the
program’s GUI event-loop processing stream (you don’t need to call themainloop
method of new windows to
activate them).
Example 8-3
builds a root and two pop-up windows.
Example 8-3. PP4E\Gui\Tour\toplevel0.py
import sys
from tkinter import Toplevel, Button, Label
win1 = Toplevel() # two independent windows
win2 = Toplevel() # but part of same process
Button(win1, text='Spam', command=sys.exit).pack()
Button(win2, text='SPAM', command=sys.exit).pack()
Label(text='Popups').pack() # on default Tk() root window
win1.mainloop()
The
toplevel0
script gets a root window by
default (that’s what theLabel
is
attached to, since it doesn’t specify a real parent), but it also creates
two standaloneToplevel
windows that
appear and function independently of the root window, as seen in
Figure 8-3
.
Figure 8-3. Two Toplevel windows and a root window
The twoToplevel
windows on the
right are full-fledged windows; they can be independently iconified,
maximized, and so on.Toplevel
s are
typically used to implement multiple-window displays and pop-up modal and
nonmodal dialogs (more on dialogs in the next section). They stay up until
they are explicitly destroyed or until the application that created them
exits.
In fact, as coded here, pressing theX
in the upper right corner of either of theTop
level
windows kills that window only. On
the other hand, the entire program and all it remaining windows are closed
if you press either of the created buttons or the main window’sX
(more on shutdown protocols in a
moment).
It’s important to know that althoughToplevel
s are independently active windows, they
are not separate processes; if your program exits, all of its windows are
erased, including allToplevel
windows
it may have created. We’ll learn how to work around this rule later by
launching independent GUI programs.
AToplevel
is roughly like aFrame
that is split off into its own
window and has additional methods that allow you to deal with top-level
window properties. TheTk
widget is roughly
like aToplevel
, but it is used to
represent the application root window.Top
level
windows have parents, butTk
windows do not—they are the true roots of
the widget hierarchies we build when making tkinter GUIs.
We got aTk
root for free in
Example 8-3
because theLabel
had a default parent,
designated by not having a widget in the first argument of its
constructor call:
Label(text='Popups').pack() # on default Tk() root window
PassingNone
to a widget
constructor’s first argument (or to itsmaster
keyword argument) has the same
default-parent effect. In other scripts, we’ve made theTk
root more explicit by creating it directly,
like this:
root = Tk()
Label(root, text='Popups').pack() # on explicit Tk() root window
root.mainloop()
In fact, because tkinter GUIs are a hierarchy, by default you
always
get at least oneTk
root window, whether it is named
explicitly, as here, or not. Though not typical, there may be more than
oneTk
root if you make them
manually, and a program ends if all itsTk
windows are closed. The firstTk
top-level window created—whether explicitly
by your code, or automatically by Python when needed—is used as the
default parent window of widgets and other windows if no parent is
provided.
You should generally use theTk
root window to display top-level information of some sort. If you don’t
attach widgets to the root, it may show up as an odd empty window when
you run your script (often because you used the default parent
unintentionally in your code by omitting a widget’s parent and didn’t
pack widgets attached to it). Technically, you can suppress the default
root creation logic and make multiple root windows with theTk
widget, as in
Example 8-4
.
Example 8-4. PP4E\Gui\Tour\toplevel1.py
import tkinter
from tkinter import Tk, Button
tkinter.NoDefaultRoot()
win1 = Tk() # two independent root windows
win2 = Tk()
Button(win1, text='Spam', command=win1.destroy).pack()
Button(win2, text='SPAM', command=win2.destroy).pack()
win1.mainloop()
When run, this script displays the two pop-up windows of the
screenshot in
Figure 8-3
only (there is no third root window). But it’s more common to use theTk
root as a main window and createToplevel
widgets for an application’s
pop-up windows. Notice how this GUI’s windows use a window’sdestroy
method to close just one window,
instead ofsys.exit
to shut down the
entire program; to see how this method really does its work, let’s move
on to window protocols.
BothTk
andToplevel
widgets
export extra methods and features tailored for their
top-level role, as illustrated in
Example 8-5
.
Example 8-5. PP4E\Gui\Tour\toplevel2.py
"""
pop up three new windows, with style
destroy() kills one window, quit() kills all windows and app (ends mainloop);
top-level windows have title, icon, iconify/deiconify and protocol for wm events;
there always is an application root window, whether by default or created as an
explicit Tk() object; all top-level windows are containers, but they are never
packed/gridded; Toplevel is like Frame, but a new window, and can have a menu;
"""
from tkinter import *
root = Tk() # explicit root
trees = [('The Larch!', 'light blue'),
('The Pine!', 'light green'),
('The Giant Redwood!', 'red')]
for (tree, color) in trees:
win = Toplevel(root) # new window
win.title('Sing...') # set border
win.protocol('WM_DELETE_WINDOW', lambda:None) # ignore close
win.iconbitmap('py-blue-trans-out.ico') # not red Tk
msg = Button(win, text=tree, command=win.destroy) # kills one win
msg.pack(expand=YES, fill=BOTH)
msg.config(padx=10, pady=10, bd=10, relief=RAISED)
msg.config(bg='black', fg=color, font=('times', 30, 'bold italic'))
root.title('Lumberjack demo')
Label(root, text='Main window', width=30).pack()
Button(root, text='Quit All', command=root.quit).pack() # kills all app
root.mainloop()
This program adds widgets to theTk
root window, immediately pops up threeTop
level
windows with attached buttons, and
uses special top-level protocols. When run, it generates the scene
captured in living black-and-white in
Figure 8-4
(the buttons’ text
shows up blue, green, and red on a color display).
Figure 8-4. Three Toplevel windows with configurations
There are a few operational details worth noticing here, all of
which are more obvious if you run this script on your machine:
protocol
Because the
window manager close event has been intercepted by
this script using the top-level widgetprotocol
method, pressing theX
in the top-right corner doesn’t do
anything in the threeToplevel
pop ups. The name stringWM_DELETE_WINDOW
identifies the close
operation. You can use this interface to disallow closes apart
from the widgets your script creates. The function created by this
script’slambda:None
does
nothing but returnNone
.
destroy
Pressing the big black buttons in any one of the three pop
ups kills that pop up only, because the pop up runs the widgetdestroy
method. The other
windows live on, much as you would expect of a pop-up dialog
window. Technically, this call destroys the subject widget and any
other widgets for which it is a parent. For windows, this includes
all their content. For simpler widgets, the widget is
erased.
BecauseToplevel
windows have parents, too, their relationships might
matter on adestroy
—destroying
a window, even the automatic or first-madeTk
root which is used as the default
parent, also destroys all its child windows. SinceTk
root windows
have no parents, they are unaffected by destroys of other windows.
Moreover, destroying the lastTk
root window remaining (or the onlyTk
root created) effectively
ends the program.Toplevel
windows, however, are always destroyed with their parents, and
their destruction doesn’t impact other windows to which they are
not ancestors. This makes them ideal for pop-up dialogs.
Technically, aToplevel
can be
a child of any type of widget and will be destroyed with it,
though they are usually children of an automatic or explicitTk
.
quit
To kill all the windows at
once and end the GUI application (really, its activemainloop
call), the root
window’s button runs thequit
method instead. That is, pressing the root window’s button ends
the program. In general, thequit
method immediately ends the entire
application and closes all its windows. It can be called through
any tkinter widget, not just through the top-level window; it’s
also available on frames, buttons, and so on. See the discussion
of thebind
method and its
events later in
this chapter for more onquit
anddestroy
.
title
As introduced in
Chapter 7
, top-level window
widgets (Tk
andToplevel
) have atitle
method that lets you change the
text displayed on the top border. Here, the window title text is
set to the string'Sing...'
in
the pop-ups to override the default'tk'
.
iconbitmap
Theiconbitmap
method
changes a top-level window’s icon. It accepts an
icon or bitmap file and uses it for the window’s icon graphic when
it is both minimized and open. On Windows, pass in the name of a
.ico
file (this example uses one in the
current directory); it will replace the default red “Tk” icon that
normally appears in the upper-lefthand corner of the window as
well as in the Windows taskbar. On other platforms, you may need
to use other icon file conventions if the icon calls in this book
won’t work for you (or simply comment-out the calls altogether if
they cause scripts to fail); icons tend to be a platform-specific
feature that is dependent upon the underlying window
manager.
Top-level windows are containers for other widgets, much
like a standaloneFrame
. Unlike
frames, though, top-level window widgets are never themselves
packed (or gridded, or placed). To embed widgets, this script
passes its windows as parent arguments to label and button
constructors.
It is also possible to
fetch the maximum window size (the physical screen
display size, as a [width, height] tuple) with themaxsize()
method, as well as set the
initial size of a window with the top-levelgeometry("
width
x
height
+
x
+
y
")
method. It is generally
easier and more user-friendly to let tkinter (or your users) work
out window size for you, but display size may be used for tasks
such as scaling images (see the discussion on PyPhoto in
Chapter 11
for an example).
In addition, top-level window widgets support other kinds of
protocols that we will utilize later on in this tour:
Theiconify
andwithdraw
top-level
window object methods allow scripts to hide and
erase a window on the fly;deiconify
redraws a hidden or erased
window. Thestate
method
queries or changes a window’s state; valid states passed in or
returned includeiconic
,withdrawn
,zoomed
(full screen on Windows: usegeometry
elsewhere), andnormal
(large enough for window
content). The methodslift
andlower
raise and lower a window
with respect to its siblings (lift
is the Tkraise
command, but avoids a Python
reserved word). See the alarm scripts near the end of
Chapter 9
for usage.
Each top-level
window can have its own window menus too; both theTk
and theToplevel
widgets have amenu
option used to associate a
horizontal menu bar of pull-down option lists. This menu bar looks
as it should on each platform on which your scripts are run. We’ll
explore menus early in
Chapter 9
.
Most top-level window-manager-related methods can also be named
with a “wm_” at the front; for instance,state
andprotocol
can also be calledwm_state
andwm_protocol
.
Notice that the script in
Example 8-3
passes itsToplevel
constructor calls an explicit parent
widget—theTk
root window (that is,Toplevel(root)
).Toplevel
s can be associated with a parent just
as other widgets can, even though they are not visually embedded in
their parents. I coded the script this way to avoid what seems like an
odd feature; if coded instead like this:
win = Toplevel() # new window
and if noTk
root yet exists,
this call actually generates a defaultTk
root window to serve as theToplevel
’s parent, just like any other widget
call without a parent argument. The problem is that this makes the
position of the following line crucial:
root = Tk() # explicit root
If this line shows up above theToplevel
calls, it creates the single root
window as expected. But if you move this line below theToplevel
calls, tkinter creates a defaultTk
root window that is different from
the one created by the script’s explicitTk
call. You wind up with twoTk
roots just as in
Example 8-4
. Move theTk
call below theTop
level
calls and rerun it to see what I
mean. You’ll get a fourth window that is completely empty! As a rule of
thumb, to avoid such oddities, make yourTk
root windows early on and make them
explicit.
All of the top-level protocol interfaces are available only on
top-level window widgets, but you can often access them by going through
other widgets’master
attributes—links to the widget parents. For example, to set the title of
a window in which a frame is contained, say something like this:
theframe.master.title('Spam demo') # master is the container window
Naturally, you should do so only if you’re sure that the frame
will be used in only one kind of window. General-purpose attachable
components coded as classes, for instance, should leave window property
settings to their client applications.
Top-level widgets have additional tools, some of which we may not
meet in this book. For instance, under Unix window managers, you can
also set the name used on the window’s icon (iconname
). Because some icon options may be
useful when scripts run on Unix only, see other Tk and tkinter resources
for more details on this topic. For now, the next scheduled stop on this
tour explores one of the more common uses of top-level
windows.