Starting with Tk 8.0, Tkinter supports platform independent font descriptors. You can specify a font as tuple containing a family name, a height in points, and optionally a string with one or more styles. Examples:
To get the default size and style, you can give the font name as a single string. If the family name doesn’t include spaces, you can also add size and styles to the string itself:
Here are some families available on most Windows platforms:
Arial (corresponds to Helvetica), Courier New (Courier), Comic Sans MS, Fixedsys, MS Sans Serif, MS Serif, Symbol, System, Times New Roman (Times), and Verdana:
Note that if the family name contains spaces, you must use the tuple syntax described above.
The available styles are normal, bold, roman, italic, underline, and overstrike.
Tk 8.0 automatically maps Courier, Helvetica, and Times to their corresponding native family names on all platforms. In addition, a font specification can never fail under Tk 8.0; if Tk cannot come up with an exact match, it tries to find a similar font. If that fails, Tk falls back to a platform-specific default font. Tk’s idea of what is “similar enough” probably doesn’t correspond to your own view, so you shouldn’t rely too much on this feature.
Tk 4.2 under Windows supports this kind of font descriptors as well. There are several restrictions, including that the family name must exist on the platform, and not all the above style names exist (or rather, some of them have different names).
Font names
In addition, Tk 8.0 allows you to create named fonts and use their names when specifying fonts to the widgets.
The tkFont module provides a Font class which allows you to create font instances. You can use such an instance everywhere Tkinter accepts a font specifier. You can also use a font instance to get font metrics, including the size occupied by a given string written in that font.
If you modify a named font (using the config method), the changes are automatically propagated to all widgets using the font.
The Font constructor supports the following style options (note that the constants are defined in the tkFont module):
family
Font family.
size
Font size in points. To give the size in pixels, use a negative value.
weight
Font thickness. Use one of NORMAL or BOLD. Default is NORMAL.
slant
Font slant. Use one of NORMAL or ITALIC. Default is NORMAL.
underline
Font underlining. If 1 (true), the font is underlined. Default is 0 (false).
overstrike
Font strikeout. If 1 (true), a line is drawn over text written with this font. Default is 0 (false).
System fonts
Tk also supports system specific font names. Under X, these are usually font aliases like fixed, 6x10, etc.
Under Windows, these include ansi, ansifixed, device, oemfixed, system, and systemfixed:
On the Macintosh, the system font names are application and system.
Note that the system fonts are full font names, not family names, and they cannot be combined with size or style attributes. For portability reasons, avoid using these names wherever possible.
X Font Descriptors
X Font Descriptors are strings having the following format (the asterisks represent fields that are usually not relevant. For details, see the Tk documentation, or an X manual):
--family-weight-slant-–-size----*-charset
The font family is typically something like Times, Helvetica, Courier or Symbol.
The weight is either Bold or Normal. Slant is either R for “roman” (normal), I for italic, or O for oblique (in practice, this is just another word for italic).
Size is the height of the font in decipoints (that is, points multiplied by 10). There are usually 72 points per inch, but some low-resolution displays may use larger “logical” points to make sure that small fonts are still legible. The character set, finally, is usually ISO8859-1 (ISO Latin 1), but may have other values for some fonts.
The following descriptor requests a 12-point boldface Times font, using the ISO Latin 1 character set:
--Times-Bold-R-–-120----*-ISO8859-1
If you don’t care about the character set, or use a font like Symbol which has a special character set, you can use a single asterisk as the last component:
--Symbol---–-80-
A typical X server supports at least Times, Helvetica, Courier, and a few more fonts, in sizes like 8, 10, 12, 14, 18, and 24 points, and in normal, bold, and italic (Times) or oblique (Helvetica, Courier) variants. Most servers also support freely scaleable fonts. You can use programs like xlsfonts and xfontsel to check which fonts you have access to on a given server.
This kind of font descriptors can also be used on Windows and Macintosh. Note that if you use Tk 4.2, you should keep in mind that the font family must be one supported by Windows (see above).
Text Formatting
While text labels and buttons usually contain a single line of text, Tkinter also supports multiple lines. To split the text across lines, simply insert newline characters (\n) where necessary.
By default, the lines are centered. You can change this by setting the justify option to LEFT or RIGHT. The default value is CENTER.
You can also use the wraplength option to set a maximum width, and let the widget wrap the text over multiple lines all by itself. Tkinter attempts to wrap on whitespace, but if the widget is too narrow, it may break individual words across lines.
Borders
All Tkinter widgets have a border (though it’s not visible by default for some widgets). The border consists of an optional 3D relief, and a focus highlight region. Relief #
The relief settings control how to draw the widget border:
borderwidth (or bd)
This is the width of the border, in pixels. Most widgets have a default borderwidth of one or two pixels. There’s hardly any reason to make the border wider than that.
relief
This option controls how to draw the 3D border. It can be set to one of SUNKEN, RAISED, GROOVE, RIDGE, and FLAT.
Focus Highlights
The highlight settings control how to indicate that the widget (or one of its children) has keyboard focus. In most cases, the highlight region is a border outside the relief. The following options control how this extra border is drawn:
highlightcolor
This option is used to draw the highlight region when the widget has keyboard focus. It’s usually black, or some other distinct contrast color.
highlightbackground
This option is used to draw the highlight region when the widget doesn’t have focus. It’s usually same as the widget background.
highlightthickness
This option is the width of the highlight region, in pixels. It is usually one or two pixels for widgets that can take keyboard focus.
Cursors
cursor
This option controls which mouse cursor to use when the mouse is moved over the widget.
If this option isn’t set, the widget uses the same mouse pointer as its parent.
Note that some widgets, including the Text and Entry widgets, set this option by default.
As was mentioned earlier, a Tkinter application spends most of its time inside an event loop (entered via the mainloop method). Events can come from various sources, including key presses and mouse operations by the user, and redraw events from the window manager (indirectly caused by the user, in many cases).
Tkinter provides a powerful mechanism to let you deal with events yourself. For each widget, you can bind Python functions and methods to events.
1
widget.bind(event, handler)
If an event matching the event description occurs in the widget, the given handler is called with an object describing the event.
In this example, we use the bind method of the frame widget to bind a callback function to an event called . Run this program and click in the window that appears. Each time you click, a message like “clicked at 44 63” is printed to the console window.
Keyboard events are sent to the widget that currently owns the keyboard focus. You can use the focus_set method to move focus to a widget:
If you run this script, you’ll find that you have to click in the frame before it starts receiving any keyboard events.
Events
Events are given as strings, using a special event syntax:
The type field is the most important part of an event specifier. It specifies the kind of event that we wish to bind, and can be user actions like Button, and Key, or window manager events like Enter, Configure, and others. The modifier and detail fields are used to give additional information, and can in many cases be left out. There are also various ways to simplify the event string; for example, to match a keyboard key, you can leave out the angle brackets and just use the key as is. Unless it is a space or an angle bracket, of course.
Instead of spending a few pages on discussing all the syntactic shortcuts, let’s take a look on the most common event formats:
Event Formats
A mouse button is pressed over the widget. Button 1 is the leftmost button, button 2 is the middle button (where available), and button 3 the rightmost button. When you press down a mouse button over a widget, Tkinter will automatically “grab” the mouse pointer, and subsequent mouse events (e.g. Motion and Release events) will then be sent to the current widget as long as the mouse button is held down, even if the mouse is moved outside the current widget. The current position of the mouse pointer (relative to the widget) is provided in the x and y members of the event object passed to the callback.
You can use ButtonPress instead of Button, or even leave it out completely: <Button-1>, <ButtonPress-1>, and <1> are all synonyms. For clarity, I prefer the <Button-1> syntax.
The mouse is moved, with mouse button 1 being held down (use B2 for the middle button, B3 for the right button). The current position of the mouse pointer is provided in the x and y members of the event object passed to the callback.
Button 1 was released. The current position of the mouse pointer is provided in the x and y members of the event object passed to the callback.
Button 1 was double clicked. You can use Double or Triple as prefixes. Note that if you bind to both a single click (<Button-1>) and a double click, both bindings will be called.
The mouse pointer entered the widget (this event doesn’t mean that the user pressed the Enter key!).
The mouse pointer left the widget.
Keyboard focus was moved to this widget, or to a child of this widget.
Keyboard focus was moved from this widget to another widget.
The user pressed the Enter key. You can bind to virtually all keys on the keyboard. For an ordinary 102-key PC-style keyboard, the special keys are Cancel (the Break key), BackSpace, Tab, Return(the Enter key), Shift_L (any Shift key), Control_L (any Control key), Alt_L (any Alt key), Pause, Caps_Lock, Escape, Prior (Page Up), Next (Page Down), End, Home, Left, Up, Right, Down, Print, Insert, Delete, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Num_Lock, and Scroll_Lock.
The user pressed any key. The key is provided in the char member of the event object passed to the callback (this is an empty string for special keys).
a
The user typed an “a”. Most printable characters can be used as is. The exceptions are space (<space>) and less than (<less>). Note that 1 is a keyboard binding, while <1> is a button binding.
The user pressed the Up arrow, while holding the Shift key pressed. You can use prefixes like Alt, Shift, and Control.
The widget changed size (or location, on some platforms). The new size is provided in the width and height attributes of the event object passed to the callback.
The Event Object
The event object is a standard Python object instance, with a number of attributes describing the event.
Event Attributes
widget
The widget which generated this event. This is a valid Tkinter widget instance, not a name. This attribute is set for all events.
x, y
The current mouse position, in pixels.
x_root, y_root
The current mouse position relative to the upper left corner of the screen, in pixels.
char
The character code (keyboard events only), as a string.
keysym
The key symbol (keyboard events only).
keycode
The key code (keyboard events only).
num
The button number (mouse button events only).
width, height
The new size of the widget, in pixels (Configure events only).
type
The event type.
For portability reasons, you should stick to char, height, width, x, y, x_root, y_root, and widget. Unless you know exactly what you’re doing, of course…
Instance and Class Bindings
The bind method we used in the above example creates an instance binding. This means that the binding applies to a single widget only; if you create new frames, they will not inherit the bindings.
But Tkinter also allows you to create bindings on the class and application level; in fact, you can create bindings on four different levels:
the widget instance, using bind.
the widget’s toplevel window (Toplevel or root), also using bind.
the widget class, using bind_class (this is used by Tkinter to provide standard bindings).
the whole application, using bind_all.
For example, you can use bind_all to create a binding for the F1 key, so you can provide help everywhere in the application. But what happens if you create multiple bindings for the same key, or provide overlapping bindings?
First, on each of these four levels, Tkinter chooses the “closest match” of the available bindings. For example, if you create instance bindings for the and events, only the second binding will be called if you press the Enter key.
However, if you add a binding to the toplevel widget, both bindings will be called. Tkinter first calls the best binding on the instance level, then the best binding on the toplevel window level, then the best binding on the class level (which is often a standard binding), and finally the best available binding on the application level. So in an extreme case, a single event may call four event handlers.
A common cause of confusion is when you try to use bindings to override the default behavior of a standard widget. For example, assume you wish to disable the Enter key in the text widget, so that the users cannot insert newlines into the text. Maybe the following will do the trick?
def ignore(event): pass text.bind(““, ignore)
or, if you prefer one-liners:
text.bind(““, lambda e: None)
(the lambda function used here takes one argument, and returns None)
Unfortunately, the newline is still inserted, since the above binding applies to the instance level only, and the standard behavior is provided by a class level bindings.
You could use the bind_class method to modify the bindings on the class level, but that would change the behavior of all text widgets in the application. An easier solution is to prevent Tkinter from propagating the event to other handlers; just return the string “break” from your event handler:
By the way, if you really want to change the behavior of all text widgets in your application, here’s how to use the bind_class method:
top.bind_class(“Text”, ““, lambda e: None)
But there are a lot of reasons why you shouldn’t do this. For example, it messes things up completely the day you wish to extend your application with some cool little UI component you downloaded from the net. Better use your own Text widget specialization, and keep Tkinter’s default bindings intact:
In addition to event bindings, Tkinter also supports a mechanism called protocol handlers. Here, the term protocol refers to the interaction between the application and the window manager. The most commonly used protocol is called WM_DELETE_WINDOW, and is used to define what happens when the user explicitly closes a window using the window manager.
You can use the protocol method to install a handler for this protocol (the widget must be a root or Toplevel widget):
widget.protocol(“WM_DELETE_WINDOW”, handler)
Once you have installed your own handler, Tkinter will no longer automatically close the window. Instead, you could for example display a message box asking the user if the current data should be saved, or in some cases, simply ignore the request. To close the window from this handler, simply call the destroy method of the window:
Capturing destroy events
from Tkinter import * import tkMessageBox
def callback(): if tkMessageBox.askokcancel(“Quit”, “Do you really wish to quit?”): root.destroy()
Note that even you don’t register an handler for WM_DELETE_WINDOW on a toplevel window, the window itself will be destroyed as usual (in a controlled fashion, unlike X). However, as of Python 1.5.2, Tkinter will not destroy the corresponding widget instance hierarchy, so it is a good idea to always register a handler yourself:
top = Toplevel(…)
make sure widget instances are deleted
top.protocol(“WM_DELETE_WINDOW”, top.destroy)
Future versions of Tkinter will most likely do this by default.
Other Protocols
Window manager protocols were originally part of the X window system (they are defined in a document titled Inter-Client Communication Conventions Manual, or ICCCM). On that platform, you can install handlers for other protocols as well, like WM_TAKE_FOCUS and WM_SAVE_YOURSELF. See the ICCCM documentation for details.
Before we look at what to put in that application work area, let’s take a look at another important part of GUI programming: displaying dialogs and message boxes.
Starting with Tk 4.2, the Tk library provides a set of standard dialogs that can be used to display message boxes, and to select files and colors. In addition, Tkinter provides some simple dialogs allowing you to ask the user for integers, floating point values, and strings. Where possible, these standard dialogs use platform-specific mechanisms, to get the right look and feel.
Message Boxes
The tkMessageBox module provides an interface to the message dialogs.
The easiest way to use this module is to use one of the convenience functions: showinfo, showwarning, showerror, askquestion, askokcancel, askyesno, or askretrycancel. They all have the same syntax:
The title argument is shown in the window title, and the message in the dialog body. You can use newline characters (\n) in the message to make it occupy multiple lines. The options can be used to modify the look; they are explained later in this section.
The first group of standard dialogs is used to present information. You provide the title and the message, the function displays these using an appropriate icon, and returns when the user has pressed OK. The return value should be ignored.
Here’s an example:
try:
fp = open(filename)
except:
tkMessageBox.showwarning(
"Open file",
"Cannot open this file\n(%s)" % filename
)
return
The showinfo dialog The showwarning dialog The showerror dialog
The second group is used to ask questions. The askquestion function returns the strings “yes” or “no” (you can use options to modify the number and type of buttons shown), while the others return a true value of the user gave a positive answer (ok, yes, and retry, respectively).
if tkMessageBox.askyesno("Print", "Print this report?"):
report.print()
The askquestion dialog The askokcancel dialog The askyesno dialog The askretrycancel dialog
[Screenshots made on a Swedish version of Windows 95. Hope you don’t mind…]
Message Box Options
If the standard message boxes are not appropriate, you can pick the closest alternative (askquestion, in most cases), and use options to change it to exactly suit your needs. You can use the following options (note that message and title are usually given as arguments, not as options).
default constant
Which button to make default: ABORT, RETRY, IGNORE, OK, CANCEL, YES, or NO (the constants are defined in the tkMessageBox module).
icon (constant)
Which icon to display: ERROR, INFO, QUESTION, or WARNING
message (string)
The message to display (the second argument to the convenience functions). May contain newlines.
parent (widget)
Which window to place the message box on top of. When the message box is closed, the focus is returned to the parent window.
title (string)
Message box title (the first argument to the convenience functions).
type (constant)
Message box type; that is, which buttons to display: ABORTRETRYIGNORE, OK, OKCANCEL, RETRYCANCEL, YESNO, or YESNOCANCEL.
In the simple examples we’ve used this far, there’s only one window on the screen; the root window. This is automatically created when you call the Tk constructor, and is of course very convenient for simple applications:
from Tkinter import *
root = Tk()
create window contents as children to root…
root.mainloop()
If you need to create additional windows, you can use the Toplevel widget. It simply creates a new window on the screen, a window that looks and behaves pretty much like the original root window:
from Tkinter import *
root = Tk()
create root window contents…
top = Toplevel()
create top window contents…
root.mainloop()
There’s no need to use pack to display the Toplevel, since it is automatically displayed by the window manager (in fact, you’ll get an error message if you try to use pack or any other geometry manager with a Toplevel widget).
Menus
Tkinter provides a special widget type for menus. To create a menu, you create an instance of the Menu class, and use add methods to add entries to it:
add_command(label=string, command=callback) adds an ordinary menu entry.
add_separator() adds an separator line. This is used to group menu entries.
add_cascade(label=string, menu=menu instance) adds a submenu (another Menu instance). This is either a pull-down menu or a fold-out menu, depending on the parent.
In this example, we start out by creating a Menu instance, and we then use the config method to attach it to the root window. The contents of that menu will be used to create a menubar at the top of the root window. You don’t have to pack the menu, since it is automatically displayed by Tkinter.
Next, we create a new Menu instance, using the menubar as the widget parent, and the add_cascade method to make it a pulldown menu. We then call add_command to add commands to the menu (note that all commands in this example use the same callback), and add_separator to add a line between the file commands and the exit command.
Finally, we create a small help menu in the same fashion.
Toolbars
Many applications place a toolbar just under the menubar, which typically contains a number of buttons for common functions like open file, print, undo, etc.
In the following example, we use a Frame widget as the toolbar, and pack a number of ordinary buttons into it. Creating a simple toolbar
from Tkinter import *
root = Tk()
def callback(): print “called the callback!”
create a toolbar
toolbar = Frame(root)
b = Button(toolbar, text=”new”, width=6, command=callback) b.pack(side=LEFT, padx=2, pady=2)
b = Button(toolbar, text=”open”, width=6, command=callback) b.pack(side=LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)
mainloop()
The buttons are packed against the left side, and the toolbar itself is packed against the topmost side, with the fill option set to X. As a result, the widget is resized if necssary, to cover the full with of the parent widget.
Also note that I’ve used text labels rather than icons, to keep things simple. To display an icon, you can use the PhotoImage constructor to load a small image from disk, and use the image option to display it.
Status Bars
Finally, most applications sport a status bar at the bottom of each application window. Implementing a status bar with Tkinter is trivial: you can simply use a suitably configured Label widget, and reconfigure the text option now and then. Here’s one way to do it:
status = Label(master, text=””, bd=1, relief=SUNKEN, anchor=W) status.pack(side=BOTTOM, fill=X)
If you wish to be fancy, you can use the following class instead. It wraps a label widget in a convenience class, and provides set and clear methods to modify the contents.
The set method works like C’s printf function; it takes a format string, possibly followed by a set of arguments (a drawback is that if you wish to print an arbitrary string, you must do that as set(“%s”, string)). Also note that this method calls the update_idletasks method, to make sure pending draw operations (like the status bar update) are carried out immediately.
But the real trick here is that we’ve inherited from the Frame widget. At the cost of a somewhat awkward call to the frame widget’s constructor, we’ve created a new kind of custom widget that can be treated as any other widget. You can create and display the status bar using the usual widget syntax:
status = StatusBar(root) status.pack(side=BOTTOM, fill=X)
We could have inherited from the Label widget itself, and just extended it with set and clear methods. This approach have a few drawbacks, though:
It makes it harder to maintain the status bar’s integrity. Some team members may cheat, and use config instead of set. That’s not a big deal, until the day you decide to do some extra processing in the set method. Or the day you decide to use a Canvas widget to implement a fancier status bar.
It increases the risk that your additional methods conflict with attributes or methods used by Tkinter. While the Frame and Toplevel widgets have relatively few methods, other widgets can have several dozens of widget specific attributes and methods.
Future versions of Tkinter may use factory functions rather than class constructors for most widgets. However, it’s more or less guaranteed that such versions will still provide Frame and Toplevel classes. Better safe than sorry, in other words.