• No results found

Tkinter (Tk interface) is Python’s default GUI toolkit, although there are many others. It is reasonably simple and lightweight and should be perfectly adequate for GUI programs of small to medium complexity. Let’s have a look at a simple Tkinter program:

def draw_screen(): global text for i in range(20): print print text def get_event(): return raw_input() def process_event(event): global text if event == "quit": quit()

elif event == "hello":

text = "Hello to you too"

elif event == "goodbye": text = "Goodbye"

draw_screen()

process_event("quit")

else:

print "I don't know that event"

# set up a global variable

text = ""

# start the event loop

while True:

event = get_event() process_event(event) draw_screen()

from Tkinter import *

# create the root window

root = Tk()

# add a label

label = Label(root, text="Hello World") label.grid(column=0,row=0)

# enter the main loop

root.mainloop()

If you run1the above program, you should see a window which looks like Figure 13.2.

Figure 13.2: Tkinter Hello World .

The structure of the program above is typical of a Tkinter program and has the following general steps: 1. create the root window (root = Tk()),

2. add user interface elements (in this case a label),

3. specify where in the interface the elements should go (by calling the grid method),

4. enter the main loop.

An astute reader may be asking what sort of variables are root and label? These are special types of variables defined by the Tkinter toolkit. Such variables are called objects, and are the subject of object-

oriented programming. Python is a multi-paradigm language, and we’ve focused mostly on the procedural paradigm in this book. Object-oriented programming is a different sort of paradigm - something you will learn much more of in COMP160. For now, let’s treat these objects just like other variables, except that we access functions associated with the objects using the dot-notation as above (e.g. label.grid(...)). You may also have noticed the special form of some of the function calls like Label(root, text=’Hello World’). The parameter text=’Hello World’ is an example of a named parameter. Named parame- ters are extremely useful when combined with default parameter values. Here’s an example:

1In general, Tkinter programs should not be run from within Idle. The easiest way is to use a terminal and run from the command line.

def read_value_type(prompt='Enter a value> ', convert=float): val = input(prompt)

return convert(val)

print read_value_type()

print read_value_type(prompt='Enter a float> ')

print read_value_type(convert=int)

print read_value_type(prompt='Enter a boolean> ', convert=bool)

The only limitation on default parameters is that they must come after non-default parameters in the func- tion definition.

One other piece of notation you should be familiar with is the term widget. A widget is a GUI element. Every GUI element can be called a widget. Tkinter has several different types of widgets. The most useful ones are:

Tk: the root widget,

Label: a widget containing fixed text, Button: a button widget,

Entry: a widget for entry of single lines of text,

Canvas: a widget for drawing shapes onto the screen.

13.3

Introducing callbacks

To do anything really useful with GUI programming, we need some way of associating an action with a widget. Usually, when the user presses a button on a GUI she expects something to happen as a result. Let’s make our Hello World program a bit more complex, by adding two buttons:

from Tkinter import * def say_hi():

print "hi there"

root = Tk()

b1 = Button(root, text="QUIT", fg="red", command=root.quit) b1.grid(column=1,row=0)

b2 = Button(root, text="Hello", command=say_hi) b2.grid(column=0,row=0)

root.mainloop()

If we run the above program, we will get a window that looks like Figure 13.3. In both calls to the function Button, there is a parameter called command. The value of the command parameter is the callback or function associated with the action of pressing the button. So, if we press the Hello button, it should print ‘hi there’ to the system console because that’s what the function say hi does. If we press the Quit button, then the special function root.quit will be called which, unsurprisingly, causes the application to terminate.

Figure 13.3: Tkinter Buttons .

Since we’re writing a GUI program, it would be very unusual (and quite disconcerting) to make use of the console at all. When we want to give a message to the user, it is more usual to use a dialog widget. The module tkMessageBox includes numerous useful dialogs. The above program can be modified to use a showinfodialog box instead:

from Tkinter import * from tkMessageBox import * def say_hi():

showinfo(message="Hi there") root = Tk()

b1 = Button(root, text="Hello", command=say_hi) b1.grid(column=0,row=0)

b2 = Button(root, text="QUIT", fg="red", command=root.quit) b2.grid(column=1,row=0)

root.mainloop()

Now when we press the Hello button, we should see something similar to Figure 13.4.

Figure 13.4: Tkinter Hello World Dialog .

13.4

User input

Tkinter has several widgets available for getting input from the user. Dialog boxes allow one sort of input, but most interfaces have some form of input on the main window. There are two main widgets for doing this: Entry and Text. We will use the Entry widget for our simple application.

To make use of user input widgets, we need a mechanism for getting information back out of the widget. The easiest way to do that is to associate a variable with the widget when the widget is created. Tkinter has special types that you can use for this purpose. They are: StringVar, IntVar, DoubleVar, and BooleanVar, which hold a string, integer, double value or boolean value respectively.

Let’s have a look at an example of how to use an Entry widget:

from Tkinter import * from tkMessageBox import * def say_hi():

global inVal

showinfo(message="Hi there "+inVal.get()) root = Tk()

b1 = Button(root, text="Hello", command=say_hi) b1.grid(column=2,row=0)

b2 = Button(root, text="QUIT", fg="red", command=root.quit) b2.grid(column=3,row=0)

inVal = StringVar()

