Tutorial de OptionMenu de Tkinter
Aprende a construir menús desplegables con Tkinter OptionMenu: callbacks, actualizaciones en vivo, agregar/quitar opciones, consejos de estilo y un ejemplo completo ejecutable.
Progreso del Tutorial
1 Introducción a Tkinter OptionMenu
OptionMenu en Tkinter crea una lista desplegable vinculada a una variable. Es útil para opciones como temas, idiomas o cualquier conjunto finito de opciones. Internamente, almacena el valor seleccionado en una variable y puede llamar a una función cada vez que el usuario selecciona un nuevo elemento.
Al final de esta guía, tendrás una aplicación de selector de temas ejecutable que actualizará un panel de vista previa y mostrará un mensaje con el tema seleccionado.
2 Prerrequisitos: Python y Tkinter
Necesitarás Python 3.7+. Tkinter viene con la mayoría de las instalaciones de Python en macOS y Windows. Si estás en Linux, instala el paquete tk:
# Debian/Ubuntu
sudo apt-get install python3-tk
Verifica que Tkinter esté disponible:
python -m tkinter
Si se abre una pequeña ventana, Tkinter está funcionando.
3 OptionMenu Mínimo: Las Piezas Clave
Necesitas tres elementos: una variable de Tkinter (generalmente StringVar), una lista de opciones y un OptionMenu conectado a la variable.
import tkinter as tk
root = tk.Tk()
root.title("OptionMenu Mínimo")
# 1) Variable para almacenar la selección actual
choice = tk.StringVar(value="A")
# 2) Crea el OptionMenu, pasando la variable y las opciones
menu = tk.OptionMenu(root, choice, "A", "B", "C")
menu.pack(padx=20, pady=20)
root.mainloop()
- StringVar mantiene el valor actual sincronizado con el menú desplegable.
value="A"establece el valor predeterminado; sin él, generalmente se selecciona la primera opción.- Actualizar
choice.set("B")moverá la selección del OptionMenu programáticamente.
4 Reaccionar a Cambios: Añadir un Callback
Pasa una función al argumento command. Tkinter la llamará con el valor recién seleccionado.
import tkinter as tk
from tkinter import messagebox
def on_select(value):
messagebox.showinfo("Selección", f"Elegiste: {value}")
root = tk.Tk()
choice = tk.StringVar(value="Ocean")
menu = tk.OptionMenu(root, choice, "Ocean", "Forest", "Sunset", command=on_select)
menu.pack(padx=20, pady=20)
root.mainloop()
Usa callbacks para sincronizar otros widgets, activar validación o actualizar un panel de información.
5 Crea una Vista Previa en Vivo (Como Nuestro Ejemplo)
Almacena los datos del tema, luego actualiza un panel de vista previa cada vez que cambie la selección.
import tkinter as tk
from tkinter import messagebox
themes = {
"Ocean": {"primary": "#1a73e8", "secondary": "#4285f4", "text": "white"},
"Forest": {"primary": "#2e7d32", "secondary": "#4caf50", "text": "white"},
"Sunset": {"primary": "#d32f2f", "secondary": "#f44336", "text": "white"},
}
def update_preview(theme_name):
t = themes[theme_name]
preview.configure(bg=t["primary"])
title.configure(bg=t["primary"], fg=t["text"])
button.configure(bg=t["secondary"], fg=t["text"], activebackground=t["primary"], activeforeground=t["text"])
root = tk.Tk()
choice = tk.StringVar(value="Ocean")
menu = tk.OptionMenu(root, choice, *themes.keys(), command=lambda v: update_preview(v))
menu.pack(padx=20, pady=10)
preview = tk.Frame(root, width=220, height=120, relief="raised", bd=2)
preview.pack(pady=10)
title = tk.Label(preview, text="Vista Previa del Tema")
title.pack(pady=6)
button = tk.Button(preview, text="OK", command=lambda: messagebox.showinfo("Tema", f"{choice.get()}"))
button.pack(pady=6)
update_preview(choice.get())
root.mainloop()
6 Cambiar Opciones y Deshabilitar Elementos
OptionMenu no expone una API de añadir/eliminar integrada. El enfoque habitual es reconstruir el menú con las opciones actualizadas y preservar la selección actual.
def rebuild_options(new_options, keep_value=True):
current = choice.get() if keep_value else new_options[0]
menu["menu"].delete(0, "end")
for opt in new_options:
menu["menu"].add_command(label=opt, command=lambda v=opt: choice.set(v))
choice.set(current)
# Empieza con estas
choices = ["A", "B", "C"]
choice = tk.StringVar(value="B")
menu = tk.OptionMenu(root, choice, *choices)
menu.pack(padx=20, pady=20)
# Más tarde, elimina "B"
rebuild_options([opt for opt in choices if opt != "B"])
Para deshabilitar un elemento específico, envuélvelo en un submenú de cascada y usa state="disabled". Eliminar o reordenar es más simple con una reconstrucción.
7 Estilo y Diseño: Dominando la Apariencia
Usa configure(width=...) para controlar el ancho. Para fuentes y colores, establece atributos en el menú y su widget de menú interno. Combinar ttk.Frame con tk.OptionMenu puede ordenar los espacios.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
frame = ttk.Frame(root, padding=16)
frame.pack()
choice = tk.StringVar(value="Ocean")
menu = tk.OptionMenu(frame, choice, "Ocean", "Forest", "Sunset")
menu.configure(width=14, font=("Helvetica", 11))
menu["menu"].configure(font=("Helvetica", 11))
ttk.Label(frame, text="Elige un tema:").pack(anchor="w", pady=(0, 6))
menu.pack(anchor="w")
root.mainloop()
8 Errores Comunes y Trampas
- Olvidar establecer un valor inicial puede resultar en una selección vacía.
- Los callbacks reciben el nuevo valor, no la variable. Usa la variable si necesitas un estado más amplio.
- OptionMenu no tiene una API directa de "eliminar opción"—reconstruye a través del widget interno
menu. - Mezclar
tkyttkpuede producir diferencias sutiles de estilo. - En algunos sistemas, cambiar las fuentes en el menú interno no tiene efecto debido a la representación de la plataforma.
9 Ejercicio: Añadir y Eliminar Opciones
Extiende el ejemplo mínimo: añade un campo de entrada y un botón que inserte la opción escrita en el menú (si no está presente) y la seleccione. Luego, añade otro botón para eliminar la opción actualmente seleccionada.
import tkinter as tk
root = tk.Tk()
choice = tk.StringVar(value="A")
options = ["A", "B", "C"]
menu = tk.OptionMenu(root, choice, *options)
menu.pack(padx=16, pady=16)
entry = tk.Entry(root)
entry.pack(padx=16)
def add_option():
val = entry.get().strip()
if not val or val in options:
return
options.append(val)
menu["menu"].add_command(label=val, command=lambda v=val: choice.set(val))
choice.set(val)
def remove_selected():
val = choice.get()
if val not in options:
return
options.remove(val)
rebuild_options()
tk.Button(root, text="Añadir", command=add_option).pack(pady=6)
tk.Button(root, text="Eliminar Seleccionado", command=remove_selected).pack(pady=6)
def rebuild_options():
current = choice.get()
menu["menu"].delete(0, "end")
for opt in options:
menu["menu"].add_command(label=opt, command=lambda v=opt: choice.set(v))
if options:
choice.set(current if current in options else options[0])
root.mainloop()
10 Proyecto Completo: Selector de Temas con Vista Previa
Copia esto en un archivo llamado theme_selector.py y ejecútalo con python theme_selector.py.
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class ColorThemeSelector(tk.Tk):
def __init__(self):
super().__init__()
# Configurar la ventana principal
self.title("Ejemplo de OptionMenu")
self.geometry("400x300")
self.configure(padx=20, pady=20)
# Definir temas de color
self.color_themes = {
"Ocean": {"primary": "#1a73e8", "secondary": "#4285f4", "text": "white"},
"Forest": {"primary": "#2e7d32", "secondary": "#4caf50", "text": "white"},
"Sunset": {"primary": "#d32f2f", "secondary": "#f44336", "text": "white"},
"Lavender": {"primary": "#7b1fa2", "secondary": "#9c27b0", "text": "white"}
}
# Crear y configurar un frame
self.main_frame = ttk.Frame(self)
self.main_frame.pack(expand=True, fill='both')
# Crear una StringVar para almacenar la opción seleccionada
self.selected_theme = tk.StringVar()
self.selected_theme.set("Ocean") # Establecer valor predeterminado
# Crear una etiqueta
self.label = ttk.Label(
self.main_frame,
text="Selecciona Tu Tema de Color:",
font=('Helvetica', 12)
)
self.label.pack(pady=10)
# Crear el OptionMenu
self.theme_menu = tk.OptionMenu(
self.main_frame,
self.selected_theme,
*self.color_themes.keys(),
command=self.update_preview
)
self.theme_menu.configure(width=15)
self.theme_menu.pack(pady=10)
# Crear un frame de vista previa
self.preview_frame = tk.Frame(
self.main_frame,
width=200,
height=100,
relief="raised",
bd=2
)
self.preview_frame.pack(pady=20)
# Crear elementos de vista previa
self.preview_title = tk.Label(
self.preview_frame,
text="Vista Previa del Tema",
font=('Helvetica', 10)
)
self.preview_title.pack(pady=5)
self.preview_button = tk.Button(
self.preview_frame,
text="Botón de Muestra",
command=self.show_selection
)
self.preview_button.pack(pady=10)
# Inicializar la vista previa
self.update_preview()
def update_preview(self, *args):
"""Actualiza el frame de vista previa con los colores del tema seleccionado"""
theme = self.color_themes[self.selected_theme.get()]
# Actualizar el fondo del frame de vista previa
self.preview_frame.configure(bg=theme["primary"])
self.preview_title.configure(
bg=theme["primary"],
fg=theme["text"]
)
self.preview_button.configure(
bg=theme["secondary"],
fg=theme["text"],
activebackground=theme["primary"],
activeforeground=theme["text"]
)
def show_selection(self):
"""Muestra un cuadro de mensaje con la selección actual"""
messagebox.showinfo(
"Tema Seleccionado",
f"¡Has seleccionado el tema {self.selected_theme.get()}!"
)
if __name__ == "__main__":
app = ColorThemeSelector()
app.mainloop()
Ejecútalo: python theme_selector.py. Intenta cambiar de tema, ¡observa cómo la vista previa se actualiza en tiempo real!