ZetCode

菜单和工具栏

最后修改于 2023 年 1 月 10 日

GUI 应用程序中常见的部分是菜单栏。 菜单栏由称为菜单的对象组成。 顶级菜单在其菜单栏上显示标签。 菜单有菜单项。 菜单项是执行应用程序内部特定操作的命令。 菜单也可以有子菜单,它们有自己的菜单项。 以下三个类用于在 wxPython 中创建菜单栏:wx.MenuBarwx.Menuwx.MenuItem

简单菜单

在我们的第一个例子中,我们将创建一个带有文件菜单的菜单栏。 菜单将只有一个菜单项。 通过选择该项目,应用程序退出。

simple_menu.py
#!/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 控件。 它设置菜单栏。

A simple menu example
图:一个简单的菜单示例

图标和快捷方式

下一个例子与前一个例子基本相同。 这次,我们手动创建 wx.MenuItem

icons_shortcuts.py
#!/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() 方法。

Icons and shortcuts
图:图标和快捷方式

子菜单和分隔符

每个菜单也可以有一个子菜单。 这样我们可以将类似的命令放入组中。 例如,我们可以将隐藏/显示各种工具栏(例如个人栏、地址栏、状态栏或导航栏)的命令放入一个名为工具栏的子菜单中。 在菜单中,我们可以用分隔符分隔命令。 它是一条简单的线。 通常的做法是用单个分隔符分隔诸如“新建”、“打开”、“保存”之类的命令与诸如“打印”、“打印预览”之类的命令。 在我们的例子中,我们将看到如何创建子菜单和菜单分隔符。

submenu.py
#!/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() 方法附加到文件菜单中。

A submenu example
图:一个子菜单示例

复选菜单项

有三种菜单项。

在下面的例子中,我们将演示复选菜单项。 复选菜单项在菜单中由一个勾号表示。

checkmenu_item.py
#!/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_NORMALAppend() 方法返回一个 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() 方法找出复选菜单项的状态。 同样适用于工具栏。

Check menu item
图:复选菜单项

上下文菜单

一个*上下文菜单*是一组在某些上下文下出现的命令。 例如,在 Firefox Web 浏览器中,当我们右键单击一个网页时,我们会得到一个上下文菜单。 在这里,我们可以重新加载页面、返回或查看页面源代码。 如果我们右键单击一个工具栏,我们将获得另一个用于管理工具栏的上下文菜单。 上下文菜单有时称为弹出菜单。

context_menu.py
#!/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() 方法。

Context menu
图:上下文菜单

工具栏

菜单将我们在应用程序中可以使用的所有命令分组。工具栏提供对最常用命令的快速访问。

要创建工具栏,我们调用框架控件的 CreateToolBar() 方法。

toolbar.py
#!/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 事件绑定器。

Simple toolbar
图:简单工具栏

如果我们想创建多个工具栏,我们必须以不同的方式完成。

toolbars.py
#!/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)

我们创建了两个工具栏对象。 并将它们放入一个垂直框中。

Toolbars
图:工具栏

启用 & 禁用

在下面的例子中,我们展示了如何启用和禁用工具栏按钮。 我们还添加了一条分隔线。

undo_redo.py
#!/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() 方法。

Undo redo
图:撤销重做

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