菜单和工具栏
最后修改于 2023 年 1 月 10 日
GUI 应用程序中常见的部分是菜单栏。 菜单栏由称为菜单的对象组成。 顶级菜单在其菜单栏上显示标签。 菜单有菜单项。 菜单项是执行应用程序内部特定操作的命令。 菜单也可以有子菜单,它们有自己的菜单项。 以下三个类用于在 wxPython 中创建菜单栏:wx.MenuBar
、wx.Menu
和 wx.MenuItem
。
简单菜单
在我们的第一个例子中,我们将创建一个带有文件菜单的菜单栏。 菜单将只有一个菜单项。 通过选择该项目,应用程序退出。
#!/usr/bin/env python """ ZetCode wxPython tutorial This example shows a simple menu. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): menubar = wx.MenuBar() fileMenu = wx.Menu() fileItem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application') menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar) self.Bind(wx.EVT_MENU, self.OnQuit, fileItem) self.SetSize((300, 200)) self.SetTitle('Simple menu') self.Centre() def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
这是一个带有最少菜单栏功能的小例子。
menubar = wx.MenuBar()
首先我们创建一个菜单栏对象。
fileMenu = wx.Menu()
接下来我们创建一个菜单对象。
fileItem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application')
我们将一个菜单项附加到菜单对象中。 第一个参数是菜单项的 ID。 标准 ID 将自动添加一个图标和一个快捷方式,在本例中为 Ctrl+Q。 第二个参数是菜单项的名称。 最后一个参数定义当选择菜单项时,在状态栏上显示的短帮助字符串。 在这里,我们没有明确地创建 wx.MenuItem
。 它是在后台由 Append()
方法创建的。 该方法返回创建的菜单项。 此引用将在稍后用于绑定事件。
self.Bind(wx.EVT_MENU, self.OnQuit, fileItem)
我们将菜单项的 wx.EVT_MENU
绑定到自定义 OnQuit()
方法。 此方法将关闭应用程序。
menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar)
之后,我们将一个菜单附加到菜单栏中。 & 字符创建加速键。 在 & 之后出现的字符被下划线。 通过这种方式,可以通过 Alt+F 快捷方式访问菜单。 最后,我们调用 SetMenuBar()
方法。 此方法属于 wx.Frame
控件。 它设置菜单栏。

图标和快捷方式
下一个例子与前一个例子基本相同。 这次,我们手动创建 wx.MenuItem
。
#!/usr/bin/env python """ ZetCode wxPython tutorial In this example, we manually create a menu item. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx APP_EXIT = 1 class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): menubar = wx.MenuBar() fileMenu = wx.Menu() qmi = wx.MenuItem(fileMenu, APP_EXIT, '&Quit\tCtrl+Q') qmi.SetBitmap(wx.Bitmap('exit.png')) fileMenu.Append(qmi) self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT) menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar) self.SetSize((350, 250)) self.SetTitle('Icons and shortcuts') self.Centre() def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在此示例中,我们创建一个退出菜单项。 我们为菜单项选择一个自定义图标和快捷方式。
qmi = wx.MenuItem(fileMenu, APP_EXIT, '&Quit\tCtrl+Q') qmi.SetBitmap(wx.Bitmap('exit.png')) fileMenu.Append(qmi)
我们创建一个 wx.MenuItem
对象。 & 字符指定一个加速键。 在 & 之后的字符被下划线。 实际的快捷方式由字符组合定义。 我们已经指定了 Ctrl+Q 字符。 因此,如果我们按下 Ctrl+Q,我们将关闭应用程序。 我们在 & 字符和快捷方式之间放置一个制表符。 通过这种方式,我们设法在它们之间留出一些空间。 要为菜单项提供图标,我们调用 SetBitmap()
方法。 通过调用 AppendItem()
方法,将手动创建的菜单项附加到菜单中。
self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT)
当我们选择创建的菜单项时,将调用 OnQuit()
方法。

