Lección 16 · 16 de 20
Temporizadores con after() · tkinter
Programe tareas en el tiempo con after() de tkinter: una cuenta atrás que se repite cada segundo, se pausa con after_cancel y no bloquea la ventana.

root.after(1000, tick) pide a tkinter que llame a tick una vez, dentro de 1000 milisegundos, y vuelve enseguida: la ventana sigue respondiendo mientras tanto. No use nunca time.sleep en una aplicación de ventanas: lo congela todo hasta que termina.
Para que se repita, tick se vuelve a programar a sí misma: resta un segundo, actualiza el texto (:02d rellena con ceros, 04:59) y la barra, y pide otra llamada mientras queden segundos. Al llegar a cero deja job en None y se para.
after devuelve un identificador, que se guarda en job. Con él, after_cancel(job) anula la llamada pendiente: es lo que hace toggle para pausar. Al reanudar, programa otra llamada, y el botón cambia entre «Pause» y «Start».
La cuenta atrás empieza sola en 05:00; por eso la ventana de la imagen, capturada un instante después de abrirse, ya marca 04:59. La barra de progreso, con value=left, se va vaciando a medida que pasa el tiempo.
En DMarlon Studio es el Temporizador de la paleta: no se ve en la ventana; en el inspector se elige el intervalo y el método que se llama, y el estudio genera una pequeña clase que usa after para llamarlo una y otra vez y que se activa o se detiene con enabled.
Código · main.py
import tkinter as tkfrom tkinter import ttkroot = tk.Tk()root.title("Countdown")root.geometry("380x230")START = 5 * 60 # five minutes, in secondsleft, job = START, None # job: the id of the pending after() calldisplay = ttk.Label(root, text="05:00", font=("Helvetica", 56, "bold"))display.pack(pady=(20, 4))bar = ttk.Progressbar(root, maximum=START, value=START, length=300) # time leftbar.pack()def tick(): # runs once a second and books the next run itself global left, job left -= 1 display.config(text=f"{left // 60:02d}:{left % 60:02d}") bar.config(value=left) job = root.after(1000, tick) if left > 0 else Nonedef toggle(): # after_cancel() removes the pending call: the clock stops global job if job: root.after_cancel(job) job = None else: job = root.after(1000, tick) button.config(text="Pause" if job else "Start")button = ttk.Button(root, text="Pause", command=toggle)button.pack(pady=16)job = root.after(1000, tick) # after(ms, function): call it once, laterroot.mainloop()Para recordar
after(ms, función)llama una vez, más tarde, sin bloquear.- Para repetir, la función se vuelve a programar.
after_cancel(id)anula la llamada pendiente.- Nunca
time.sleepen una ventana.
Ejercicio
Añada un botón «Reset» que vuelva a 05:00 y haga que, al llegar a cero, el texto se ponga rojo y diga «Time's up!».