Как сделать меню в python
Для создания иерархического меню в tkinter применяется виджет Menu . Основные параметры Menu:
- activebackground : цвет активного пункта меню
- activeborderwidth : толщина границы активного пункта меню
- activeforeground : цвет текста активного пункта меню
- background / bg : фоновый цвет
- bd : толщина границы
- cursor : курсор указателя мыши при наведении на меню
- disabledforeground : цвет, когда меню находится в состоянии DISABLED
- font : шрифт текста
- foreground / fg : цвет текста
- tearoff : меню может быть отсоединено от графического окна. В частности, при создании подменю а скриншоте можно увидеть прерывающуюся линию в верху подменю, за которую его можно отсоединить. Однако при значении tearoff=0 подменю не сможет быть отсоединено.
Меню может содержать много элементов, причем эти элементы сами могут представлять меню и содержать другие элементы. В зависимости от того, какой тип элементов мы хотим добавить в меню, будет отличаться метод, используемый для их добавления. В частности, нам доступны следующие методы:
- add_command(options) : добавляет элемент меню через параметр options
- add_cascade(options) : добавляет элемент меню, который в свою очередь может представлять подменю
- add_separator() : добавляет линию-разграничитель
- add_radiobutton(options) : добавляет в меню переключатель
- add_checkbutton(options) : добавляет в меню флажок
Создадим простейшее меню:
from tkinter import * root = Tk() root.title("METANIT.COM") root.geometry("250x150") main_menu = Menu() main_menu.add_cascade(label="File") main_menu.add_cascade(label="Edit") main_menu.add_cascade(label="View") root.config(menu=main_menu) root.mainloop()
Для добавления пунктов меню у объекта Menu вызывается метод add_cascade() . В этот метод передаются параметры пункта меню, в данном случае они представлены текстовой меткой, устанавливаемой через параметр label .
Но просто создать меню — еще недостаточно. Его надо установить для текущего окна с помощью параметра menu в методе config() . В итоге графическое окно будет иметь следующее меню:
Теперь добавим подменю:
from tkinter import * root = Tk() root.title("METANIT.COM") root.geometry("250x150") main_menu = Menu() file_menu = Menu() file_menu.add_command(label="New") file_menu.add_command(label="Save") file_menu.add_command(label="Open") file_menu.add_separator() file_menu.add_command(label="Exit") main_menu.add_cascade(label="File", menu=file_menu) main_menu.add_cascade(label="Edit") main_menu.add_cascade(label="View") root.config(menu=main_menu) root.mainloop()
Здесь определяется подменю file_menu, которое добавляется в первый пункт основного меню благодаря установке опции menu=file_menu :
main_menu.add_cascade(label="File", menu=file_menu)

Но обратите внимание на пунктирную линию в подменю, которая совершенно не нужна и непонятно откуда появляется. Чтобы избавиться от этой линии, надо для нужного пункта меню установить параметр tearoff=0 :
file_menu = Menu(tearoff=0)
Однако так как подпунктов меню может быть много, чтобы для кажлого не прописывать этот параметр, то проще отключить все это глобально с помощью следующей строки кода
root.option_add("*tearOff", FALSE)
from tkinter import * root = Tk() root.title("METANIT.COM") root.geometry("250x150") root.option_add("*tearOff", FALSE) main_menu = Menu() file_menu = Menu() file_menu.add_command(label="New") file_menu.add_command(label="Save") file_menu.add_command(label="Open") file_menu.add_separator() file_menu.add_command(label="Exit") main_menu.add_cascade(label="File", menu=file_menu) main_menu.add_cascade(label="Edit") main_menu.add_cascade(label="View") root.config(menu=main_menu) root.mainloop()

