DevelopmentOctober 20, 20253 min read

Digital Clock using Python and Tkinter

Build a live digital clock with Python and Tkinter — updating time display, date, and 12/24-hour format in a resizable always-on-top window.

Galvan

Galvan

Founder & Creator

Introduction

A digital clock is the smallest possible live-updating GUI — and that's exactly why it's worth building. The challenge isn't showing the time; it's *re-showing it every second* inside Tkinter's event loop, without freezing the window. The answer is the after() method, Tkinter's gentle timer, and it is the same scheduling idea behind the Pomodoro timer's autorefresh.

Fifty lines get you a resizable, always-on-top clock with 12/24-hour toggle and the current date.

Features

  • Live seconds — the display updates every second, smoothly.
  • 12/24-hour toggle — one button, instant switch.
  • Date line — full weekday, month, day, and year.
  • Always on top — stays visible above other windows.
  • Resizable display — font scales with the window.
  • Prerequisites

  • Python 3.8+ with Tkinter (bundled on Windows/macOS; sudo apt install python3-tk on Ubuntu).
  • Step 1: Create the Script

    Save as digital_clock.py:

    code
    import tkinter as tk
    from datetime import datetime
    
    
    class DigitalClock:
        def __init__(self, root):
            self.root = root
            self.root.title("Digital Clock")
            self.root.attributes("-topmost", True)
            self.root.configure(bg="#0e1117")
            self.use_24h = tk.BooleanVar(value=True)
    
            self.time_label = tk.Label(
                root, text="", font=("Consolas", 64, "bold"),
                fg="#10b981", bg="#0e1117",
            )
            self.time_label.pack(expand=True)
    
            self.date_label = tk.Label(
                root, text="", font=("Arial", 14),
                fg="#9ca3af", bg="#0e1117",
            )
            self.date_label.pack(pady=(0, 10))
    
            tk.Checkbutton(
                root, text="24-hour", variable=self.use_24h,
                command=self.tick, bg="#0e1117", fg="#9ca3af",
                selectcolor="#0e1117", activebackground="#0e1117",
            ).pack(pady=(0, 8))
    
            self.root.bind("<Configure>", self.rescale)
            self.tick()
    
        def rescale(self, event):
            if event.widget == self.root:
                size = max(24, min(120, event.width // 7))
                self.time_label.config(font=("Consolas", size, "bold"))
    
        def tick(self):
            now = datetime.now()
            fmt = "%H:%M:%S" if self.use_24h.get() else "%I:%M:%S %p"
            self.time_label.config(text=now.strftime(fmt))
            self.date_label.config(text=now.strftime("%A, %B %d, %Y"))
            self.root.after(1000, self.tick)
    
    
    if __name__ == "__main__":
        root = tk.Tk()
        DigitalClock(root)
        root.mainloop()

    Step 2: Run the App

    code
    python digital_clock.py

    The clock appears and ticks every second. Toggle the format, resize the window — the font follows.

    How It Works

    The entire app hangs on one line: self.root.after(1000, self.tick). It tells Tkinter: *call tick again in 1000 milliseconds* — and tick reschedules itself at the end, creating an infinite, non-blocking loop inside the event loop. This is the desktop equivalent of the countdown timer's autorefresh, and the crucial contrast is with time.sleep(1000), which would freeze the entire window — no toggling, no dragging, no closing.

    Format switching is a strftime pattern swap: %H:%M:%S for 24-hour, %I:%M:%S %p for 12-hour with AM/PM. The toggle's command=self.tick re-renders immediately rather than waiting up to a full second for the next tick — instant feedback for one line of extra code.

    The rescale binding watches <Configure> events (resize/move) and maps window width to font size, clamped between 24 and 120. The event.widget == self.root check is the subtle bit — Configure events fire for *every child widget too*, and without the guard the font would flicker on unrelated resizes.

    Common Errors & Fixes

  • Window freezes after a few seconds — you called tick recursively without after, or used time.sleep; only after() keeps the event loop alive.
  • Clock drifts a fraction per hourafter(1000) measures from the end of the previous tick; for perfect accuracy, compute the delay to the *next* second boundary: after(1000 - now.microsecond // 1000, self.tick).
  • Checkbutton text invisible on dark background — Tkinter checkbuttons need explicit fg, bg, selectcolor, and activebackground set, as the code does.
  • Font resizes on startup — the Configure binding fires at window creation; harmless, the clamp handles it.
  • Key Concepts

  • `after()` scheduling — non-blocking timers inside the event loop.
  • Self-rescheduling ticks — the recursive after pattern for continuous updates.
  • `strftime` formats — time display as pattern strings.
  • Event widget filtering — reacting only to the window's own resize events.
  • What to Try Next

  • Add a world clock row — three labels for three time zones using datetime.now(timezone(timedelta(hours=n))).
  • Add a stopwatch tab with Start/Lap/Reset — the tick pattern drives the stopwatch display.
  • Add alarm support — compare against a target time and ring with root.bell().
  • Style it like the photo booth's dark theme with a fullscreen mode via attributes("-fullscreen", True).
  • FAQ

    Why not just print the time in a terminal loop?

    You can — but then you have a script, not an app. The point of this project is the GUI update pattern, which transfers to every Tkinter program you'll write.

    Does after(1000) guarantee exactly one second?

    It guarantees *at least* 1000ms — the callback runs when the event loop gets to it. For a clock, the display re-reading datetime.now() each tick keeps the time correct regardless of small delays.

    Can I make the background transparent?

    On Windows, root.attributes("-transparentcolor", "#0e1117") works; macOS and Linux support varies. It's a fun trick with portability caveats.