La barra de menús, los menús y sus ítems, tk_popup

Toda la funcionalidad de una aplicación visual debería ser accesible desde el menú de su ventana principal. Incluso en algunas ventanas de la aplicación puede ser interesante el uso de un menú contextual (o emergente).

El widget tkinter.Menu es la base de los menús que se pueden mostrar en una aplicación visual desarrollada con tkinter. Su prototipo es

Menu(master=None, **options)

Un menú tiene una estructura arbórea, donde sus ramas son menús y sus hojas actúan como botones (muestran un texto y/o una imagen y cuando son pulsados ejecutan cierta función). En el menú principal de una aplicación, las ramas que surgen de la raíz forman lo que llamamos barra de menús, cada una de ellas es un menú que agrupa diversos ítems, que a su vez pueden ser menús.

En algunos casos, los ítems de un menú pueden aparecer marcados con una marca de verificación, también pueden aparecer en un tono grisáceo que indica al usuario que esa opción no está disponible.

Para crear una barra de menús se usa la orden tkinter.Menu(master), donde master es la ventana de alto nivel que la contiene. Los menús que aparecerán en la barra de menús se han de crear con la misma orden, esta vez indicando que su master es el identificador usado para la barra de menús.

Una aplicación puede tener más de una barra de menús, pero sólo puede usar una de ellas, lo que se consigue con su método config.

root.config(menu=barra_de_menús)

Métodos

Para añadir ítems a un menú se pueden usar las órdenes

add(itemType, cnf={}, **kw)
add_cascade(cnf={}, **kw)
add_checkbutton(cnf={}, **kw)
add_command(cnf={}, **kw)
add_radiobutton(cnf={}, **kw)

Se pueden separar los ítems con

add_separator(cnf={}, **kw)

Para cambiar las opciones del widget se puede usar

config = configure(self, cnf=None, **kw)

Si queremos borrar alguna entrada del menú usaremos

