DMarlon Studio

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.

tkinter · Temporizadores con after() en DMarlon Studio: el código del ejemplo y la ventana que abre al ejecutarlo

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.sleep en 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!».