Lección 18 · 18 de 20
Variables de control y trace · tkinter
Use StringVar, IntVar y BooleanVar de tkinter con trace_add para recalcular un total en directo cada vez que cambia un campo.

Las variables de control (StringVar, IntVar, DoubleVar, BooleanVar) guardan un valor que comparten los componentes unidos a ellas: el Combobox con textvariable=product, el Spinbox con quantity, la casilla con variable=member y la etiqueta del total con textvariable=total. Se leen con get() y se cambian con set(), y el componente se actualiza solo.
trace_add("write", recalc) pide que se llame a recalc cada vez que la variable cambie, tanto si lo hace el usuario como el código. El bucle lo hace con las tres variables, así que elegir otro producto, cambiar la cantidad o marcar la casilla recalcula el total al momento.
Las funciones de trace reciben tres argumentos (el nombre de la variable, un índice y el modo) que aquí no hacen falta: por eso *args. Al final, recalc() se llama una vez a mano para que el total aparezca desde el principio: 3 × 4,50 € con un 10 % de descuento son 12,15 €.
IntVar.get() lanza tk.TclError si el campo está vacío o tiene letras (por ejemplo, mientras el usuario borra el número). El try lo captura y enseña «Total: -» en vez de romper la función.
En DMarlon Studio, el estudio crea estas variables por usted: cada campo de texto, lista desplegable, casilla o deslizador tiene la suya (por ejemplo, self.Entry1_var), las opciones comparten una por grupo, y se leen y se cambian igual desde el código.
Código · main.py
import tkinter as tkfrom tkinter import ttkPRICES = {"Coffee": 1.80, "Tea": 1.50, "Sandwich": 4.50, "Salad": 6.90}root = tk.Tk()root.title("Order")root.geometry("420x240")root.configure(padx=24, pady=20)# Control variables: one value shared by every widget that uses it.product, quantity = tk.StringVar(value="Sandwich"), tk.IntVar(value=3)member, total = tk.BooleanVar(value=True), tk.StringVar()ttk.Label(root, text="Product:").grid(row=0, column=0, sticky="w", pady=6)ttk.Combobox(root, textvariable=product, values=list(PRICES), state="readonly").grid(row=0, column=1)ttk.Label(root, text="Quantity:").grid(row=1, column=0, sticky="w", pady=6)ttk.Spinbox(root, textvariable=quantity, from_=1, to=20, width=6).grid(row=1, column=1, sticky="w")ttk.Checkbutton(root, text="Member (10% off)", variable=member).grid(row=2, column=1, sticky="w", pady=6)ttk.Label(root, textvariable=total, font=("Helvetica", 22, "bold")).grid(row=3, column=0, columnspan=2, pady=14)def recalc(*args): # trace callbacks get (name, index, mode) try: amount = PRICES[product.get()] * quantity.get() except tk.TclError: # the spinbox is empty or not a number return total.set("Total: -") if member.get(): amount *= 0.9 total.set(f"Total: {amount:.2f} €")for var in (product, quantity, member): var.trace_add("write", recalc) # call recalc on every changerecalc()root.mainloop()Para recordar
- Una variable de control une un valor con sus componentes.
get()lee yset()cambia; la pantalla se actualiza sola.trace_add("write", f)avisa de cada cambio.IntVar.get()falla con texto no numérico: usetry.
Ejercicio
Añada un campo «Tip (%)» con su propia IntVar y su trace, y súmelo al total.