Подобным образом можно создавать и более глубокие иерархии меню:
from tkinter import * root = Tk() root.title("METANIT.COM") root.geometry("250x150") root.option_add("*tearOff", FALSE) main_menu = Menu() file_menu = Menu() settings_menu = Menu() settings_menu.add_command(label="Save") settings_menu.add_command(label="Open") file_menu.add_cascade(label="Settings", menu=settings_menu) file_menu.add_separator() file_menu.add_command(label="Exit") main_menu.add_cascade(label="File", menu=file_menu) root.config(menu=main_menu) root.mainloop()

Взаимодействие с меню
Отличительной особенностью элементов меню является способность реагировать на нажатия пользователя. Для этого у каждого элемента меню можно задать параметр command , который устанавливает ссылку на функцию, выполняемую при нажатии.
from tkinter import * from tkinter import messagebox root = Tk() root.title("METANIT.COM") root.geometry("250x150") root.option_add("*tearOff", FALSE) def edit_click(): messagebox.showinfo("GUI Python", "Нажата опция Edit") 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()
Создание меню и подменю

Пришло время создать первый интерфейсный элемент – меню. Я напомню, что для его создания используется три класса:
- MenuBar – для создания панели меню;
- Menu – для создания вкладки меню;
- MenuItem – для создания отдельного пункта.
Давайте для начала создадим вот такое простое меню. На уровне языка Python это выглядит так:
class MyFrame(wx.Frame ): def __init__(self, parent, title): super().__init__(parent, -1, title) menubar = wx.MenuBar() fileMenu = wx.Menu() item = wx.MenuItem(fileMenu, wx.ID_EXIT, "Выход", "Выход из приложения") fileMenu.Append(item) menubar.Append(fileMenu, "&File") self.SetMenuBar(menubar)
Смотрите, мы сначала создаем экземпляр класса MenuBar и экземпляр класса Menu. Далее, указываем, что создаем пункт во вкладке fileMenu со строкой «Выход». Параметр ID_EXIT – это id нашего пункта. Оно выбрано как стандартное и, по идее, должно добавлять соответствующую иконку и комбинацию клавиш Ctrl+Q (но это не всегда срабатывает). В конце идет описание этого пункта, которое можно позже вывести, например, в статусную строку. Далее, мы добавляем созданный пункт во вкладку fileMenu с помощью метода Append и на панели menubar размещаем эту вкладку, также вызывая метод Append класса MenuBar. В конце размещаем панель меню в нашем окне. Запустим программу и видим ожидаемый функционал. И смотрите, если сейчас нажать комбинацию клавиш Alt+F то наша вкладка «File» будет автоматически выбрана. Так получилось благодаря амперсанду перед символом F. Если мы его поставим, например, перед буквой «i», то вкладка откроется при нажатии Alt+i И так далее. Вот в этом смысл этого амперсанда. Конечно, если сейчас выбрать пункт меню, то ничего не произойдет, т.к. мы еще не назначили ему обработчик. Но, прежде чем мы это сделаем, отметим следующее. Для удобства вот эти две строчки:
item = wx.MenuItem(fileMenu, wx.ID_EXIT, "Выход", "Выход из приложения") fileMenu.Append(item)
можно объединить в одну:
item = fileMenu.Append(wx.ID_EXIT, "Выход", "Выход из приложения")
Результат будет абсолютно таким же. В дальнейшем мы будем создавать пункты именно так, если не понадобится первый способ. Теперь свяжем данный пункт с обработчиком. Например, пусть при его выборе вызывается метод:
def onQuit(self, event): self.Close()
- event – тип события, связанный с определенным интерфейсным объектом;
- handler – ссылка на функцию-обработчик;
- source – источник, генерирующий событие.
self.Bind(wx.EVT_MENU, self.onQuit, item)
Мы здесь указали тип события – EVT_MENU, далее, ссылка на метод onQuit, и, наконец, источник – наш пункт меню item. Все, теперь при запуске программы сработает метод onQuit и окно будет закрыто. Добавим теперь нашему пункту дополнительный функционал. Во-первых, мы хотим, чтобы при комбинации клавиш Ctrl+Q этот пункт автоматически вызывался и выполнялся связанный с ним обработчик. Назначить горячие клавиши очень просто, указав их через символ табуляции:
item = fileMenu.Append(wx.ID_EXIT, "Выход\tCtrl+Q", "Выход из приложения")
Далее, добавим нашему пункту изображение иконки. Вначале определим константу
APP_EXIT = 1
которая будет связана с ним. И создадим пункт, используя класс MenuItem:
item = wx.MenuItem(fileMenu, APP_EXIT, "Выход\tCtrl+Q", "Выход из приложения")
В данном случае это необходимо, т.к. перед его добавление во вкладку мы присвоим ему иконку:
item.SetBitmap(wx.Bitmap('exit16.png')) fileMenu.Append(item)

