All 5.1 32 min read 10 question quiz 18 exam marks

Building Interfaces with tkinter

Windows, widgets, layout and events. Turning a console program into something with buttons that a real person would use.

Your first window

Python
import tkinter as tk

window = tk.Tk()
window.title("My First App")
window.geometry("400x300")

label = tk.Label(window, text="Hello, world!", font=("Arial", 16))
label.pack(pady=20)

window.mainloop()

What each line does

  • tk.Tk() creates the main window
  • title() sets the text in the title bar
  • geometry("400x300") sets the width and height in pixels
  • tk.Label(window, ...) creates a label inside that window
  • pack() places the widget on screen. Nothing appears until it is placed.
  • mainloop() starts the event loop, which waits for the user to do something. Everything after it runs only when the window closes.
Creating a widget is not enough

A widget you never pack, grid or place will not appear. This is the single most common beginner problem with tkinter.

Widgets and layout

The widgets you will use most

Python
tk.Label(window, text="Some text")
tk.Button(window, text="Click me", command=do_something)
tk.Entry(window, width=30)                 # single line text box
tk.Text(window, height=5, width=40)        # multi line
tk.Checkbutton(window, text="Agree", variable=agreed)
tk.Radiobutton(window, text="Red", variable=colour, value="red")
tk.Listbox(window)
tk.Scale(window, from_=0, to=100, orient="horizontal")
tk.Frame(window)                           # a container for grouping

Three layout managers

pack stacks widgets in order.

Python
label.pack(side="top", pady=10)
button.pack(side="bottom", fill="x")

grid arranges widgets in rows and columns. This is the one to use for forms.

