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).
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.
Here’s an example:
from Tkinter import * def callback(): print "called the callback!" root = Tk() # create a menu menu = Menu(root) root.config(menu=menu) filemenu = Menu(menu) menu.add_cascade(label="File", menu=filemenu) filemenu.add_command(label="New", command=callback) filemenu.add_command(label="Open...", command=callback) filemenu.add_separator() filemenu.add_command(label="Exit", command=callback) helpmenu = Menu(menu) menu.add_cascade(label="Help", menu=helpmenu) helpmenu.add_command(label="About...", command=callback) mainloop()
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.
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.
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.
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.
class StatusBar(Frame): def __init__(self, master): Frame.__init__(self, master) self.label = Label(self, bd=1, relief=SUNKEN, anchor=W) self.label.pack(fill=X) def set(self, format, *args): self.label.config(text=format % args) self.label.update_idletasks() def clear(self): self.label.config(text="") self.label.update_idletasks()
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.