Запускаем программу и видим такой эффект: Разумеется, картинка exit16.png должна располагаться в том же каталоге, что и запускаемый файл с программой на питоне. Размеры картинки должны соответствовать нашему пункту. Если ее взять больше:
item.SetBitmap(wx.Bitmap('exit32.png'))

то это может выглядеть не так эстетично. Кстати, используя нашу собственную константу APP_EXIT в качестве id пункта, в дальнейшем, мы можем связывать через нее обработчик в методе Bind:
self.Bind(wx.EVT_MENU, self.onQuit, id=APP_EXIT)
Такой подход более предпочтителен, чем использование ссылок, т.к. их легко изменить или многократно использовать для разных пунктов. А вот id остается неизменным.
Подменю и сепараторы
Давайте расширим нашу вкладку, добавим на нее еще несколько стандартных пунктов, например, такие:
fileMenu.Append(wx.ID_NEW, '&Новый\tCtrl+N') fileMenu.Append(wx.ID_OPEN, '&Открыть\tCtrl+O') fileMenu.Append(wx.ID_SAVE, '&Сохранить\tCtrl+S')

И смотрите, нам бы здесь хотелось визуально отделить эти пункты от последнего. Как раз это можно сделать с помощью специального пункта, который называется separator. Он добавляется специальным методом:
fileMenu.AppendSeparator()

Теперь наша вкладка выглядит привычнее. Следующим шагом добавим подменю. Создадим еще одну вкладку:
expMenu = wx.Menu()
пропишем туда вот такие пункты:
expMenu.Append(wx.ID_ANY, "Экспорт изображения") expMenu.Append(wx.ID_ANY, "Экспорт видео") expMenu.Append(wx.ID_ANY, "Экспорт данных")
и добавим на вкладку fileMenu:
fileMenu.AppendSubMenu(expMenu, "&Экспорт")

Запускаем программу и видим такой результат:
Типы пунктов меню
- ITEM_NORMAL – обычный текст;
- ITEM_SEPARATOR – разделитель (сепаратор);
- ITEM_CHECK – пункт с флажком;
- ITEM_RADIO – пункт с возможностью перебора.
viewMenu = wx.Menu()
Добавим туда следующие строчки:
viewMenu.Append(wx.ID_ANY, 'Статустная строка', kind=wx.ITEM_CHECK) viewMenu.Append(wx.ID_ANY, 'Тип RGB', 'Тип RGB', kind=wx.ITEM_RADIO) viewMenu.Append(wx.ID_ANY, 'Тип sRGB', 'Тип sRGB', kind=wx.ITEM_RADIO)
Смотрите, здесь последним именованным параметром идет kind, определяющий тип пункта меню. У первого стоит ITEM_CHECK, а у двух других — ITEM_RADIO. Добавим эту вкладку на панель меню:
menubar.Append(viewMenu, "&Вид")