input = Entry(root, textvar=inVal) input.grid(column=1,row=0)

label = Label(root, text="Enter your name: ") label.grid(column=0,row=0)

root.mainloop()

The most important thing to notice about this program, is that we have created a variable called inVal which we have associated with the input variable. When the callback say hi is called, the string rep- resentation of inVal is retrieved and included as part of the dialog message. The main window for this program can be seen in Figure 13.5.

Figure 13.5: Tkinter Entry Example .

13.5

Mini-case study

Let’s write a simple GUI program that can convert from Fahrenheit to Celsius. The conversion equation is quite simple:

c = 5

9(f − 32), (13.1)

where f is the temperature in Fahrenheit and c is the temperature in Celsius.

For this program, we’ll need an Entry widget, an output Label widget, a Button widget to do the con- version and a Button widget to quit the application. For our Entry widget, we want to input numbers and not text, and since we need real numbers, a DoubleVar is the appropriate type to use. The program, tk converter1.py, is shown below:

from Tkinter import * def convert(): global inVal global outputLabel dval = inVal.get() dval = (dval-32)*5.0/9.0 outputLabel.configure(text = str(dval)) root = Tk() inVal = DoubleVar()

outputLabel = Label(root, text="0.0", width=20) outputLabel.grid(column=1,row=1)

outputName = Label(root, text="Fahrenheit", width=20) outputName.grid(column=1, row=0)

inputEntry = Entry(root, textvariable=inVal) inputEntry.grid(column=0,row=1)

inputName = Label(root, text="Celsius") inputName.grid(column=0, row=0)

convertButton = Button(root, text="Convert", command=convert) convertButton.grid(row=2, column=0)

quitButton = Button(root, text="Quit", command=root.quit) quitButton.grid(row=2, column=1)

root.mainloop()

The function convert does the conversion. Notice how it first gets the value of inVal which is what the user entered into the entry field. Then it does the conversion, and finally, it changes the text on the output field by calling the configure method. If you type in and run the above program, you should get an application that looks like Figure 13.6.

Figure 13.6: Fahrenheit to Celsius Converter .

13.6

Glossary

Graphical User Interface (GUI): a program interface using graphical output and input is often taken di- rectly from the keyboard and mouse.

Event Driven: A programming style where the flow of control of the program is determined by external events such as mouse-clicks, keyboard presses, etc.

Global variables: Variables that can be accessed from anywhere within a program. The use of global vari- ables should be limited as much as possible.

Objects: A special type of compound variable that has specific functions (called methods) associated with that variable type.

Object-Oriented Programming: Programming in a language that supports objects.

Named Parameter: When making a function call it is possible to explicitly name the parameters as in read value type(prompt=’Enter a float> ’).

Default Parameter: A parameter which is assigned a default value in a function definition.

Widget: A GUI element such as a window, button, label, etc.

13.7

Laboratory exercises

1. Add a function called convert f to c to tk converter1.py. convert f to c should take the temperature in Fahrenheit as input and return the temperature in Celsius. It should pass the following doctests: def convert_f_to_c(f): """ >>> c = convert_f_to_c(-40) >>> abs(c+40.0)<1e-8 True >>> c = convert_f_to_c(100) >>> abs(c-37.7777777777)<1e-8 True """

Note that this is the correct way to test equality of real numbers, you should never use a direct com- parison. You can have the doctests tested each time you run the program by including the following code just before the call to root.mainloop:

import doctest doctest.testmod()

2. Modify the function convert so that it makes use of the new function convert f to c. 3. Create a new function called convert c to f and devise appropriate doctests to test it.

4. In the convert function, create a variable called forwardConvert which can be either True or False. Modify convert to use an if-else statement to call convert f to c if forwardConvert is True and convert c to f if forwardConvert is False.

5. Add a Checkbutton button to your interface with the following code:

do_f_to_c = BooleanVar() do_f_to_c.set(True)

check = Checkbutton(root, text="do forward conversion", variable=do_f_to_c) check.grid(column=0,row=3)

6. Finally, modify the convert function by adding the following line:

forwardConvert = do_f_to_c.get()

Verify that selecting or unselecting the check box allows you to convert from Fahrenheit to Celsius or from Celsius to Fahrenheit.

7. Extension Exercise: Using the code from section 13.4 as a starting point write a program which reads mathematical expressions from the user and displays a message box containing the result.

Lecture 14

Case study: Catch

14.1

Graphics

The Tkinter canvas widget can be used to draw shapes to a window. We are going to spend the next two lectures building a simple computer game. The game is called catch — the game tosses the ball across the screen, and the player has to catch the ball with their mitt. The game is just for a bit of fun, but it will make use of several important concepts we’ve introduced so far in the course. Let’s start by creating a canvas in a file called circle.py and drawing a circle onto it:

from Tkinter import *

root = Tk()

gameCanvas = Canvas(root, width=800, height=600) gameCanvas.grid(column=0,row=0)

circle = gameCanvas.create_oval(400, 300, 425, 325, fill="blue") root.mainloop()

If you run the above program, you should get output similar to Figure 14.1. The important line in circle.py is the call to gameCanvas.create oval which can produce an ellipse shape (circles are a type of ellipse). The gameCanvas.create oval function returns a variable which we can use later to modify various aspects of the display.