Note
前几篇的窗口都是裸 QWidget,而桌面应用通常需要一个”标准骨架”:顶部菜单栏、菜单栏下的工具栏、底部状态栏、中间的工作区,以及两侧可停靠的面板。本篇讲解 QMainWindow 的五大区域,并用 QAction 把菜单项、工具按钮和快捷键统一起来。适合已掌握控件与布局的读者。
QMainWindow 的五大区域
QMainWindow 在 QWidget 之上做了固定的区域划分,它已经内置了这些区域,你只需要往里放东西,不需要再写任何布局代码:
- 菜单栏(QMenuBar):窗口最顶部,横向排列若干菜单,每个菜单下拉出菜单项;
- 工具栏(QToolBar):紧贴菜单栏下方,放常用操作的快捷按钮;
- 锚接区(Dock Area):工具栏四周的四个区域,可停靠 QDockWidget 浮动面板(如资源管理器、属性面板);
- 状态栏(QStatusBar):窗口最底部,显示提示消息、进度等状态信息;
- 中心部件(Central Widget):被以上区域包围的中央工作区,是唯一必须自己设置的部件,普通 QWidget、QTextEdit、QTableWidget 都可以充当。
Warning
中心部件必须设置(setCentralWidget),否则窗口打开就是空的。QMainWindow 自身不做布局——不要对它调用 setLayout,各区域的位置由它内部自动管理。
QAction:一个动作,多处触发
QAction 是 Qt 设计中最优雅的抽象之一:“打开文件”这个动作只定义一次,菜单里有一项、工具栏有一个按钮、快捷键 Ctrl+O 也能触发——它们共享同一个 QAction 对象,禁用时三处同时变灰。
QAction *openAct = new QAction(QIcon(":/open.png"), tr("打开(&O)"), this);
openAct->setShortcut(QKeySequence::Open); // Ctrl+O,跨平台标准键
openAct->setStatusTip(tr("打开一个已有文件")); // 悬停时显示在状态栏
connect(openAct, &QAction::triggered, this, &MainWindow::openFile);文本里的 &O 定义 Alt+O 助记键;triggered 信号是绑定处理函数的入口。创建好的 QAction 既可以直接塞进菜单和工具栏,也常作为类的成员变量保存(命名习惯 xxxAct),方便在其他地方启用/禁用。
菜单栏:addMenuBar 与菜单项组织
菜单栏通过 menuBar() 获取——QMainWindow 的懒加载设计:第一次调用时自动创建,无需手动 new:
QMenu *fileMenu = menuBar()->addMenu(tr("文件(&F)")); // 顶级菜单
fileMenu->addAction(openAct); // 挂入前面创建的 QAction
fileMenu->addSeparator(); // 分隔线
fileMenu->addAction(quitAct); // 退出动作
QMenu *editMenu = menuBar()->addMenu(tr("编辑(&E)"));
editMenu->addAction(undoAct);addSeparator() 是菜单设计的好习惯:把”打开/保存”和”退出”这类性质不同的操作隔开。子菜单用 fileMenu->addMenu("最近打开") 创建后再往里加项。
工具栏:addToolBar 与按钮分组
QToolBar *toolBar = addToolBar(tr("主工具栏"));
toolBar->addAction(openAct); // 与菜单共享同一个 QAction
toolBar->addAction(saveAct);
toolBar->setObjectName("mainToolBar"); // 保存窗口状态时需要标识名
toolBar->setFloatable(false); // 禁止拖出来变成浮动窗口工具栏的按钮可以显示纯图标、纯文字或图文混排(setToolButtonStyle())。同样思路创建状态栏:
statusBar()->showMessage(tr("就绪"), 3000); // 显示 3 秒后消失
// 常驻部件:往状态栏右侧加一个永久标签
statusBar()->addPermanentWidget(new QLabel("v1.0"));Success
状态栏也是懒加载的,直接调
statusBar()->showMessage("...")就能创建并显示消息。showMessage()的第二个参数是超时毫秒数,不传则一直显示;常驻信息(如版本号)用 addPermanentWidget 更合适。
完整可运行示例
下面是一个带菜单、工具栏、状态栏的完整主窗口,Qt5 / Qt6 兼容:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QAction>
#include <QTextEdit>
#include <QLabel>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
{
setWindowTitle("记事本示例");
resize(640, 420);
// ---- 中心部件:一个多行编辑器 ----
QTextEdit *editor = new QTextEdit(this);
setCentralWidget(editor); // 必须设置中心部件
// ---- 动作:只定义一次,菜单与工具栏共用 ----
QAction *newAct = new QAction(tr("新建(&N)"), this);
newAct->setShortcut(QKeySequence::New); // Ctrl+N
newAct->setStatusTip(tr("创建一个空白文档"));
connect(newAct, &QAction::triggered, editor, &QTextEdit::clear);
QAction *quitAct = new QAction(tr("退出(&Q)"), this);
quitAct->setShortcut(tr("Ctrl+Q"));
quitAct->setStatusTip(tr("关闭整个程序"));
connect(quitAct, &QAction::triggered, qApp, &QApplication::quit);
QAction *aboutAct = new QAction(tr("关于(&A)"), this);
aboutAct->setStatusTip(tr("查看程序信息"));
connect(aboutAct, &QAction::triggered, this, [this](){
statusBar()->showMessage(tr("记事本示例 v1.0"), 5000);
});
// ---- 菜单栏:懒加载,直接 addMenu ----
QMenu *fileMenu = menuBar()->addMenu(tr("文件(&F)"));
fileMenu->addAction(newAct);
fileMenu->addSeparator();
fileMenu->addAction(quitAct);
QMenu *helpMenu = menuBar()->addMenu(tr("帮助(&H)"));
helpMenu->addAction(aboutAct);
// ---- 工具栏:复用同一个 QAction ----
QToolBar *toolBar = addToolBar(tr("主工具栏"));
toolBar->setObjectName("mainToolBar");
toolBar->addAction(newAct);
toolBar->addAction(quitAct);
// ---- 状态栏:临时消息 + 常驻标签 ----
statusBar()->showMessage(tr("就绪"));
statusBar()->addPermanentWidget(new QLabel(tr("就绪"), this));
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow win;
win.show();
return app.exec();
}Warning
这个单文件示例在 qmake 项目里需要
CONFIG += C++11且因内含 Q_OBJECT 而要经过 moc 处理(#include "main.moc"已处理)。如果编译报”未定义的 vtable”,优先检查 Q_OBJECT 宏与 moc 是否生效。入门练习时也可以把类拆成 main.cpp + mainwindow.h/cpp 三件套,让构建系统自动处理。
Success
记住 QAction 的复用思想:新增一个功能的正确顺序是”先建 QAction → 再挂到菜单和工具栏 → 最后 connect 到槽函数”。这样功能入口天然统一,后期加快捷键、改图标都只动一处。
自测:三个问题检验掌握程度
- QMainWindow 的五大区域分别是什么?哪个区域是必须手动设置的?
- 为什么推荐用 QAction 而不是分别创建菜单项和工具按钮?
- 想在状态栏显示一个 5 秒后自动消失的提示,用哪个函数?想放一个常驻版本号标签呢? (答案都在上文)