菜单示例

菜单示例演示了如何在主窗口应用程序中使用菜单。

菜单小部件可以是菜单栏中的下拉菜单或独立上下文菜单。下拉菜单在用户单击相应项或按下指定的快捷键时由菜单栏显示。上下文菜单通常通过某些特殊键盘键或通过右键单击来调用。

菜单由一系列 动作 项组成。在应用程序中,许多常见命令可以通过菜单、工具栏按钮以及键盘快捷键来触发。由于用户期望命令以相同的方式执行,而不论使用的是哪种用户界面,因此将每个命令表示为动作是有用的。

菜单示例包含一个单类,MainWindow,该类继承自 QMainWindow 类。当在应用程序中选择一个动作项时,它将在其中心小部件中显示项的路径。

MainWindow 类定义

QMainWindow 提供了一个主应用程序窗口,其中包含菜单栏、工具栏、停靠小部件以及围绕一个大中心小部件的状态栏。

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

protected:
#ifndef QT_NO_CONTEXTMENU
    void contextMenuEvent(QContextMenuEvent *event) override;
#endif // QT_NO_CONTEXTMENU

在这示例中,我们将看到如何实现下拉菜单以及上下文菜单。为了实现自定义上下文菜单,我们必须重写 QWidgetcontextMenuEvent() 函数以接收主窗口的上下文菜单事件。

private slots:
    void newFile();
    void open();
    void save();
    void print();
    void undo();
    void redo();
    void cut();
    void copy();
    void paste();
    void bold();
    void italic();
    void leftAlign();
    void rightAlign();
    void justify();
    void center();
    void setLineSpacing();
    void setParagraphSpacing();
    void about();
    void aboutQt();

我们还必须实现一系列私有槽来响应用户激活我们的任何菜单项。请注意,这些槽尚未在此文档中说明,因为它们是微不足道的,即大多数只需要在主窗口中心小部件中显示动作路径。

private:
    void createActions();
    void createMenus();

我们选择通过实现两个私有便利函数来简化构造函数,这些函数用于创建各种动作、将它们添加到菜单中以及将菜单插入主窗口的菜单栏中。

    QMenu *fileMenu;
    QMenu *editMenu;
    QMenu *formatMenu;
    QMenu *helpMenu;
    QActionGroup *alignmentGroup;
    QAction *newAct;
    QAction *openAct;
    QAction *saveAct;
    QAction *printAct;
    QAction *exitAct;
    QAction *undoAct;
    QAction *redoAct;
    QAction *cutAct;
    QAction *copyAct;
    QAction *pasteAct;
    QAction *boldAct;
    QAction *italicAct;
    QAction *leftAlignAct;
    QAction *rightAlignAct;
    QAction *justifyAct;
    QAction *centerAct;
    QAction *setLineSpacingAct;
    QAction *setParagraphSpacingAct;
    QAction *aboutAct;
    QAction *aboutQtAct;
    QLabel *infoLabel;
};

最后,我们在全局范围中声明了各种菜单、动作以及一个简单的信息标签。

QMenu 类为菜单栏、上下文菜单和其他弹出式菜单提供了一个菜单小部件,而 QAction 类提供了一个抽象的用户界面动作,该动作可以插入到小部件中。

在某些情况下,将动作分组在一起是有用的,例如,我们有一个 左对齐 动作、一个 右对齐 动作、一个 两端对齐 动作和一个 居中 动作,并且我们只想在任何时候激活这些动作中的一个。实现此目的的一种简单方法是用 QActionGroup 类将动作组合在一起。

MainWindow 类实现

在构造函数中,我们首先创建一个普通的 QWidget 并将其设为我们的主窗口的中央部件。请注意,主窗口将接管部件指针的拥有权并在适当的时间删除它。

MainWindow::MainWindow()
{
    QWidget *widget = new QWidget;
    setCentralWidget(widget);

    QWidget *topFiller = new QWidget;
    topFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    infoLabel = new QLabel(tr("<i>Choose a menu option, or right-click to "
                              "invoke a context menu</i>"));
    infoLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    infoLabel->setAlignment(Qt::AlignCenter);

    QWidget *bottomFiller = new QWidget;
    bottomFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setContentsMargins(5, 5, 5, 5);
    layout->addWidget(topFiller);
    layout->addWidget(infoLabel);
    layout->addWidget(bottomFiller);
    widget->setLayout(layout);

然后,我们创建了信息标签,以及一些顶部和底部的填充部件,我们将这些添加到一个布局中,并将该布局安装到中央部件上。 QMainWindow 对象自带自己的自定义布局,在主窗口上设置布局或以主窗口为父创建布局会被认为是错误的。您应该在中央部件上设置自己的布局。

    createActions();
    createMenus();

    QString message = tr("A context menu is available by right-clicking");
    statusBar()->showMessage(message);

    setWindowTitle(tr("Menus"));
    setMinimumSize(160, 160);
    resize(480, 320);
}

要创建操作和菜单,我们调用了我们的两个便利函数: createActions()createMenus()。我们稍后会回到这些。