Запустим программу и увидим вот такой эффект: Смотрите, первую строчку можно отмечать флажком, а последние две только перебирать: либо RGB, либо sRGB. Давайте теперь посмотрим как все это можно отследить в обработчиках этих событий. Пропишем в начале вот такие константы:
VIEW_STATUS = 2 VIEW_RGB = 3 VIEW_SRGB = 4
Укажем их при создании строчек меню:
self.vStatus = viewMenu.Append(VIEW_STATUS, 'Статустная строка', kind=wx.ITEM_CHECK) self.vRgb = viewMenu.Append(VIEW_RGB, 'Тип RGB', 'Тип RGB', kind=wx.ITEM_RADIO) self.vSrgb = viewMenu.Append(VIEW_SRGB, 'Тип sRGB', 'Тип sRGB', kind=wx.ITEM_RADIO)
И, далее, повесим обработчики:
self.Bind(wx.EVT_MENU, self.onStatus, id=VIEW_STATUS) self.Bind(wx.EVT_MENU, self.onImageType, id=VIEW_RGB) self.Bind(wx.EVT_MENU, self.onImageType, id=VIEW_SRGB)
Объявим два метода:
def onStatus(self, event): if self.vStatus.IsChecked(): print("Показать статусную строку") else: print("Скрыть статусную строку") def onImageType(self, event): if self.vRgb.IsChecked(): print("Режим RGB") elif self.vSrgb.IsChecked(): print("Режим sRGB")
Смотрите, мы здесь используем метод IsChecked(), чтобы определить: выбран ли данный пункт. И в соответствии с этим выводим в консоль сообщения. Запустим программу и убедимся, что все работает корректно.
Виджет Menu в tkinter
Меню – это виджет, который присутствует во многих пользовательских приложениях. Находится оно под строкой заголовка и представляет собой выпадающие списки под словами-пунктами меню. Пункты конечных списков представляют собой команды, обычно выполняющие какие-либо действия или открывающие диалоговые окна.
В tkinter экземпляр меню создается от класса Menu , далее его надо привязать к виджету, на котором оно будет расположено. Обычно таковым выступает главное окно приложения. Его опции menu присваивается экземпляр Menu через имя связанной с экземпляром переменной.
from tkinter import * root = Tk() mainmenu = Menu(root) root.config(menu=mainmenu) root.mainloop()
Если выполнить данный код, то никакого меню вы не увидите. Только тонкую полоску под заголовком окна, ведь ни одного пункта меню не было создано. Метод add_command добавляет пункт меню:
… mainmenu.add_command(label='Файл') mainmenu.add_command(label='Справка') …

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

На основное меню 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="О программе") …

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

В tkinter можно создать всплывающее меню, оно же контекстное (если настроить его появление по клику правой кнопкой мыши). Для этого экземпляр меню подвязывается не через опцию menu к родительскому виджету, а к меню применяется метод post , аргументами которого являются координаты того места, где должно появляться меню.
from tkinter import * 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) x = 0 y = 0 root = Tk() c = Canvas(width=300, height=300, bg='white') c.pack() c.bind("", popup) menu = Menu(tearoff=0) menu.add_command(label="Круг", command=circle) menu.add_command(label="Квадрат", command=square) menu.add_command(label="Треугольник", command=triangle) root.mainloop()

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

Курс с примерами решений практических работ: pdf-версия
X Скрыть Наверх
Tkinter. Программирование GUI на Python
Меню, подменю и панель инструментов в Tkinter

В этой части обучения Tkinter мы поработаем с меню и панелями инструментов. Меню – одна из наиболее заметных и используемых частей графического интерфейса приложений.
Фактически, это группа команд, которая объединена в одно меню. В консольных приложениях нам нужно запоминать большое количество тайных команд. Здесь же все команды объединены в удобные группы и расположены на видном месте.
Существуют определенные стандарты, которые позволяют пользователям быстрее осваивать как работает новое приложение. Меню объединяют группы команд, которые мы можем использовать для управления приложением. Панели инструментов дают нам быстрый доступ к тем командам, которые мы используем чаще всего.
Создание простого меню в Tkinter
Первый пример создает простое меню.
