0% found this document useful (0 votes)
14 views52 pages

TK Inter

Uploaded by

ashanagaraju50
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views52 pages

TK Inter

Uploaded by

ashanagaraju50
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 52

An introduction to GUI programming

with Tkinter

Erik Spence

SciNet HPC Consortium

9 September 2014

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 1 / 49
An introduction to Tkinter
The purpose of this class is to introduce you to the basics of GUI
programming in Python, using Tkinter . There are several GUI interfaces
available in Python:
Tkinter is the Python interface to the Tk GUI toolkit.
wxPython is an open-source Python interface for wxWindows.
JPython is a Python port for Java which gives Python scripts access
to Java class libraries.
Many others are also available. We will use Tkinter, due to the fact that it
is the de facto standard Python GUI library.

Note that I will be using Python 2.7.8 in this class. The examples will
work with Python 3.X, but with slightly different syntax.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 2 / 49
What is Tk?
If Tkinter is the Python interface to the Tk GUI toolkit, what is Tk?
Tk started life as Tcl extension (1991). It is now written in C.
Tk is a high-level windowing toolkit. You can interface with it directly
using C or other languages.
Tk interfaces are also available in Python, Ruby, Perl, Tcl, and
probably other languages.
What Tk itself is interfacing with depends on your system:
I Mac: Tk provides interfaces to the MacOS windowing system.
I Windows: Tk provides interfaces to the Microsoft windowing system.
I Other platforms: Tk 8.X attempts to look like the Motif window
manager, but without using Motif libraries. Prior to that it interfaced
with the X window system.

Let it suffice to say that Tk provides a high-level means of accessing your


system’s windowing infrastructure.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 3 / 49
Running code in today’s class
All the code for today’s class should be launched from the command line,
rather than an interactive prompt. Those using Linux or Macs should run
python from a terminal:
ejspence@mycomp ~>
ejspence@mycomp ~> python mycode.py
ejspence@mycomp ~>

If you are running Windows, run your code from the cmd.exe prompt:
C:\Users\ejspence>
C:\Users\ejspence> c:\Python27\python.exe mycode.py
C:\Users\ejspence>

Do NOT use IDLE, or any other graphical Python interface. Some of


these use Tk as a back end, and today’s code may break or confuse
the interface.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 4 / 49
Our first Tkinter program
# firstTkinter.py
The Tkinter module is needed from Tkinter import Tk, Label
to build Tkinter widgets.
# Create the window.
First thing generated is the
top = Tk()
parent window, upon which
everything else will be placed. # Create a Label.
l = Label(top, text = "Hello World")
The Label widget is generated.
Arrange the Label using the # Arrange the Label.
pack command. l.pack()

The ’mainloop’ command is # Run the parent, and its children.


used to launch the window, and top.mainloop()
start the event loop.
The window can be moved, The window has the ’look’ of
resized, and closed. whatever system you
are running.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 5 / 49
Event-driven programming
The previous example was trivial, but it illustrates steps which are seen in
most Tkinter programs. Some notes:

The mainloop method puts the label on the window, the window on
the screen and enters the program into a Tkinter wait state.
In the wait state, the code waits for user-generated activity, called
’events’.
This is called event-driven programming.
The programs are essentially a set of event handlers that share
information rather than a single linear control flow.

This style of programming is notably different from what most of us are


accustomed.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 6 / 49
Our first Tkinter program, continued
We can tweak the appearance of our # firstTkinter2.py
from Tkinter import Tk, Label
main window:
Use the ’title’ option to change top = Tk()
the title of the window.
l = Label(top, "Hello World")
The ’minsize/maxsize’ arguments l.pack()
set the minimum/maximum size
# Give the window a title.
of the window.
top.title("My App")
The ’configure’ argument can be
used to set a variety of different # Change the minimum size.
top.minsize(400, 400)
window features, such as the
background colour. # Change the background colour.
top.configure(bg = "green")

# Run the widget.