子菜单和分隔符
每个菜单也可以有一个子菜单。 这样我们可以将类似的命令放入组中。 例如,我们可以将隐藏/显示各种工具栏(例如个人栏、地址栏、状态栏或导航栏)的命令放入一个名为工具栏的子菜单中。 在菜单中,我们可以用分隔符分隔命令。 它是一条简单的线。 通常的做法是用单个分隔符分隔诸如“新建”、“打开”、“保存”之类的命令与诸如“打印”、“打印预览”之类的命令。 在我们的例子中,我们将看到如何创建子菜单和菜单分隔符。
#!/usr/bin/env python """ ZetCode wxPython tutorial In this example, we create a submenu and a menu separator. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): menubar = wx.MenuBar() fileMenu = wx.Menu() fileMenu.Append(wx.ID_NEW, '&New') fileMenu.Append(wx.ID_OPEN, '&Open') fileMenu.Append(wx.ID_SAVE, '&Save') fileMenu.AppendSeparator() imp = wx.Menu() imp.Append(wx.ID_ANY, 'Import newsfeed list...') imp.Append(wx.ID_ANY, 'Import bookmarks...') imp.Append(wx.ID_ANY, 'Import mail...') fileMenu.AppendMenu(wx.ID_ANY, 'I&mport', imp) qmi = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W') fileMenu.AppendItem(qmi) self.Bind(wx.EVT_MENU, self.OnQuit, qmi) menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar) self.SetSize((350, 250)) self.SetTitle('Submenu') self.Centre() def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在上面的例子中,我们创建了“新建”、“打开”和“保存”标准菜单项。 这些用水平分隔符与子菜单分开。 一个子菜单有另外三个菜单项。
fileMenu.Append(wx.ID_NEW, '&New') fileMenu.Append(wx.ID_OPEN, '&Open') fileMenu.Append(wx.ID_SAVE, '&Save')
这里我们有三个常见的菜单项:“新建”、“打开”和“保存”。
fileMenu.AppendSeparator()
菜单分隔符使用 AppendSeparator()
方法附加。
imp = wx.Menu() imp.Append(wx.ID_ANY, 'Import newsfeed list...') imp.Append(wx.ID_ANY, 'Import bookmarks...') imp.Append(wx.ID_ANY, 'Import mail...') fileMenu.AppendMenu(wx.ID_ANY, 'I&mport', imp)
子菜单也是一个 wx.Menu
。 三个菜单项被附加到菜单中。 子菜单通过 AppenMenu()
方法附加到文件菜单中。

复选菜单项
有三种菜单项。
- 普通项
- 复选框项
- 单选框项
在下面的例子中,我们将演示复选菜单项。 复选菜单项在菜单中由一个勾号表示。
#!/usr/bin/env python """ ZetCode wxPython tutorial This example creates a checked menu item. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): menubar = wx.MenuBar() viewMenu = wx.Menu() self.shst = viewMenu.Append(wx.ID_ANY, 'Show statusbar', 'Show Statusbar', kind=wx.ITEM_CHECK) self.shtl = viewMenu.Append(wx.ID_ANY, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK) viewMenu.Check(self.shst.GetId(), True) viewMenu.Check(self.shtl.GetId(), True) self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.shst) self.Bind(wx.EVT_MENU, self.ToggleToolBar, self.shtl) menubar.Append(viewMenu, '&View') self.SetMenuBar(menubar) self.toolbar = self.CreateToolBar() self.toolbar.AddTool(1, '', wx.Bitmap('texit.png')) self.toolbar.Realize() self.statusbar = self.CreateStatusBar() self.statusbar.SetStatusText('Ready') self.SetSize((450, 350)) self.SetTitle('Check menu item') self.Centre() def ToggleStatusBar(self, e): if self.shst.IsChecked(): self.statusbar.Show() else: self.statusbar.Hide() def ToggleToolBar(self, e): if self.shtl.IsChecked(): self.toolbar.Show() else: self.toolbar.Hide() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
我们有一个查看菜单,其中有两个复选菜单项。 这两个菜单项将显示和隐藏状态栏和工具栏。
self.shst = viewMenu.Append(wx.ID_ANY, 'Show statusbar', 'Show Statusbar', kind=wx.ITEM_CHECK) self.shtl = viewMenu.Append(wx.ID_ANY, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)
如果我们想附加一个复选菜单项,我们将 kind
参数设置为 wx.ITEM_CHECK
。 默认参数是 wx.ITEM_NORMAL
。 Append()
方法返回一个 wx.MenuItem
。
viewMenu.Check(self.shst.GetId(), True) viewMenu.Check(self.shtl.GetId(), True)
当应用程序启动时,状态栏和工具栏都可见。 因此,我们使用 Check()
方法选中这两个菜单项。
def ToggleStatusBar(self, e): if self.shst.IsChecked(): self.statusbar.Show() else: self.statusbar.Hide()
我们根据复选菜单项的状态显示或隐藏状态栏。 我们使用 IsChecked()
方法找出复选菜单项的状态。 同样适用于工具栏。

