Tk tutorial
latest

Contents:

  • Introduction
  • Button
  • Radio Button
  • Checkbutton
  • Entry, Combox, Spinbox
  • Grid geometry mangager
  • Basic widgets
  • Events
  • Listbox
  • Menu
    • Adding a menu bar
    • Adding items to the menu bar
    • Menus with sub-menus
    • Insert an item at a specific position
    • Different menus for each window
    • Creating new widgets from the menu
    • Insert a popup menu
  • Canvas
  • Scrollbar
  • Text
  • Treeview
  • Windows
  • Time
  • Applications
Tk tutorial
  • Docs »
  • Menu
  • Edit on GitHub

Menu¶

This section describes how to create menubars and popup menus. Menus are widgets as well. We add the following code to the App() class. Creating the menu bar:

menubar = tk.Menu(root)

Adding the menu bar to the root window:

root['menu'] = menubar

Placing the menu bar on a menus stack for accessing to the menu items later on.

  • menus[0] is always the menu bar
  • menus[1] is the first menu item created
  • menus[-1] is the last menu item created

This initializes the menu stack:

menus = [menubar]

Adding a menu bar¶

The Menu() class is defined like this:

class Menu(tk.Menu):
    """Add a Menu() node to which a menu Item() can be attached."""
    def __init__(self, label, id=0, **kwargs):
        super(Menu, self).__init__(App.menus[0], **kwargs)
        App.menus[id].add_cascade(menu=self, label=label)
        App.menus.append(self)

To create a menubar we just can instantiate a series of Menu() widgets:

for i in range(1, 7):
    Menu('Menu ' + str(i))
../_images/menu1.png
"""Add menus to the menubar."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Menu bar without items", font="Arial 24")
        
        for i in range(1, 7):
            Menu('Menu ' + str(i))

Demo().run()

menu1.py

Adding items to the menu bar¶

To add items to a menu bar, we first call Menu() and then Item() for each menu item to add to the menu: The Item object has the following parameters:

  • label: the text to appear in the menu item
  • cmd: an executable string
  • acc (accelerator): a shortcut composed of Control/Command - (hyphen) and a lower-case letter

The following exemple adds 6 items to the 6 menus previously created:

for i in range(1, 7):
    Menu('Menu ' + str(i))
    for c in "ABCDEF":
        label = 'Item {}{}'.format(i, c)
        cmd = 'print("{}")'.format(label)
        Item(label, cmd, 'Command-{}'.format(c.lower()))
../_images/menu2.png
"""Add menus and items."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Menu bar with items", font="Arial 24")

        for i in range(1, 7):
            Menu('Menu ' + str(i))
            for c in "ABCDEF":
                label = 'Item {}{}'.format(i, c)
                cmd = 'print("{}")'.format(label)
                Item(label, cmd, 'Command-{}'.format(c.lower()))

Demo().run()

menu2.py

Menus with sub-menus¶

Each new menu can be attached to the menu bar (default) or can be attached to an existing menu. The parameter id.

The following code adds a new submenu at the end of the previously created menu. Then it adds 3 subitems, and finally an item which is directly attached to the parent Menu():

for i in range(1, 7):
    Menu('Submenu in {}'.format(i), id=i)
    for c in 'XYZ':
        Item('Item ' + c, 'print(123)', 'Control-' + c.lower())
    Item('Add Item to {}'.format(i), id=i)

This is the Menu class definition.

class Menu(tk.Menu):
    """Add a Menu() node to which a menu Item() can be attached."""

    def __init__(self, label='Menu', id=0, **kwargs):
        super(Menu, self).__init__(App.menus[0], **kwargs)
        App.menus[id].add_cascade(menu=self, label=label)
        App.menus.append(self)

This is the Item class definition.

