Виджет Menu

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

В 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')
 
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)
 
x = 0
y = 0
def popup(event):
    global x, y
    x = event.x
    y = event.y
    menu.post(event.x_root, event.y_root)
 
c.bind("<Button-3>", popup)
 
root.mainloop()

Всплывающее меню

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

Практическая работа

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

Пример программы на Tkinter с меню

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Комментарии

Практическая работа
from tkinter import *
from tkinter import messagebox as mb
from tkinter import filedialog as fd
def insert_text():
    try:
        file_name=fd.askopenfilename()
        f=open(file_name)
        s=f.read()
        text.insert(1.0,s)
        f.close()
    except FileNotFoundError:
        mb.showinfo("Внимание", "Файл не загружен")
 
def extract_text():
    try:
        file_name=fd.asksaveasfilename(filetypes=(("TXT files", "*.txt"),
                                              ("HTML files", "*.html; *.htm"),
                                              ("All files", "*.*")))
        f=open(file_name, 'w')
        s=text.get(1.0, END)
        f.write(s)
        f.close()
    except FileNotFoundError:
        mb.showinfo("Внимание", "Файл не сохранён")
 
def delete_text():
    answer=mb.askyesno("Подтверждение", message="Вы хотите удалить текст?")
    if answer==True:
        text.delete(0.0, END)             
 
root=Tk()
text=Text(width=50, height=25)
text.grid(columnspan=2)
mainmenu=Menu(root)
root.config(menu=mainmenu)
mainmenu.add_command(label="Открыть", command=insert_text)
mainmenu.add_command(label="Сохранить", command=extract_text)
menu=Menu(tearoff=0)
menu.add_command(label="Очистить", command=delete_text)
text.bind("<Button-3>", lambda event: menu.post(event.x_root, event.y_root))
 
root.mainloop()

Ответ на от aleks

filemenu = Menu(mainmenu, tearoff=0, background = 'lightskyblue')
filemenu.add_command(label="Открыть")
filemenu.add_command(label="Создать")
filemenu.add_command(label="Сохранить")
filemenu.add_command(label="Выход")
 
 
 
helpmenu=Menu(mainmenu, tearoff=0, background = 'lightskyblue')
helpmenu.add_command(label="О программе")
 
ast=helpmenu.add_command(label="Разработчики")
 
 
mainmenu.add_cascade(label="Файл",menu=filemenu)
mainmenu.add_cascade(label="Справка",menu=helpmenu)
все вышло. теперь подскажите пожалуйста как сделать так что бы при нажатии допустим *справки* и *о программе* выходила новая форма тк??

from tkinter import *
from tkinter import filedialog as fd
from tkinter import messagebox as mb
from os import path
 
def insertText():
    try:
        file_name = fd.askopenfilename(filetypes=(("TXT files", "*.txt"),))
        text.delete(1.0, END)
        with open(file_name) as f:
            s = f.read()
            text.insert(1.0, s)
    except TypeError:
        mb.showerror("Error", "Choose any file")
    except FileNotFoundError:
        mb.showerror("Error", "Indicate file")
 
def extractText():
    try:
        file_name = fd.asksaveasfilename(initialdir= path.dirname('/home/aleksey1652/*'),
        filetypes=(("TXT files", "*.txt"),))
        with open(file_name,'w') as f:
            s = text.get(1.0, END)
            f.write(s)
 
    except FileNotFoundError:
        mb.showerror("Error", "Indicate file")
    except TypeError:
        mb.showerror("Error", "Choose any file")
def clearText():
    m=mb.askyesno(message="Are you sure?")
    if m==True:
      text.delete(1.0, END)
 
def menu_clear(event):
    menu2.delete(0)
    menu2.add_command(label='Clear',command=clearText)
    menu2.post(event.x_root, event.y_root)
    text.bind('<Button-1>', lambda event:menu2.delete(0))    
 
root = Tk()
mainmenu = Menu(root)
root.config(menu=mainmenu)
menu2=Menu(root, tearoff=0)
menu2.add_command(label='Clear',command=clearText)
text = Text(width=50, height=25)
text.grid(columnspan=3)
mainmenu.add_command(label='Open',command=insertText)
mainmenu.add_command(label='Save',command=extractText)
text.bind('<Button-3>',menu_clear )
root.mainloop()