$$ \newcommand{\tp}{\thinspace .} $$

 

 

 

This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.

A glimpse of graphical user interfaces

Maybe you find it somewhat strange that the usage of the programs we have made so far in this document - and the programs we will make in the rest of the book - are less graphical and intuitive than the computer programs you are used to from school or entertainment. Those programs are operated through some self-explaining graphics, and most of the things you want to do involve pointing with the mouse, clicking on graphical elements on the screen, and maybe filling in some text fields. The programs in this document, on the other hand, are run from the command line in a terminal window or inside IPython, and input is also given here in form of plain text.

The reason why we do not equip the programs in this document with graphical interfaces for providing input, is that such graphics is both complicated and tedious to write. If the aim is to solve problems from mathematics and science, we think it is better to focus on this part rather than large amounts of code that merely offers some "expected" graphical cosmetics for putting data into the program. Textual input from the command line is also quicker to provide. Also remember that the computational functionality of a program is obviously independent from the type of user interface, textual or graphic.

As an illustration, we shall now show a Celsius to Fahrenheit conversion program with a graphical user interface (often called a GUI). The GUI is shown in Figure 1. We encourage you to try out the graphical interface - the name of the program is c2f_gui.py. The complete program text is listed below.


Figure 1: Screen dump of the graphical interface for a Celsius to Fahrenheit conversion program. The user can type in the temperature in Celsius degrees, and when clicking on the is button, the corresponding Fahrenheit value is displayed.

from Tkinter import *
root = Tk()
C_entry = Entry(root, width=4)
C_entry.pack(side='left')
Cunit_label = Label(root, text='Celsius')
Cunit_label.pack(side='left')

def compute():
    C = float(C_entry.get())
    F = (9./5)*C + 32
    F_label.configure(text='%g' % F)

compute = Button(root, text=' is ', command=compute)
compute.pack(side='left', padx=4)

F_label = Label(root, width=4)
F_label.pack(side='left')
Funit_label = Label(root, text='Fahrenheit')
Funit_label.pack(side='left')

root.mainloop()

The goal of the forthcoming dissection of this program is to give a taste of how graphical user interfaces are coded. The aim is not to equip you with knowledge on how you can make such programs on your own.

A GUI is built of many small graphical elements, called widgets. The graphical window generated by the program above and shown in Figure 1 has five such widgets. To the left there is an entry widget where the user can write in text. To the right of this entry widget is a label widget, which just displays some text, here "Celsius". Then we have a button widget, which when being clicked leads to computations in the program. The result of these computations is displayed as text in a label widget to the right of the button widget. Finally, to the right of this result text we have another label widget displaying the text "Fahrenheit". The program must construct each widget and pack it correctly into the complete window. In the present case, all widgets are packed from left to right.

The first statement in the program imports functionality from the GUI toolkit Tkinter to construct widgets. First, we need to make a root widget that holds the complete window with all the other widgets. This root widget is of type Tk. The first entry widget is then made and referred to by a variable C_entry. This widget is an object of type Entry, provided by the Tkinter module. Widgets constructions follow the syntax

variable_name = Widget_type(parent_widget, option1, option2, ...)
variable_name.pack(side='left')

When creating a widget, we must bind it to a parent widget, which is the graphical element in which this new widget is to be packed. Our widgets in the present program have the root widget as parent widget. Various widgets have different types of options that we can set. For example, the Entry widget has a possibility for setting the width of the text field, here width=4 means that the text field is 4 characters wide. The pack statement is important to remember - without it, the widget remains invisible.

The other widgets are constructed in similar ways. The next fundamental feature of our program is how computations are tied to the event of clicking the button is. The Button widget has naturally a text, but more important, it binds the button to a function compute through the command=compute option. This means that when the user clicks the button is, the function compute is called. Inside the compute function we first fetch the Celsius value from the C_entry widget, using this widget's get function, then we transform this string (everything typed in by the user is interpreted as text and stored in strings) to a float before we compute the corresponding Fahrenheit value. Finally, we can update (configure) the text in the Label widget F_label with a new text, namely the computed degrees in Fahrenheit.

A program with a GUI behaves differently from the programs we construct in this document. First, all the statements are executed from top to bottom, as in all our other programs, but these statements just construct the GUI and define functions. No computations are performed. Then the program enters a so-called event loop: root.mainloop(). This is an infinite loop that "listens" to user events, such as moving the mouse, clicking the mouse, typing characters on the keyboard, etc. When an event is recorded, the program starts performing associated actions. In the present case, the program waits for only one event: clicking the button is. As soon as we click on the button, the compute function is called and the program starts doing mathematical work. The GUI will appear on the screen until we destroy the window by click on the X up in the corner of the window decoration. More complicated GUIs will normally have a special Quit button to terminate the event loop.

In all GUI programs, we must first create a hierarchy of widgets to build up all elements of the user interface. Then the program enters an event loop and waits for user events. Lots of such events are registered as actions in the program when creating the widgets, so when the user clicks on buttons, move the mouse into certain areas, etc., functions in the program are called and "things happen".

Many books explain how to make GUIs in Python programs, see for instance [3] [4] [5] [6].