class Item(Callback):
    """Add a menu item to a Menu() node. Default is the last menu (id=-1)."""

    def __init__(self, label, cmd='', acc='', id=-1, **kwargs):
        self.cmd = cmd
        if isinstance(cmd, str):
            cmd = self.cb
        if acc != '':
            key = '<{}>'.format(acc)
            App.root.bind(key, self.cb)

        if label == '-':
            App.menus[id].add_separator()
        elif label[0] == '*':
            App.menus[id].add_checkbutton(
                label=label[1:], command=cmd, accelerator=acc, **kwargs)
        elif label[0] == '#':
            App.menus[id].add_radiobutton(
                label=label[1:], command=cmd, accelerator=acc, **kwargs)
        else:
            App.menus[id].add_command(
                label=label, command=cmd, accelerator=acc, **kwargs)
../_images/menu6.png
"""Insert items at a specific index position."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Insert items at specific index.", font="Arial 24")
   
        Menu('Menu')
        for i in range(5):
            Item('Item {}'.format(i), cmd=lambda : print(i), acc='Command-'+str(i))

        Item('Item', 'print("Left")', acc='Command-Left')
        Item('Item', 'print("Up")', acc='Command-Up')
        Item('Item', 'print("Right")', acc='Command-Right')
        Item('Item', 'print("Down")', acc='Command-Down')
        
        App.menus[-1].insert(2, 'command', label='Extra item')
        App.menus[-1].insert(100, 'command', label='Extra item 100')
        App.menus[-1].insert(10, 'command', label='Extra item 10')

        Menu('Window', name='window')
        Menu('Help', name='help')

Demo().run()

menu6.py 6 Checkbuttones and radiobuttons —————————

Items can be configured as checkboxes or radiobuttons. The first symbol of the label decides if a command, ceckbox or radiobuttion is:

  • asterisk (*) : make the item a checkbox
  • hashtag (#) : make the item a radiobutton

This is the result:

../_images/menu4.png
"""Menus with items, separators, checkboxes and radiobuttons."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Checkbuttones and radiobuttons.", font="Arial 24")

        Menu('Checkbutton')
        for i in range(5):
            Item('*Item {}'.format(i))

        Menu('Radiobutton')
        radio = tk.StringVar()
        for i in range(5):
            Item('#Item {}'.format(i), variable=radio)

        Menu('Help', name='help')

Demo().run()

menu6.py 6 Context menu ————

The same Menu and Item widgets can be used to create context menus. Contrary to the Menu which is attached to the root window, the ContextMenu is attached to a specific widget. When the widget is clicked, a callback function is called.

The Menu.post() method is used to open the context menu at the current cursor position:

class ContextMenu(tk.Menu):
    def __init__(self, widget):
        """Create a context menu attached to a widget."""
        super(ContextMenu, self).__init__(widget)
        App.menus.append(self)

        if (App.root.tk.call('tk', 'windowingsystem') == 'aqua'):
            widget.bind('<2>', self.popup)
            widget.bind('<Control-1>', self.popup)
        else:
            widget.root.bind('<3>', self.popup)

    def popup(self, event):
        """Open a popup menu."""
        self.post(event.x_root, event.y_root)
        return 'break'
../_images/menu5.png
"""Create a context menu."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        l1 = Label("Context Menu", font="Arial 24")
        l2 = Label("Context Menu 2", font="Arial 48")
        
        ContextMenu(l1)
        Item('Item 1', 'print(1)')
        Item('Item 2', 'print(2)')
        Item('Item 3', 'print(3)')
        
        ContextMenu(l2)
        for c in 'ABC':
            Item('Item ' + c)

        ContextMenu(App.root)
        Item('Cut', 'print("Cut")')
        Item('Copy', 'print("Copy")')

Demo().run()

menu5.py

Insert an item at a specific position¶

Normally we build menus in order. We create a Menu() object, and then we add the Item() objects subsequently. With the insert method it is possible to attach an item anywhere in the existing menu.

This inserts an new command at index=2:

App.menus[-1].insert(2, 'command', label='Extra item')

This inserts a new command at the end:

App.menus[-1].insert(100, 'command', label='Extra item 100')
App.menus[-1].insert(10, 'command', label='Extra item 10')
../_images/menu6.png
"""Insert items at a specific index position."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Insert items at specific index.", font="Arial 24")
   
        Menu('Menu')
        for i in range(5):
            Item('Item {}'.format(i), cmd=lambda : print(i), acc='Command-'+str(i))

        Item('Item', 'print("Left")', acc='Command-Left')
        Item('Item', 'print("Up")', acc='Command-Up')
        Item('Item', 'print("Right")', acc='Command-Right')
        Item('Item', 'print("Down")', acc='Command-Down')
        
        App.menus[-1].insert(2, 'command', label='Extra item')
        App.menus[-1].insert(100, 'command', label='Extra item 100')
        App.menus[-1].insert(10, 'command', label='Extra item 10')

        Menu('Window', name='window')
        Menu('Help', name='help')

