转自:http://thisis.yorven.site/blog/index.php/2017/09/17/wxpython-jiaocheng-san-caidan-yu-gongjulan/

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
'''

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()

上面是一个最小功能的菜单栏的例子。我们挑重点行来看一下:

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
'''

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()

在这个例子中,我们新建了一个退出菜单项,选择了自定义的图标和快捷键。

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
'''

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三个标准菜单项,并使用水平分割线将他们与其他区分。另外,我们还创建了一个包含三个菜单项的子菜单。

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
'''

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菜单项,这两个菜单项可以控制显示或隐藏状态栏和工具栏。

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
ef ToggleStatusBar(self, e):

if self.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
70
#!/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()
`

在这个例子中,我们为主窗口新建了上下文菜单,他有两个菜单项,一个用来最小化应用,另一个结束应用。

1
2
3
4
class MyPopupMenu(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
def OnRightDown(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
'''

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()

在例子中,我们新建了包含一个工具的工具栏。当我们点击这一工具时,程序将退出 。

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
'''

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()

在上面的例子中,我们新建了两个水平的工具栏

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
'''

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次改变,撤销和反撤销功能也需要对应的启用或禁用。

1
2
self.toolbar.EnableTool(wx.ID_REDO, False)
self.toolbar.AddSeparator()

在最开始,撤销按钮是禁用的。我们调用EnableTool()函数,并传递False参数来实现。调用AddSeparator()函数可以添加一条竖线来分隔不同的工具项。

1
2
3
4
5
6
7
8
9
 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()中。
撤销、反撤销

在本节教程中,我们学习了菜单和工具栏。