65 - tkinter : control Menu

Para implementar los típicos menú de barra horizontales que aparecen en las aplicaciones cuando utilizamos la librería Tk necesitamos crear objetos de la clase Menu que se encuentra declarada en el paquete tkinter y no en el paquete tkinter.ttk.

Veremos con un ejemplo implementar un menú con una serie de opciones.

Problema:

Confeccionar una aplicación que muestre dos opciones en el menú de barra superior. La primer opción despliega un submenú que permita cambiar el color de fondo del formulario y la segunda permita cambiar el tamaño de formulario:

Menu

Programa: ejercicio241.py

Ver video

import tkinter as tk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        menubar1 = tk.Menu(self.ventana1)
        self.ventana1.config(menu=menubar1)
        opciones1 = tk.Menu(menubar1)
        opciones1.add_command(label="Rojo", command=self.fijarrojo)
        opciones1.add_command(label="Verde", command=self.fijarverde)
        opciones1.add_command(label="Azul", command=self.fijarazul)
        menubar1.add_cascade(label="Colores", menu=opciones1)        
        opciones2 = tk.Menu(menubar1)
        opciones2.add_command(label="640x480", command=self.ventanachica)
        opciones2.add_command(label="1024x800", command=self.ventanagrande)
        menubar1.add_cascade(label="Tamaños", menu=opciones2)        
        self.ventana1.mainloop()

    def fijarrojo(self):
        self.ventana1.configure(background="red")

    def fijarverde(self):
        self.ventana1.configure(background="green")

    def fijarazul(self):
        self.ventana1.configure(background="blue")

    def ventanachica(self):
        self.ventana1.geometry("640x480")

    def ventanagrande(self):
        self.ventana1.geometry("1024x800")

aplicacion1=Aplicacion()

Importamos solo el módulo tkinter ya que no utilizamos controles del módulo tkinter.ttk:

import tkinter as tk

Luego de crear el objeto de la clase Tk procedemos a crear un objeto de la clase Menu y pasar como referencia la ventana:

        self.ventana1=tk.Tk()
        menubar1 = tk.Menu(self.ventana1)

