Day 28: Basic GUI Development with Tkinter

🐍 Day 28: Basic GUI Development with Tkinter


1. Learning Objectives

By the end of Day 28, you will be able to:

  • Understand event‑driven programming and how GUIs differ from console apps
  • Create windows, labels, buttons, entry fields, and text areas with Tkinter
  • Arrange widgets using geometry managers (pack, grid, place)
  • Respond to user interactions with event handlers and callbacks
  • Build a fully functional desktop application (calculator, to‑do list)
  • Style widgets with colours, fonts, and padding

2. Concept Explanation

2.1 What is Tkinter? — Python's Built‑in GUI Toolkit

Tkinter is Python's standard GUI (Graphical User Interface) library. It's a thin wrapper around Tcl/Tk, which has been battle‑tested for decades. Tkinter ships with every Python installation — no extra installs needed.

Why Tkinter?

StrengthDetail
Built‑inWorks out of the box with any Python install
Cross‑platformWindows, macOS, Linux — same code
SimpleIdeal for learning GUI concepts
SufficientPerfect for internal tools, small apps, prototypes

Alternatives (for later exploration):

  • PyQt / PySide — professional, feature‑rich, steeper learning curve
  • Kivy — multi‑touch, mobile‑friendly
  • Dear PyGui — modern, fast, GPU‑accelerated

2.2 Event‑Driven Programming — A New Paradigm

Console programs run sequentially — line by line, waiting for input() when needed. GUI programs are event‑driven — they start, draw the window, then enter an event loop that waits for the user to click, type, or move the mouse.

Console flow:                      GUI flow:
─────► start                        ─────► start
  │                                    ├─► draw window
  ├─► input()                          └─► event loop (infinite)
  ├─► process                              ├─► user clicks button → handler()
  ├─► print()                              ├─► user types text   → handler()
  └─► end                                  └─► user closes window → exit

Your code reacts to events by binding functions (callbacks) to widgets.


2.3 Your First Tkinter Window

import tkinter as tk

# Create the main window
root = tk.Tk()
root.title("My First GUI")
root.geometry("400x300")     # width x height in pixels

# Create a label widget
label = tk.Label(root, text="Hello, Tkinter!", font=("Arial", 16))
label.pack(pady=20)          # add it to the window with vertical padding

# Start the event loop
root.mainloop()

Every Tkinter app follows this skeleton:

  1. Create the root window (tk.Tk()).
  2. Create widgets (Label, Button, Entry, etc.) and attach them to a parent.
  3. Arrange widgets with a geometry manager (pack, grid, or place).
  4. Start the event loop (root.mainloop()).

2.4 Core Widgets — Your GUI Building Blocks

WidgetPurposeExample
LabelDisplay text or imagetk.Label(root, text="Name:")
ButtonClickable buttontk.Button(root, text="Submit", command=callback)
EntrySingle‑line text inputtk.Entry(root, width=30)
TextMulti‑line text areatk.Text(root, height=5, width=40)
CheckbuttonOn/off checkboxtk.Checkbutton(root, text="Agree")
RadiobuttonOne‑of‑many selectiontk.Radiobutton(root, text="Option 1", variable=var, value=1)
ListboxScrollable listtk.Listbox(root)
FrameContainer for other widgetstk.Frame(root)
ScaleSlider controltk.Scale(root, from_=0, to=100)

Every widget's constructor takes:

  • parent — the container it belongs to (usually root or a Frame)
  • Configuration options like text, width, height, font, bg, fg

2.5 Geometry Managers — Arranging Widgets

Tkinter offers three layout systems:

pack() — Stack widgets vertically or horizontally

label1 = tk.Label(root, text="Top")
label1.pack(side="top", fill="x")

label2 = tk.Label(root, text="Bottom")
label2.pack(side="bottom")
OptionEffect
side"top", "bottom", "left", "right"
fill"x" (horizontal), "y" (vertical), "both"
expandTrue to use extra space
padx, padyExternal padding in pixels
ipadx, ipadyInternal padding

grid() — Row‑column layout (most versatile)

tk.Label(root, text="Username:").grid(row=0, column=0, sticky="e")
tk.Entry(root).grid(row=0, column=1)

tk.Label(root, text="Password:").grid(row=1, column=0, sticky="e")
tk.Entry(root, show="*").grid(row=1, column=1)

tk.Button(root, text="Login").grid(row=2, column=0, columnspan=2)
OptionEffect
row, columnPosition (0‑indexed)
sticky"n", "s", "e", "w" — which edge to stick to
columnspan, rowspanSpan multiple cells
padx, padyInternal padding

place() — Absolute positioning (rarely used)

tk.Label(root, text="X: 100, Y: 50").place(x=100, y=50)

