Lección 13 · 13 de 20
Ventanas secundarias · tkinter
Abra más ventanas en una aplicación de tkinter con Toplevel: título, tamaño y posición, ventanas modales y destroy() para cerrarlas.

Una aplicación tiene una sola tk.Tk(), pero puede tener muchas tk.Toplevel: ventanas secundarias, con su propia barra de título, que comparten el mismo mainloop. No hay que llamar a nada más para que se vean.
open_details crea una ventana por contacto con title() y geometry("360x220+500+110"), que además del tamaño lleva la posición: +x+y desde la esquina superior izquierda de la pantalla. La principal se coloca en +80+80 para que las dos se vean juntas.
resizable(False, False) le da un tamaño fijo, como a un cuadro de diálogo. Si quiere que se quede siempre delante de la principal y se minimice con ella, añada win.transient(root); y si debe ser modal —que no se pueda tocar la principal hasta cerrarla—, win.grab_set().
El botón «Close» llama a win.destroy, que cierra solo esa ventana; cerrar la principal las cierra todas. «Open details» abre otra para el contacto elegido en la lista, y al empezar ya hay una abierta, la de Ana.
En DMarlon Studio cada ventana que diseña es un formulario de la aplicación; las ventanas secundarias como esta se abren desde su código con tk.Toplevel.
Código · main.py
import tkinter as tkfrom tkinter import ttkcontacts = {"Ana López": ("ana@example.com", "600 123 456"), "Luis Gil": ("luis@example.com", "611 222 333"), "Marta Ruiz": ("marta@example.com", "622 444 555")}root = tk.Tk()root.title("Contacts")root.geometry("380x260+80+80")names = tk.Listbox(root, bg="white", fg="black", highlightthickness=0, height=6, font=("Helvetica", 14))names.insert("end", *contacts)names.pack(fill="both", expand=True, padx=12, pady=(12, 6))def open_details(name): # Toplevel: one more window of the same application (and the same mainloop). win = tk.Toplevel(root) win.title(name) win.geometry("360x220+500+110") win.resizable(False, False) # a fixed size, like a dialog ttk.Label(win, text=name, font=("Helvetica", 20, "bold")).pack(pady=(24, 8)) for value in contacts[name]: ttk.Label(win, text=value).pack() ttk.Button(win, text="Close", command=win.destroy).pack(pady=20)def show(): for i in names.curselection(): open_details(names.get(i))ttk.Button(root, text="Open details", command=show).pack(pady=(0, 12))names.selection_set(0)open_details("Ana López") # one window open from the startroot.mainloop()Para recordar
- Una sola
Tk()y tantasToplevelcomo hagan falta. geometry("AxB+x+y")da tamaño y posición.transient()la ata a la principal ygrab_set()la hace modal.destroy()cierra solo esa ventana.
Ejercicio
Haga modal la ventana de detalles con grab_set() y evite que se abran dos ventanas para el mismo contacto.