wxPython 菜单 是 GUI 应用中的通用部件。菜单栏由多项菜单组成,顶级菜单在菜单栏上显示标签。菜单包含菜单项,菜单项在应用中执行特定的命令。菜单也可以包含子菜单,子菜单自身又包含菜单项。创建菜单栏的类有下面三个:wx.MenuBar,wx.Menu和wx.MenuItem。
简单例子
在我们的第一个例子中,我们将创建一个菜单栏,其包含“File”菜单,该菜单只包含一个菜单项。选择这一菜单项,应用会退出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
上面是一个最小功能的菜单栏的例子。我们挑重点行来看一下:
1
|
menubar=wx.MenuBar()
|
首先我们新建了一个 menubar 对象,
1
|
fileMenu=wx.Menu()
|
然后我们新建一个menu对象,
1
|
fitem=fileMenu.Append(wx.ID_EXIT,'Quit','Quit application')
|
我们将一个菜单项添加到menu对象中。第一个参数表明菜单项的ID。使用标准ID可以协助不同的操作系统更好的识别该菜单的功能,并自动为其添加对应的图标和快捷键(本例中为Ctrl+Q)。第二个参数为该菜单项的名称。最后一个参数定义了当选中菜单时应用状态栏所显示的帮助文字。这里我们没有显式的创建wx.MenuItem,而是通过Append()函数来添加它,这个方法会返回一个创建好的菜单项,其从属关系已经建立。保留这一参数可留作后面绑定事件用。
1
|
self.Bind(wx.EVT_MENU,self.OnQuit, fitem)
|
我们绑定了菜单项的wx.EVT_MENU事件到自定义的OnQuit()函数,这一函数将关闭程序。wx.EVT_MENU即选择菜单项事件。
1
2
|
menubar.Append(fileMenu,'File')
self.SetMenuBar(menubar)
|
之后,我们将菜单加入到菜单栏中,&符号创建了一个加速键(accelerator key),在其之后的一个字母会带有下划线,使用Alt+F快捷键后,可通过该字母快速选择菜单项。最后,我们调用了SetMenuBar()方法,这一方法属于wx.Frame,它为Frame设定菜单栏。
图:一个简单的菜单样例
图标和快捷键
下一个例子跟上一个例子本质上是一样的,但这次我们手动创建一个菜单项,即wx.MenuItem。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
#!/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
'''
importwx
APP_EXIT=1
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在这个例子中,我们新建了一个退出菜单项,选择了自定义的图标和快捷键。
1
2
3
|
qmi=wx.MenuItem(fileMenu, APP_EXIT,'QuittCtrl+Q')
qmi.SetBitmap(wx.Bitmap('exit.png'))
fileMenu.AppendItem(qmi)
|
我们创建了一个wx.MenuItem对象。‘&’符号申明了加速键,它后面的一个字母会有下划线标识。真正的快捷键由’t’后面的字母组合定义。我们定义了Ctrl+Q,如果我们按下这一快捷键,应用就会退出。使用SetBitmap()函数,我们为菜单项提供图标。AppendItem则将菜单项添加到菜单中去。
1
|
self.Bind(wx.EVT_MENU,self.OnQuit,id=APP_EXIT)
|
当我们选择刚才创建的菜单项时,OnQuit()函数会被调用。这里通过id来绑定,也可以通过菜单项对象来绑定,像上一个例子那样。
图:图标和快捷键
子菜单和分隔符
每个菜单可以包含子菜单,这样就可以把相似的命令放到同一组中。比如我们可以把显示或隐藏个人信息栏、地址栏、状态栏或者导航栏的功能放到一个子菜单中去。在菜单中,我们可以通过分隔符(separator)来分割不同的命令,其实就是简单的一条线。比较常用的是将新建、打开、保存等命令和打印、打印预览通过分割线分开。在下面的例子中,我们将学习如何创建子菜单和菜单分隔符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在上面的例子中,我们创建了New、Open和Save三个标准菜单项,并使用水平分割线将他们与其他区分。另外,我们还创建了一个包含三个菜单项的子菜单。
1
2
3
|
fileMenu.Append(wx.ID_NEW,'&New')
fileMenu.Append(wx.ID_OPEN,'&Open')
fileMenu.Append(wx.ID_SAVE,'&Save')
|
通过上面三行,我们得到通用的菜单项:新建、打开和保存。
1
|
fileMenu.AppendSeparator()
|
AppendSeparator()函数添加了分隔符。
1
2
3
4
5
6
|
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菜单项在菜单中会呈现一个标记符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defToggleStatusBar(self, e):
ifself.shst.IsChecked():
self.statusbar.Show()
else:
self.statusbar.Hide()
defToggleToolBar(self, e):
ifself.shtl.IsChecked():
self.toolbar.Show()
else:
self.toolbar.Hide()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
我们新建了一个view菜单,它包括两个check菜单项,这两个菜单项可以控制显示或隐藏状态栏和工具栏。
1
2
3
4
|
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。
1
2
|
viewMenu.Check(self.shst.GetId(),True)
viewMenu.Check(self.shtl.GetId(),True)
|
当应用开始的时候,状态栏和工具栏都应可见。所以我们使用Check()方法勾选check菜单项。
1
2
3
4
5
6
|
defToggleStatusBar(self, e):
ifself.shst.IsChecked():
self.statusbar.Show()
else:
self.statusbar.Hide()
|
我们通过check菜单项的状态来显示或隐藏状态栏,可以通过ISChecked()函数来获取check菜单项的状态。工具栏同理。
图:check菜单项
上下文菜单
上下文菜单(context menu)即在特定上下文中出现的命令列表。比如在Firefox浏览器中,当我们在一个web页面中点击右键时,就会得到一个上下文菜单,包含刷新、后退或查看页面文件等命令。当我们在菜单栏右键时,我们又会得到另一个上下文菜单,它提供管理工具栏的各种命令。上下文菜单有时也叫做弹出菜单。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#!/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
'''
importwx
classMyPopupMenu(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)
defOnMinimize(self, e):
self.parent.Iconize()
defOnClose(self, e):
self.parent.Close()
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(self):
self.Bind(wx.EVT_RIGHT_DOWN,self.OnRightDown)
self.SetSize((250,200))
self.SetTitle('Context menu')
self.Centre()
self.Show(True)
defOnRightDown(self, e):
self.PopupMenu(MyPopupMenu(self), e.GetPosition())
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在这个例子中,我们为主窗口新建了上下文菜单,他有两个菜单项,一个用来最小化应用,另一个结束应用。
1
2
3
4
|
classMyPopupMenu(wx.Menu):
def__init__(self, parent):
super(MyPopupMenu,self).__init__()
|
我们创建了一个单独的类叫做MyPopupMenu,它继承自wx.Menu。
1
2
3
|
mmi=wx.MenuItem(self, wx.NewId(),'Minimize')
self.AppendItem(mmi)
self.Bind(wx.EVT_MENU,self.OnMinimize, mmi)
|
上面代码新建了菜单项、添加到上下文菜单并绑定了事件处理函数。
1
|
self.Bind(wx.EVT_RIGHT_DOWN,self.OnRightDown)
|
如果我们在frame中点击右键,将调用OnRightDown()方法,这是通过绑定wx.EVT_RIGHT_DOWN事件来绑定的。
1
2
|
defOnRightDown(self, e):
self.PopupMenu(MyPopupMenu(self), e.GetPosition())
|
在OnRightDown()函数中,我们调用了PopupMenu()方法,这个方法来自于wx.Frame。第一个参数是要显示的菜单,第二个参数为显示的位置。为了让上下文菜单显示在鼠标光标处,我们需要得到鼠标位置。事件对象的GetPosition()方法可以得到这一信息。
图:上下文菜单
工具栏
菜单将所有命令整合在一起,而工具栏可以为常用命令提供更方面的入口。我们可以使用frame部件的CreateToolBar()函数来新建工具栏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在例子中,我们新建了包含一个工具的工具栏。当我们点击这一工具时,程序将退出 。
1
|
toolbar=self.CreateToolBar()
|
我们新建了toolbar。默认情况下,工具栏是水平、无边框且显示图标的。
1
|
qtool=toolbar.AddLabelTool(wx.ID_ANY,'Quit', wx.Bitmap('texit.png'))
|
我们调用AddLabelTool()方法来新建工具栏的工具。第一个参数为ID,第二个参数为工具的标签,第三个为工具的图标。需要注意的是,标签默认情况下不会显示,仅会显示图标。
1
|
toolbar.Realize()
|
在我们把工具项放到工具栏之后,我们调用Realize()方法。在Linux中,该方法的调用不是必须的,但在windows却是必须的。
1
|
self.Bind(wx.EVT_TOOL,self.OnQuit, qtool)
|
为了处理工具栏事件,我们使用wx.EVT_TOOL事件绑定器。
图:简单的工具栏
如果我们想创建多个工具栏,必须用不一样的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在上面的例子中,我们新建了两个水平的工具栏
1
2
3
|
toolbar1=wx.ToolBar(self)
...
toolbar2=wx.ToolBar(self)
|
我们新建了两个工具栏对象,并将他们放到了一个竖直box中。关于sizer我们将在后面详细讨论。
图:多个工具栏
开启、禁用
在下面的例子中,我们将展示如何启用、禁用工具栏的按钮。同时,我们还将展示如何添加分割线。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
#!/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
'''
importwx
classExample(wx.Frame):
def__init__(self,*args,**kwargs):
super(Example,self).__init__(*args,**kwargs)
self.InitUI()
defInitUI(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)
defOnUndo(self, e):
ifself.count >1andself.count <=5:
self.count=self.count-1
ifself.count==1:
self.toolbar.EnableTool(wx.ID_UNDO,False)
ifself.count==4:
self.toolbar.EnableTool(wx.ID_REDO,True)
defOnRedo(self, e):
ifself.count <5andself.count >=1:
self.count=self.count +1
ifself.count==5:
self.toolbar.EnableTool(wx.ID_REDO,False)
ifself.count==2:
self.toolbar.EnableTool(wx.ID_UNDO,True)
defOnQuit(self, e):
self.Close()
defmain():
ex=wx.App()
Example(None)
ex.MainLoop()
if__name__=='__main__':
main()
|
在这个例子中,我们有三个工具栏按钮。一个按钮用来退出应用,其余两个按钮的功能为撤销和反撤销,在程序中模拟撤销、反撤销的功能。模拟了4次改变,撤销和反撤销功能也需要对应的启用或禁用。
1
2
|
self.toolbar.EnableTool(wx.ID_REDO,False)
self.toolbar.AddSeparator()
|
在最开始,撤销按钮是禁用的。我们调用EnableTool()函数,并传递False参数来实现。调用AddSeparator()函数可以添加一条竖线来分隔不同的工具项。
1
2
3
4
5
6
7
8
9
10
|
defOnUndo(self, e):
ifself.count >1andself.count <=5:
self.count=self.count-1
ifself.count==1:
self.toolbar.EnableTool(wx.ID_UNDO,False)
ifself.count==4:
self.toolbar.EnableTool(wx.ID_REDO,True)
|
我们模拟了撤销和反撤销的功能。如果没有什么可以撤销的,撤销按钮需要禁用。类似的逻辑也体现在OnRedo()中。
图:撤销、反撤销