Python
tk.Label(window, text="Name:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
name_entry = tk.Entry(window)
name_entry.grid(row=0, column=1, padx=5, pady=5)

tk.Label(window, text="Age:").grid(row=1, column=0, sticky="e", padx=5, pady=5)
age_entry = tk.Entry(window)
age_entry.grid(row=1, column=1, padx=5, pady=5)

tk.Button(window, text="Submit").grid(row=2, column=0, columnspan=2, pady=10)

place positions by exact pixel coordinates. Avoid it, because the layout breaks the moment the window is resized.

Never mix pack and grid in the same container

tkinter will freeze with no error message. Use one or the other per frame.

Reading and writing widget values

Python
name = name_entry.get()          # read an Entry
name_entry.delete(0, tk.END)     # clear it
name_entry.insert(0, "default")  # put text in

label.config(text="Updated!")    # change a Label after creation

Variables that update automatically

Python
score = tk.IntVar(value=0)
message = tk.StringVar(value="Ready")

tk.Label(window, textvariable=message).pack()

def add_point():
    score.set(score.get() + 1)
    message.set(f"Score: {score.get()}")

Changing the variable updates every widget bound to it, with no manual refresh.

A tkinter window with its widgets, the code that makes it, and the event loopA small tkinter window called Mark book, built from widgets laid out in a grid. One, a Label reading Name next to two, an Entry that holds the name Aisha; the same again for the mark; three, a Button labelled Add; and four, a Listbox showing the marks added so far. Beside it is the code: tk.Tk creates the window, tk.Label creates a label and grid places it in a row and column, tk.Entry creates an entry, tk.Button creates a button whose command is a function, and root.mainloop starts the event loop. Five: mainloop waits. When the user clicks, the button's function, here add, is called, the screen is redrawn, and the program goes back to waiting.Mark bookName:AishaMark:72AddAisha 72Ben 551234app.pyroot = tk.Tk()tk.Label(root, text='Name:').grid(row=0, column=0)entry = tk.Entry(root)entry.grid(row=0, column=1)tk.Button(root, text='Add',          command=add).grid(row=2, column=1)root.mainloop()Label: text to readEntry: text to typeButton: runs a functionListbox: a list to showmainloop() waits for events5waitclickcall add()redraw
  1. A Label shows text.
  2. An Entry lets the user type.
  3. A Button runs a function when clicked.
  4. A Listbox shows a list.
  5. mainloop() waits for events. A click calls your function, the screen is redrawn, then it waits again.
Widgets are created, then placed with grid(). Nothing happens until mainloop() starts waiting for the user.
A tkinter window laid out with grid, the code that does it, and the three layout managersHow grid lays out a tkinter window. The window is a table of three columns and three rows. A Label reading Name is in row 0, column 0. An Entry holding Aisha is in row 0, column 1 and uses columnspan 2 so that it crosses columns 1 and 2. A Label reading Mark is in row 1, column 0, an Entry holding 72 is in row 1, column 1, and a Button labelled Add is in row 1, column 2. A Listbox is in row 2, column 0 with columnspan 3 so that it crosses the whole width. The code beside it has one grid call for each widget, with the row and column. Below are the three layout managers: pack puts widgets one after another, grid places them in a table of rows and columns, and place puts them at an exact x and y position and is rarely used. Use one manager in each container.grid(): rows and columns1column 0column 1column 2row 0row 1row 2Name:AishaMark:72AddAisha 72columnspan lets a widget cross several columnslayout.pyname_label.grid(row=0, column=0)name_entry.grid(row=0, column=1,               columnspan=2)mark_label.grid(row=1, column=0)mark_entry.grid(row=1, column=1)add_button.grid(row=1, column=2)listbox.grid(row=2, column=0,             columnspan=3)2Three ways to place a widget3pack()one after another, top to bottomgrid()a table of rows and columnsplace()exact x and y, rarely used
  1. Grid is a table. Every widget gets a row and a column, counted from 0.
  2. One grid() call per widget, and columnspan lets a widget cross several columns.
  3. pack, grid and place are the three layout managers. Use only one of them inside a container.
Plan the window as a table on paper first, then every grid() call is just reading off a cell.

Events and a complete app

Buttons

Python
def on_click():
    print("Clicked")

tk.Button(window, text="Go", command=on_click).pack()

Note there are no brackets after on_click. You are passing the function itself, not calling it.

Passing arguments

Python
tk.Button(window, text="Add 5", command=lambda: add_score(5)).pack()

Binding other events

Python
window.bind("<Return>", lambda event: submit())
window.bind("<Key>", on_keypress)
canvas.bind("<Button-1>", on_left_click)

Message boxes

Python
from tkinter import messagebox

messagebox.showinfo("Saved", "Your work has been saved.")
messagebox.showerror("Error", "That is not a valid number.")
if messagebox.askyesno("Confirm", "Delete this record?"):
    delete_record()

A complete application

Python
import tkinter as tk
from tkinter import messagebox


class MarkBookApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Mark Book")
        self.root.geometry("420x420")
        self.marks = []
        self.build_interface()

    def build_interface(self):
        form = tk.Frame(self.root, padx=12, pady=12)
        form.pack(fill="x")

        tk.Label(form, text="Student name:").grid(row=0, column=0, sticky="e", pady=4)
        self.name_entry = tk.Entry(form, width=22)
        self.name_entry.grid(row=0, column=1, pady=4)

        tk.Label(form, text="Mark out of 100:").grid(row=1, column=0, sticky="e", pady=4)
        self.mark_entry = tk.Entry(form, width=22)
        self.mark_entry.grid(row=1, column=1, pady=4)

        tk.Button(form, text="Add", width=12, command=self.add_mark)\
            .grid(row=2, column=0, columnspan=2, pady=8)

        self.listbox = tk.Listbox(self.root, height=10)
        self.listbox.pack(fill="both", expand=True, padx=12)

        self.summary = tk.StringVar(value="No marks yet")
        tk.Label(self.root, textvariable=self.summary, pady=8).pack()

        self.root.bind("<Return>", lambda event: self.add_mark())

    def add_mark(self):
        name = self.name_entry.get().strip()
        entry = self.mark_entry.get().strip()

        if name == "":
            messagebox.showerror("Missing name", "Please enter a student name.")
            return
        if not entry.isdigit() or not 0 <= int(entry) <= 100:
            messagebox.showerror("Invalid mark", "Enter a whole number between 0 and 100.")
            return

        mark = int(entry)
        self.marks.append(mark)
        self.listbox.insert(tk.END, f"{name:<20} {mark}")

        average = sum(self.marks) / len(self.marks)
        self.summary.set(f"{len(self.marks)} marks, average {average:.1f}, highest {max(self.marks)}")

        self.name_entry.delete(0, tk.END)
        self.mark_entry.delete(0, tk.END)
        self.name_entry.focus()


root = tk.Tk()
app = MarkBookApp(root)
root.mainloop()

This program validates every input, gives clear error messages, updates its summary automatically and supports the enter key. That is the standard to aim for.

Key terms you must be able to define

tkinter
Python's standard library for building graphical user interfaces.
Widget
A single interface component such as a label, button or text box.
Window
The top level container created with Tk(), holding all the widgets.
mainloop
The event loop that waits for user actions and keeps the window open.
pack
A layout manager that stacks widgets in the order they are added.
grid
A layout manager that places widgets in rows and columns.
Event
Something the user does, such as a click or a key press, which the program responds to.
command
The Button option naming the function to call when the button is pressed.
StringVar
A tkinter variable that automatically updates any widget bound to it.

Flashcards

Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.

Definetkinter
AnswerPython's standard library for building graphical user interfaces.
DefineWidget
AnswerA single interface component such as a label, button or text box.
DefineWindow
AnswerThe top level container created with Tk(), holding all the widgets.
Definemainloop
AnswerThe event loop that waits for user actions and keeps the window open.
Definepack
AnswerA layout manager that stacks widgets in the order they are added.
Definegrid
AnswerA layout manager that places widgets in rows and columns.
DefineEvent
AnswerSomething the user does, such as a click or a key press, which the program responds to.
Definecommand
AnswerThe Button option naming the function to call when the button is pressed.

Reach the top grade on this topic

  • Use grid for anything form shaped, and never mix grid and pack in one container
  • Pass the function name without brackets to command, and use lambda when arguments are needed
  • Validate every Entry before using its value, since get() always returns a string
  • Use StringVar and IntVar so labels update themselves
  • Structure a larger app as a class, so widgets are attributes rather than globals

Mistakes that cost marks here

Avoid these
  • Creating a widget but never calling pack or grid, so it never appears.
  • Writing command=on_click() with brackets, which calls the function immediately instead of on click.
  • Mixing pack and grid in the same container, which freezes the program with no error.
  • Forgetting that Entry.get() returns a string, then comparing it with a number.
  • Putting code after mainloop() and wondering why it never runs.
Fun fact

tkinter has been part of Python's standard library since 1994, which means it is already installed on almost every computer running Python. No pip install, no dependencies, nothing to go wrong on the school network.

Knowledge check

0 of 10 answered
1What does window.mainloop() do?
2Why might a widget not appear on screen?
3What is wrong with tk.Button(window, command=save())?
4Which layout manager is best for a form with labels and text boxes?
5What happens if you use both pack and grid in the same container?
6What does entry.get() return?
7What is a StringVar used for?
8How do you pass an argument to a button's command function?
9What does label.config(text="New") do?
10Which line creates the main application window?
Score 0 / 10

Exam-style questions

Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.

18 marks
1Explain the purpose of the mainloop method in a tkinter program.3 marks
Explain

Mark scheme0 / 3
  • LIt starts the event loop
  • LIt keeps the window open and waits for user actions such as clicks and key presses
  • LCode after it does not run until the window closes
Model answer

The mainloop method starts tkinter's event loop, which is what makes the interface interactive. It keeps the window on screen and continuously waits for events such as button clicks, key presses and mouse movements, dispatching each one to whatever function has been bound to it. Because the program stays inside mainloop until the window is closed, any code written after the mainloop call will not run while the application is open, which is a common source of confusion for beginners.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2A student creates a label with label = tk.Label(window, text="Hello") but nothing appears when the program runs. Explain why and give the correction.2 marks
Explain

Mark scheme0 / 2
  • LThe widget has not been added to the window with a layout manager
  • LAdd label.pack() or label.grid() to display it
Model answer

Creating a widget builds it in memory but does not put it on screen. A layout manager must be called to add it to the window, so the student needs to add a line such as label.pack() or label.grid(row=0, column=0) after creating the label. Without this the label exists but is never displayed.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3Explain the difference between the pack and grid layout managers, and state when each is most suitable.4 marks
Explain

Mark scheme0 / 4
  • Lpack stacks widgets in the order they are added
  • Lgrid places widgets in rows and columns
  • Lpack suits simple stacked layouts
  • Lgrid suits forms where labels and inputs must line up
Model answer

The pack layout manager stacks widgets one after another in the order they are added, using a side such as top, bottom, left or right. It is quick and suits simple layouts where widgets sit in a single column or row, such as a title, a list and a status bar down the window. The grid layout manager places widgets in numbered rows and columns like a table, which makes it far better for forms where a column of labels must line up neatly against a column of entry boxes. Grid also allows a widget to span several columns and to be aligned within its cell. The two managers must never be used within the same container, because tkinter will hang without producing an error message.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
4Write a tkinter program that displays a text box and a button, and when the button is clicked shows the entered text in a label.6 marks
Write

Mark scheme0 / 6
  • LImports tkinter and creates the main window
  • LCreates an Entry widget and displays it
  • LCreates a Label to show the result
  • LDefines a function that reads the entry
  • LUpdates the label using config or a StringVar
  • LAttaches the function to the button with command and starts mainloop
Model answer

import tkinter as tk def show_text(): result.config(text="You typed: " + entry.get()) window = tk.Tk() window.title("Echo") entry = tk.Entry(window, width=30) entry.pack(pady=10) tk.Button(window, text="Show", command=show_text).pack() result = tk.Label(window, text="") result.pack(pady=10) window.mainloop() The function is passed to command without brackets, so it is called when the button is clicked rather than immediately when the program starts. The function reads the current contents of the entry with get and updates the label using config. Every widget is packed, otherwise none of them would appear.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5Explain why input taken from a tkinter Entry widget should be validated before it is used.3 marks
Explain

Mark scheme0 / 3
  • LEntry.get() always returns a string, whatever the user typed
  • LThe user may type letters or leave it blank when a number is expected
  • LWithout validation the program may crash or produce incorrect results
Model answer

The get method of an Entry widget always returns a string, no matter what the user typed, so any value intended as a number must be converted before it can be used in a calculation. Users can type anything at all into an entry box, including letters, symbols or nothing, so if the program calls int on the contents without checking it first, a ValueError will be raised and the application will crash. Validating the input, for example by checking isdigit and then checking the value falls within an acceptable range, allows the program to display a clear message asking the user to correct the entry instead, which is what makes the application robust.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
Machine mark 0 / 18