Lecture 22 — TKInter¶
- We will learn about event driven programming
- We will discuss graphical user interfaces (GUI)
- We will see how to use TkInter implementation for Python to develop GUIs
Graphical User Interface¶
All our programs so far had a terminal based input/output through Wing. This is the simplest type of user interface: enter some text and get some output.
In real life, we rarely use such programs. All Web interfaces and mobile apps involve elements like buttons, text input fields, radio buttons.
This type of a user interface is called a graphical user interface or GUI.
Designing a GUI is not a simple matter. It requires one to understand effective communication of information through visual interfaces. It is highly recommend you take a class on Visual Communication or Human Computer Interaction to learn more about these.
Many GUI packages will have some default settings that are meant to help you design interfaces that are at least not awful! So, make sure you do not override them until you know what you are doing.
Event Driven Programming¶
- A graphical user interface is designed to do something in response to an event.
- Example events are:
- Button push (most popular so far)
- Selection from a list of options
- Mouse movement
- The interface usually runs within a main window (often called
root) and listens continuously for events it knows how to process.
- Hence, a GUI is always in an infinite loop.
- When an event happens, it executes the function corresponding to the event and continues to listen.
- To finish the GUI loop, often an explicit termination command has to happen.
- TkInter is a Python module implementing the Tk toolkit for building graphical user interfaces. Other languages also have Tk implementations.
- Note: The most important distinction between regular and event based programming is that the action corresponding to an event gets executed in parallel with the rest of the program, it does not block the execution of functions in response to other events.
TkInter First Program¶
Let’s see a very basic program.
from Tkinter import * ### (1) root = Tk() ### (2) root.mainloop() ### (3) print "Hello" ### (4)
It is common to import everything from Tkinter to make it easy to write GUI programs. Compare this with the following program that does the same exact thing but requires using Tkinter for all calls to the module.
import Tkinter root = Tkinter.Tk() root.mainloop()
- We create the main window that the whole application will run in. This main window has many properties already: it can be resized and moved like any window on your computer.
- We tell the main window to run in an infinite loop, until it is explicitly terminated. In this case, you need to explicitly click on the upper left corner to close this application. This generates a “destroy” event, which terminates the main window.
- To understand the infinite loop notice that
print "Hello"command is not executed until the window terminates and the main loop ends.
Widgets and Containers¶
- GUIs involve two main types of objects: widgets and containers
- Widgets are objects that have a function and are visible. These
are the main elements of an interface. We will see the following
- Buttons: can do something when clicked
- Canvas: you can draw shapes or put shapes in them
- There are many other widgets like radio boxes, check boxes, menu of items, etc. You can learn these as we get comfortable with these two.
- Containers are invisible, but they allow us to group widgets and
arrange them in visual groups. We will see only one type of container
in this lecture:
- Frames: can contain any of the widgets of the above type
- Containers’ size will grow and shrink to fit the objects contained within. Containers can contain other containers as well.
Running Tkinter programs using classes¶
Already our programs are becoming long and so far the program does nothing. However, we want our programs to accomplish tasks in response to user actions, for example, when a button is pressed.
Unfortunately, it is tricky to pass variables between different Tkinter events, so we use classes to implement the main Tk applications.
We can then use the member attributes will help us pass values between events.
Look at the rewrite of our simple user interface:
from Tkinter import * class MyApp(object): def __init__(self, parent): self.main_frame = Frame(parent) self.main_frame.pack() self.top_frame = Frame(self.main_frame) self.top_frame.pack(side=TOP) self.bottom_frame = Frame(self.main_frame) self.bottom_frame.pack(side=BOTTOM) self.button1 = Button(self.top_frame, text="Top 1") self.button1.pack(side=LEFT) self.button2 = Button(self.top_frame, text="Top 2") self.button2.pack(side=RIGHT) self.button3 = Button(self.bottom_frame, text="Bottom 1") self.button3.pack(side=LEFT) self.button4 = Button(self.bottom_frame, text="Bottom 2") self.button4.pack(side=RIGHT) if __name__ == "__main__": root = Tk() myapp = MyApp(root) root.mainloop()
This is the main idea: Any object outside of root is defined within a class, using root as the parent.
We can now add functionality to this class that will modify the widgets.
This is the last widget we will see today. Canvas is a blank area. You can continuously draw things in it or put text in it. Here are some basic operations.
canvas = Canvas(parent, height=200, width=200) canvas.pack()
Draw something in a canvas (an oval within the given box coordinates):
Remember, like Image objects, (0,0) represents the top left corner.
When you draw multiple things in a canvas, it may not immediately show what you draw and a small delay may be noticed. You can force the canvas to show you what you draw with:
You can also draw lines, rectangle and text. See more on:
Timing of GUI Events¶
Remember, GUI events are triggered when you click a button (or other events you watch in more sophisticated systems).
They execute in parallel. If the processing of an event takes some time, it may overlap another event.
Sometimes drawing events are too fast for the eye to see the progress. You can incorporate some delays to make this more pleasing to the eye.
which will add a small delay waittime to the program.
Note that after and update functions apply to many other GUI elements. Mastering them will help you get the exact timing effect from your interface.
Here is a final program to put together a lot of the things we learned. It features a private class method (new_button) that is only accessible to the __init__ function. It shows how functions can be used to automate repetitive tasks.
from Tkinter import * class MyApp(object): def __init__(self, parent): ## method internal to constructor method for creating buttons ## shortens the program code def new_button(parent, cmd, buttontext, packlocation): button = Button(parent, command=cmd) button.configure(text=buttontext) button.configure(width=button_width, padx=button_padx, pady=button_pady ) button.pack(side=packlocation) return button #------ constants for controlling layout ------ button_width = 10 button_padx = "2m" button_pady = "1m" buttons_frame_padx = "3m" buttons_frame_pady = "2m" buttons_frame_ipadx = "3m" buttons_frame_ipady = "1m" # -------------- end constants ---------------- #---------variables for controlling the function----- self.canvas_dimension = 600 ##Canvas will be a square self.wait_time = 8 self.repetitions = 2 #----------end of variables-------------------------- self.myParent = parent self.main_frame = Frame(parent) self.main_frame.pack () ## Two frames inside the main frame, one for the canvas ## on top and the second one for buttons in the bottom self.draw_frame = Frame(self.main_frame) self.draw_frame.pack(side=TOP) self.info_canvas = Canvas(self.draw_frame, height=20, width=self.canvas_dimension) self.info_canvas.pack(side=TOP) self.text_area = self.info_canvas.create_text(10,10,anchor='nw') self.info_canvas.itemconfigure(self.text_area,text="#circles = %d" %self.repetitions) self.main_canvas = Canvas(self.draw_frame, \ height=self.canvas_dimension, width=self.canvas_dimension) self.main_canvas.pack() self.button_frame = Frame(self.main_frame) self.button_frame.pack(side=BOTTOM) self.draw_button = new_button(self.button_frame,self.draw, 'Draw', LEFT) self.clear_button = new_button(self.button_frame,self.clear, 'Clear', LEFT) self.increase_button = new_button(self.button_frame,self.increase, 'Increase', LEFT) self.reduce_button = new_button(self.button_frame,self.reduce, 'Reduce', LEFT) self.quit_button = new_button(self.button_frame,self.quit, 'Quit', RIGHT) def clear(self): self.main_canvas.delete("all") def reduce(self): if self.repetitions > 1: self.repetitions /= 2 self.put_info() def increase(self): if self.repetitions < 200: self.repetitions *= 2 self.put_info() def put_info(self): ## Change the text field in the canvas self.info_canvas.itemconfigure(self.text_area,text="#circles = %d" %self.repetitions) def draw(self): x = self.canvas_dimension/2 radius = x/self.repetitions inc = radius for i in range(self.repetitions): self.main_canvas.create_oval((x-radius,x-radius,x+radius,x+radius)) radius += inc self.main_canvas.update() # Actually refresh the drawing on the canvas. # Pause execution. This allows the eye to catch up self.main_canvas.after(self.wait_time) def quit(self): self.myParent.destroy() if __name__ == "__main__": root = Tk() root.title("Drawing a circle") ##Give a title to the program myapp = MyApp(root) root.mainloop()
- Graphical user interfaces are event driven. You need to write functions to initialize the interface and change the interface when events happen.
- The most common events are button clicks, but many others are possible.
- Most GUI elements are visible and are called widgets.
- Widgets are placed in invisible containers like frames to group them together.
- Many containers have built-in methods for placing multiple widgets in a way that is pleasing the eye.
- When building an interface, some of the challenges involve making the interface easy and intuitive to use. Many communications classes concentrate on these issues.