上下文菜单
一个*上下文菜单*是一组在某些上下文下出现的命令。 例如,在 Firefox Web 浏览器中,当我们右键单击一个网页时,我们会得到一个上下文菜单。 在这里,我们可以重新加载页面、返回或查看页面源代码。 如果我们右键单击一个工具栏,我们将获得另一个用于管理工具栏的上下文菜单。 上下文菜单有时称为弹出菜单。
#!/usr/bin/env python """ ZetCode wxPython tutorial In this example, we create a context menu. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class MyPopupMenu(wx.Menu): def __init__(self, parent): super(MyPopupMenu, self).__init__() self.parent = parent mmi = wx.MenuItem(self, wx.NewId(), 'Minimize') self.Append(mmi) self.Bind(wx.EVT_MENU, self.OnMinimize, mmi) cmi = wx.MenuItem(self, wx.NewId(), 'Close') self.Append(cmi) self.Bind(wx.EVT_MENU, self.OnClose, cmi) def OnMinimize(self, e): self.parent.Iconize() def OnClose(self, e): self.parent.Close() class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.SetSize((350, 250)) self.SetTitle('Context menu') self.Centre() def OnRightDown(self, e): self.PopupMenu(MyPopupMenu(self), e.GetPosition()) def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在示例中,我们为主窗口创建一个上下文菜单。 它有两个项目。 一个将最小化应用程序,另一个将终止它。
class MyPopupMenu(wx.Menu): def __init__(self, parent): super(MyPopupMenu, self).__init__()
我们创建一个单独的 wx.Menu
类。
mmi = wx.MenuItem(self, wx.NewId(), 'Minimize') self.Append(mmi) self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)
创建一个菜单项并将其附加到上下文菜单。 将事件处理程序绑定到此菜单项。
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
如果我们右键单击该框架,我们会调用 OnRightDown()
方法。 为此,我们使用 wx.EVT_RIGHT_DOWN
事件绑定器。
def OnRightDown(self, e): self.PopupMenu(MyPopupMenu(self), e.GetPosition())
在 OnRightDown()
方法中,我们调用 PopupMenu()
方法。 此方法显示上下文菜单。 第一个参数是要显示的菜单。 第二个参数是上下文菜单出现的位置。 上下文菜单出现在鼠标光标的点上。 要获取实际的鼠标位置,我们调用所提供的事件对象的 GetPosition()
方法。

工具栏
菜单将我们在应用程序中可以使用的所有命令分组。工具栏提供对最常用命令的快速访问。
要创建工具栏,我们调用框架控件的 CreateToolBar()
方法。
#!/usr/bin/env python """ ZetCode wxPython tutorial This example creates a simple toolbar. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): toolbar = self.CreateToolBar() qtool = toolbar.AddTool(wx.ID_ANY, 'Quit', wx.Bitmap('texit.png')) toolbar.Realize() self.Bind(wx.EVT_TOOL, self.OnQuit, qtool) self.SetSize((350, 250)) self.SetTitle('Simple toolbar') self.Centre() def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在我们的示例中,我们有一个带有一个工具的工具栏。 当我们单击该工具时,该工具将关闭应用程序。
toolbar = self.CreateToolBar()
我们创建一个工具栏。 默认情况下,工具栏是水平的,没有边框,并且显示图标。
qtool = toolbar.AddTool(wx.ID_ANY, 'Quit', wx.Bitmap('texit.png'))
要创建工具栏工具,我们调用 AddTool()
方法。 第二个参数是该工具的标签,第三个是该工具的图像。 请注意,标签不可见,因为默认样式仅显示图标。
toolbar.Realize()
在我们将项目放入工具栏后,我们调用 Realize()
方法。 在 Linux 上调用此方法不是强制性的。 在 Windows 上是。
self.Bind(wx.EVT_TOOL, self.OnQuit, qtool)
要处理工具栏事件,我们使用 wx.EVT_TOOL
事件绑定器。