top.mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 7 / 49
Our second Tkinter program
# secondTkinter.py
The Tkinter Button command from Tkinter import Label, Button, Tk
creates a button.
# The ’callback function’. Invoked
The first argument is the
# when the button is pressed.
parent window. def hello callback(): print "Hello"
The ’pack’ command makes
top = Tk()
the widget visible, and tells the
parent to resize to fit the # Make a Label.
children. l = Label(top, text = "My Button:")
l.pack()
When the button is pushed,
the callback function # Make a button.
’hello callback’ is called. b = Button(top, text = "Hello",
command = hello callback)
If the upper-right-corner ’X’ is b.pack()
not visible the window is too
small. Resize the window. top.mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 8 / 49
A better second Tkinter program
Widgets are usually created as # MyApp.py
from Tkinter import Label, Button
objects, so let’s recast our example
as such. class MyApp:

# secondTkinter2.py def init (self, master):


import Tkinter self.l = Label(master,
from MyApp import MyApp text = "My Button:")
self.l.pack()
top = Tkinter.Tk()
self.b = Button(master,
# Note that the constructor takes the text = "Hello",
# parent window as an argument. command = self.hello)
app = MyApp(top) self.b.pack()

top.mainloop() # Function called when the button


# is pressed.
def hello(self): print "Hello"

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 9 / 49
An even better second Tkinter program
Generally speaking, objects should # MyApp2.py, continued
be invokable on their own.
self.l = Label(self,
text = "My Button:")
# MyApp2.py
self.l.pack()
from Tkinter import Label, Button,
Frame
self.b = Button(self,
text = "Hello",
# Extend the Frame class, to inherit
command = self.hello)
# the mainloop function.
self.b.pack()
class MyApp(Frame):
# Function called when the button
def init (self, master = None):
# is pressed.
def hello(self): print "Hello"
# Construct the Frame object.
Frame. init (self, master)
# Allow the class to run stand-alone.
self.pack()
if name == " main ":
MyApp().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 10 / 49
Callback functions
’Callback functions’ are invoked by widgets due to an ’event’, such as the
pressing of a button. These functions need to be handled carefully:
Notice that we used ”command = self.hello” rather than
”command = self.hello()” in the code for the Button in the last
example.
If you do ”command = func()” in the widget declaration, func() will
be run upon the widget creation, not when it is needed.
But without the brackets there is no way to pass arguments to the
function! If arguments are needed you must use lambda, or another
indirection layer.
(Global variables may also work, but are not recommended.)
Functions invoked using lambda are only called at runtime, not when
the widget is created.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 11 / 49
Many different Tkinter widgets are available

The list of widgets which can be added to a Tkinter window is extensive:

Buttons, Checkbuttons, Radiobuttons, Menubuttons


Canvas (for drawing shapes)
Entry (for text field entries)
Message (for displaying text messages to the user)
Labels (text captions, images)
Frames (a container for other widgets)
Scale, Scrollbar
Text (for displaying and editting text)
and others...

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 12 / 49
Tkinter control variables
More often then not, we want to tie the value of variables to the specific
states of widgets, or to specific events. This is called tracing.
Native Python variables don’t track events as they occur.
However Tkinter contains wrapper objects for variables which change
value with changing events. These are called Tkinter variables.
Because they are objects, Tkinter variables are invoked using a
constructor: var = IntVar().
These variables have a number of important functions:
Checkbuttons use a control variable to hold the status of the button.
Radiobuttons use a single control variable to indicate which button
has been set.
Control variables hold text strings for several different widgets (Entry,
Label, Text).

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 13 / 49
How to use Tkinter control variables
There are four types of control variables: StringVar (string), IntVar
(integers), DoubleVar (floats), BooleanVar (booleans). Each variable has a
default value. How do these variables tend to manifest themselves?

Button: set its ’textvariable’ to a StringVar. When the StringVar is


changed the Button’s text will change.
Checkbutton: set the ’variable’ option to an IntVar. Note that you
can also use other values for a Checkbutton (string, boolean).
Entry: set the ’textvariable’ option to a StringVar.
Radiobutton: the ’variable’ option must be set to either an IntVar or
StringVar.
Scale: set the ’variable’ option to any control variable type. Then set
the ’from ’ and ’to’ values to set the range.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 14 / 49
Tkinter control variables example
# MyCheckbutton.py # MyCheckbutton.py, continued
from Tkinter import IntVar, BOTH def click(self):
Checkbutton, Frame if (self.var.get() == 1):
self.master.title("Checkbutton")
class MyCheckbutton(Frame): else: self.master.title("")
def init (self, master = None):
Frame. init (self, master) if name == " main ":
self.pack(expand = True, MyCheckbutton().mainloop()
fill = BOTH)
self.master.title("")
self.master.minsize(200, 100) The IntVar object tracks the
checkbox value (0 or 1).
# Object variables.
self.var = IntVar() .get()/.set(’x’) returns/sets the
value of the control variable.
# Create a checkbutton.
The ’place’ function locates
cb = Checkbutton(self, text =
"Show title", variable = the checkbox in the window,
self.var, command = self.click) from the upper-right
cb.place(x = 50, y = 50) corner.
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 15 / 49
Pop quiz!

Create an application which


Accepts a numeric entry from the user, using the Entry widget.
The Entry widget has a Label widget beside it, which says ”lbs”.
Has a ’Calculate’ button. When the ’Calculate’ button is pressed:
I The application calculates the number of kilograms, assuming that the
value given in the Entry widget is numeric, and is in pounds
(1 pound = 0.453592 kilograms).
I Prints the value to the command line.

Hint: create a StringVar() for the entry widget. When the button is
pressed grab the value and go.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 16 / 49
Pop quiz one
# lbs2kgs.py # lbs2kgs.py, continued
from Tkinter import * def calc(self):
class lbs2kgs(Frame): try:
def init (self, master = None): value = float(self.lbs.get())
Frame. init (self, master) print "The number of
self.pack() kgs is", 0.453592 * value
except ValueError: pass
self.lbs = StringVar()
lbs entry = Entry(self, width = 7, if name == " main ":
textvariable = self.lbs) lbs2kgs().mainloop()
lbs entry.pack(side = LEFT)
.focus() moves the window
Label(self, text = "lbs").pack(
side = LEFT) focus: type without clicking
Button(self, text = "Calculate", .winfo children() returns a
command = self.calc).pack(
list of all child widgets.
side = LEFT)
lbs entry.focus() .pack configure() adjusts
for c in self.master.winfo children(): the packing of
c.pack configure(padx = 5, pady = 5)
the widgets.
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 17 / 49
Widgets and assignments
Did you notice the strange lines # lbs2kgs.py
from Tkinter import *
of code in the previous
class lbs2kgs(Frame):
example? What’s wrong here? def init (self, master = None):
Frame. init (self, master)
self.pack()

self.lbs = StringVar()
lbs entry = Entry(master, width = 7,
textvariable = self.lbs)
lbs entry.pack(side = LEFT)

Label(self, text = "lbs").pack(


side = LEFT)
Button(self, text = "Calculate",
command = self.calc).pack(
side = LEFT)
lbs entry.focus()
for c in master.winfo children():
c.pack configure(padx = 5, pady = 5)
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 18 / 49
Widgets and assignments
Did you notice the strange lines # lbs2kgs.py
from Tkinter import *
of code in the previous
class lbs2kgs(Frame):
example? What’s wrong here? def init (self, master = None):
Frame. init (self, master)
Because these widgets were not self.pack()
assigned to a name, they should
be garbage collected as soon as self.lbs = StringVar()
lbs entry = Entry(master, width = 7,
the pack() command is finished.
textvariable = self.lbs)
lbs entry.pack(side = LEFT)

Label(self, text = "lbs").pack(


side = LEFT)
Button(self, text = "Calculate",
command = self.calc).pack(
side = LEFT)
lbs entry.focus()
for c in master.winfo children():
c.pack configure(padx = 5, pady = 5)
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 18 / 49
Widgets and assignments
Did you notice the strange lines # lbs2kgs.py
from Tkinter import *
of code in the previous
class lbs2kgs(Frame):
example? What’s wrong here? def init (self, master = None):
Frame. init (self, master)
Because these widgets were not self.pack()
assigned to a name, they should
be garbage collected as soon as self.lbs = StringVar()
lbs entry = Entry(master, width = 7,
the pack() command is finished.
textvariable = self.lbs)
lbs entry.pack(side = LEFT)
Tkinter emits Tk calls when
objects are constructed. Tkinter Label(self, text = "lbs").pack(
internally cross-links widget side = LEFT)
objects into a long-lived tree Button(self, text = "Calculate",
command = self.calc).pack(
used to build the display. As side = LEFT)
such the widgets are retained,
lbs entry.focus()
even if not in the code itself. for c in master.winfo children():
c.pack configure(padx = 5, pady = 5)
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 18 / 49
Using images with Tkinter

There are several packages available for using images in Tkinter :


The PhotoImage class can read GIF and PGM/PPM images, as well
as base64-encoded GIF files from strings.
The Python Imaging Library (PIL) contains classes that can handle
over 30 file formats. This package is no longer being maintained, and
has been succeeded by the Pillow package.
Important: you must keep a reference to your PhotoImage object (of
either the PhotoImage of PIL class). This is in direct contrast to
what was shown on the last slide.
If you do not keep a reference the object will be garbage collected
even if the widget is still operating!

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 19 / 49
Using images within widgets
To embed an image in a widget, # MyImage.py
from Tkinter import Label, Frame
there are several steps:
from PIL import Image, ImageTk
First open the image file in
class MyImage(Frame):
question.
def init (self, master = None):
Then convert it to a Tkinter Frame. init (self, master)
-compatible image object. self.pack()

Then embed it into a widget. img = Image.open("tatras.jpg")


Again: you must keep a pic = ImageTk.PhotoImage(img)
label = Label(self, image = pic)
reference to your PhotoImage
object, otherwise the object # Keep a reference!
will be garbage collected. # (or don’t and see what happens)
label.image = pic
label.pack()

if name == " main ":


MyImage().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 20 / 49
Pop quiz two!

Create an application which:


Consists of a single button.
On the button is an image. Start with ”Happy.jpg”.
When the button is pressed the image changes to ”Sad.jpg”.
When the button is pressed again the image changes back to
”Happy.jpg”.
And so on.
Hint: use the Button’s ’.configure(image = newimage)’ to change the
image.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 21 / 49
Pop quiz two!
# switchface.py # switchface.py, continued
from Tkinter import Button, Frame
from PIL.Image import open # Create button, add image.
from PIL.ImageTk import PhotoImage self.b = Button(master, image =
self.p0, command = self.switch)
class switchface(Frame): self.b.pack()
def init (self, master = None):
Frame. init (self, master) # Keep a reference.
self.pack() self.b.image = self.p0

# The pic currently displayed. def switch(self):


self.current = 0 if (self.current == 0):
self.b.configure(image = self.p1)
# Open the images. self.current = 1
self.i0 = open("Happy.jpg") else:
self.i1 = open("Sad.jpg") self.b.configure(image = self.p0)
self.current = 0
# Make them tkinter-compatible.
self.p0 = PhotoImage(self.i0) if name == " main ":
self.p1 = PhotoImage(self.i1) switchface().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 22 / 49
Arranging your widgets
There are three Geometry Managers in Tkinter for arranging widgets:
’grid’, ’pack’ and ’place’. We’ve already used the latter two. Do not try to
mix grid and pack in the same window (’container’).
pack
I Lays widgets out along the sides of a box.
I Works best when everything is in one row or one column.
I Can be tricky to make more-complicated layouts until you understand
the packing algorithm, which we won’t cover here. It’s best not to try.
grid
I Lays out widgets in a grid (along row and column boundaries)
I Good for creating tables and other structured types of layouts.
place
I Can place a widget at an absolute position, a given x and y
I Can place a widget relatively, such as at the edge of another widget.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 23 / 49
Grid
The grid Geometry Manager # MyGrid.py
from Tkinter import Label, Entry,
puts widgets in a 2-D table.
Checkbutton, Frame
The ’container’ widget is split
into rows and columns, and class MyGrid(Frame):
each cell in the table can hold def init (self, master = None):
Frame. init (self, master)
a widget.
self.pack()

Label(self, text = "First").grid()


Label(self, text = "Second").grid()

Entry(self).grid(row = 0, column = 1)
Entry(self).grid(row = 1, column = 1)

Checkbutton(self, text = "More?").grid(


The column defaults to 0 if not columnspan = 2)
specified. The row defaults to
the first unused row in the grid. if name == " main ":
MyGrid().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 24 / 49
Grid keywords
Grid takes a number of useful keywords:
column/row: the column or row into which the widget will be placed.
I If no column is specified, column = 0 is used.
I If no row is specified, the next unused row is used.
I If you put two widgets in the same cell, both will be visible, with
potentially odd results.
columnspan/rowspan, number of columns/rows to span, to the
right/down.
sticky, defines how to expand the widget if the cell is larger than the
widget.
I Can be any combination of S, N, E, W, NE, NW, SW, SE.
I Default is to be centred.
padx/pady, optional horizontal/vertical padding to place around the
widget, within the cell.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 25 / 49
Pack, vertical example
The pack Geometry Manager # MyPack1.py
from Tkinter import Label, X, Frame, BOTH
lays widgets on the side of a
box, in this example on the class MyPack1(Frame):
top side. Pack can allow the def init (self, master = None):
widget to change size if the Frame. init (self, master)
self.pack(expand = True, fill = BOTH)
window is resized.
self.master.minsize(100, 70)

Label(self, text = "Red", bg = "red",


fg = "white").pack()
Label(self, text = "Green",
bg = "green").pack(fill = X)
Label(self, text = "Blue", bg = "blue",
fg = "white").pack()
Using ’fill = X’ will cause the
widget to fill in the horizontal if name == " main ":
direction if the window is MyPack1().mainloop()
resized.
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 26 / 49
Pack, horizontal example
Use the ’side’ argument to # MyPack2.py
from Tkinter import Label, Frame,
indicate which side of the box
BOTH, LEFT
pack should pack against.
class MyPack2(Frame):
def init (self, master = None):
Frame. init (self, master)
self.pack(expand = True, fill = BOTH)

self.master.minsize(130, 100)

Label(self, text = "Red", bg = "red",


fg = "white").pack(side = LEFT)
Label(self, text = "Green",
Resizing the window will not
bg = "green").pack(side = LEFT)
cause the widgets to grow in Label(self, text = "Blue", bg = "blue",
this case, the way that ’fill’ fg = "white").pack(side = LEFT)
does, though they will stay
if name == " main ":
centered on the left side.
MyPack2().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 27 / 49
Pack keywords

Pack takes several useful keywords:


side, which side to pack against. Options are LEFT, TOP (default),
RIGHT, BOTTOM. You can mix them within the same parent
widget, but you’ll likely get unexpected results.
fill, specifies whether the widget should occupy all the space provided
to it by the parent widget. Options are NONE (default), X
(horizontal fill), Y (vertical fill), or BOTH.
expand, specifies whether the widget should be expanded to fill extra
space inside the parent widget. Options are False (default) and True.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 28 / 49
Place
The place Geometry # MyPlace.py
from Tkinter import Label, NW, E, CENTER,
Manager is the simplest of
Frame, BOTH
the three to use. It places class MyPlace(Frame):
the widget either in absolute def init (self, master = None, **options):
or relative terms. However, Frame. init (self, master, **options)
self.pack(expand = True, fill = BOTH)
it is a pain to use for general
placement of widgets, self.config(width = 100, height = 100)
though can be useful in Label(self, text = "Red", bg = "red",
special cases. fg = "white").pack(anchor = NW,
relx = 0.4, y = 10)
Label(self, text = "Green",
bg = "green").pack(anchor = E,
relx = 0.2, rely = 0.8)
Label(self, text = "Blue", bg = "blue",
fg = "white").pack(anchor = CENTER,
x = 80, rely = 0.4)

if name == " main ": MyPlace().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 29 / 49
Place keywords

Place takes a number of useful keywords:


relx/rely: between 0 and 1, the position of the widget, in the x/y
direction, relative to the parent window in which its embedded.
x/y: in pixels, the absolute position of the widget, in the window in
which the widget is embedded.
If both relx and x are specified then the relative position is calculated
first, and the absolute position is added after.
anchor: the point on the widget that you are actually positioning.
Options are the eight points of the compass (E, S, NW, ...) and
CENTER.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 30 / 49
Centering your widget
The default location of # MyCentre.py
from Tkinter import Frame
your widget depends on
the Window Manager. class MyCentre(Frame):
Generally it’s in the def init (self, master = None):
upper-left corner.
Frame. init (self, master)
Use self.pack()
winfo screenwidth() # Width and height of the window.
to get the window w = 200; h = 50
width. Similarly for
# Upper-left corner of the window.
height. x = (self.master.winfo screenwidth() - w) / 2
The geometry y = (self.master.winfo screenheight() - h) / 2
command is used to
# Set the height and location.
set the location of master.geometry("%dx%d+%d+%d" % (w, h, x, y))
the window’s
upper-left corner. if name == " main ": MyCentre().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 31 / 49
Pop-up windows
Pop-up windows are fun. Every app # MyPopup.py
from Tkinter import Button, Frame
needs a pop-up window. The
from tkMessageBox import showinfo
easiest package to use for pop-up
windows is tkMessageBox. class MyPopup(Frame):

Like the main window, the pop-up def init (self, master = None):
Frame. init (self, master)
windows have a default look which
self.pack()
depends upon the system running
the code. Button(self, text = "Pop-up!",
command = self.popup).pack()

def popup(self):
showinfo("My Pop-Up", "Hello")

if name == " main ":


MyPopup().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 32 / 49
Many pre-made pop-up windows are
available
If you’re going to use pop-up windows, the defaults that come with
tkMessageBox should be sufficient:

single-button pop-ups:
I “Ok”: showinfo, showwarning, showerror
double-button pop-ups:
I “Yes-No”: askquestion, returns the strings “yes”, “no”
I “Yes-No”: askyesno, returns True/False
I “Ok-Cancel”: askokcancel, returns True/False
I “Retry-Cancel”: askretrycancel, returns True/False

These functions all have the same syntax:


tkMessageBox.function(title, message [, options]).

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 33 / 49
Toplevel windows
# MyToplevel.py
Sometimes the pre-made from Tkinter import Button, Frame,
pop-up windows don’t meet Toplevel
your needs, since these are class MyToplevel(Frame):
canned pop-ups. For these cases
def init (self, master = None):
one uses Toplevel windows. Frame. init (self, master)
self.pack()
Toplevel windows behave like
main windows, but are actually Button(self, text = "A new window!",
command = self.new window).pack()
children of whichever window
spawned them. # A new functional window.
def new window(self):
top = Toplevel(master = self)
Button(top, text = "Quit",
command = top.quit).pack()

if name == " main ":


MyToplevel().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 34 / 49
File manager windows
# MyFile.py # MyFile.py, continued
from Tkinter import Button, Frame
from tkFileDialog import if name == " main ":
askopenfilename MyFile().mainloop()

class MyFile(Frame): Pre-made file manager dialog


def init (self, master = None):
Frame. init (self, master)
boxes are available through the
self.pack() tkFileDialog module.

Button(self, text = "Get a file!",


command = self.getfile).pack()

def getfile(self):
filename = askopenfilename(
parent = self,
title = "Please select a file")

if (len(filename) > 0):


print "You chose %s" % filename
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 35 / 49
Quitting cleanly
We want our program to close # badquit.py
from Tkinter import Tk, Button
cleanly. But we must be careful
how we do so: # behaviour 1
t1 = Tk()
All Tkinter widgets come with
t1.b = Button(t1, text = "push me",
the ’quit’ function built in. command = lambda:t1.b.destroy())
This will close the t1.b.pack()
entire Tkinter program, which t1.mainloop()
may not be what you want.
# behaviour 2
Alternatively, you can use the t2 = Tk()
’destroy’ function, which will t2.b = Button(t2, text = "me too!",
command = lambda:t2.b.quit())
only close the particular widget
t2.b.pack()
which you are referencing. t2.mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 36 / 49
Quitting cleanly
# badquit.py
The lambda command needs to be from Tkinter import Tk, Button
used in this case because the
callback command which is being # behaviour 1
t1 = Tk()
referenced is self-referential. t1.b = Button(t1, text = "push me",
command = lambda:t1.b.destroy())
t1.b.pack()
t1.mainloop()

# behaviour 2
t2 = Tk()
t2.b = Button(t2, text = "me too!",
command = lambda:t2.b.quit())
t2.b.pack()
t2.mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 36 / 49
A ’Quit’ button class
Let’s create a class that we can use # MyQuitter.py, continued
in future widgets.
def myquit(self):
if askokcancel("Quit",
# MyQuitter.py
"Do you really wish to quit?"):
from Tkinter import Button, LEFT, YES,
Frame.quit(self)
BOTH, Frame
from tkMessageBox import askokcancel
The askokcancel function
# Extends the Frame class. returns True if ’OK’ is pressed.
class MyQuitter(Frame):
def init (self, master = None): The ’LEFT’ argument indicates
the position of the button.
Frame. init (self, master)
The second ’pack’ is invoked
self.pack()
after the Button is created,
b = Button(self, text = "Quit", and so can go in
command = self.myquit) the same line.
b.pack(side = LEFT, expand = YES,
fill = BOTH)

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 37 / 49
Capturing destroy events
Tkinter lets you manipulate # MyCapture.py
from Tkinter import Frame
’protocol handlers’
from MyQuitter import MyQuitter
These handle the interaction
between the application and class MyCapture(Frame):
def init (self, master = None):
the window manager
Frame. init (self, master)
The most-used way to do this self.pack()
is re-assigning the
q = MyQuitter(self)
WM DELETE WINDOW
q.pack()
protocol (invoked by pressing
the ’X’ in the upper-right self.master.protocol(
corner). "WM DELETE WINDOW", q.myquit)

if name == " main ":


MyCapture().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 38 / 49
Notes on code re-usability

Modular programming is always to be encouraged, and GUI programming


is no exception.

Throughout this class we have crafted our examples such that they
are classes that can be embedded in other widgets.
Custom GUI classes can be written as extensions of existing classes,
the most common choice being Frame.
Using widgets that are extensions of existing classes allows uniform
and consistent modification of the look-and-feel of your widgets.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 39 / 49
Code re-usability example
Because we set up our previous # ABunchOfWidgets.py
from Tkinter import Frame, RAISED
code examples as classes, we can
from lbs2kgs import lbs2kgs
just drop them into other widgets. from MyPopup import MyPopup
from MyPlace import MyPlace

class ABunchOfWidgets(Frame):
def init (self, master = None):

Frame. init (self, master)


self.pack()

lbs2kgs(self).pack()
MyPopup(self).pack()
MyPlace(self, borderwidth = 2,
relief = RAISED).pack()

if name == " main ":


ABunchOfWidgets().mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 40 / 49
Drop-down menus
# MyMenus.py # MyMenus.py, continued
from Tkinter import Menu, Frame
class MyMenus(Frame): # ...and stick into the menubar.
self.mbar.add cascade(label =
def init (self, master = None): "File", menu = self.filemenu)
Frame. init (self, master)
self.pack() # Add entries to filemenu.
self.master.minsize(100,100) self.filemenu.add command(label =
"New", command = self.new call)
# Create a menu instance, self.filemenu.add command(label =
# the menu does not need packing. "Open", command = self.o call)
self.mbar = Menu(self)
# The callback functions.
# Attach to the root window. def new call(self): print "New call"
self.master.config(menu =
self.mbar) def o call(self): print "o call"

# Create a new menu instance... if name == " main ":


self.filemenu = Menu(self.mbar, MyMenus().mainloop()
tearoff = 0)
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 41 / 49
Bindings
Any user action (keyboard or # MyBindings.py, continued
mouse), is called an ’event’.
self.master.minsize(100, 100)
Events can be captured by the self.master.bind("a",
application, and specific actions self.a callback)
taken. This is accomplished using self.master.bind("<Button-1>",
self.b callback)
the ’bind’ function.
Event actions can be bound to any # Called when the ’a’ is pressed.
def a callback(self, event):
widget, not just the main window.
if not askyesno("A query",
# MyBindings.py "Did you press the ’a’ button?"):
from Tkinter import Frame showerror("I am aghast!", "Liar!")
from tkMessageBox import showerror,
askyesno def b callback(self, event):
print "clicked", event.x, event.y
class MyBindings(Frame):
def init (self, master = None): if name == " main ":
Frame. init (self, master) MyBindings().mainloop()
self.pack()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 42 / 49
Event formats
A partial list of possible bindings:
”<Button-1>”: a mouse button is pressed over the widget. Button 2 is
the middle, 3 is the right. ”<Button-1>” and ”<1>” are synonyms.
”<Enter>”/”<Leave>”: the mouse pointer entered/left the widget.
”<Return>”: the user pressed the Enter key.
”<key>”: the user pressed the any key.
”<Control-p>”: the user pressed Ctrl-p.

The event object has a number of standard attributes:


x, y: current mouse position, in pixels.
char: the character code, as a string.
type: the event type.
and others...

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 43 / 49
Bindings pop quiz!
Create an application which
Has a label at the top of the frame which says ”Are you an idiot?”
Has a button below the label which contains the text ”No”.
Moves to a random location on the screen every time you try to press
the ”No” button.
Don’t bother making this one a class, since that doesn’t really make
sense.

Hints:
Use the ”Enter” binding to bind the mouse pointer.
When the mouse enters the ”No” button, move the window (use the
random.random() function to get a random number).
master.winfo screenwidth() and height might be useful.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 44 / 49
Bindings pop quiz!
# idiot.py # idiot.py, continued
from Tkinter import Tk, Label, Button
from random import random master.minsize(w, h)
master.title("Let me check")
def moveme(event):
x = xsize * random() Label(master,
y = ysize * random() text = "Are you an idiot?").pack()
b = Button(master, text = "No!")
# Move the window. b.pack()
master.geometry( "%dx%d+%d+%d" % b.bind("<Enter>", moveme)
(w, h, x, y))
master.mainloop()
master = Tk()
xsize = master.winfo screenwidth()
ysize = master.winfo screenheight()
w = 200
h = 50

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 45 / 49
Threads and GUIs
In general, we recommend against using Python’s threading capabilities:
Python’s Global Interpreter Lock prevents more than one thread from
running at a given time.
Consequently there is no increase in computational performance.

However, these concerns do not apply when dealing with GUIs, since
computational performance is not usually at issue. There are some details
worth noting:
The main event loop runs in a single thread.
If a callback function is invoked, it runs in the same thread.
If the function takes a long time to complete, you will notice:
I the windows will not update (resize, redraw, minimize)
I the windows will not respond to new events.

Threads can be useful to fix this problem.

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 46 / 49
Illustrating the problem

# MySummer.py Here we illustrate the problem


from Tkinter import Tk, Button, Label
that GUIs can have. Perform the
def button press():
following steps:
total = 0 Run the GUI.
for i in xrange(100000000):
total += i Press the button.
label.config(text = str(total)) While the calculation is
being performed, resize the
master = Tk()
Button(master, text = "Add it up", window.
command = button press).pack() What happens?
label = Label(master)
label.pack()
master.mainloop()

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 47 / 49
Fixing the problem, using threads
# MySummer.threaded.py Because the control of the
from Tkinter import Tk, Button,
calculation is in a separate thread,
Label
import threading
control returns to the event loop.
# MySummer.threaded.py, continued
def button press():
master = Tk()
# Create a function for the thread.
Button(master, text = "Add it up",
def callback():
command = button press).pack()
total = 0
label = Label(master)
for i in xrange(100000000):
label.pack()
total += i
master.mainloop()
label.config(text = str(total))

# Launch the thread. If you get an ’infinite loop’ error


threading.Thread( then your Tcl was not compiled
target = callback).start() with threading support. If so, try
using the ’mtTkinter’
package.
Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 48 / 49
Enough to get started

Using the material here, you should have enough to get started. More
information can be found on the web. A few good websites are:

http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
http://www.effbot.org/tkinterbook/tkinter-index.htm
http://cs.mcgill.ca/ hv/classes/MS/TkinterPres
http://www.python-course.eu/python tkinter.php

Erik Spence (SciNet HPC Consortium) Programming with Tkinter 9 September 2014 49 / 49

You might also like