菜单示例演示了如何在主窗口应用程序中使用菜单。
菜单小部件可以是菜单栏中的下拉菜单,也可以是独立的上下文菜单。当用户单击相应项目或按下指定的快捷键时,菜单栏会显示下拉菜单。上下文菜单通常通过一些特殊的键盘键或右键单击来调用。
菜单由一系列操作项目组成。在应用程序中,许多常见的命令可以通过菜单、工具栏按钮以及键盘快捷键调用。由于用户期望以相同的方式执行命令,无论使用何种用户界面,因此将每个命令表示为一个操作是很有用的。
Menus示例由一个派生自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
在这个例子中,我们将看到如何实现下拉菜单以及上下文菜单。为了实现自定义上下文菜单,我们必须重新实现QWidget的contextMenuEvent()函数,以接收主窗口的上下文菜单事件。
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()。我们很快就会回到这些。
QMainWindow的statusBar()函数返回主窗口的状态栏(如果状态栏不存在,此函数将创建并返回一个空的状态栏)。我们初始化状态栏和窗口标题,将窗口调整到适当的大小,并确保主窗口的大小不能小于给定的大小。
现在,让我们仔细看看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);
}
创建左对齐、右对齐、对齐和居中操作后,我们还可以创建前面提到的操作组。
每个操作都使用QActionGroup的addAction()函数添加到组中。请注意,也可以将操作添加到组中,方法是将组作为其父组进行创建。由于操作组在默认情况下是独占的,因此在任何时候都只检查组中的一个操作(这可以使用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);
QMenuBar的addMenu()函数将一个具有给定标题的新QMenu附加到菜单栏(请注意,菜单栏拥有菜单的所有权)。我们使用QWidget的addAction()函数将每个操作添加到相应的菜单中。
或者,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);
}
注意“格式”菜单。首先,使用QMenu的addMenu()函数将其作为子菜单添加到编辑菜单中。其次,看看对齐操作:在createActions()函数中,我们将leftAlignAct、rightAlignAct,justifyAct和centerAct操作添加到一个操作组中。尽管如此,当动作组在幕后发挥其魔力时,我们必须将每个动作单独添加到菜单中。
#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
要提供自定义上下文菜单,我们必须重新实现QWidget的contextMenuEvent()函数来接收小部件的上下文菜单事件(注意,默认实现只是忽略这些事件)。
每当我们收到这样的事件时,我们都会创建一个包含剪切、复制和粘贴操作的菜单。上下文菜单可以使用popup()函数异步执行,也可以使用exec()函数同步执行。在本例中,我们选择使用其exec()函数来显示菜单。通过将事件的位置作为参数传递,我们确保上下文菜单显示在预期的位置。