Demo().run()

menu6.py

Different menus for each window¶

If no new menu is defined for a new window, the root menu will be used for the new window. If a new menu is added after the window is created, this window will have it’s own menu. Whenever a window becomes active, it will display it’s own menu bar.

This shows the creation of two new windows with their different menubars:

Window("Letters")
Button()
Entry()
Menu1()
Menu('Help', name='help')

Window("Numbers")
Entry()
Menu2()
../_images/menu7.png
"""Insert different menus for each window."""
from tklib import *

def Menu1():
    Menu('Letters')
    for c in 'ABC':
        Item('Item ' + c) 

def Menu2():
    Menu('Numbers')
    for c in '123':
        Item('Item ' + c) 

class Demo(App):
    # constructor
    def __init__(self):
        super().__init__()
        Label("Different menus for each window.", font="Arial 24")

        Menu1()
        Menu2()

        Window("Letters")
        Button()
        Entry()
        Menu1()
        Menu('Help', name='help')

        Window("Numbers")
        Entry()
        Menu2()

Demo().run()

menu7.py

Creating new widgets from the menu¶

The next example shows how to add widgets dynamically. The Widget menu contains specific widgets as items which are added to the current window:

Menu('Widgets')
Item('Button', 'Button()', 'Command-b')
Item('Label', 'Label()', 'Command-l')
Item('Entry', 'Entry()', 'Command-e')
Item('Radiobutton', 'Radiobutton()', 'Command-r')
Item('Checkbutton', 'Checkbutton()', 'Command-k')
Item('Canvas', 'Canvas()', 'Command-c')
Item('Listbox', 'Listbox(height=5)', 'Command-i')
Item('Scale', 'Scale()', 'Command-s')
Item('Text', 'Text(width=30, height=5)', 'Command-t')
../_images/menu8.png
"""Insert widgets via a menu."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Insert widgets via the menu.", font="Arial 24")

        Menu('Widgets')
        Item('Button', 'Button()', 'Command-b')
        Item('Label', 'Label()', 'Command-l')
        Item('Entry', 'Entry()', 'Command-e')
        Item('Radiobutton', 'Radiobutton()', 'Command-r')
        Item('Checkbutton', 'Checkbutton()', 'Command-k')
        Item('Canvas', 'Canvas()', 'Command-c')
        Item('Listbox', 'Listbox(height=5)', 'Command-i')
        Item('Scale', 'Scale()', 'Command-s')
        Item('Text', 'Text(width=30, height=5)', 'Command-t')

Demo().run()

menu8.py

Insert a popup menu¶

../_images/menu9.png
"""Insert a pop-up menu."""
from tklib import *

class Demo(App):
    def __init__(self):
        super().__init__()
        Label("Insert widgets via the menu.", font="Arial 24")

        text = Text('Initial text...')
        
        ContextMenu(text)
        Item('Item 1', 'print(1)')
        Item('Item 2', 'print(2)')
        Item('Item 3', 'print(3)')

Demo().run()

menu9.py

Next Previous

© Copyright 2019-2020, Raphael Holzer Revision e7a7697a.

Built with Sphinx using a theme provided by Read the Docs.