Python

Python Tkinter Tutorial

Today we will be talking about one python library that is used for GUI programming which is Tkinter.
 
 
 
 
 
 
 
 
 
 

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 buttons
  • Canvas – to draw shapes
  • Checkbutton – to display number of options
  • Entry – to display a single-line text field
  • Frame – to organize other widgets
  • Label – to provide single-line caption
  • Listbox – to provide list options to a user
  • Menubutton – to display menus
  • Menu – to provide various commands to a user
  • Message – to display multiple text fields
  • Radiobutton – to display a number of options as radio buttons
  • Scale – to provide a slider widget
  • Scrollbar – to add scrolling capability
  • Text – to display text in multiple times
  • Toplevel – to provide a separate window container
  • Spinbox – to select from a fixed number of values
  • PanedWindow – it is a container widget
  • LabelFrame – it is a simple container widget which is used to act as a spacer
  • tkMessageBox – 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.

Downoload
You can download the full source code of this example here: Python-Tkinter-tutorial.zip

Aleksandr Krasnov

Aleksandr is passionate about teaching programming. His main interests are Neural Networks, Python and Web development. Hobbies are game development and translating. For the past year, he has been involved in different international projects as SEO and IT architect.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button