Adding a menu and menu items

Menus offer a very compact way of presenting a large number of choices to the user without cluttering the interface. Tkinter offers the following two widgets to handle menus:

  • The Menu widget: This appears at the top of applications, which is always visible to end users
  • The menu Items: This shows up when a user clicks on a menu

We will use the following code to add Toplevel menu buttons:

my_menu = Menu(parent, **options)

For example, to add a File menu, we will use the following code:

# Adding Menubar in the widget
menu_bar = Menu(root)

file_menu = Menu(menu_bar, tearoff=0)
# all file menu-items will be added here next
menu_bar.add_cascade(label='File', menu=file_menu)
root.config(menu=menu_bar)

The following screenshot is the result of the preceding code:

Similarly, we will add the Edit, View, and About menus. See 2.01.py.

We will also define a constant, as follows:

PROGRAM_NAME = " Footprint Editor "

Then, we'll set the root window tile as follows:

root.title(PROGRAM_NAME)

Most Linux platforms support tear-off menus. When tearoff is set to 1 (enabled), the menu appears with a dotted line above the menu options. Clicking on the dotted line enables the user to literally tear off or separate the menu from the top. However, as this is not a cross-platform feature, we have decided to disable tear-off, marking it as tearoff = 0.

Adding menu items

Next, we will add menu items in every individual menu. Not surprisingly, the code for the menu items needs to be added in the respective menu instance, as shown in the following screenshot:

In our example, we will add the following menu items (see the 2.02.py code in the code bundle).

The View menu has certain menu item variations, which will be tackled in the following section and is therefore not dealt with here.

Menu items are added by using the add_command() method. The format used to add menu items is as follows:

my_menu.add_command(label="Menu Item Label", accelerator='KeyBoard Shortcut', compound='left', image=my_image, underline=0, command=callback)

For example, you can create the Undo menu item by using the following syntax:

edit_menu.add_command(label="Undo", accelerator='Ctrl + Z', compound='left', image=undo_icon, command=undo_callback)

Some new menu-specific options that are introduced in the preceding code are as follows:

  • accelerator: This option is used to specify a string, typically the keyboard shortcut, which can be used to invoke a menu. The string specified as an accelerator appears next to the text of the menu item. Please note that this does not automatically create bindings for the keyboard shortcut. We will have to manually set them up. This will be discussed later.
  • compound: Specifying a compound option for a menu item lets you add images beside a menu label. A specification such as compound='left', label= 'Cut', image=cut_icon means that the cut icon will appear to the left of the Cut menu label. The icons that we will use here are stored and referenced from a separate folder called icons.
  • underline: The underline option lets you specify the index of a character in the menu text that needs to be underlined. The indexing starts at 0, which means that specifying underline=1 underlines the second character of the text. Besides underlining, Tkinter also uses it to define the default bindings for the keyboard traversal of menus. This means that we can select the menu either with the mouse pointer, or with the Alt + <character_at_the_underlined_index> shortcut.

To add the New menu item in the File menu, use the following code:

file_menu.add_command(label="New", accelerator='Ctrl+N', compound='left', image=new_file_icon, underline=0, command=new_file)

Note

Menu separators

Occasionally, in menu items, you will come across code such as my_menu.add_separator(). This widget displays a separator bar and is solely used to organize similar menu items in groups, separating groups by horizontal bars.

Next, we will add a Frame widget to hold the shortcut icons. We will also add a Text widget to the left to display line numbers, as shown in the following screenshot (refer to the 2.02.py code in the code bundle):

Note

When working with the pack geometry manager, it is important to add widgets in the order in which they will appear because pack() uses the concept of available space to fit the widgets. This is why the text content widget will appear lower in the code as compared to the two label widgets.

Having reserved the space, we can later add shortcut icons or line numbers and keep the Frame widget as the parent widget. Adding frames is easy; we have done that in the past. The code is as follows (refer to 2.02.py):

shortcut_bar = Frame(root,  height=25, background='light sea green')
shortcut_bar.pack(expand='no', fill='x')

line_number_bar = Text(root, width=4, padx=3, takefocus=0, border=0,
background='khaki', state='disabled', wrap='none')
line_number_bar.pack(side='left', fill='y')

We applied a background color to these two widgets for now to discern them from the body of the Toplevel window.

Lastly, let's add the main Text widget and Scrollbar widget, as follows (refer to the 2.02.py code in the code bundle):

content_text = Text(root, wrap='word')
content_text.pack(expand='yes', fill='both')
scroll_bar = Scrollbar(content_text)
content_text.configure(yscrollcommand=scroll_bar.set)
scroll_bar.config(command=content_text.yview)
scroll_bar.pack(side='right', fill='y')

The code is similar to how we instantiated all the other widgets so far. However, note that the scrollbar is configured to yview of the Text widget and the Text widget is configured to connect to the Scrollbar widget. This way, we cross-connected both the widgets to each other.

Now, when you scroll down the Text widget, the scrollbar reacts to it. Alternatively, when you move the scrollbar, the Text widget reacts in return.