QMainWindowstatusBar() 函数返回主窗口的状态栏(如果状态栏不存在,此函数将创建并返回一个空的状态栏)。我们初始化状态栏和窗口标题,将窗口调整到合适的大小,并确保主窗口不能调整到小于给定的大小。

现在,让我们更仔细地看看创建各种操作的便利函数 createActions()

void MainWindow::createActions()
{
    newAct = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentNew),
                         tr("&New"), this);
    newAct->setShortcuts(QKeySequence::New);
    newAct->setStatusTip(tr("Create a new file"));
    connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
    ...

QAction 对象可以包含一个图标、文本、快捷键、状态提示、"这是什么?" 文本和工具提示。其中大部分可以在构造函数中设置,但也可以通过提供的便利函数独立设置。

createActions() 函数中,我们首先创建一个 newAct 操作,通过使用主题图标常量传递文本和图标。我们使用 QAction::setShortcut() 函数将其快捷键设为 Ctrl+N,并使用 QAction::setStatusTip() 函数设置其状态提示(状态提示在操作的最顶层父部件提供的状态栏上显示)。我们还将其 triggered() 信号连接到 newFile() 插槽。

其余的操作以类似的方式创建。请参阅源代码以获取详细信息。

    alignmentGroup = new QActionGroup(this);
    alignmentGroup->addAction(leftAlignAct);
    alignmentGroup->addAction(rightAlignAct);
    alignmentGroup->addAction(justifyAct);
    alignmentGroup->addAction(centerAct);
    leftAlignAct->setChecked(true);
}

创建完 左对齐右对齐居中对齐 和一个 居中 操作后,我们还可以创建之前提到的操作组。

使用 QActionGroupaddAction() 函数将每个操作添加到组中。请注意,操作也可以通过将组设置为父对象时创建来添加到组中。由于操作组默认是排他的,因此在该组中任何时候只有一个操作被选中(这可以通过 QActionGroup::setExclusive() 函数来更改)。

创建所有操作后,我们使用 createMenus() 函数将操作添加到菜单,并将菜单插入到菜单栏中

void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(newAct);
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);
    fileMenu->addAction(printAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);

    editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(undoAct);
    editMenu->addAction(redoAct);
    editMenu->addSeparator();
    editMenu->addAction(cutAct);
    editMenu->addAction(copyAct);
    editMenu->addAction(pasteAct);
    editMenu->addSeparator();

    helpMenu = menuBar()->addMenu(tr("&Help"));
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);

QMenuBaraddMenu() 函数将给定标题的新 QMenu 追加到菜单栏(注意,菜单栏接管菜单的拥有权)。我们使用 QWidgetaddAction() 函数将每个操作添加到相应的菜单。

或者,QMenu 类提供了几个 addAction() 便利函数,这些函数可以创建并添加从给定的文本和/或图标中新的操作。您还可以提供成员,此成员将自动连接到新操作的 triggered() 信号,以及一个由 QKeySequence 实例表示的快捷键。

函数 QMenu::addSeparator() 创建并返回一个新的分隔符操作,即对于该操作,QAction::isSeparator() 返回 true,并将新操作添加到菜单的操作列表中。

    formatMenu = editMenu->addMenu(tr("&Format"));
    formatMenu->addAction(boldAct);
    formatMenu->addAction(italicAct);
    formatMenu->addSeparator()->setText(tr("Alignment"));
    formatMenu->addAction(leftAlignAct);
    formatMenu->addAction(rightAlignAct);
    formatMenu->addAction(justifyAct);
    formatMenu->addAction(centerAct);
    formatMenu->addSeparator();
    formatMenu->addAction(setLineSpacingAct);
    formatMenu->addAction(setParagraphSpacingAct);
}

注意“格式”菜单。首先,它是通过使用 QMenuaddMenu() 函数作为“编辑”菜单的子菜单添加的。其次,看看对齐操作:在 createActions() 函数中,我们将 leftAlignActrightAlignActjustifyActcenterAct 操作添加到操作组中。尽管如此,在操作组在幕后进行魔术处理的同时,我们必须将每个操作单独添加到菜单中。

#ifndef QT_NO_CONTEXTMENU
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu(this);
    menu.addAction(cutAct);
    menu.addAction(copyAct);
    menu.addAction(pasteAct);
    menu.exec(event->globalPos());
}
#endif // QT_NO_CONTEXTMENU

要提供一个自定义上下文菜单,我们必须重新实现 QWidgetcontextMenuEvent() 函数以接收小部件的上下文菜单事件(注意,默认实现简单地忽略了这些事件)。

每次接收到此类事件时,我们都会创建一个包含 剪切复制粘贴 操作的菜单。可以使用 popup() 函数异步执行上下文菜单或使用 exec() 函数同步执行。在本例中,我们选择了使用其 exec() 函数来显示菜单。通过传递事件的位置作为参数,我们确保上下文菜单出现在预期位置。

示例项目 @ code.qt.io

© 2024 Qt 公司有限公司。本文档中包含的文档贡献是各自所有者的版权。本文档是根据自由软件基金会发布并由其使用的 GNU 自由文档许可版本 1.3 许可的。Qt 及其相关标志是芬兰及/或其他国家/地区的 Qt 公司的商标。所有其他商标均为各自所有者的财产。