Note

前几篇的窗口都是裸 QWidget,而桌面应用通常需要一个”标准骨架”:顶部菜单栏、菜单栏下的工具栏、底部状态栏、中间的工作区,以及两侧可停靠的面板。本篇讲解 QMainWindow 的五大区域,并用 QAction 把菜单项、工具按钮和快捷键统一起来。适合已掌握控件与布局的读者。

QMainWindow 的五大区域

QMainWindow 在 QWidget 之上做了固定的区域划分,它已经内置了这些区域,你只需要往里放东西,不需要再写任何布局代码:

  • 菜单栏(QMenuBar):窗口最顶部,横向排列若干菜单,每个菜单下拉出菜单项;
  • 工具栏(QToolBar):紧贴菜单栏下方,放常用操作的快捷按钮;
  • 锚接区(Dock Area):工具栏四周的四个区域,可停靠 QDockWidget 浮动面板(如资源管理器、属性面板);
  • 状态栏(QStatusBar):窗口最底部,显示提示消息、进度等状态信息;
  • 中心部件(Central Widget):被以上区域包围的中央工作区,是唯一必须自己设置的部件,普通 QWidget、QTextEdit、QTableWidget 都可以充当。

QMainWindow 五大区域结构示意

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 到槽函数”。这样功能入口天然统一,后期加快捷键、改图标都只动一处。