# Menu - виджет меню

Меню – это виджет, который присутствует во многих пользовательских приложениях. Находится оно под строкой заголовка и представляет собой выпадающие списки под словами - пунктами меню. Пункты конечных списков представляют собой команды, обычно выполняющие какое-либо действия или открывающие диалоговые окна.

Меню может содержать много элементов, причем эти элементы сами могут представлять меню и содержать другие элементы. В зависимости от того, какой тип элементов мы хотим добавить в меню, будет отличаться метод, используемый для их добавления. В частности, нам доступны следующие методы:

  • add_command(options): добавляет элемент меню через параметр options
  • add_cascade(options): добавляет элемент меню, который в свою очередь может представлять подменю
  • add_separator(): добавляет линию-разграничитель
  • add_radiobutton(options): добавляет в меню переключатель
  • add_checkbutton(options): добавляет в меню флажок

В tkinter экземпляр меню создается от класса Menu, далее его надо привязать к виджету, на котором оно будет расположено. Обычно таковым выступает главное окно приложения. Его опции menu присваивается экземпляр Menu через имя связанной с экземпляром переменной:

from tkinter import *

root = Tk()
mainmenu = Menu(root)
root.config(menu=mainmenu)

root.mainloop()
1
2
3
4
5
6
7

Если выполнить данный код, то никакого меню вы не увидите. Только тонкую полоску под заголовком окна, ведь ни одного пункта меню не было создано. Метод add_command() добавляет пункт меню:

…
mainmenu.add_command(label='Файл')
mainmenu.add_command(label='Справка')
1
2
3
4

menu_01

В данном случае «Файл» и «Справка» – это команды. К ним можно добавить опцию command, связав тем самым с какой-либо функцией-обработчиком клика. Хотя такой вариант меню имеет право на существование, в большинстве приложений панель меню содержит выпадающие списки команд, а сами пункты на панели командами по сути не являются. Клик по ним приводит лишь к раскрытию соответствующего списка.

# Упражнения

  1. Создайте визуально похожее меню программы блокнот или skype.

    note_file note_edit note_format

  2. Реализуйте генерацию меню. Образец меню записан в виде массива:

    menu_example = [
        ['file', ['new', 'open', 'close']],
        ['edit', ['undo', 'cut', 'paste', 'delete']],
        ['format', ['word wrap', 'font']],
        ['view', ['zoom', 'status bar']],
        ['help', ['view help', 'about notepad']]
    ]
    
    1
    2
    3
    4
    5
    6
    7

# Подменю - Submenu

Подменю - это меню, подключенное к другому объекту меню.

В tkinter проблема решается созданием новых экземпляров Menu и подвязыванием их к главному меню с помощью метода add_cascade():

from tkinter import *

root = Tk()

mainmenu = Menu(root)
root.config(menu=mainmenu)

filemenu = Menu(mainmenu, tearoff=0)
filemenu.add_command(label="Открыть...")
filemenu.add_command(label="Новый")
filemenu.add_command(label="Сохранить...")
filemenu.add_command(label="Выход")

helpmenu = Menu(mainmenu, tearoff=0)
helpmenu.add_command(label="Помощь")
helpmenu.add_command(label="О программе")

mainmenu.add_cascade(label="Файл", menu=filemenu)
mainmenu.add_cascade(label="Справка", menu=helpmenu)

root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

menu_02

На основное меню mainmenu, добавляются не команды, а другие меню. У filemenu и helpmenu в качестве родительского виджета указывается не root, а mainmenu. Команды добавляются только к дочерним меню. Значение 0 опции tearoff отключает возможность открепления подменю, иначе его можно было бы делать плавающим кликом мыши по специальной линии (в случае tearoff=0 она отсутствует).

Точно также можно подвязывать дочерние меню к filemenu и helpmenu, создавая многоуровневые списки пунктов меню:

…
helpmenu = Menu(mainmenu, tearoff=0)

helpmenu2 = Menu(helpmenu, tearoff=0)
helpmenu2.add_command(label="Локальная справка")
helpmenu2.add_command(label="На сайте")