Pasamos al parámetro menu de la ventana el objeto de la clase Menu que acabamos de crear (si no hacemos esto no aparecerá luego el menú de opciones:

        self.ventana1.config(menu=menubar1)

Creamos un segundo objeto de la clase Menu, pero en este caso le pasamos la referencia del primer objeto de la clase Menu que creamos, también añadimos las tres opciones que mostrará:

        opciones1 = tk.Menu(menubar1)
        opciones1.add_command(label="Rojo", command=self.fijarrojo)
        opciones1.add_command(label="Verde", command=self.fijarverde)
        opciones1.add_command(label="Azul", command=self.fijarazul)

Finalmente llamamos al método 'add_cascade' del menubar1 que creamos anteriormente indicando en el parámetro menu el otro objeto de la clase Menu:

        menubar1.add_cascade(label="Colores", menu=opciones1)        

De forma idéntica creamos el segundo menú desplegable:

        opciones2 = tk.Menu(menubar1)
        opciones2.add_command(label="640x480", command=self.ventanachica)
        opciones2.add_command(label="1024x800", command=self.ventanagrande)
        menubar1.add_cascade(label="Tamaños", menu=opciones2)        

Cuando ejecutamos alguna de las opciones se dispara el método que hemos configurado en el parámetro command, por ejemplo si seleccionamos 'Verde':

Menu

Variantes en un menú.

  • Podemos agregar líneas separadoras entre opciones mediante la llamada al método 'add_separator':

            opciones1 = tk.Menu(menubar1)
            opciones1.add_command(label="Rojo", command=self.fijarrojo)
            opciones1.add_command(label="Verde", command=self.fijarverde)
            opciones1.add_separator()
            opciones1.add_command(label="Azul", command=self.fijarazul)
    

    La interfaz visual del menú queda con la siguiente apariencia:

    Menu separator
  • Los menú desplegables muestran una línea de puntos antes de la primer opción. Si el operador hace clic sobre la misma el menú desplegable se muestra en una barra de herramientas fuera de la ventana principal (puede tener sentido si en un programa debemos seleccionar opciones en forma muy seguida y no queremos tener que estar abriendo el menú desplegable):

    Menu

    Si queremos evitar esta característica de los menú desplegables de tkinter debemos pasar el parámetro 'tearoff' con el valor 0:

            opciones1 = tk.Menu(menubar1, tearoff=0)
            opciones1.add_command(label="Rojo", command=self.fijarrojo)
            opciones1.add_command(label="Verde", command=self.fijarverde)
            opciones1.add_separator()        
            opciones1.add_command(label="Azul", command=self.fijarazul)
    

    Podemos comprobar que no aparece la línea de puntos:

    Menu tearoff
  • Otra posibilidad es agregar teclas de acceso rápido a opciones de nuestro menú. Esto se resuelve la parte visual agregando el parámetro 'acelerator', y por otro lado asignar a cada una de las teclas de acceso rápido la ejecución de un método:

    import tkinter as tk
    
    class Aplicacion:
        def __init__(self):
            self.ventana1=tk.Tk()
            menubar1 = tk.Menu(self.ventana1)
            self.ventana1.config(menu=menubar1)
            opciones1 = tk.Menu(menubar1, tearoff=0)
            opciones1.add_command(label="Rojo", command=self.fijarrojo, accelerator="Ctrl+R")
            opciones1.add_command(label="Verde", command=self.fijarverde, accelerator="Ctrl+V")
            opciones1.add_separator()        
            opciones1.add_command(label="Azul", command=self.fijarazul, accelerator="Ctrl+A")
            self.ventana1.bind_all("<Control-r>", self.cambiar)
            self.ventana1.bind_all("<Control-v>", self.cambiar)
            self.ventana1.bind_all("<Control-a>", self.cambiar)
            menubar1.add_cascade(label="Colores", menu=opciones1)        
            opciones2 = tk.Menu(menubar1)
            opciones2.add_command(label="640x480", command=self.ventanachica)
            opciones2.add_command(label="1024x800", command=self.ventanagrande)
            menubar1.add_cascade(label="Tamaños", menu=opciones2)        
            self.ventana1.mainloop()
    
        def cambiar(self, event):
            if event.keysym=="r":
                self.fijarrojo()
            if event.keysym=="v":
                self.fijarverde()
            if event.keysym=="a":
                self.fijarazul()
    
        def fijarrojo(self):
            self.ventana1.configure(background="red")
    
        def fijarverde(self):
            self.ventana1.configure(background="green")
    
        def fijarazul(self):
            self.ventana1.configure(background="blue")
    
        def ventanachica(self):
            self.ventana1.geometry("640x480")
    
        def ventanagrande(self):
            self.ventana1.geometry("1024x800")
    
    aplicacion1=Aplicacion()
    

    El método cambiar se ejecuta cuando presionamos al mismo tiempo la tecla Ctrl y alguna de las otras teclas "R", "V" o "A":

    Menu tecla de acceso rápido
  • Podemos disponer dentro de un menú desplegable una opción que despliegue otro submenú, la interfaz visual debe ser similar a esta:

    Menu submenu

    El código en Python que implementa el menú de la imagen es:

    import tkinter as tk
    
    class Aplicacion:
        def __init__(self):
            self.ventana1=tk.Tk()
            menubar1 = tk.Menu(self.ventana1)
            self.ventana1.config(menu=menubar1)
            opciones1 = tk.Menu(menubar1)
            opciones1.add_command(label="Rojo", command=self.fijarrojo)
            opciones1.add_command(label="Verde", command=self.fijarverde)
            opciones1.add_command(label="Azul", command=self.fijarazul)
            menubar1.add_cascade(label="Colores", menu=opciones1)        
            opciones2 = tk.Menu(menubar1)
            opciones2.add_command(label="640x480", command=self.ventanachica)
            opciones2.add_command(label="1024x800", command=self.ventanagrande)
            submenu1=tk.Menu(menubar1)
            submenu1.add_command(label="1024x1024", command=self.tamano1)
            submenu1.add_command(label="1280x1024", command=self.tamano2)        
            opciones2.add_cascade(label="Otros tamaños", menu= submenu1)        
            menubar1.add_cascade(label="Tamaños", menu=opciones2)        
            self.ventana1.mainloop()
    
        def fijarrojo(self):
            self.ventana1.configure(background="red")
    
        def fijarverde(self):
            self.ventana1.configure(background="green")
    
        def fijarazul(self):
            self.ventana1.configure(background="blue")
    
        def ventanachica(self):
            self.ventana1.geometry("640x480")
    
        def ventanagrande(self):
            self.ventana1.geometry("1024x800")
    
        def tamano1(self):
            self.ventana1.geometry("1024x1024")
    
        def tamano2(self):
            self.ventana1.geometry("1280x1024")
    
    aplicacion1=Aplicacion()
    

Problema propuesto

  • Mediante dos controles de tipo Entry permitir el ingreso de dos números. Crear un menú que contenga una opción que cambie el tamaño de la ventana con los valores ingresados por teclado. Finalmente disponer otra opción que finalice el programa

    Ver video

Solución

ejercicio242.py

import tkinter as tk
from tkinter import ttk
import sys

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        menubar1 = tk.Menu(self.ventana1)
        self.ventana1.config(menu=menubar1)
        opciones1 = tk.Menu(menubar1)
        opciones1.add_command(label="Cambiar dimensión ventana", command=self.fijartamano)
        opciones1.add_command(label="Finalizar", command=self.finalizar)
        menubar1.add_cascade(label="Opciones", menu=opciones1)
        self.label1=ttk.Label(text="Ingrese ancho de la ventana en X:")
        self.label1.grid(column=0, row=0)
        self.ancho=tk.StringVar()
        self.entry1=ttk.Entry(self.ventana1, width=10, textvariable=self.ancho)
        self.entry1.grid(column=0, row=1)        
        self.label2=ttk.Label(text="Ingrese ancho de la ventana en Y:")
        self.label2.grid(column=0, row=2)
        self.alto=tk.StringVar()
        self.entry2=ttk.Entry(self.ventana1, width=10, textvariable=self.alto)
        self.entry2.grid(column=0, row=3)        

        self.ventana1.mainloop()

    def fijartamano(self):
        self.ventana1.geometry(self.ancho.get()+"x"+self.alto.get())

    def finalizar(self):
        sys.exit()

aplicacion1=Aplicacion()