Python Tkinter Tutorial
Today we will be talking about one python library that is used for GUI programming which is Tkinter
.
Table Of Contents
1. What is Tkinter
1.1. More about Tkinter
Tkinter
is a library that is used for creating interfaces. Sometimes, we not only need to think about software itself, complicated algorithms, but a good interface as well. Without good interface there is a very little chance that our software product would be popular. It’s quite simple to create simple interfaces using the library. Without hesitation, let’s get started, shall we?
1.2. Installing Tkinter
Tkinter
isn’t usually pre-installed. I will show how to install it on both: Windows and Linux.
Installing Tkinter (Windows)
pip install python-tk
If it doesn’t work, there may be some problems. One problem might be that you are using 64 bit version when you need 32 bit version. Another one could be a problem with pip itself. In this case, I would suggest downloading wheel or binaries from python. If that fails, go to ActiveState
and follow easy instructions on how to download it. Chances that you may be experiencing any problems with downloading Tkinter
are close to none, but it’s always better to know how to solve issues that might happen.
Installing Tkinter (Debian Linux)
sudo apt-get install python3-tk
That works great on all Debian-based systems. However, that would be unfair to tell that it works on all linux systems. So I should spend some time to tell you how to install it if you are using different systems. If you are an Arch Linux user, something like this can work great for you!
Installing Tkinter (Arch Linux)
sudo pacman -S tk
If you are a fan of RHEL, CentOS, Oracle, just use yum:
Installing Tkinter (RHEL, CentOS, Oracle Linux)
yum install tkinter
In this case, you might have some problems. If you do, that means that tk-devel
needs to be installed. So in other way, just do the following:
Installing Tkinter (RHEL, CentOS, Oracle Linux)
yum install -y tkinter tk-devel
By the time writing this article I wasn’t quite aware that Mac OS users have some problems installing Tkinter
. So it’s best to show you how to install it.
First, you go to www.activestate.com, and follow along the links to download the Community Edition of ActiveTcl, available as a universal binary. Make sure you’re downloading an 8.5.x version, not an older 8.4.x version. Once you finish all necessary downloads, you will find Wish
application installed on your machine. Go ahead and open it. You will have another thing popped up which is your console. It will run all Tcl/Tk commands. It’s also wise to verify the exact version of Tcl/Tk that you are running. To do this, type the following:
Installing Tkinter (Mac OS)
% info patchlevel
We would like to receive something like this: Verified install using ActiveTcl 8.5.10.1 on Mac OS X 10.7.1.
. Alright, I guess I have covered all possible installations. Let’s get started, shall we?
2. Examples
2.1. Basic examples
First of all, we need to check if Tkinter
has been successfully installed. To do so, we need to run the following:
Test
import tkinter tkinter._test()
If everything is installed correctly, you will see a pop-up window that has some text and two buttons. If you don’t see it, go to the first section and download Tkinter
first.
Alright, let’s go ahead and code! There are 2 ways how to import Tkinter
: you may do import tkinter
, or type from tkinter import *
. We will use the second option, because we won’t have to name the module before using any methods. In any application, we always have some main window that eventually leads users to other widgets. Object-window is a variable root
. Go ahead and run this:
Main window
from tkinter import * root = Tk()
Now, let’s create widget! We will only have one button there. To access the button we will use Button()
method. The class Button
has one mandatory attribute – object that is associated with a button. The idea is that button cannot exist by itself.
Adding button
from tkinter import * root = Tk() but = Button(root)
Let’s put some text on the button by doing the following:
Adding button
from tkinter import * root = Tk() but = Button(root) but["text"] = "Hi there"
Surely, button alone doesn’t do much. We need to have some action with it. For instance, we want to print some text once we click the button.
Adding button
def printer(event): print("Hi there, this button works")
Don’t forget that it’s essential to put this function in the beginning of the code because it’s an event. If we do left mouse click, it is Button
. We have to bind function with the button in order to make it work.
Adding button
but.bind("", printer)
So what we do here is we are binding the function printer
with the event (left mouse click on the button). Before we run the program, we need to pack it. The easiest way to do that is type the following:
Packing
but.pack()
If we don’t type this, the program will still run. We won’t see the button but it will be there. Lastly, the main window won’t show up if we don’t call it:
Main window
root.mainloop()
Finally, our first program will look something like this:
basic.py
from tkinter import * def printer(event): print ("Hi there, this button works!'") root = Tk() but = Button(root) but["text"] = "Hi there" but.bind("",printer) but.pack() root.mainloop()
If you have ever read my article about Class Inheritance in Python, you know that I am fan of Object oriented programming. Let’s recreate the same program but use OOP!
basic.py
from tkinter import * class But_print: def __init__(self): self.but = Button(root) self.but["text"] = "Hi there" self.but.bind("",self.printer) self.but.pack() def printer(self,event): print ("Hi there, this button works!'") root = Tk() obj = But_print() root.mainloop()
2.2. Widget
Buttons have many attributes. Some of them are bg
– background, fg
– foreground. Here is a simple example of the button:
button.py
from tkinter import * root = Tk() but = Button(root, text="It is a button", # text on the button width=30,height=5, # width and height of the button bg="white",fg="blue") # background and foreground but.pack() root.mainloop()
There are many ways of how text can be displayed. For example, let’s take labels.
Labels
from tkinter import * root = Tk() lab = Label(root, text="This is a label \n written in 2 lines", font="Arial 36") lab.pack() root.mainloop()
We can make entries by using Entry
module.
Entry
from tkinter import * root = Tk() ent = Entry(root,width=20,bd=10) ent.pack() root.mainloop()
There is also another way to make a user input field.
Entry
from tkinter import * root = Tk() tex = Text(root,width=40, font="Verdana 12", wrap=WORD) tex.pack() root.mainloop()
We can create radio buttons.
Radio buttons
from tkinter import * root = Tk() var=IntVar() var.set(1) # set the radiobutton chosen as rad1 rad0 = Radiobutton(root,text="First", variable=var,value=0) rad1 = Radiobutton(root,text="Second", variable=var,value=1) rad2 = Radiobutton(root,text="Third", variable=var,value=2) rad0.pack() rad1.pack() rad2.pack() root.mainloop()
We can create check buttons as easy as radio buttons.
Check buttons
from tkinter import * root = Tk() c1 = IntVar() c2 = IntVar() che1 = Checkbutton(root,text="First checkbox", variable=c1,onvalue=1,offvalue=0) che2 = Checkbutton(root,text="Second checkbox", variable=c2,onvalue=2,offvalue=0) che1.pack() che2.pack() root.mainloop()
There is probably one more feature that you need to know. Check it out for yourself!
Lists
from tkinter import * root = Tk() r = ['Linux','Python','Tk','Tkinter'] lis = Listbox(root,selectmode=SINGLE,height=4) for i in r: lis.insert(END,i) lis.pack() root.mainloop()
Let’s check what we can do already, shall we? I want you to try to create the program that asks for the address and gives users a chance to leave some comments. It also asks for something using radio and check buttons. If you are curious, look at the code below. It is something that has already been covered in this article and coded using OOP principles.
interface.py
from tkinter import * class Form: def __init__(self): self.root = Tk() self.root.title('Response form') self.root.geometry('500x400+300+200') self.width = 50 class Question: def __init__(self, form, question): self.lab = Label(form.root, text=question) self.ent = Entry(form.root, width=form.width+16) self.lab.pack() self.ent.pack() class BigQuestion: def __init__(self, form, question): self.lab = Label(form.root, text=question) self.txt = Text(form.root, width=form.width, height=4) self.lab.pack() self.txt.pack() class RadioBut: def __init__(self, form, question): self.lab = Label(form.root, text=question) self.lab.pack() self.var = IntVar() self.var.set(1) self.rad0 = Radiobutton(form.root, text="$", variable=self.var,value=1) self.rad1 = Radiobutton(form.root, text='$$', variable=self.var, value=2) self.rad2 = Radiobutton(form.root, text='$$$', variable=self.var, value=3) self.rad3 = Radiobutton(form.root, text='$ $ $ $', variable=self.var, value=4) self.rad0.pack() self.rad1.pack() self.rad2.pack() self.rad3.pack() class Flags: def __init__(self, form, question): self.lab = Label(form.root, text=question) self.lab.pack() self.c0 = IntVar() self.c1 = IntVar() self.c2 = IntVar() self.c3 = IntVar() self.che0 = Checkbutton(form.root, text="Red",bg='red', variable=self.c0, onvalue=1, offvalue=0) self.che1 = Checkbutton(form.root, text="Blue", bg='blue', variable=self.c1, onvalue=1, offvalue=0) self.che2 = Checkbutton(form.root, text="Green", bg='green', variable=self.c2, onvalue=1, offvalue=0) self.che3 = Checkbutton(form.root, text="Yellow",bg='yellow', variable=self.c3, onvalue=1, offvalue=0) self.che0.pack() self.che1.pack() self.che2.pack() self.che3.pack() class Scene: def __init__(self): self.form = Form() self.qstn = Question(self.form, 'Your address:') self.comm = BigQuestion(self.form, 'Comments:') self.radi = RadioBut(self.form, 'How much?') self.flag = Flags(self.form, 'What color?') self.butt = Button(self.form.root, text='Send') self.butt.pack() self.butt.bind('', self.go) self.form.root.mainloop() def go(self, event): print('Send:\n', 'Colors:', self.flag.c0.get(), self.flag.c1.get(), self.flag.c2.get(), self.flag.c3.get(), 'Count:', self.radi.var.get()) scene = Scene()
2.3. Frame
As you may have already figured, frames are good tools to organize the application and widgets inside. Moreover, it’s wise to use it for a better app structure.
Frames
from tkinter import * root = Tk() fra1 = Frame(root,width=500,height=100,bg="darkred") fra2 = Frame(root,width=300,height=200,bg="green",bd=20) fra3 = Frame(root,width=500,height=150,bg="darkblue") fra1.pack() fra2.pack() fra3.pack() root.mainloop()
Let’s go ahead and just jump into a task. Let’s create a horizontal scrollbar.
scrollbar.py
from tkinter import * root = Tk() root.title("Scrollbar") root.minsize(width=300, height=150) fr = Frame(root, width=150, height=100, bg="lightgreen", bd=15) sc = Scale(fr,orient=HORIZONTAL, length=300, from_=0, to=100, tickinterval=10,resolution=5) fr2 = Frame(root, width=400, height=100, bg="green", bd=25) ent = Entry(fr2, width=10) fr.pack() sc.pack() fr2.pack() ent.pack() root.mainloop()
That was pretty easy. Let’s just get our thinking “outside of the box”. We will create event handler for canvas. In other words, we want to draw something and change the color every time we click on it.
canvas.py
from tkinter import * from random import randrange as rnd root = Tk() fr = Frame(root) root.geometry('800x600') bt1 = Button(fr,text = 'Hello 1') bt2 = Button(fr,text = 'Hello 2') bt1.pack(side='left',padx=2) bt2.pack(side='left',padx=2) fr.pack(pady=5, fill = X) canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) colors = ['orange','red','blue','yellow','green'] def click(event): global color color = colors[rnd(len(colors))] color = colors[rnd(len(colors))] def move(event): x = event.x y = event.y canv.create_oval(x-30,y-30,x+30,y+30,fill=color) canv.bind('',move) canv.bind('
2.4. Event handling
Sometimes, in many applications and especially in games we need to know what actions a user does. We can do it by implementing bind
method. The structure is following: widget.bind(event, function)
.
Before we go any further, just keep in mind couple events:
<Button - 1>
– left mouse click<Button - 2>
– middle mouse click<Button - 3>
– right mouse click<Double - Button - 1>
– double left click<Motion>
– mouse movements
So basically in order to do some event handling, we just use such events.
events.py
from tkinter import * def b1(event): root.title("Left mouse click") def b2(event): root.title("Middle mouse click") def b3(event): root.title("Right mouse click") def db1(event): root.title("Double left mouse click") def move(event): root.title("Mouse movement") root = Tk() root.minsize(width = 500, height=400) root.bind('',b1) root.bind('', b2) root.bind('',b3) root.bind('',db1) root.bind('',move) root.mainloop()
Now, if you are really careful, you understand that what we did is only mouse handling events. Surely, there are keys on keyboard and we need some handling for that too, right?
Well, that’s not too hard:
<Return>
– Enter press<Space>
– Space press<Control - Shift>
– Ctrl and Shift press
Another example to show you how it works:
keys.py
from tkinter import * def exit_(event): root.destroy() def caption(event): t = ent.get() lbl.configure(text = t) root = Tk() ent = Entry(root, width = 40) lbl = Label(root, width = 80) ent.pack() lbl.pack() ent.bind('',caption) root.bind('',exit_) root.mainloop()
Let’s head to another task. Let’s create an interface where we can use check boxes and see what’s left unchecked. By doing this task, we will revise frames and event handling. Here is my representation of how it can be done:
keys.py
import tkinter as tk class myapp: def __init__(self): self.root = tk.Tk() self.root.title("Event handling") self.root.bind("q",self.exitself) self.text = tk.Label(self.root,text='Press "q" to exit"') self.flagsDic = { 'First':1, 'Second':2, 'Third':3, 'Fourth':4 } for key in self.flagsDic: self.flagsDic[key] = tk.IntVar() Flag = tk.Checkbutton(self.root,text=key,variable=self.flagsDic[key]) Flag.grid(sticky='w') self.ent = tk.Entry(self.root,width=20) self.fstatus = tk.Text(self.root) self.text.grid(row=4,column=0,columnspan=2) self.fstatus.grid(row=0,column=1,rowspan=len(self.flagsDic)) self.fstatus.bind("",self.display) def display(self,event): checked = [] unchecked = [] for key, value in self.flagsDic.items(): if value.get(): checked.append(key) else: unchecked.append(key) if len(checked) == 0: message = "Unchecked:" for s in unchecked: message = message + " " + s print(message) if len(unchecked) == 0: message = "Checked:" for s in checked: message = message + " " + s if len(checked) != 0 and len(unchecked) != 0: message = "Unchecked:" for s in unchecked: message = message + " " + s message = message + "\nChecked:" for s in checked: message = message + " " + s self.fstatus.delete(0.0,tk.END) self.fstatus.insert(0.0,message) def exitself(self,event): self.root.destroy() def main(): app = myapp() app.root.mainloop() if __name__ == "__main__": main()
3. Summary
Tkinter
is a powerful library that is used for building interfaces. In order to use it, I want to put a table of what you might want to use:
Button
– to display buttonsCanvas
– to draw shapesCheckbutton
– to display number of optionsEntry
– to display a single-line text fieldFrame
– to organize other widgetsLabel
– to provide single-line captionListbox
– to provide list options to a userMenubutton
– to display menusMenu
– to provide various commands to a userMessage
– to display multiple text fieldsRadiobutton
– to display a number of options as radio buttonsScale
– to provide a slider widgetScrollbar
– to add scrolling capabilityText
– to display text in multiple timesToplevel
– to provide a separate window containerSpinbox
– to select from a fixed number of valuesPanedWindow
– it is a container widgetLabelFrame
– it is a simple container widget which is used to act as a spacertkMessageBox
– to display message boxes in your applications
4. Homework
One of the most easiest and still quite useful feature is calculator, right? So why not try to recreate it using Tkinter
? There are 2 options how to do it:
- We create one window and put some RAW data there. The program itself understands what is what, and prints the output
- We create a fully functional GUI calculator
The first option is below:
calculator_basic.py
from tkinter import * win = Tk() win.title("Calculator") def test(e): try: lab2["text"] = eval(ent.get()) except: lab2["text"] = "Incorrect input, try again" lab = Label(text="Put raw data using +, -, *, /, //, %, **") lab.pack() ent = Entry(width=50,bg="white",justify=CENTER) ent.focus() ent.pack() ent.bind('', test) lab2 = Label() lab2.pack() win.mainloop()
Another version is sort of GUI calculator which is more classic:
calculator_classic.py
from tkinter import * import parser root = Tk() root.title('Calculator') i = 0 def factorial(): """Calculates the factorial of the number entered.""" whole_string = display.get() number = int(whole_string) fact = 1 counter = number try: while counter > 0: fact = fact*counter counter -= 1 clear_all() display.insert(0, fact) except Exception: clear_all() display.insert(0, "Error") def clear_all(): """clears all the content in the Entry widget""" display.delete(0, END) def get_variables(num): """Gets the user input for operands and puts it inside the entry widget""" global i display.insert(i, num) i += 1 def get_operation(operator): """Gets the operand the user wants to apply on the functions""" global i length = len(operator) display.insert(i, operator) i += length def undo(): """removes the last entered operator/variable from entry widget""" whole_string = display.get() if len(whole_string): ## repeats until ## now just decrement the string by one index new_string = whole_string[:-1] print(new_string) clear_all() display.insert(0, new_string) else: clear_all() display.insert(0, "Error, press AC") def calculate(): whole_string = display.get() try: formulae = parser.expr(whole_string).compile() result = eval(formulae) clear_all() display.insert(0, result) except Exception: clear_all() display.insert(0, "Error!") root.columnconfigure(0,pad=3) root.columnconfigure(1,pad=3) root.columnconfigure(2,pad=3) root.columnconfigure(3,pad=3) root.columnconfigure(4,pad=3) root.rowconfigure(0,pad=3) root.rowconfigure(1,pad=3) root.rowconfigure(2,pad=3) root.rowconfigure(3,pad=3) display = Entry(root, font = ("Calibri", 13)) display.grid(row = 1, columnspan = 6 , sticky = W+E) one = Button(root, text = "1", command = lambda : get_variables(1), font=("Calibri", 12)) one.grid(row = 2, column = 0) two = Button(root, text = "2", command = lambda : get_variables(2), font=("Calibri", 12)) two.grid(row = 2, column = 1) three = Button(root, text = "3", command = lambda : get_variables(3), font=("Calibri", 12)) three.grid(row = 2, column = 2) four = Button(root, text = "4", command = lambda : get_variables(4), font=("Calibri", 12)) four.grid(row = 3 , column = 0) five = Button(root, text = "5", command = lambda : get_variables(5), font=("Calibri", 12)) five.grid(row = 3, column = 1) six = Button(root, text = "6", command = lambda : get_variables(6), font=("Calibri", 12)) six.grid(row = 3, column = 2) seven = Button(root, text = "7", command = lambda : get_variables(7), font=("Calibri", 12)) seven.grid(row = 4, column = 0) eight = Button(root, text = "8", command = lambda : get_variables(8), font=("Calibri", 12)) eight.grid(row = 4, column = 1) nine = Button(root , text = "9", command = lambda : get_variables(9), font=("Calibri", 12)) nine.grid(row = 4, column = 2) cls = Button(root, text = "AC", command = clear_all, font=("Calibri", 12), foreground = "red") cls.grid(row = 5, column = 0) zero = Button(root, text = "0", command = lambda : get_variables(0), font=("Calibri", 12)) zero.grid(row = 5, column = 1) result = Button(root, text = "=", command = calculate, font=("Calibri", 12), foreground = "red") result.grid(row = 5, column = 2) plus = Button(root, text = "+", command = lambda : get_operation("+"), font=("Calibri", 12)) plus.grid(row = 2, column = 3) minus = Button(root, text = "-", command = lambda : get_operation("-"), font=("Calibri", 12)) minus.grid(row = 3, column = 3) multiply = Button(root,text = "*", command = lambda : get_operation("*"), font=("Calibri", 12)) multiply.grid(row = 4, column = 3) divide = Button(root, text = "/", command = lambda : get_operation("/"), font=("Calibri", 12)) divide.grid(row = 5, column = 3) # adding new operations pi = Button(root, text = "pi", command = lambda: get_operation("*3.14"), font =("Calibri", 12)) pi.grid(row = 2, column = 4) modulo = Button(root, text = "%", command = lambda : get_operation("%"), font=("Calibri", 12)) modulo.grid(row = 3, column = 4) left_bracket = Button(root, text = "(", command = lambda: get_operation("("), font =("Calibri", 12)) left_bracket.grid(row = 4, column = 4) exp = Button(root, text = "exp", command = lambda: get_operation("**"), font = ("Calibri", 10)) exp.grid(row = 5, column = 4) ## To be added : # sin, cos, log, ln undo_button = Button(root, text = "<-", command = undo, font =("Calibri", 12), foreground = "red") undo_button.grid(row = 2, column = 5) fact = Button(root, text = "x!", command = factorial, font=("Calibri", 12)) fact.grid(row = 3, column = 5) right_bracket = Button(root, text = ")", command = lambda: get_operation(")"), font =("Calibri", 12)) right_bracket.grid(row = 4, column = 5) square = Button(root, text = "^2", command = lambda: get_operation("**2"), font = ("Calibri", 10)) square.grid(row = 5, column = 5) root.mainloop()
5. Download the source code
You can find all materials needed in the file below.
You can download the full source code of this example here: Python-Tkinter-tutorial.zip