At some point in your Python experience, you might want to create a program with a graphical user interface or GUI (pronounced “gooey” by most). Many toolkits for Python exist to accomplish this, including Python’s own native GUI library, TkInter. Setting up GUIs in Tkinter isn’t complicated, though in the end it depends on the complexity of your own design. Here we will look at of the commonly used parts of the Tkinter module through some simple GUI examples. Before getting into the examples, let’s briefly cover the commonly used widgets (building blocks of the GUI) the Tkinter library has to offer.
In a nutshell all of Tkinter’s widgets provide either simple input (user-input) to the GUI, output (graphical display of information) from the GUI, or a combination of the two. The following are two tables of the most commonly used widgets for input and output with brief descriptions.
Tkinter widgets commonly used for input:
|Entry||the Entry widget is the most basic text box, usually a one line-high field where the user can type text; doesn’t allow much formatting|
|Text||Lets the user enter multiple lines of text, and stores the text; provides formatting options (styles and attributes)|
|Button||A basic method for the user to tell the GUI to perform a command, e.g. “OK” or “Cancel” in a dialog|
|Radiobutton||Lets the user choose one option from a list|
|Checkbutton||Lets the user choose multiple options from a list|
Tkinter widgets commonly used for output:
|Label||Most basic way to display info through text or images|
|PhotoImage/BitmapImage||These are more like class objects than actual widgets, as they must be used in conjunction with Labels or other display widgets; they display images and bitmaps, respectively|
|Listbox||Displays a list of text items, from which the user can highlight one (or many, depending on the configuration)|
|Menu||Provides a menu for the user|
Though the widgets above are grouped into “input” and “output” categories, in reality they all are dually for input and output, especially when callbacks (or event bindings) come into play. We’ll examine callbacks and event bindings more in a bit, for now we’ll look at some examples of some of the widgets listed above in applications.
To use the Tkinter module, we have to import it into our global namespace. This is often done with a statement like this:
import tkinter as tk
import Tkinter as tk
This gives us access to the module without having to type out the full module name each time. You can see this when we reference it:
>>> tk <module 'Tkinter' from 'C:\Python27\lib\lib-tk\Tkinter.pyc'>
Alright, we’re ready to make our GUI now—but before we do let’s briefly visualize how we want it to look, and examine Tkinter GUI structure. We’ll keep our design simple for now, one output widget and one input widget – a
Label widget and an
Every Tkinter widget is governed by or belongs to another widget called its “parent” (the widgets that belong to another are called that widget’s “children”). The one widget in a GUI that doesn’t have a parent is the master (or root) window. All other widgets should be the root window’s children. Also, a widget is usually physically located inside of their parent. This is the basic structure. Now let’s begin building ours. Step one is to create our root window:
root1 = tk.Tk()
A Tkinter Tk object is our root window that we can stuff with children. The first is our Label.
label = tk.Label(root1, text='our label widget')
Presto, we have a Label. The first argument we used was the name of the parent (
root1). The second argument was a standard keyword option for Labels to set the text it will display, if any.
Next we initialize our input widget, the Entry:
entry = tk.Entry(root1)
With the Label and Entry widgets initialized and options we want set, what’s left is to tell Tkinter to physically put them inside their parent on the screen. Though we set the parent and options, our widgets won’t be displayed unless we specifically tell Tkinter to display them.
To handle this process smoothly, Tkinter provides some built-in graphical organizers called geometry managers. The following table has descriptions of each of the geometry managers available.
|pack||used to fill space in the parent with widgets|
|grid||puts widgets that it manages on a grid in the parent. each widget has its own box or cell in the grid, though they can be made to cover multiple cells with some option|
|place||Allows explicit setting of the size and position of a window; used mostly for implementing custom window layouts rather than ordinary layouts|
As we can see from the description of the third manager, the pack and grid managers are the ones used for regular common layouts, and we’ll be using pack for this GUI. We want our Label to be on top of the Entry, so we’ll pack it first and use an option to set it on top, then pack the Entry after that (it will automatically show below the Label):
Done! Now our widgets will be seen. However, you’ll notice that if you’re in Python 2.7 IDLE, no GUI has shown up yet. Just as with any other widget, Tkinter must be told to display the root window, but for the root window instead of a geometry manager the main Tk loop has to be called, like so:
Now we should be able to see a new window that looks something like this…
It might differ depending on your settings/environment, but not by much. With the Entry box we can type as much text as we want into the window, though it won’t do anything but sit there since we haven’t made it to do anything else (even if we hit “Enter,” the same goes for the Label).
Let’s close this one and look at an example with more widgets. We’ll also take a quick look at event bindings and the grid manager. Note that after we close our GUI, our root window and all its children will become unusable, as you’ll discover if you try to start root1’s mainloop again, or try to change label’s configuration.
For our second GUI we’ll have a Label and 3 Radiobuttons. We’ll start as we did before with the root window, then initializing the Label:
root2 = tk.Tk() label2 = tk.Label(root2, text='Choose a button')
We have our root window and Label now, but before initializing the Radiobuttons we’ll create a Tkinter variable object to keep track of the values that each Radiobutton holds:
# Object of the Tkinter StringVar class buttonstr = tk.StringVar()
Now we’ll create the three Radiobuttons and connect their values to our buttonstr variable:
buttonA = tk.Radiobutton(root2, text='Button A', variable=buttonstr, value='ButtonA string') buttonB = tk.Radiobutton(root2, text='Button B', variable=buttonstr, value='ButtonB string') buttonC = tk.Radiobutton(root2, text='Button C', variable=buttonstr, value='ButtonC string')
With our Radiobuttons created, we’ll now examine what happens when we use callbacks to connect methods to each of them. Callbacks are used to tell the GUI to perform some command or action when a widget becomes active. We’ll use callbacks to print the value for each Radiobutton when it’s selected:
def showstr(event=None): print(buttonstr.get())
This function we just defined does nothing more than print the value of our StringVar accessed using its get() method. We can now reference this function as a callback in our Radiobuttons by using the
buttonA.config(command=showstr) buttonB.config(command=showstr) buttonC.config(command=showstr)
Alright now that all of our widgets are initialized we’ll set them up for display with the grid manager:
label2.grid(column=0, row=0) buttonA.grid(column=0, row=1) buttonB.grid(column=0, row=2) buttonC.grid(column=0, row=3)
We used the options
row to tell grid exactly where to place the widgets, but if we didn’t grid would display them in the default order (column is zero). Without further ado, we’ll now run our GUI.
It should look like this:
Note: before we have selected one it appears that all of the Radiobuttons are selected—this is simply because we did not select a default Radiobutton with the select() method. If we had used this method on the first button, it would display like so:
Now if we select any of the radio-buttons (for example,
B), then we should see the results of the callbacks for each of them:
>>> root2.mainloop() ButtonA string ButtonC string ButtonB string
In addition to callbacks which are used to link functions to the command options of widgets, event bindings can be used to link certain user “events” (e.g. keystrokes and mouse clicks) to widgets that lack a command option. Let’s try this with our first GUI example. With all other settings the same, right after packing the Entry box and before starting root1’s mainloop we’ll set an event binding for the “Enter” key so that if it’s pressed, the text in the Entry box will be printed to the IDLE window. We’ll need a method to do this first:
# Prints the entry text def showentry(event): print(entry.get())
Now that we have the method to display entry’s text, we have to bind the event (when the user hits the “Enter” key with the entry box in focus):
>>> entry.bind('<Return>', showentry) '29748480showentry'
With this line we used the
bind method (available to every widget) to connect our keyboard event (the first argument) to our method (the second argument). Also note that our event name is
return key, and NOT
as this marks a completely different kind of event in Tkinter. The short string of numbers followed by our function name that was returned can be ignored, it doesn’t mean anything special to us.
If we now run the mainloop of the root window, type some text into our Entry box, and hit the
Enter key, Python will display what we typed (try it!). This same functionality can be used with any widget in your Tkinter GUI, and also multiple widgets can be bound simultaneously with the
bind_class methods. In the same way unbound with the
That wraps up our introduction to the Python Tkinter library—the tools it provides are basic compared to some of the other third-party packages available out there, but don’t be afraid to get creative with it!