💡 Recommendation: grid() is the most flexible. Use it for forms and most layouts. pack() is fine for simple stacks. Avoid place() for responsive designs.


2.6 Event Handling — Making Widgets Interactive

The command parameter — simplest way to attach a callback to a Button:

def say_hello():
    label.config(text="Hello, World!")

btn = tk.Button(root, text="Click Me", command=say_hello)

Retrieving data from widgets:

def submit():
    name = entry.get()           # Get text from Entry
    text_area.insert("end", f"{name}\n")  # Append to Text widget
    entry.delete(0, "end")      # Clear the Entry

entry = tk.Entry(root)
btn = tk.Button(root, text="Submit", command=submit)

Binding additional events:

def on_key_press(event):
    print(f"Key pressed: {event.char}")

root.bind("<Key>", on_key_press)  # Bind to all key presses
entry.bind("<Return>", lambda e: submit())  # Enter key triggers submit

Common event patterns:

EventDescription
"<Button-1>"Left mouse click
"<Double-Button-1>"Double click
"<Return>"Enter key
"<Key>"Any key press
"<FocusIn>"Widget receives focus
"<FocusOut>"Widget loses focus

2.7 Changing Widget Appearance

label = tk.Label(
    root,
    text="Styled Label",
    font=("Helvetica", 14, "bold"),
    fg="white",           # foreground (text) colour
    bg="#333333",         # background colour
    padx=10,
    pady=5,
    relief="ridge",       # border style
    borderwidth=2
)

Colour names: "red", "blue", "green", "white", "black", "gray" — or hex codes like "#FF5733".


2.8 Message Boxes and Dialogs

from tkinter import messagebox

def confirm_exit():
    answer = messagebox.askyesno("Exit", "Are you sure you want to quit?")
    if answer:
        root.destroy()

# Types of message boxes:
# messagebox.showinfo(title, message)
# messagebox.showwarning(title, message)
# messagebox.showerror(title, message)
# messagebox.askyesno(title, message)
# messagebox.askokcancel(title, message)

3. Code Examples

Example 1: Simple Login Form

import tkinter as tk
from tkinter import messagebox

def login():
    username = entry_user.get()
    password = entry_pass.get()
    if username == "admin" and password == "1234":
        messagebox.showinfo("Success", "Login successful!")
    else:
        messagebox.showerror("Error", "Invalid credentials")

root = tk.Tk()
root.title("Login")
root.geometry("300x200")

