QMainWindow布局框架折腾记录
QMainWindow 这套布局框架,我折腾了两天才搞明白
最近在学 Qt 的窗口系统,之前几章一直用 QWidget 当主窗口,写着写着发现不对劲——菜单栏、工具栏、状态栏全得自己用布局拼,太蠢了。翻了下文档才知道 Qt 有个 QMainWindow,专门干这事的。
但说实话,刚上手的时候踩了不少坑,记录一下。
先说 QMainWindow 的结构
QMainWindow 把一个窗口分成了固定的几块区域:
- 最上面是菜单栏(QMenuBar)
- 菜单栏下面可以放工具栏(QToolBar),而且能放好几条
- 中间是核心工作区(Central Widget),这个必须设置
- 最下面是状态栏(QStatusBar)
- 左右两边可以停靠浮动窗口(QDockWidget)
我一开始以为 QMainWindow 和 QWidget 差不多,直接往里面 addLayout 就行。结果发现它根本不让你这么干——你必须用 setCentralWidget() 设置中央控件,其他区域用专门的 API 添加。
菜单栏这块
菜单栏的层级关系是:QMenuBar → QMenu → QAction。
QMenuBar *mb = menuBar(); // QMainWindow 自带的,不用 new
QMenu *fileMenu = mb->addMenu("文件(&F)");
QAction *actOpen = fileMenu->addAction("打开(&O)");
QAction *actSave = fileMenu->addAction("保存(&S)");
fileMenu->addSeparator(); // 加条分隔线
QAction *actQuit = fileMenu->addAction("退出(&Q)");
这里有个细节:&F 这种写法是设置快捷键下划线的,按 Alt+F 就能打开文件菜单。我之前一直不知道这个语法。
然后 QAction 被点击时会发出 triggered() 信号,连接到槽函数就行:
connect(actOpen, &QAction::triggered, this, &MainWindow::onOpen);
一个 QAction 可以同时加到菜单和工具栏里,这点设计得挺好的——同一个动作不用写两遍。
工具栏
QToolBar *toolbar = addToolBar("主工具栏");
toolbar->addAction(actOpen); // 直接复用菜单里的 QAction
toolbar->addAction(actSave);
toolbar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
setAllowedAreas 可以限制工具栏只能停靠在哪些边。不设置的话用户可以把它拖到任意一边,有时候会很奇怪。
状态栏
状态栏是最简单的一块:
statusBar()->showMessage("已保存", 3000); // 显示 3 秒后自动消失
第二个参数是毫秒数,传 0 或者不传就一直显示。我手贱试了一下传负数,结果也是一直显示,不会报错。
浮动窗口(Dock Widget)
这个东西我觉得挺实用的,类似 VS Code 左边的文件树面板那种效果:
QDockWidget *dock = new QDockWidget("文件列表", this);
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
QListWidget *list = new QListWidget(dock);
list->addItems({"main.cpp", "mainwindow.h", "mainwindow.cpp"});
dock->setWidget(list);
addDockWidget(Qt::LeftDockWidgetArea, dock);
用户可以拖动 dock 到允许的区域,也可以把它拖出来变成独立浮动窗口。关闭后想再显示,得通过菜单里的 action 来控制(QDockWidget 自带一个 toggleViewAction)。
对话框踩坑
Qt 内置了好几种对话框,都是静态方法一行调用,挺方便的。但有些地方不注意会出问题。
QFileDialog
QString path = QFileDialog::getOpenFileName(
this, "打开文件", "",
"文本文件 (*.txt);;所有文件 (*)"); // 注意是两个分号 ;;
我第一次写的时候用逗号分隔过滤器,结果过滤器显示成一整坨字符串,找了半天才发现要用 ;;。
QMessageBox
auto ret = QMessageBox::question(this, "确认", "要保存吗?",
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
// 保存逻辑
}
QColorDialog 和 QFontDialog
// 选颜色
QColor color = QColorDialog::getColor(Qt::black, this, "选颜色");
if (color.isValid()) {
editor->setTextColor(color);
}
// 选字体
bool ok;
QFont font = QFontDialog::getFont(&ok, editor->font(), this);
if (ok) editor->setFont(font);
这俩都是返回值 + bool 判断的模式。颜色用 isValid() 判断用户是否点了取消,字体用传入的 bool* 参数。风格不统一,但习惯就好。
模态和非模态
这个概念之前没接触过:
- 模态(
exec()):弹出对话框后,父窗口不能操作,必须先关掉对话框 - 非模态(
show()):对话框和父窗口可以同时操作
上面那些 getOpenFileName、getColor 之类的静态方法内部都是模态的。如果要非模态,得自己 new 一个 QDialog 然后 show():
QDialog *dlg = new QDialog(this);
dlg->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动 delete,不然内存泄漏
dlg->show();
WA_DeleteOnClose 这个属性很重要,不设的话每次 show 都 new 一个,关了也不释放,内存就慢慢涨上去了。
最后
写了个简易文本编辑器把这些东西串起来练了一遍,菜单栏打开/保存文件,工具栏快捷操作,状态栏显示反馈,dock 放个文件列表。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)