helpmenu.add_cascade(label="Помощь", menu=helpmenu2)

helpmenu.add_command(label="О программе")
1
2
3
4
5
6
7
8
9
10
11

menu_03

Метод add_separator() добавляет линию разделитель в меню. Используется для визуального разделения групп команд.

# Взаимодействие с меню

Отличительной особенностью элементов меню является способность реагировать на нажатия пользователя. Для этого у каждого элемента меню можно задать параметр command, который устанавливает ссылку на функцию, выполняемую при нажатии:

from tkinter import *
from tkinter import messagebox


def edit_click():
    messagebox.showinfo("GUI Python", "Нажата опция Edit")

root = Tk()
root.title("GUI на Python")
root.geometry("300x250")

main_menu = Menu()

main_menu.add_cascade(label="File")
main_menu.add_cascade(label="Edit", command=edit_click)
main_menu.add_cascade(label="View")

root.config(menu=main_menu)

root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

menu_06

# Контекстное меню - Popup menu

В следующем примере мы создаем всплывающее меню. Всплывающее меню также называется контекстным меню. Это может быть показано в любом месте на клиентской области окна:

from tkinter import *

def showMenu(event):
    print(event)
    menu.post(event.x_root, event.y_root)

def onQuit():
    quit()

root = Tk()
root.geometry("250x150+300+300")
root.title("Popup menu")

menu = Menu(root, tearoff=0)
menu.add_command(label="Exit", command=root.destroy)
menu.add_command(label="Quit", command=onQuit)

root.bind("<Button-3>", showMenu)
root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

В нашем примере мы создаем всплывающее меню с двумя командами:

  • контекстное меню - это обычный виджет меню. Функция отрыва отключена. Теперь невозможно разделить меню на новое окно верхнего уровня:
menu = Menu(root, tearoff=0)
1
  • Мы связываем событие <Button-3> с методом showMenu(). Событие генерируется, когда мы щелкаем правой кнопкой мыши на клиентской области окна:
root.bind("<Button-3>", showMenu)
1
  • Метод showMenu() показывает контекстное меню. Всплывающее меню отображается в координатах x и y щелчка мыши:
def showMenu(event):
    print(event)
    menu.post(event.x_root, event.y_root)
1
2
3

Результат выполнения может:

popup_menu_01

В tkinter можно создать всплывающее меню и настроить его появление по клику правой кнопкой мыши. Для этого экземпляр меню подвязывается не через опцию menu к родительскому виджету, а к меню применяется метод post(), аргументами которого являются координаты того места, где должно появляться меню:

from tkinter import *

x = 0
y = 0

def circle():
    c.create_oval(x, y, x+30, y+30)

def square():
    c.create_rectangle(x, y, x+30, y+30)

def triangle():
    c.create_polygon(x, y, x-15, y+30, x+15, y+30,
                    fill='white', outline='black')

def popup(event):
    global x, y
    x = event.x
    y = event.y
    menu.post(event.x_root, event.y_root)

root = Tk()

c = Canvas(width=300, height=300, bg='white')
c.pack()

menu = Menu(tearoff=0)
menu.add_command(label="Круг", command=circle)
menu.add_command(label="Квадрат", command=square)
menu.add_command(label="Треугольник", command=triangle)

c.bind("<Button-3>", popup)

root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

menu_04

Здесь при клике на холсте правой кнопкой мыши в этой точке всплывает меню. При выборе пункта меню рисуется соответствующая фигура в этом же месте.

# Упражнения

  1. Напишите визуальное подобие простейшего текстового редактора, например программы Блокнот.
  2. Реализуйте главное меню File добавив кнопки: open, save и close.
  3. Реализуйте работу кнопок (open, save и close) используя диалоговые окна.
  4. Добавьте текстовое поле с корректным отображением при масштабировании окна.
  5. Добавьте вызов контекстного меню с командами: cut, copy и paste.
  6. Реализуйте работу кнопок (cut, copy и paste), обратившись к свойствам текстового поля.
  7. Реализуйте функцию кнопки меню "переноса текста": при активации перенос текста происходит по словам.