总目录:wxPython 教程目录 本节内容:wxPython 菜单 工具栏 本节译自:zetcode 上一篇:wxpython 教程(二): 开始编写 下一篇:wxpython 教程 (四): 布局管理
wxPython 菜单 是 GUI 应用中的通用部件。菜单栏由多项菜单组成,顶级菜单在菜单栏上显示标签。菜单包含菜单项,菜单项在应用中执行特定的命令。菜单也可以包含子菜单,子菜单自身又包含菜单项。创建菜单栏的类有下面三个:wx.MenuBar,wx.Menu和wx.MenuItem。
简单例子
在我们的第一个例子中,我们将创建一个菜单栏,其包含“File”菜单,该菜单只包含一个菜单项。选择这一菜单项,应用会退出。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By achen 2017/0 === ZetCode wxPython tutorial This example shows a simple menu. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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() fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application') menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar) self.Bind(wx.EVT_MENU, self.OnQuit, fitem) self.SetSize((300, 200)) self.SetTitle('Simple menu') self.Centre() self.Show(True) def OnQuit(self, e): self.Close() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
上面是一个最小功能的菜单栏的例子。我们挑重点行来看一下:
menubar = wx.MenuBar()
首先我们新建了一个 menubar 对象,
fileMenu = wx.Menu()
然后我们新建一个menu对象,
fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application')
我们将一个菜单项添加到menu对象中。第一个参数表明菜单项的ID。使用标准ID可以协助不同的操作系统更好的识别该菜单的功能,并自动为其添加对应的图标和快捷键(本例中为Ctrl+Q)。第二个参数为该菜单项的名称。最后一个参数定义了当选中菜单时应用状态栏所显示的帮助文字。这里我们没有显式的创建wx.MenuItem,而是通过Append()函数来添加它,这个方法会返回一个创建好的菜单项,其从属关系已经建立。保留这一参数可留作后面绑定事件用。
self.Bind(wx.EVT_MENU, self.OnQuit, fitem)
我们绑定了菜单项的wx.EVT_MENU事件到自定义的OnQuit()函数,这一函数将关闭程序。wx.EVT_MENU即选择菜单项事件。
menubar.Append(fileMenu, 'File') self.SetMenuBar(menubar)
之后,我们将菜单加入到菜单栏中,&符号创建了一个加速键(accelerator key),在其之后的一个字母会带有下划线,使用Alt+F快捷键后,可通过该字母快速选择菜单项。最后,我们调用了SetMenuBar()方法,这一方法属于wx.Frame,它为Frame设定菜单栏。
图:一个简单的菜单样例
图标和快捷键
下一个例子跟上一个例子本质上是一样的,但这次我们手动创建一个菜单项,即wx.MenuItem。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 ==== ZetCode wxPython tutorial In this example, we manually create a menu item. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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, '&QuittCtrl+Q') qmi.SetBitmap(wx.Bitmap('exit.png')) fileMenu.AppendItem(qmi) self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT) menubar.Append(fileMenu, '&File') self.SetMenuBar(menubar) self.SetSize((250, 200)) self.SetTitle('Icons and shortcuts') self.Centre() self.Show(True) def OnQuit(self, e): self.Close() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在这个例子中,我们新建了一个退出菜单项,选择了自定义的图标和快捷键。
qmi = wx.MenuItem(fileMenu, APP_EXIT, 'QuittCtrl+Q') qmi.SetBitmap(wx.Bitmap('exit.png')) fileMenu.AppendItem(qmi)
我们创建了一个wx.MenuItem对象。‘&’符号申明了加速键,它后面的一个字母会有下划线标识。真正的快捷键由’t’后面的字母组合定义。我们定义了Ctrl+Q,如果我们按下这一快捷键,应用就会退出。使用SetBitmap()函数,我们为菜单项提供图标。AppendItem则将菜单项添加到菜单中去。
self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT)
当我们选择刚才创建的菜单项时,OnQuit()函数会被调用。这里通过id来绑定,也可以通过菜单项对象来绑定,像上一个例子那样。
图:图标和快捷键
子菜单和分隔符
每个菜单可以包含子菜单,这样就可以把相似的命令放到同一组中。比如我们可以把显示或隐藏个人信息栏、地址栏、状态栏或者导航栏的功能放到一个子菜单中去。在菜单中,我们可以通过分隔符(separator)来分割不同的命令,其实就是简单的一条线。比较常用的是将新建、打开、保存等命令和打印、打印预览通过分割线分开。在下面的例子中,我们将学习如何创建子菜单和菜单分隔符。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial In this example, we create a submenu and a menu separator. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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, 'Import', imp) qmi = wx.MenuItem(fileMenu, wx.ID_EXIT, '&QuittCtrl+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() self.Show(True) def OnQuit(self, e): self.Close() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在上面的例子中,我们创建了New、Open和Save三个标准菜单项,并使用水平分割线将他们与其他区分。另外,我们还创建了一个包含三个菜单项的子菜单。
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对象,三个菜单项被添加到该菜单对象。子菜单随后通过AppendMenu()方法被添加到file菜单中。
图:子菜单例子
Check菜单项
菜单项有三种:普通菜单项、check菜单项、radio菜单项。
在下面的例子中,我们将展示check菜单项的使用。check菜单项在菜单中会呈现一个标记符。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial This example creates a checked menu item. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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() viewMenu = wx.Menu() self.shst = viewMenu.Append(wx.ID_ANY, 'Show statubar', '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(fileMenu, '&File') menubar.Append(viewMenu, '&View') self.SetMenuBar(menubar) self.toolbar = self.CreateToolBar() self.toolbar.AddLabelTool(1, '', wx.Bitmap('texit.png')) self.toolbar.Realize() self.statusbar = self.CreateStatusBar() self.statusbar.SetStatusText('Ready') self.SetSize((350, 250)) self.SetTitle('Check menu item') self.Centre() self.Show(True) 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(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
我们新建了一个view菜单,它包括两个check菜单项,这两个菜单项可以控制显示或隐藏状态栏和工具栏。
self.shst = viewMenu.Append(wx.ID_ANY, 'Show statubar', 'Show Statusbar', kind=wx.ITEM_CHECK) self.shtl = viewMenu.Append(wx.ID_ANY, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)
如果我们想添加一个check菜单项,我们可以设定kind参数为wx.ITEM_CHECK,该参数默认为wx.ITEM_NORMAL。Append()方法返回一个wx.MenuItem。
viewMenu.Check(self.shst.GetId(), True) viewMenu.Check(self.shtl.GetId(), True)
当应用开始的时候,状态栏和工具栏都应可见。所以我们使用Check()方法勾选check菜单项。
def ToggleStatusBar(self, e): if self.shst.IsChecked(): self.statusbar.Show() else: self.statusbar.Hide()
我们通过check菜单项的状态来显示或隐藏状态栏,可以通过ISChecked()函数来获取check菜单项的状态。工具栏同理。
图:check菜单项
上下文菜单
上下文菜单(context menu)即在特定上下文中出现的命令列表。比如在Firefox浏览器中,当我们在一个web页面中点击右键时,就会得到一个上下文菜单,包含刷新、后退或查看页面文件等命令。当我们在菜单栏右键时,我们又会得到另一个上下文菜单,它提供管理工具栏的各种命令。上下文菜单有时也叫做弹出菜单。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial In this example, we create a context menu. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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.AppendItem(mmi) self.Bind(wx.EVT_MENU, self.OnMinimize, mmi) cmi = wx.MenuItem(self, wx.NewId(), 'Close') self.AppendItem(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((250, 200)) self.SetTitle('Context menu') self.Centre() self.Show(True) def OnRightDown(self, e): self.PopupMenu(MyPopupMenu(self), e.GetPosition()) def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在这个例子中,我们为主窗口新建了上下文菜单,他有两个菜单项,一个用来最小化应用,另一个结束应用。
class MyPopupMenu(wx.Menu): def __init__(self, parent): super(MyPopupMenu, self).__init__()
我们创建了一个单独的类叫做MyPopupMenu,它继承自wx.Menu。
mmi = wx.MenuItem(self, wx.NewId(), 'Minimize') self.AppendItem(mmi) self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)
上面代码新建了菜单项、添加到上下文菜单并绑定了事件处理函数。
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
如果我们在frame中点击右键,将调用OnRightDown()方法,这是通过绑定wx.EVT_RIGHT_DOWN事件来绑定的。
def OnRightDown(self, e): self.PopupMenu(MyPopupMenu(self), e.GetPosition())
在OnRightDown()函数中,我们调用了PopupMenu()方法,这个方法来自于wx.Frame。第一个参数是要显示的菜单,第二个参数为显示的位置。为了让上下文菜单显示在鼠标光标处,我们需要得到鼠标位置。事件对象的GetPosition()方法可以得到这一信息。
图:上下文菜单
工具栏
菜单将所有命令整合在一起,而工具栏可以为常用命令提供更方面的入口。我们可以使用frame部件的CreateToolBar()函数来新建工具栏。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial This example creates a simple toolbar. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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.AddLabelTool(wx.ID_ANY, 'Quit', wx.Bitmap('texit.png')) toolbar.Realize() self.Bind(wx.EVT_TOOL, self.OnQuit, qtool) self.SetSize((250, 200)) self.SetTitle('Simple toolbar') self.Centre() self.Show(True) def OnQuit(self, e): self.Close() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在例子中,我们新建了包含一个工具的工具栏。当我们点击这一工具时,程序将退出 。
toolbar = self.CreateToolBar()
我们新建了toolbar。默认情况下,工具栏是水平、无边框且显示图标的。
qtool = toolbar.AddLabelTool(wx.ID_ANY, 'Quit', wx.Bitmap('texit.png'))
我们调用AddLabelTool()方法来新建工具栏的工具。第一个参数为ID,第二个参数为工具的标签,第三个为工具的图标。需要注意的是,标签默认情况下不会显示,仅会显示图标。
toolbar.Realize()
在我们把工具项放到工具栏之后,我们调用Realize()方法。在Linux中,该方法的调用不是必须的,但在windows却是必须的。
self.Bind(wx.EVT_TOOL, self.OnQuit, qtool)
为了处理工具栏事件,我们使用wx.EVT_TOOL事件绑定器。
图:简单的工具栏
如果我们想创建多个工具栏,必须用不一样的方式:
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial In this example, we create two horizontal toolbars. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('tnew.png')) toolbar1.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('topen.png')) toolbar1.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('tsave.png')) toolbar1.Realize() toolbar2 = wx.ToolBar(self) qtool = toolbar2.AddLabelTool(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((300, 250)) self.SetTitle('Toolbars') self.Centre() self.Show(True) def OnQuit(self, e): self.Close() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在上面的例子中,我们新建了两个水平的工具栏
toolbar1 = wx.ToolBar(self) ... toolbar2 = wx.ToolBar(self)
我们新建了两个工具栏对象,并将他们放到了一个竖直box中。关于sizer我们将在后面详细讨论。
图:多个工具栏
开启、禁用
在下面的例子中,我们将展示如何启用、禁用工具栏的按钮。同时,我们还将展示如何添加分割线。
#!/usr/bin/python # -*- coding: utf-8 -*- ''' Translated By Achen 2017/9 === ZetCode wxPython tutorial In this example, we create two horizontal toolbars. author: Jan Bodnar website: www.zetcode.com last modified: September 2011 ''' 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.AddLabelTool(wx.ID_UNDO, '', wx.Bitmap('tundo.png')) tredo = self.toolbar.AddLabelTool(wx.ID_REDO, '', wx.Bitmap('tredo.png')) self.toolbar.EnableTool(wx.ID_REDO, False) self.toolbar.AddSeparator() texit = self.toolbar.AddLabelTool(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((250, 200)) self.SetTitle('Undo redo') self.Centre() self.Show(True) 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(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在这个例子中,我们有三个工具栏按钮。一个按钮用来退出应用,其余两个按钮的功能为撤销和反撤销,在程序中模拟撤销、反撤销的功能。模拟了4次改变,撤销和反撤销功能也需要对应的启用或禁用。
self.toolbar.EnableTool(wx.ID_REDO, False) self.toolbar.AddSeparator()
在最开始,撤销按钮是禁用的。我们调用EnableTool()函数,并传递False参数来实现。调用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()中。
图:撤销、反撤销
在本节教程中,我们学习了菜单和工具栏。
本章的内容“图标和快捷键”,第36行代码‘ qmi = wx.MenuItem(fileMenu, APP_EXIT, ‘&QuittCtrl+Q’)’应无法实现图中效果。
参考原文https://zetcode.com/wxpython/menustoolbars/,如想实现图中效果应为‘ qmi = wx.MenuItem(fileMenu, APP_EXIT, ‘&Quit\tCtrl+Q’)’。