PySimpleGUI 菜单从 0 到专业导航:一个嵌套列表搞定所有菜单
【免费下载链接】PySimpleGUIPython GUIs for Humans! Create any GUI simple or complicated in a way that's intuitive. Launched in 2018. NEW for 2026 - the LGPL3 Version 6. Transforms tkinter, Qt, WxPython, and Remi into a simple, intuitive, and fun experience for both hobbyists and expert users.项目地址: https://gitcode.com/gh_mirrors/py/PySimpleGUI
做桌面程序绕不开一个问题:功能越来越多,用户靠什么找到它们?答案几乎总是——菜单。PySimpleGUI 的菜单系统把这件事做到了极简:不管是顶部菜单栏、右键弹出、按钮下拉还是换色定制,全部由同一种数据结构驱动。掌握这套 PySimpleGUI 菜单玩法,你的 Python GUI 导航就有了专业级的骨架。

一个典型的 PySimpleGUI 应用窗口:顶部导航、操作区、输出区各司其职
一个核心思想:菜单就是嵌套列表 🌲
整个 PySimpleGUI 菜单系统只有一个基本单元:嵌套列表。最外层是一个列表,每个元素是「顶栏标签 + 点击后展开的项目列表」的二元组;某个项目本身又是一个列表时,就向右弹出下一级子菜单——层级就是这么递归出来的。
menu_def = [ ['&报表', ['新建报表', '导入数据', '---', '&导出 Ctrl-E', '!打印']], ['&数据', ['查询', ['按客户', '按区域'], '刷新']], ['&帮助', ['关于::about_key']] ]这段代码定义了三个顶层入口,其中「数据」下藏着一个二级子菜单。列表里有 5 个记号值得刻进记忆:
| 记号 | 写法 | 作用 |
|---|---|---|
& | &导出 | 定义 Alt 快捷键(Alt+X),界面上以下划线提示,事件里不会带出这个符号 |
:: | 关于::about_key | 给菜单项起一个程序用的唯一 key,用户看不到它,事件循环里以它为准 |
--- | 单独一项 | 画一条分隔线,用来给相关命令分组 |
! | !打印 | 把该菜单项置灰禁用 |
Ctrl- | 导出 Ctrl-E | 纯文本提示快捷键(空格对齐),只是显示,不绑定真实按键 |
记住这张表,后面所有菜单类型的定义方式都不用再学。
从 0 搭出第一个菜单
三步走:定义 → 挂到布局 → 读事件。
第一步,定义就是上面那份menu_def,不用多说。
第二步,用sg.Menu把定义挂进 layout 的某一行,它就是窗口顶部的菜单栏:
layout = [ [sg.Menu(menu_def, tearoff=False, key='-BAR-')], [sg.Text('报表工作台主区域', size=(40, 1))], [sg.Output(size=(60, 10))] ] window = sg.Window('报表工作台', layout)第三步,菜单选择走的是和其他控件一样的事件通道——window.read()返回的event就是被点击项的文本(注意&已被剥离):
while True: event, values = window.read() if event in (sg.WIN_CLOSED, 'Quit'): break if event == '导出': sg.popup('导出任务已开始') elif event == 'about_key': # 带 :: 的项按 key 匹配 sg.popup('关于', '报表工作台 v1.0')到这一步,一个能点、能分组、能弹子菜单的三级导航就完整跑起来了。
四种菜单,各管一段导航
同一种列表结构,四个「宿主」,各占一块领地。按场景选:
| 类型 | 入口 API | 适合场景 |
|---|---|---|
| 顶部菜单栏 | sg.Menu(menu_def)放进 layout | 全局功能的主入口,用户第一眼找的地方 |
| 右键上下文菜单 | sg.Window(..., right_click_menu=...) | 与当前位置/对象相关的操作(复制、粘贴、属性) |
| 按钮下拉菜单 | sg.ButtonMenu(文本, 定义)放进 layout | 工具栏,把一组命令收进一个按钮 |
| 自定义菜单栏 | sg.MenubarCustom(定义)放进 layout | 要品牌配色、或配合自定义标题栏时使用 |
右键菜单:给sg.Window传一个right_click_menu即可,结构仍是老样子(第一个标签不会被显示,放占位字符串即可):
ctx_menu = ['未使用', ['复制', '粘贴', '剪切', '---', '属性']] window = sg.Window('报表工作台', layout, right_click_menu=ctx_menu)按钮菜单:ButtonMenu是一个会弹出下拉列表的按钮,key方便你之后单独改它:
layout = [ [sg.ButtonMenu('工具', [['撤销', '重做', '---', '设置']], key='-TOOL-', text_color='blue')] ]自定义菜单栏:sg.Menu的顶栏本身没法换色(只有下拉面板可以换),想要整条菜单栏的品牌色就上MenubarCustom,它本质是一排拼接起来的 ButtonMenu,颜色、字体、内边距全归你管:
layout = [ [sg.MenubarCustom(menu_def, pad=0, key='-CUSTBAR-', bar_background_color='#2c3e50', bar_text_color='white', background_color='#34495e', text_color='white')] ]PySimpleGUI 内置主题的视觉效果预览:换成 MenubarCustom 后,菜单风格就能跟着品牌走
让菜单活起来:动态更新与事件细节
菜单不是建完就定死的,运行中随时可以换。
整表替换。拿到 Menu 元素的 key 后调update,注意必须提交完整的菜单定义,不能只改其中一项:
# 用户登录某个受限角色后,把"查询"整个分支置灰 menu_def[1][1][0] = '!查询' window['-BAR-'].update(menu_def)改动会在下一次window.read()/refresh时生效。ButtonMenu的update同理,还能顺带改按钮文字、颜色、可见性。
事件细节,三个容易踩的约定:
- 用户点的是文本,事件值就是文本(无
&);给项加了::key后,事件值变成那个 key——两种匹配别混用。 !开头的项是禁用态,永远不会有事件回来,适合做「占位但未解锁」的功能。- 右键菜单的第一项(顶栏标签)只是占位,永远不会成为事件。
一个能直接跑的完整例子
把顶部菜单、右键菜单、按钮菜单和动态刷新放进同一个「报表工作台」,整段可运行:
import PySimpleGUI as sg menu_def = [ ['&报表', ['新建报表', '导出', '---', '退出::exit_key']], ['&数据', ['查询', ['按客户', '按区域'], '刷新::refresh']], ['&帮助', ['关于']] ] ctx_menu = ['未使用', ['复制', '粘贴', '剪切']] layout = [ [sg.Menu(menu_def, key='-BAR-')], [sg.Text('右键下方区域试试上下文菜单', size=(40, 1))], [sg.ButtonMenu('工具', [['撤销', '重做', '---', '设置']], key='-TOOL-')], [sg.Output(size=(60, 8))] ] window = sg.Window('报表工作台', layout, right_click_menu=ctx_menu) while True: event, values = window.read() if event in (sg.WIN_CLOSED, 'exit_key'): break if event == 'refresh': menu_def[1][1][0] = '!查询' # 刷新时临时禁用查询 window['-BAR-'].update(menu_def) else: print(f'菜单事件: {event}') window.close()运行后依次试一遍:点「报表 → 导出」,在输出区右键看上下文菜单,点「工具」按钮看工具栏下拉,再点「数据 → 刷新」看菜单被动态置灰——四种形态与动态更新,全在这 30 行里。
少走弯路:常见坑与上手建议
坑:菜单整个不出现→ 检查menu_def是不是「每个顶层项都是 [标签, 列表] 的二元组」。最常见的是把项目直接写进最外层列表。
坑:点了菜单没有反应→ 事件值和带&的原文对不上。匹配时用不带&的文本,或者干脆给项加::key再按 key 匹配,一劳永逸。
坑:想改菜单栏颜色,参数却没用→sg.Menu的颜色参数只作用于下拉面板,顶栏本身改不了。要换整条栏的配色,换成sg.MenubarCustom。
坑:update 之后界面无变化→ 一是漏了「提交完整定义」,二是改完没有经过下一次window.read()或refresh,界面不会自己刷新。
设计建议(来自大量真实应用的共识):
- 层级压到 3 层以内,越深越没人点;
- 用
---按功能分组,一组一隔; - 高频项给快捷键,
&配 Alt,Ctrl-文本做提示; - 排序遵循「常用的靠前、相关的挨着」。
仓库里还有三份现成参考,直接打开就能读:
- 四种菜单形态的完整演示:Demo_Menus.py
- 自定义菜单栏(含配色与 tearoff):Demo_Menubar_Custom.py
- 菜单 + 工具栏组合:Demo_Menu_With_Toolbar.py
下次再给程序加功能时,先别急着堆按钮:拿一份嵌套列表,把新功能塞进对应分支,导航自己会生长。
【免费下载链接】PySimpleGUIPython GUIs for Humans! Create any GUI simple or complicated in a way that's intuitive. Launched in 2018. NEW for 2026 - the LGPL3 Version 6. Transforms tkinter, Qt, WxPython, and Remi into a simple, intuitive, and fun experience for both hobbyists and expert users.项目地址: https://gitcode.com/gh_mirrors/py/PySimpleGUI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考