Events & callbacks#

A tkinter app spends almost all its time idle inside mainloop(), waiting. Your code runs in response to events — a button click, a keypress, a window resize, a theme change. You connect an event to a callback (a function to run), most often with the command option or with bind.

Note

This page covers the essentials. For the full binding system — binding scope and bindtags, sharing and stopping bindings, and defining and dispatching your own events — see the Events guide.

command — the built-in action#

Buttons, checkbuttons, and similar controls take a command option: a callable to run when the widget is activated. This is the common case:

def save():
    print("saved")

ttk.Button(app, text="Save", command=save, bootstyle="primary").pack()

command takes the function itself — command=save, not command=save() (the second calls it immediately and passes the result). To pass arguments, wrap it in a lambda:

ttk.Button(app, text="Delete", command=lambda: delete(item_id)).pack()

bind — any event, any widget#

For events without a command option — mouse motion, individual keystrokes, focus changes — use bind. You give it an event pattern and a callback that receives an event object describing what happened:

entry = ttk.Entry(app)
entry.pack()

def on_return(event):
    print("submitted:", event.widget.get())

entry.bind("<Return>", on_return)

The callback takes one argument, the event. Its most useful attributes are event.widget (the widget the event fired on), event.x/event.y (cursor position), and event.keysym (the key name). Common patterns include "<Button-1>" (left click), "<Double-Button-1>", "<KeyPress>", and "<Motion>". The Event reference lists them all.

after — run code later#

To run a callback after a delay, or to drive a timer, use after — the loop-driven scheduler introduced in How a tkinter app runs (never time.sleep, which freezes mainloop):

app.after(2000, lambda: status.configure(text="Ready"))   # in 2 seconds

Virtual events#

Some notifications aren’t raw input but named higher-level events, written in double angle brackets — <<ThemeChanged>>, <<LocaleChanged>>. You bind them exactly like physical events. For example, recolor something you drew yourself whenever the theme switches:

canvas.bind("<<ThemeChanged>>", lambda e: repaint(canvas))

You can also define and fire your own virtual events to decouple parts of an app — see the Events guide.

A worked example#

A command starts a countdown that drives itself with after and updates a label — no blocking, the window stays responsive throughout:

import ttkbootstrap as ttk

app = ttk.App(title="Countdown")

label = ttk.Label(app, text="10", font=("", 48), bootstyle="primary")
label.pack(padx=40, pady=(20, 10))

def countdown(n):
    label.configure(text=str(n))
    if n > 0:
        app.after(1000, countdown, n - 1)   # extra args are passed to the callback

ttk.Button(app, text="Start", bootstyle="success",
           command=lambda: countdown(10)).pack(pady=(0, 20))

app.mainloop()

See also