如果我们想创建多个工具栏,我们必须以不同的方式完成。
#!/usr/bin/env python ''' ZetCode wxPython tutorial In this example, we create two horizontal toolbars. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 ''' import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): vbox = wx.BoxSizer(wx.VERTICAL) toolbar1 = wx.ToolBar(self) toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('tnew.png')) toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('topen.png')) toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('tsave.png')) toolbar1.Realize() toolbar2 = wx.ToolBar(self) qtool = toolbar2.AddTool(wx.ID_EXIT, '', wx.Bitmap('texit.png')) toolbar2.Realize() vbox.Add(toolbar1, 0, wx.EXPAND) vbox.Add(toolbar2, 0, wx.EXPAND) self.Bind(wx.EVT_TOOL, self.OnQuit, qtool) self.SetSizer(vbox) self.SetSize((350, 250)) self.SetTitle('Toolbars') self.Centre() def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在上面的例子中,我们创建了两个水平工具栏。
toolbar1 = wx.ToolBar(self) ... toolbar2 = wx.ToolBar(self)
我们创建了两个工具栏对象。 并将它们放入一个垂直框中。

启用 & 禁用
在下面的例子中,我们展示了如何启用和禁用工具栏按钮。 我们还添加了一条分隔线。
#!/usr/bin/env python """ ZetCode wxPython tutorial In this example, we create two horizontal toolbars. author: Jan Bodnar website: www.zetcode.com last modified: July 2020 """ import wx class Example(wx.Frame): def __init__(self, *args, **kwargs): super(Example, self).__init__(*args, **kwargs) self.InitUI() def InitUI(self): self.count = 5 self.toolbar = self.CreateToolBar() tundo = self.toolbar.AddTool(wx.ID_UNDO, '', wx.Bitmap('tundo.png')) tredo = self.toolbar.AddTool(wx.ID_REDO, '', wx.Bitmap('tredo.png')) self.toolbar.EnableTool(wx.ID_REDO, False) self.toolbar.AddSeparator() texit = self.toolbar.AddTool(wx.ID_EXIT, '', wx.Bitmap('texit.png')) self.toolbar.Realize() self.Bind(wx.EVT_TOOL, self.OnQuit, texit) self.Bind(wx.EVT_TOOL, self.OnUndo, tundo) self.Bind(wx.EVT_TOOL, self.OnRedo, tredo) self.SetSize((350, 250)) self.SetTitle('Undo redo') self.Centre() def OnUndo(self, e): if self.count > 1 and self.count <= 5: self.count = self.count - 1 if self.count == 1: self.toolbar.EnableTool(wx.ID_UNDO, False) if self.count == 4: self.toolbar.EnableTool(wx.ID_REDO, True) def OnRedo(self, e): if self.count < 5 and self.count >= 1: self.count = self.count + 1 if self.count == 5: self.toolbar.EnableTool(wx.ID_REDO, False) if self.count == 2: self.toolbar.EnableTool(wx.ID_UNDO, True) def OnQuit(self, e): self.Close() def main(): app = wx.App() ex = Example(None) ex.Show() app.MainLoop() if __name__ == '__main__': main()
在我们的示例中,我们有三个工具栏按钮。 一个按钮用于退出应用程序。 另外两个按钮是撤消和重做按钮。 它们模拟应用程序中的撤消/重做功能。(有关实际示例,请参阅提示和技巧)我们有 4 个更改。 撤消和重做按钮将相应地被禁用。
self.toolbar.EnableTool(wx.ID_REDO, False) self.toolbar.AddSeparator()
开始时,重做按钮被禁用。 我们通过调用 EnableTool()
方法来做到这一点。 我们可以在工具栏中创建一些逻辑组。 我们可以通过一条小的垂直线分隔各种按钮组。 为此,我们调用 AddSeparator()
方法。
def OnUndo(self, e): if self.count > 1 and self.count <= 5: self.count = self.count - 1 if self.count == 1: self.toolbar.EnableTool(wx.ID_UNDO, False) if self.count == 4: self.toolbar.EnableTool(wx.ID_REDO, True)
我们模拟撤消和重做功能。 我们有四个更改。 如果没有什么可撤消的,撤消按钮将被禁用。 撤消第一个更改后,我们启用重做按钮。 同样的逻辑也适用于 OnRedo()
方法。

在本部分的 wxPython 教程中,我们使用了菜单和工具栏。