tk.Label(root, text="Username:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
entry_user = tk.Entry(root, width=25)
entry_user.grid(row=0, column=1, padx=10)

tk.Label(root, text="Password:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
entry_pass = tk.Entry(root, show="*", width=25)
entry_pass.grid(row=1, column=1, padx=10)

tk.Button(root, text="Login", command=login, width=12).grid(row=2, column=0, columnspan=2, pady=15)

root.mainloop()

Example 2: Simple Calculator

import tkinter as tk

def click(key):
    if key == "=":
        try:
            result = eval(display.get())
            display.delete(0, "end")
            display.insert("end", str(result))
        except:
            display.delete(0, "end")
            display.insert("end", "Error")
    elif key == "C":
        display.delete(0, "end")
    else:
        display.insert("end", key)

root = tk.Tk()
root.title("Calculator")

display = tk.Entry(root, font=("Arial", 20), justify="right", width=15)
display.grid(row=0, column=0, columnspan=4, padx=5, pady=5)

buttons = [
    "7", "8", "9", "/",
    "4", "5", "6", "*",
    "1", "2", "3", "-",
    "C", "0", "=", "+",
]

for i, text in enumerate(buttons):
    row = i // 4 + 1
    col = i % 4
    tk.Button(root, text=text, width=5, height=2,
              font=("Arial", 14),
              command=lambda t=text: click(t)).grid(row=row, column=col, padx=2, pady=2)

root.mainloop()

Example 3: To‑Do List App

import tkinter as tk
from tkinter import messagebox

def add_task():
    task = entry.get().strip()
    if task:
        listbox.insert("end", task)
        entry.delete(0, "end")
    else:
        messagebox.showwarning("Warning", "Task cannot be empty.")

def remove_task():
    try:
        selected = listbox.curselection()[0]
        listbox.delete(selected)
    except IndexError:
        messagebox.showwarning("Warning", "Select a task to remove.")

def clear_tasks():
    if messagebox.askyesno("Confirm", "Remove all tasks?"):
        listbox.delete(0, "end")

root = tk.Tk()
root.title("To‑Do List")
root.geometry("400x400")

frame_top = tk.Frame(root)
frame_top.pack(pady=10)

entry = tk.Entry(frame_top, width=30, font=("Arial", 12))
entry.pack(side="left", padx=5)
entry.bind("<Return>", lambda e: add_task())

tk.Button(frame_top, text="Add", command=add_task, width=8).pack(side="left")

listbox = tk.Listbox(root, font=("Arial", 12), selectmode="single")
listbox.pack(fill="both", expand=True, padx=10, pady=5)

frame_bottom = tk.Frame(root)
frame_bottom.pack(pady=10)

tk.Button(frame_bottom, text="Remove", command=remove_task, width=10).pack(side="left", padx=5)
tk.Button(frame_bottom, text="Clear All", command=clear_tasks, width=10).pack(side="left", padx=5)

root.mainloop()

4. Hands-On Exercises

Exercise 1: Temperature Converter

Build a GUI with an Entry for Celsius, a Button labelled "Convert", and a Label that displays the result in Fahrenheit. Formula: F = C × 9/5 + 32. Handle invalid input gracefully.

Exercise 2: Counter App

Create a simple app with a Label showing a number (start at 0) and two Button widgets: "Increment" and "Decrement". Clicking them should update the label. Bonus: change the label colour to green when positive, red when negative.

Exercise 3: Registration Form

Build a form with fields: Name, Email, Age, and Password. Add a "Submit" button that validates all fields are non‑empty and age is a number. Show a success/failure message using messagebox.

Exercise 4: Word Counter

Create a Text widget where the user can type or paste text. Below it, show a Label that updates in real time showing the character count and word count. (Hint: bind to "<KeyRelease>")

Exercise 5: Colour Picker

Add three Scale widgets (sliders) for Red, Green, Blue values (0–255). A Frame or Label should display the resulting colour in real time. Show the hex colour code below it (e.g., #4A7B3C).


5. Applied Challenge Task 🏗️

Personal Finance Tracker GUI

Transform the CLI Expense Tracker (Day 23) into a desktop GUI application using Tkinter.

Requirements:

  1. Main Window: A well‑laid‑out window with sections for input and display.

  2. Input Form:

    • Entry for amount (validate positive number)
    • Combobox or OptionMenu for category (Food, Transport, Entertainment, Utilities, Other)
    • Entry for description (optional)
    • Entry for date (default to today)
    • "Add Expense" button
  3. Expense List:

    • Use a Treeview (from tkinter.ttk) to display all expenses in columns: ID, Date, Category, Amount, Description.
    • Add a scrollbar.
    • Highlight expenses over a configurable threshold (e.g., > $100 in red).
  4. Controls:

    • "Delete Selected" button — removes the selected row from DB and display.

    • "Refresh" button — reloads data from the database.

    • "Summary" button — opens a new window showing:

      • Total spent
      • Total per category (bar chart using simple Canvas rectangles)
      • Number of expenses
  5. Data Persistence:

    • Use SQLite (same schema as Day 23).
    • Load expenses on startup.
    • Save on every add/delete.
  6. Polish:

    • Use ttk themed widgets for a modern look.
    • Add keyboard shortcuts (Enter to add, Delete to remove).
    • Handle window close event to confirm exit.

Stretch goals:

  • Add a "Budget" feature: set a monthly budget per category, show warnings when exceeded.
  • Export the expense list as CSV or JSON via a File menu.
  • Add a date filter (two Entry fields for start/end date).

6. Brief Review Summary

ConceptKey Points
TkinterPython's built‑in GUI toolkit; no install needed
Event looproot.mainloop() — waits for and dispatches events
WidgetsLabel, Button, Entry, Text, Listbox, Frame, Scale, etc.
Geometry managerspack() (stack), grid() (row‑column), place() (absolute)
Callbackscommand= parameter or .bind() for events
messageboxshowinfo, showwarning, showerror, askyesno
Treeviewttk.Treeview — table‑like display with columns

7. Preview of Next Topic — Day 29

Tomorrow we’ll prepare your projects for the real world:

  • Packaging and Distribution — turning your code into installable packages
  • setup.py / pyproject.toml — defining project metadata
  • Virtual environments — review of venv and pip freeze
  • Git Integration — version control basics, .gitignore, README
  • Running Python anywherepyinstaller for standalone executables

🎯 Your Action Items for Day 28:

  1. ✅ Complete all 5 exercises
  2. ✅ Build the Personal Finance Tracker GUI challenge
  3. ✅ Experiment with ttk themed widgets for a more modern look
  4. ✅ Try binding different events (<Key>, <Button-3>, <MouseWheel>)

Comments

Popular posts from this blog

Day 1: Welcome to Python — Your Journey Begins

Python: Your Gateway to Coding Adventures

Day 11: List Comprehensions & Lambda Functions

Earn From the Comfort of Your Home

Build the skills to work comfortably from home, on your own terms.

Get Started Today