Skip to Content
Course content

386: Building a Pomodoro Timer App with tkinter

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you've got the hang of Python's logic, but things get interesting when we actually put a face on our code. Today, we're building a Pomodoro Timer. I love the Pomodoro technique for deep work, and it's the perfect project for tkinter because it requires a mix of layout management and a background loop that doesn't freeze the window.

Drafting the Interface

I don't like over-engineering the UI at the start. I want a clean window, a big label to show the time, and a couple of buttons. I'll use a class here—it just makes managing the timer state so much easier than juggling global variables.

import tkinter as tk

class PomodoroApp:
    def __init__(self, root):
        self.root = root
        self.root.title("My Focus Timer")
        self.root.geometry("300x200")

        self.timer_running = False
        self.time_left = 25 * 60  # 25 minutes in seconds

        self.label = tk.Label(root, text="25:00", font=("Helvetica", 48))
        self.label.pack(pady=20)

        self.start_button = tk.Button(root, text="Start", command=self.toggle_timer)
        self.start_button.pack()

        self.reset_button = tk.Button(root, text="Reset", command=self.reset_timer)
        self.reset_button.pack(pady=10)

    def toggle_timer(self):
        self.timer_running = not self.timer_running
        if self.timer_running:
            self.start_button.config(text="Pause")
            self.update_clock()
        else:
            self.start_button.config(text="Start")

    def reset_timer(self):
        self.timer_running = False
        self.time_left = 25 * 60
        self.label.config(text="25:00")
        self.start_button.config(text="Start")

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

The "Frozen Window" Trap

Now, here is where I almost messed up. My first instinct was to use a while self.timer_running: loop with time.sleep(1) to handle the countdown. I tried it, and the window immediately stopped responding. It looked like the app had crashed, but in reality, the main loop was stuck in my while loop, so tkinter couldn't redraw the screen or process button clicks.

In GUI programming, you can't "pause" the main thread. Instead, we use the .after() method. This tells tkinter: "Go ahead and keep the UI responsive, but come back and run this specific function again in 1,000 milliseconds." It's a recursive-style call that keeps the app breathing.

Wiring up the Countdown Logic

Let's implement the update_clock method using .after(). I'll also add a bit of math to convert those raw seconds into a pretty MM:SS format. I'm using f-strings with padding (:02d) so that "9 seconds" shows up as "09" instead of "9".

    def update_clock(self):
        if self.timer_running and self.time_left > 0:
            self.time_left -= 1
            
            # Calculate minutes and seconds
            mins, secs = divmod(self.time_left, 60)
            self.label.config(text=f"{mins:02d}:{secs:02d}")
            
            # Schedule the next update in 1 second
            self.root.after(1000, self.update_clock)
        elif self.time_left == 0:
            self.label.config(text="Time's Up!")
            self.timer_running = False
            self.start_button.config(text="Start")

Adding the Break Cycle

A Pomodoro timer isn't useful if it only does the work phase. We need a way to switch to a short break. I'll add a simple toggle. If the timer hits zero, I'll trigger a "Break" mode. To keep this example concise, I'll just add a button that lets the user manually switch between a 25-minute focus session and a 5-minute break.

I'll add a new method set_mode(self, minutes) and a couple of buttons to the UI. This allows us to reuse the same update_clock logic regardless of whether we are working or resting. It's always better to write generic logic that accepts parameters than to write separate "work_timer" and "break_timer" functions.




📋 Practical Task

Exercise: Build a Visual Alert System for the Pomodoro App

Modify the PomodoroApp class to include a visual warning system. Your task is to implement the following functionality:

  • Change the Label text color to red when the timer reaches the final 10 seconds of a session.
  • Change the background color of the entire window (root) to a light green when the timer hits 0:00 to signal it's time for a break.
  • Ensure that when the "Reset" button is clicked, both the text color and the background color return to their original defaults.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.