首页 > Python基础教程 > Tkinter
阅读:5,617
Python Tkinter Menu菜单(窗口菜单和右键菜单)使用详解
Tkinter 为菜单提供了 Menu 类,该类既可代表菜单条,也可代表菜单,还可代表上下文菜单(右键菜单)。简单来说,Menu 类就可以搞定所有菜单相关内容。
程序可调用 Menu 的构造方法来创建菜单,在创建菜单之后可通过如下方法添加菜单项:
上面的前三个方法都用于添加菜单项,因此都支持如下常用选项:
有了菜单之后,接下来就是如何使用菜单了。菜单有两种用法:
接下来程序调用 add_command 方法为自 file_menu 添加多个菜单项,直到第 36 行代码调用 file_menu 的 add_cascade() 方法再次为 file_menu 添加子菜单。
第 42 行代码位于循环中,这样程序调用 add_radiobutton() 方法添加多个单选菜单项,这些单选菜单项都绑定了一个变量,因此它们就是一组的:第 48 行代码位于循环中,这样程序调用 add_checkbutton() 方法添加多个多边菜单项,每个多选菜单项都有单独的值,因此它们都需要绑定一个变量。
运行上面程序,可以看到如图 1 所示的效果。
图 1 生成菜单
由于程序为单选菜单项、多选菜单项都绑定了事件处理方法,因此单击这些菜单项,程序将会弹出消息框提示用户的选择。
下面将会实现一个功能更全面的菜单示例,而且该示例程序还会添加一个工具条(实际上 Tkinter 并未提供工具条组件,因此本教程将以 Frame 来实现工具条,以 Button 实现工具条上的按钮)。
因此,当开发者需要改变程序界面中的菜单时,并不需要修改后面的代码,只需修改第 42~70 行代码即可。
运行上面程序,可以看到如图 2 所示的效果。
图 2 菜单和工具条
如下程序示范了创建并添加右键菜单:
程序中 ① 号代码位于 Text 组件的右键单击事件的处理函数内,这行代码调用 Menu 对象的 post() 方法弹出右键菜单,这意味着当用户在 Text 组件内单击鼠标右键时,Text 组件就会弹出右键菜单。
运行该程序,在界面上的 Text 组件内单击鼠标右键,将可以看到如图 3 所示的右键菜单。
图 3 右键菜单
程序可调用 Menu 的构造方法来创建菜单,在创建菜单之后可通过如下方法添加菜单项:
- add_command():添加菜单项。
- add_checkbutton():添加复选框菜单项。
- add_radiobutton():添加单选钮菜单项。
- add_separator():添加菜单分隔条。
上面的前三个方法都用于添加菜单项,因此都支持如下常用选项:
- label:指定菜单项的文本。
- command:指定为菜单项绑定的事件处理方法。
- image:指定菜单项的图标。
- compound:指定在菜单项中图标位于文字的哪个方位。
有了菜单之后,接下来就是如何使用菜单了。菜单有两种用法:
- 在窗口上方通过菜单条管理菜单。
- 通过鼠标右键触发右键菜单(上下文菜单)。
Menu窗口菜单
在创建菜单之后,如果要将菜单设置为窗口的菜单条(Menu 对象可被当成菜单条使用),则只要将应菜单设为窗口的 menu 选项即可。例如如下代码:from tkinter import * # 导入ttk from tkinter import ttk from tkinter import messagebox as msgbox class App: def __init__(self, master): self.master = master self.init_menu() # 创建菜单 def init_menu(self): # 创建menubar,它被放入self.master中 menubar = Menu(self.master) self.master.filenew_icon = PhotoImage(file='images/filenew.png') self.master.fileopen_icon = PhotoImage(file='images/fileopen.png') # 添加菜单条 self.master['menu'] = menubar # 创建file_menu菜单,它被放入menubar中 file_menu = Menu(menubar, tearoff=0) # 使用add_cascade方法添加file_menu菜单 menubar.add_cascade(label='文件', menu=file_menu) # 创建lang_menu菜单,它被放入menubar中 lang_menu = Menu(menubar, tearoff=0) # 使用add_cascade方法添加lang_menu菜单 menubar.add_cascade(label='选择语言', menu=lang_menu) # 使用add_command方法为file_menu添加菜单项 file_menu.add_command(label="新建", command = None, image=self.master.filenew_icon, compound=LEFT) file_menu.add_command(label="打开", command = None, image=self.master.fileopen_icon, compound=LEFT) # 使用add_command方法为file_menu添加分隔条 file_menu.add_separator() # 为file_menu创建子菜单 sub_menu = Menu(file_menu, tearoff=0) # 使用add_cascade方法添加sub_menu子菜单 file_menu.add_cascade(label='选择性别', menu=sub_menu) self.genderVar = IntVar() # 使用循环为sub_menu子菜单添加菜单项 for i, im in enumerate(['男', '女', '保密']): # 使用add_radiobutton方法为sub_menu子菜单添加单选菜单项 # 绑定同一个变量,说明它们是一组 sub_menu.add_radiobutton(label=im, command=self.choose_gender, variable=self.genderVar, value=i) self.langVars = [StringVar(), StringVar(), StringVar(), StringVar()] # 使用循环为lang_menu菜单添加菜单项 for i, im in enumerate(('Python', 'Kotlin','Swift', 'Java')): # 使用add_add_checkbutton方法为lang_menu菜单添加多选菜单项 lang_menu.add_checkbutton(label=im, command=self.choose_lang, onvalue=im, variable=self.langVars[i]) def choose_gender(self): msgbox.showinfo(message=('选择的性别为: %s' % self.genderVar.get())) def choose_lang(self): rt_list = [e.get() for e in self.langVars] msgbox.showinfo(message=('选择的语言为: %s' % ','.join(rt_list))) root = Tk() root.title("菜单测试") root.geometry('400x200') # 禁止改变窗口大小 root.resizable(width=False, height=False) App(root) root.mainloop()上面程序中第 17 行代码将 Menu 设置为窗口的 menu 选项,这意味着该菜单变成了菜单条;第 21 行、25 行代码调用 add_cascade() 方法添加菜单,这意味着为菜单条添加了两个菜单。
接下来程序调用 add_command 方法为自 file_menu 添加多个菜单项,直到第 36 行代码调用 file_menu 的 add_cascade() 方法再次为 file_menu 添加子菜单。
第 42 行代码位于循环中,这样程序调用 add_radiobutton() 方法添加多个单选菜单项,这些单选菜单项都绑定了一个变量,因此它们就是一组的:第 48 行代码位于循环中,这样程序调用 add_checkbutton() 方法添加多个多边菜单项,每个多选菜单项都有单独的值,因此它们都需要绑定一个变量。
运行上面程序,可以看到如图 1 所示的效果。
图 1 生成菜单
由于程序为单选菜单项、多选菜单项都绑定了事件处理方法,因此单击这些菜单项,程序将会弹出消息框提示用户的选择。
下面将会实现一个功能更全面的菜单示例,而且该示例程序还会添加一个工具条(实际上 Tkinter 并未提供工具条组件,因此本教程将以 Frame 来实现工具条,以 Button 实现工具条上的按钮)。
from tkinter import * # 导入ttk from tkinter import ttk from collections import OrderedDict class App: def __init__(self, master): self.master = master self.initWidgets() def initWidgets(self): # 初始化菜单、工具条用到的图标 self.init_icons() # 调用init_menu初始化菜单 self.init_menu() # 调用init_toolbar初始化工具条 self.init_toolbar() #--------------------------------- # 创建、添加左边的Frame容器 leftframe = ttk.Frame(self.master, width=40) leftframe.pack(side=LEFT, fill=Y) # 在左边窗口放一个Listbox lb = Listbox(leftframe, font=('Courier New', 20)) lb.pack(fill=Y, expand=YES) for s in ('Python', 'Ruby', 'Swift', 'Kotlin', 'Java'): lb.insert(END, s) # 创建、添加右边的Frame容器 mainframe = ttk.Frame(self.master) mainframe.pack(side=LEFT, fill=BOTH) text = Text(mainframe, width=40, font=('Courier New', 16)) text.pack(side=LEFT, fill=BOTH) scroll = ttk.Scrollbar(mainframe) scroll.pack(side=LEFT,fill=Y) # 设置滚动条与text组件关联 scroll['command'] = text.yview text.configure(yscrollcommand=scroll.set) # 创建菜单 def init_menu(self): '初始化菜单的方法' # 定义菜单条所包含的3个菜单 menus = ('文件', '编辑', '帮助') # 定义菜单数据 items = (OrderedDict([ # 每项对应一个菜单项,后面元组第一个元素是菜单图标, # 第二个元素是菜单对应的事件处理函数 ('新建', (self.master.filenew_icon, None)), ('打开', (self.master.fileopen_icon, None)), ('保存', (self.master.save_icon, None)), ('另存为...', (self.master.saveas_icon, None)), ('-1', (None, None)), ('退出', (self.master.signout_icon, None)), ]), OrderedDict([('撤销',(None, None)), ('重做',(None, None)), ('-1',(None, None)), ('剪切',(None, None)), ('复制',(None, None)), ('粘贴',(None, None)), ('删除',(None, None)), ('选择',(None, None)), ('-2',(None, None)), # 二级菜单 ('更多', OrderedDict([ ('显示数据',(None, None)), ('显示统计',(None, None)), ('显示图表',(None, None)) ])) ]), OrderedDict([('帮助主题',(None, None)), ('-1',(None, None)), ('关于', (None, None))])) # 使用Menu创建菜单条 menubar = Menu(self.master) # 为窗口配置菜单条,也就是添加菜单条 self.master['menu'] = menubar # 遍历menus元组 for i, m_title in enumerate(menus): # 创建菜单 m = Menu(menubar, tearoff=0) # 添加菜单 menubar.add_cascade(label=m_title, menu=m) # 将当前正在处理的菜单数据赋值给tm tm = items[i] # 遍历OrderedDict,默认只遍历它的key for label in tm: print(label) # 如果value又是OrderedDict,说明是二级菜单 if isinstance(tm[label], OrderedDict): # 创建子菜单、并添加子菜单 sm = Menu(m, tearoff=0) m.add_cascade(label=label, menu=sm) sub_dict = tm[label] # 再次遍历子菜单对应的OrderedDict,默认只遍历它的key for sub_label in sub_dict: if sub_label.startswith('-'): # 添加分隔条 sm.add_separator() else: # 添加菜单项 sm.add_command(label=sub_label,image=sub_dict[sub_label][0], command=sub_dict[sub_label][1], compound=LEFT) elif label.startswith('-'): # 添加分隔条 m.add_separator() else: # 添加菜单项 m.add_command(label=label,image=tm[label][0], command=tm[label][1], compound=LEFT) # 生成所有需要的图标 def init_icons(self): self.master.filenew_icon = PhotoImage(file='images/filenew.png') self.master.fileopen_icon = PhotoImage(file='images/fileopen.png') self.master.save_icon = PhotoImage(file='images/save.png') self.master.saveas_icon = PhotoImage(file='images/saveas.png') self.master.signout_icon = PhotoImage(file='images/signout.png') # 生成工具条 def init_toolbar(self): # 创建并添加一个Frame作为工具条的容器 toolframe = Frame(self.master, height=20, bg='lightgray') toolframe.pack(fill=X) # 该Frame容器放在窗口顶部 # 再次创建并添加一个Frame作为工具按钮的容器 frame = ttk.Frame(toolframe) frame.pack(side=LEFT) # 该Frame容器放在容器左边 # 遍历self.master的全部数据,根据系统图标来创建工具栏按钮 for i, e in enumerate(dir(self.master)): # 只处理属性名以_icon结尾的属性(这些属性都是图标) if e.endswith('_icon'): ttk.Button(frame, width=20, image=getattr(self.master, e), command=None).grid(row=0, column=i, padx=1, pady=1, sticky=E) root = Tk() root.title("菜单测试") # 禁止改变窗口大小 root.resizable(width=False, height=True) App(root) root.mainloop()该程序比较实用,整个程序界面中的菜单并不是写死的,而是根据程序第 42~70 行代码自动生成的,这些代码中的每个 OrderdDict 代表一个菜单,它的每个 key-value 对代表一个菜单项,其中 key 是菜单文本,value 是一个元组,元组的第一个元素是菜单图标,第二个元素是为菜单绑定的事件处理函数。
因此,当开发者需要改变程序界面中的菜单时,并不需要修改后面的代码,只需修改第 42~70 行代码即可。
由于代码功能的限制,上面的第42~70行代码只能支持二级菜单,并不支持三级菜单。实际上Tkinter 菜单完全支持三级,只是该程序没有做进一步处理。
上面程序也会自动生成工具条,只要为 self.master 添加了以 _icon 结尾的属性,程序就会自动把它们添加为工具条上的按钮。运行上面程序,可以看到如图 2 所示的效果。
图 2 菜单和工具条
Menu右键菜单
实现右键菜单很简单,程序只要先创建菜单,然后为目标组件的右键单击事件绑定处理函数,当用户单击鼠标右键时,调用菜单的 post() 方法即可在指定位置弹出右键菜单。如下程序示范了创建并添加右键菜单:
from tkinter import * # 导入ttk from tkinter import ttk from collections import OrderedDict class App: def __init__(self, master): self.master = master self.initWidgets() def initWidgets(self): self.text = Text(self.master, height=12, width=60, foreground='darkgray', font=('微软雅黑', 12), spacing2=8, # 设置行间距 spacing3=12) # 设置段间距 self.text.pack() st = 'C语言中文网成立于 2012 年初,' +\ '目前已经运营了将近 5 年,' +\ '我们致力于分享精品教程,帮助对编程感兴趣的读者\n' self.text.insert(END, st) # 为text组件的右键单击事件绑定处理方法 self.text.bind('<Button-3>',self.popup) # 创建Menu对象,准备作为右键菜单 self.popup_menu = Menu(self.master,tearoff = 0) self.my_items = (OrderedDict([('超大', 16), ('大',14), ('中',12), ('小',10), ('超小',8)]), OrderedDict([('红色','red'), ('绿色','green'), ('蓝色', 'blue')])) i = 0 for k in ['字体大小','颜色']: m = Menu(self.popup_menu, tearoff = 0) # 添加子菜单 self.popup_menu.add_cascade(label=k ,menu = m) # 遍历OrderedDict的key(默认就是遍历key) for im in self.my_items[i]: m.add_command(label=im, command=self.handlerAdaptor(self.choose, x=im)) i += 1 def popup(self, event): # 在指定位置显示菜单 self.popup_menu.post(event.x_root,event.y_root) #① def choose(self, x): # 如果用户选择修改字体大小的子菜单项 if x in self.my_items[0].keys(): # 改变字体大小 self.text['font'] = ('微软雅黑', self.my_items[0][x]) # 如果用户选择修改颜色的子菜单项 if x in self.my_items[1].keys(): # 改变颜色 self.text['foreground'] = self.my_items[1][x] def handlerAdaptor(self, fun,**kwds): return lambda fun=fun, kwds=kwds: fun(**kwds) root = Tk() root.title("右键菜单测试") App(root) root.mainloop()上面程序中,第 23-35 行代码用于创建一个 Menu,并为之添加菜单顷。这段代码与前面介绍的创建菜单、添加菜单项的代码并没有区别。
程序中 ① 号代码位于 Text 组件的右键单击事件的处理函数内,这行代码调用 Menu 对象的 post() 方法弹出右键菜单,这意味着当用户在 Text 组件内单击鼠标右键时,Text 组件就会弹出右键菜单。
运行该程序,在界面上的 Text 组件内单击鼠标右键,将可以看到如图 3 所示的右键菜单。
图 3 右键菜单
所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频