delete(index1, index2=None)
`

Con

entrycget(index, option)

obtenemos la opción pedida del ítem indicado.

entryconfig(index, cnf=None, **kw)

modifica las opciones especificadas del ítem indicado (entryconfigure es un sinónimo).

index(index)

convierte el índice (de cualquier tipo) en un número entero.

insert(index, itemType, cnf={}, **kw)

inserta un nuevo ítem en la posición que se indique, similar a add().

insert_cascade(index, cnf={}, **kw)
insert_checkbutton(index, cnf={}, **kw)
insert_command(index, cnf={}, **kw)
insert_radiobutton(index, cnf={}, **kw)
insert_separator(index, cnf={}, **kw)

son funciones similares a sus correspondientes add_X().

invoke(index)

invoca el comando del ítem indicado.

post(x, y)

muestra el menú en la posición indicada (en referencia a la ventana principal).

Para mostrar menús emergentes, con la entrada indicada:

tk_popup(self, x, y, entry='')
type(index)

devuelve el tipo de ítem indicado.

unpost()

elimina el menú mostrado con post().


xposition(index)

devuelve la posición del píxel más a la izquierda del ítem indicado.

xyposition(index)

devuelve la posición del píxel más arriba del ítem indicado.

Propiedades de los ítems

label es una cadena de caracteres. Se usa como tal, encerrada entre comillas simples o dobles, para identificar el ítem.

underline indica que se colocará una marca de subrayado en la posición indicada de la etiqueta del ítem, comenzando por 0. Permite al usuario navegar por un menú usando el teclado, tras activarlo con la tecla 'Alt'.

image permite mostrar una imagen junto al texto de la etiqueta del ítem. Con compound indicamos en qué posición queremos que se muestre la imagen, con las opciones "bottom", "center", "left", "right", "top" o "none".

variable permite asociar al ítem una variable de tipo BoolanVar, StringVar, IntVar o DoubleVar.

value

Estructura

En realidad la barra de menús es un menú más. Los menús se pueden anidar unos dentro de otros. Su uso básico es el siguiente:

  1. Crear la barra de menús con barra_de_menús = tkinter.Menu(ventana_principal). Una aplicación visual sólo muestra una barra de menús, sin embargo se puede definir más de una para mostrar, por ejemplo, opciones básicas o avanzadas.
  2. Crear los menús con nombre_del_menú = tkinter.Menu(barra_de_menús). Generalmente habrá más de un menú, los más típicos son Archivo, Edición, Vista, Ventana y Ayuda. Los menús no ejecutan comandos, están ahí para ocultar o mostrar una serie de ítems que sí ejecutarán comandos.
  3. Añadir ítems a los menús con nombre_del_menú.add_X(label="Etiqueta del ítem"[, command=nombre_función_a_ejecutar] [,var=variable_asociada][, ...]). Cada menú recoge una serie de ítems con los comandos que se pueden aplicar en su contexto o variables asociadas al ítem. Incluso uno de estos ítems puede ser un menú que tenga a su vez una colección de ítems asociado.
  4. Añadir los menús a la barra de menús con barra_de_menús.add_cascade(label="Título de menú", menu=nombre_del_menú). Así se construye la barra de menús, un menú en el que los (sub)menús añadidos son sus ítems.
  5. Asignar la barra de menús a la ventana principal con root.config(menu=barra_de_menús). Si tuviéramos más de una barra de menús, para alternar entre una y otra (no pueden mostrarse dos simultáneamente) se usaría root.config(menu=otra_barra_de_menús).

Comandos

Cuando queremos que un ítem de menú realice una acción, se define primero la función que contiene la acción y se añade el ítem a su menú indicando su etiqueta y el nombre del comando a ejecutar cuando se pulse en el ítem.

menu.add_command(label='Etiqueta', command=comando)

Checkbuttons

Los ítems que queremos puedan ser seleccionados/deseleccionados los añadimos con

menu.add_checkbutton(label='Etiqueta', variable=var, command=comando)

donde la variable var se ha de asociar a una variable de tipo tkinter.BooleanVar(), y su valor (True o False) determinará si aparece o no la marca de selección junto a la etiqueta del ítem. Al hacer clic en esta opción, la variable asociada cambia su estado (si era True pasa a ser False y viceversa).

Radiobuttons

Cuando queremos que el usuario pueda elegir uno y sólo uno de una serie de ítems, añadimos cada uno de ellos con

menu.add_radiobutton(label='Etiqueta', variable=var, command=comando)
menu.add_radiobutton(label='Etiqueta 1', variable=var1, command=comando1)

En este caso la variable var es

Ejemplo 1

from tkinter import *

def donothing():
   filewin = Toplevel(root)
   button = Button(filewin, text="Do nothing button")
   button.pack()

root = Tk()
menubar = Menu(root)

filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="New", command=donothing)
filemenu.add_command(label="Open", command=donothing)
filemenu.add_command(label="Save", command=donothing)
filemenu.add_command(label="Save as...", command=donothing)
filemenu.add_command(label="Close", command=donothing)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)

menubar.add_cascade(label="File", menu=filemenu)

editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label="Undo", command=donothing)
editmenu.add_separator()
editmenu.add_command(label="Cut", command=donothing)
editmenu.add_command(label="Copy", command=donothing)
editmenu.add_command(label="Paste", command=donothing)
editmenu.add_command(label="Delete", command=donothing)
editmenu.add_command(label="Select All", command=donothing)

menubar.add_cascade(label="Edit", menu=editmenu)

helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label="Help Index", command=donothing)
helpmenu.add_command(label="About...", command=donothing)

menubar.add_cascade(label="Help", menu=helpmenu)

root.config(menu=menubar)

root.mainloop()

Menú emergente/contextual

Para mostrar un menú cuando el usuario hace click con el botón secundario (<Button-3> en Linux y MS-Windows, <Button-2> en macOS) se debe usar su método tk_popup(posición-x, posición-y).

Ejemplo 2

from tkinter import *

def siguiente():
    root.title('Siguiente')

def anterior():
    root.title('Anterior')

def casa():
    root.title('Casa')

root = Tk()

w = Label(root, text="Right-click to display menu", width=40, height=20)
w.pack()

# create a menu
popup = Menu(root, tearoff=0)
popup.add_command(label="Next", command=siguiente) # , command=next) etc...
popup.add_command(label="Previous", command=anterior)
popup.add_separator()
popup.add_command(label="Home", command=casa)

def do_popup(event):
    # display the popup menu
    try:
        popup.tk_popup(event.x_root, event.y_root, 0)
    finally:
        # make sure to release the grab (Tk 8.0a1 only)
        popup.grab_release()
#        return None
    pass


#TODO:Detectar el SO, el original usaba "<Button-3>", que no funciona en macOS.
w.bind("<Button-2>", do_popup)

b = Button(root, text="Quit", command=root.destroy)
b.pack()

root.mainloop()

Un Menubutton tiene la apariencia de un botón con una marca de drop-down lateral que indica al usuario que al pulsar el botón se mostrará un menú. El widgets Menubutton es realmente la parte visible del menú que tiene asociado.

Si le asociamos un menú con ítems del tipo checkbutton, haciendo clic en cualquiera de sus opciones se selecciona/deselecciona y se guarda de nuevo el menú, mostrando sólo el botón con su texto y marca lateral.

Al consultar la documentación oficial de Python, help(tkinter.Menubutton) nos informa de que este widget está obsoleto desde la versión Tk8.0. Su versión tematizada, help(tkinter.ttk.Menubutton), no tiene esta advertencia por lo que en esta asignatura usaremos siempre la versión tematizada del widget.

El widget Menubutton posee su propio menú, que se despliega al hacer click sobre él. puede ser un ítem de un menú, permitiendo seleccionar al usuario cada uno de sus ítems.

OptionMenu

Un OptionMenues parecido visualmente al Menubutton, sin embargo sólo permite seleccionar una de las opciones del menú que despliega y la muestra en el botón.

Ejercicios

  1. Refactoriza el ejemplo 1 utilizando PEP8.
  2. Observa que en el ejemplo 1, en macOS aparece una opción de menú que no hemos escrito nosotros, Help / Search. Esta es una de las peculiaridades de las APIs, que se adaptan a comportamientos genéricos de las aplicaciones de cada sistema operativo. Si cambias el código menubar.add_cascade(label="Help", menu=helpmenu) por menubar.add_cascade(label="Ayuda", menu=helpmenu) observarás que esa opción extra ya no aparece.

  3. Con la ayuda de las referencias bibliográficas de este tema, reproduce las aplicaciones mostradas como ejemplos visuales de Menubutton y OptionMenu.

  4. En https://stackoverflow.com/questions/23442792/tkinter-enable-disable-menu encontramos el siguiente ejemplo.

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, root):
        tk.Frame.__init__(self, root)

        self.menubar = tk.Menu()
        self.test1Menu = tk.Menu()
        self.test2Menu = tk.Menu()
        self.menubar.add_cascade(label="Test1", menu=self.test1Menu)
        self.menubar.add_cascade(label="Test2", menu=self.test2Menu)

        self.test1Menu.add_command(label="Enable Test2", command=self.enable_menu)
        self.test1Menu.add_command(label="Disable Test2", command=self.disable_menu)
        self.test2Menu.add_command(label="One")
        self.test2Menu.add_command(label="Two")
        self.test2Menu.add_command(label="Three")
        self.test2Menu.add_separator()
        self.test2Menu.add_command(label="Four")
        self.test2Menu.add_command(label="Five")

        root.configure(menu=self.menubar)

    def enable_menu(self):
        self.menubar.entryconfig("Test2", state="normal")

    def disable_menu(self):
        self.menubar.entryconfig("Test2", state="disabled")

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("500x500")
    app = Example(root)
    app.pack(fill="both", expand=True)
    root.mainloop()

Conviértelo a Python 3 y analiza cómo construye el menú y cómo conseguir que una opción del menú esté activa o deshabilitada.

  1. Al arrancar la aplicación anterior el menú Test 2 está habilitado, por lo que no tiene sentido que la opción Test 1 / Enable Test 2 también lo esté. Cuando se inhabilita el test 2, la opción Test 1 / Enable Test 2 debería habilitarse, y la opción Test 1 / Disable Test 2 deshabilitarse. Cuando se vuelve a habilitar deberían intercambiar sus estados.
  1. Añade el menú Test 3, con la misma funcionalidad que Test 1 pero sólo un ítem.

Recetas

La sentencia pass

passes una sentencia de Python que no hace nada, pero está ahí. Es útil cuando se está desarrollando un menú con mucha funcionalidad y no se quiere escribir el código de todas las funciones apuntadas por los ítems del menú.

def AlPulsarEstaOpcion():
    pass

def AlPulsarEstaOtraOpcion():
    pass

Referencias

results matching ""

    No results matching ""