Qt窗口 ------ QMainWindow 完全解析
本文系统梳理了 Qt 主窗口编程的核心知识体系。全文涵盖 QMainWindow 结构体系、菜单栏(QMenuBar)、工具栏(QToolBar)、状态栏(QStatusBar)、停靠部件(QDockWidget)以及六大内置对话框(QMessageBox / QColorDialog / QFileDialog / QFontDialog / QInputDialog / QDialog)等内容,知识点覆盖全面,力求对每个细节进行透彻的讲解。
一、QMainWindow 概述
1.1 QMainWindow 是什么?
在 Qt 中,QMainWindow 是一个为用户提供主窗口程序界面的类,它继承自 QWidget。QWidget 是所有用户界面对象的基类,而 QMainWindow 在此基础上封装了一套完整的"主窗口框架",专门用于构建带有菜单栏、工具栏、状态栏等标准组件的桌面应用程序。
一个典型的 QMainWindow 由以下五大部分组成:
| 组成部分 | 对应类 | 说明 |
|---|---|---|
| 菜单栏 (Menu Bar) | QMenuBar | 位于窗口顶部,包含各功能菜单(文件、编辑、帮助等) |
| 工具栏 (Tool Bars) | QToolBar | 可停靠在窗口各边界的快捷操作栏 |
| 停靠部件 (Dock Widgets) | QDockWidget | 可停靠、可浮动的面板窗口 |
| 状态栏 (Status Bar) | QStatusBar | 位于窗口底部,用于显示程序状态信息 |
| 中心部件 (Central Widget) | QWidget 等 | 窗口的核心内容区域,放置主要控件 |
┌─────────────────────────────────────────────┐
│ 菜单栏 (QMenuBar) │
├─────────────────────────────────────────────┤
│ 工具栏 (QToolBar) │
├──────────┬────────────────────┬─────────────┤
│ 停靠部件 │ │ 停靠部件 │
│ (左边) │ 中心部件 │ (右边) │
│QDockWidget│ (Central Widget) │QDockWidget │
│ │ │ │
├──────────┴────────────────────┴─────────────┤
│ 状态栏 (QStatusBar) │
└─────────────────────────────────────────────┘
QMainWindow 与 QWidget 的关系:QMainWindow 本质上是一个 QWidget,但它在内部预先划分好了布局区域。如果你的程序需要一个标准的桌面应用界面(有菜单、有工具栏、有状态栏),使用 QMainWindow 是最佳选择;如果只是一个简单的对话框或控件集合,直接使用 QWidget 即可。
1.2 核心设计理念
QMainWindow 拥有自己的一套默认布局管理器,开发者不需要手动创建布局来放置菜单栏、工具栏等部件------QMainWindow 会自动将它们放置在合适的位置。这极大地简化了标准桌面应用程序的开发流程:
- 菜单栏 :通过
menuBar()获取,始终位于窗口顶部 - 工具栏 :通过
addToolBar()添加,默认位于菜单栏下方,可拖拽到四个边界 - 状态栏 :通过
statusBar()获取,始终位于窗口底部 - 中心部件 :通过
setCentralWidget()设置,占据窗口中间的所有剩余空间 - 停靠部件 :通过
addDockWidget()添加,可停靠在中心部件的四周
二、菜单栏 ------ QMenuBar
菜单栏是桌面应用程序最核心的导航组件之一。在 Qt 中,菜单栏由 QMenuBar 类表示,菜单由 QMenu 类表示,菜单中的每一项由 QAction 类表示。这三层结构的关系如下:
QMenuBar(菜单栏)
├── QMenu(菜单:文件)
│ ├── QAction(菜单项:新建)
│ ├── QAction(菜单项:打开)
│ ├── QAction(菜单项:保存)
│ └── 分隔符 ──── addSeparator()
│ └── QAction(菜单项:退出)
├── QMenu(菜单:编辑)
│ ├── QAction(菜单项:剪切)
│ ├── QAction(菜单项:复制)
│ └── QAction(菜单项:粘贴)
└── QMenu(菜单:帮助)
└── QAction(菜单项:关于)
2.1 创建菜单栏
在 QMainWindow 中,可以通过调用 menuBar() 方法获取或创建菜单栏对象:
cpp
// 函数原型
QMenuBar * QMainWindow::menuBar() const;
关键行为说明:
- 如果当前 QMainWindow 还没有菜单栏,
menuBar()会自动创建一个空的 QMenuBar 并返回其指针 - 如果菜单栏已经存在,则直接返回已有的菜单栏指针
- 也可以通过
setMenuBar()方法手动设置一个自定义的菜单栏
cpp
// 方式一:使用 QMainWindow 自动创建的菜单栏
QMenuBar *menuBar = this->menuBar();
// 方式二:手动创建菜单栏(一般不需要)
QMenuBar *menuBar = new QMenuBar(this);
this->setMenuBar(menuBar);
注意 :每个 QMainWindow 最多只能有一个菜单栏。当你重复调用
menuBar()时,返回的是同一个对象。
2.2 添加菜单
菜单栏创建好后,需要通过 addMenu() 方法向其中添加菜单(QMenu 对象):
cpp
// 函数原型
QMenu * QMenuBar::addMenu(const QString &title);
// 或
QMenu * QMenuBar::addMenu(QMenu *menu);
使用方式一:直接传入菜单标题,返回新建的 QMenu 对象
cpp
QMenu *fileMenu = menuBar->addMenu("文件(&F)");
QMenu *editMenu = menuBar->addMenu("编辑(&E)");
QMenu *helpMenu = menuBar->addMenu("帮助(&H)");
标题中的
&符号用于定义快捷键(Alt + 对应字母),例如"文件(&F)"表示按 Alt+F 即可打开该菜单。
使用方式二:先创建 QMenu 对象,再添加到菜单栏
cpp
QMenu *fileMenu = new QMenu("文件");
menuBar->addMenu(fileMenu);
2.3 创建菜单项 ------ QAction
菜单中的每一个具体选项都是一个 QAction 对象。QAction 是 Qt 中的"动作"抽象,它不仅可以用在菜单中,还可以用在工具栏和快捷键中------这正是 Qt 设计的巧妙之处:同一个 QAction 可以被菜单、工具栏和快捷键共同使用。
cpp
// QAction 构造函数
QAction(const QString &text, QObject *parent = nullptr);
QAction(const QIcon &icon, const QString &text, QObject *parent = nullptr);
基本使用步骤:
cpp
// 第一步:创建 QAction 对象
QAction *newAction = new QAction("新建(&N)", this);
QAction *openAction = new QAction("打开(&O)", this);
QAction *saveAction = new QAction("保存(&S)", this);
QAction *exitAction = new QAction("退出(&X)", this);
// 第二步:将 QAction 添加到菜单中
fileMenu->addAction(newAction);
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addAction(exitAction);
为 QAction 设置图标:
cpp
QAction *saveAction = new QAction(QIcon(":/icons/save.png"), "保存", this);
为 QAction 设置快捷键(独立的快捷触发方式):
cpp
saveAction->setShortcut(QKeySequence("Ctrl+S"));
openAction->setShortcut(QKeySequence("Ctrl+O"));
QAction 的三大用途:
- 菜单项 :通过
QMenu::addAction()添加到菜单- 工具栏按钮 :通过
QToolBar::addAction()添加到工具栏- 独立快捷键 :通过
QAction::setShortcut()设置,即使不添加到任何菜单或工具栏也能触发
2.4 添加分隔符
在菜单中,经常需要将不同功能的菜单项分组,此时可以使用 addSeparator() 方法添加一条分隔线:
cpp
fileMenu->addAction(newAction);
fileMenu->addAction(openAction);
fileMenu->addSeparator(); // 👈 添加分隔线
fileMenu->addAction(saveAction);
fileMenu->addSeparator(); // 👈 再添加一条分隔线
fileMenu->addAction(exitAction);
效果如下:
文件(&F)
├── 新建
├── 打开
├── ────────── ← 分隔线
├── 保存
├── ────────── ← 分隔线
└── 退出
2.5 完整实战案例
下面是一个完整的主窗口代码示例,展示了菜单栏的创建、菜单的添加、QAction 的创建与信号连接,以及中心部件的设置:
mainwindow.h
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void save(); // 保存文件的槽函数
void load(); // 加载文件的槽函数
private:
QTextEdit *edit; // 中心文本编辑区域
};
#endif // MAINWINDOW_H
mainwindow.cpp
cpp
#include "mainwindow.h"
#include <QFileDialog>
#include <QDebug>
#include <fstream>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// ========== 1. 设置窗口标题 ==========
this->setWindowTitle("Qt 记事本");
// ========== 2. 创建菜单栏 ==========
QMenuBar *menuBar = new QMenuBar(this);
this->setMenuBar(menuBar);
// ========== 3. 创建"文件"菜单 ==========
QMenu *fileMenu = new QMenu("文件(&F)");
menuBar->addMenu(fileMenu);
// ========== 4. 创建菜单项(QAction) ==========
QAction *saveAction = new QAction("保存(&S)", this);
QAction *loadAction = new QAction("打开(&O)", this);
// 将菜单项添加到菜单中
fileMenu->addAction(saveAction);
fileMenu->addAction(loadAction);
// ========== 5. 创建中心部件 ==========
edit = new QTextEdit(this);
this->setCentralWidget(edit);
edit->setPlaceholderText("请输入文本内容...");
// ========== 6. 连接信号与槽 ==========
connect(saveAction, &QAction::triggered, this, &MainWindow::save);
connect(loadAction, &QAction::triggered, this, &MainWindow::load);
}
// ========== 保存文件 ==========
void MainWindow::save()
{
// 获取保存路径
QFileDialog dialog(this);
QString fileName = dialog.getSaveFileName(this, "保存文件", "C:/Users/1/");
qDebug() << "fileName:" << fileName;
if (fileName.isEmpty()) {
return;
}
// 使用 C++ 标准库写入文件
std::ofstream file(fileName.toStdString().c_str());
if (!file.is_open()) {
qDebug() << "文件打开失败!";
return;
}
const QString &text = edit->toPlainText();
file << text.toStdString();
file.close();
}
// ========== 加载文件 ==========
void MainWindow::load()
{
// 获取打开路径
QFileDialog dialog(this);
QString fileName = dialog.getOpenFileName(this, "打开文件", "C:/Users/1/");
qDebug() << "fileName:" << fileName;
if (fileName.isEmpty()) {
return;
}
// 使用 C++ 标准库读取文件
std::ifstream file(fileName.toStdString().c_str());
if (!file.is_open()) {
qDebug() << "文件打开失败!";
return;
}
std::string content;
std::string line;
while (std::getline(file, line)) {
content += line;
content += "\n";
}
file.close();
// 将内容显示到文本编辑区
QString text = QString::fromStdString(content);
edit->setPlainText(text);
}
要点总结:
QMainWindow通过setMenuBar()设置菜单栏QMenuBar通过addMenu()添加菜单QMenu通过addAction()添加菜单项QAction的triggered()信号用于响应菜单项点击setCentralWidget()设置窗口中心部件,占据菜单栏、工具栏和状态栏之外的全部剩余空间- 本案例中的"保存"和"打开"功能使用了 C++ 标准库的文件读写,配合 QFileDialog 实现了简单的文本文件存取
三、工具栏 ------ QToolBar
工具栏是紧邻菜单栏下方(默认位置)的一组快捷操作按钮。它将最常用的功能以图标按钮的形式呈现给用户,提高操作效率。在 Qt 中,工具栏由 QToolBar 类负责实现。
3.1 创建工具栏
在 QMainWindow 中,通过 addToolBar() 方法创建并添加工具栏:
cpp
// 函数原型
QToolBar * QMainWindow::addToolBar(const QString &title);
// 也可以手动创建后添加
void QMainWindow::addToolBar(QToolBar *toolbar);
示例代码:
cpp
// 方式一:直接创建
QToolBar *fileToolBar = this->addToolBar("文件工具栏");
// 方式二:手动创建后添加
QToolBar *editToolBar = new QToolBar("编辑工具栏", this);
this->addToolBar(editToolBar);
3.2 设置停靠区域
工具栏可以停靠在窗口的四个边界(上、下、左、右)。通过 setAllowedAreas() 方法可以限制工具栏允许停靠的区域:
cpp
// 设置允许的停靠区域
void QToolBar::setAllowedAreas(Qt::ToolBarAreas areas);
// 获取当前允许的停靠区域
Qt::ToolBarAreas QToolBar::allowedAreas() const;
Qt 中定义的工具栏区域常量:
| 常量 | 值 | 说明 |
|---|---|---|
Qt::LeftToolBarArea |
0x1 | 允许停靠在左侧 |
Qt::RightToolBarArea |
0x2 | 允许停靠在右侧 |
Qt::TopToolBarArea |
0x4 | 允许停靠在顶部(默认) |
Qt::BottomToolBarArea |
0x8 | 允许停靠在底部 |
Qt::AllToolBarAreas |
0xF | 允许停靠所有位置(默认) |
Qt::NoToolBarArea |
0x0 | 不允许停靠在任何位置 |
使用示例:
cpp
// 只允许工具栏停靠在顶部和底部
toolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
// 只允许停靠在左侧
toolBar->setAllowedAreas(Qt::LeftToolBarArea);
// 禁止工具栏停靠
toolBar->setAllowedAreas(Qt::NoToolBarArea);
可以使用按位或运算符
|组合多个区域,这是 Qt 中常见的"标志位组合"模式。
初始停靠位置设置:
cpp
// 在添加工具栏时指定初始停靠区域
this->addToolBar(Qt::LeftToolBarArea, toolBar); // 初始停靠在左侧
3.3 设置浮动属性
除了停靠在窗口边界,工具栏还可以浮动 ------即脱离主窗口成为一个独立的小窗口。通过 setFloatable() 方法可以控制这个行为:
cpp
// 函数原型
void QToolBar::setFloatable(bool floatable);
// 判断工具栏是否可浮动
bool QToolBar::isFloatable() const;
- 参数为
true(默认值):用户可以将工具栏拖拽出来,使其成为一个独立的浮动窗口 - 参数为
false:工具栏只能停靠在窗口边界,无法脱离主窗口
cpp
// 禁止工具栏浮动
toolBar->setFloatable(false);
3.4 设置可移动属性
工具栏还支持在不同停靠区域之间移动(例如从顶部拖到底部)。通过 setMovable() 方法控制:
cpp
// 函数原型
void QToolBar::setMovable(bool movable);
// 判断工具栏是否可移动
bool QToolBar::isMovable() const;
- 参数为
true(默认值):用户可以在四个停靠区域之间拖拽工具栏 - 参数为
false:工具栏固定在当前区域,无法移动
cpp
// 禁止工具栏移动
toolBar->setMovable(false);
setFloatable与setMovable的区别:
setFloatable控制工具栏能否脱离主窗口成为独立浮动窗口setMovable控制工具栏能否在主窗口的不同停靠区域之间移动setMovable(false)会自动禁止浮动(因为浮动需要先移动),但setFloatable(false)不会影响在停靠区域之间的移动
3.5 工具栏添加动作
工具栏中的按钮同样使用 QAction 来表示------这正是 QAction"一处创建,多处使用"的强大之处:
cpp
// 创建工具栏
QToolBar *toolBar = this->addToolBar("工具栏");
// 复用菜单中的 QAction(同一个 Action 对象)
toolBar->addAction(saveAction); // saveAction 同时出现在菜单和工具栏中
toolBar->addAction(loadAction); // loadAction 同时出现在菜单和工具栏中
// 也可以在工具栏中添加分隔符
toolBar->addSeparator();
// 创建仅用于工具栏的 QAction
QAction *cutAction = new QAction(QIcon(":/icons/cut.png"), "剪切", this);
toolBar->addAction(cutAction);
完整工具栏示例:
cpp
// 创建工具栏
QToolBar *mainToolBar = this->addToolBar("主工具栏");
// 设置属性
mainToolBar->setAllowedAreas(Qt::AllToolBarAreas); // 允许所有停靠位置
mainToolBar->setFloatable(true); // 允许浮动
mainToolBar->setMovable(true); // 允许移动
// 添加动作(复用菜单的 QAction,保持一致性)
mainToolBar->addAction(saveAction);
mainToolBar->addAction(loadAction);
mainToolBar->addSeparator();
mainToolBar->addAction(exitAction);
四、状态栏 ------ QStatusBar
状态栏位于主窗口的最底部,用于向用户展示程序当前的状态信息(如操作提示、进度状态、当前坐标等)。在 Qt 中,状态栏由 QStatusBar 类负责实现。
状态栏主要有三种信息展示类型:
- 临时信息:在指定时间后自动消失,用于短暂的提示
- 正常信息:一直显示直到被新的信息覆盖
- 永久信息:一直显示在最右侧,不会被临时信息覆盖
4.1 获取状态栏对象
与菜单栏类似,QMainWindow 提供了 statusBar() 方法来获取或自动创建状态栏:
cpp
// 函数原型
QStatusBar * QMainWindow::statusBar() const;
cpp
// 获取状态栏对象
QStatusBar *statusBar = this->statusBar();
4.2 显示临时信息
showMessage() 方法用于在状态栏中显示一条临时提示信息:
cpp
// 函数原型
void QStatusBar::showMessage(const QString &text, int timeout = 0);
参数说明:
text:要显示的文本内容timeout:信息显示的持续时间(单位:毫秒)。默认为 0,表示一直显示直到被新的信息覆盖或调用clearMessage()清除
cpp
// 显示一条持续 3 秒的提示
statusBar->showMessage("文件保存成功!", 3000);
// 显示一条持续 5 秒的提示
statusBar->showMessage("正在加载数据...", 5000);
// 显示一条永久提示(直到被覆盖)
statusBar->showMessage("就绪");
4.3 添加永久控件
除了文本信息,状态栏还可以容纳任何 QWidget 控件(如 QLabel、QProgressBar 等),这些控件会永久显示在状态栏中:
cpp
// 向状态栏添加一个永久显示的标签
QLabel *statusLabel = new QLabel("欢迎使用本程序", this);
statusBar->addWidget(statusLabel);
// 向状态栏添加一个进度条
QProgressBar *progressBar = new QProgressBar(this);
progressBar->setMaximumWidth(200);
statusBar->addWidget(progressBar);
// 向状态栏最右侧添加永久控件
QLabel *versionLabel = new QLabel("v1.0.0", this);
statusBar->addPermanentWidget(versionLabel);
addWidget()与addPermanentWidget()的区别:
addWidget()添加的控件位于左侧,可能会被showMessage()的临时信息所遮挡addPermanentWidget()添加的控件固定在状态栏最右侧,不会被临时信息影响
五、停靠部件 ------ QDockWidget
停靠部件(QDockWidget)是一种可以停靠在 QMainWindow 四周、也可以浮动为独立窗口的面板控件。典型应用包括 IDE 中的"文件浏览器"、"属性编辑器"、"输出窗口"等侧边面板。
5.1 创建停靠部件
停靠部件使用 QDockWidget 类来表示,通过 addDockWidget() 方法添加到 QMainWindow 中:
cpp
// QDockWidget 构造函数
QDockWidget(const QString &title, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
// 向 QMainWindow 添加停靠部件
void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget);
示例代码:
cpp
// 创建停靠部件
QDockWidget *dockWidget = new QDockWidget("工具面板", this);
// 设置停靠部件的内容控件
QTextEdit *dockContent = new QTextEdit(dockWidget);
dockWidget->setWidget(dockContent);
// 将停靠部件添加到主窗口的右侧
this->addDockWidget(Qt::RightDockWidgetArea, dockWidget);
5.2 设置允许停靠区域
与工具栏类似,停靠部件也支持通过 setAllowedAreas() 限制其允许停靠的区域:
cpp
void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas);
Qt::DockWidgetAreas QDockWidget::allowedAreas() const;
Qt 中定义的停靠部件区域常量:
| 常量 | 说明 |
|---|---|
Qt::LeftDockWidgetArea |
允许停靠在左侧 |
Qt::RightDockWidgetArea |
允许停靠在右侧 |
Qt::TopDockWidgetArea |
允许停靠在顶部 |
Qt::BottomDockWidgetArea |
允许停靠在底部 |
Qt::AllDockWidgetAreas |
允许停靠在所有位置 |
Qt::NoDockWidgetArea |
不允许停靠(只能浮动) |
cpp
// 只允许在左侧和右侧停靠
dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
停靠部件(QDockWidget)与工具栏(QToolBar)允许区域的对比:
| 特性 | QToolBar | QDockWidget |
|---|---|---|
| 允许区域枚举 | Qt::ToolBarAreas |
Qt::DockWidgetAreas |
| 默认停靠位置 | 顶部 | 取决于 addDockWidget() 参数 |
| 可以包含任意控件 | 否(主要是 QAction 按钮) | 是(setWidget() 可以设置任意 QWidget) |
| 典型用途 | 快捷操作栏 | 侧边面板、工具窗口 |
六、对话框
6.1 对话框概述
对话框是 GUI 程序与用户进行交互的最重要途径之一。无论是确认操作、输入数据、选择文件还是选择颜色,都需要通过对话框来完成。在 Qt 中,对话框通常继承自 QDialog 类,而 QDialog 本身继承自 QWidget。
Qt 提供了多种常用的内置对话框类,极大地方便了开发者:
| 对话框类 | 用途 |
|---|---|
| QMessageBox | 消息提醒对话框(信息、警告、错误、提问等) |
| QColorDialog | 颜色选择对话框 |
| QFileDialog | 文件选择对话框(打开/保存文件) |
| QFontDialog | 字体选择对话框 |
| QInputDialog | 输入对话框(获取整数、浮点数、文本等) |
6.2 模态与非模态对话框
按照对话框与父窗口之间的交互关系,对话框可以分为模态对话框 和非模态对话框两种。
6.2.1 模态对话框
模态对话框的特点:当它弹出后,用户必须先关闭该对话框,才能继续操作父窗口。 对话框阻塞了父窗口的交互。
在 Qt 中,通过 QDialog::exec() 方法以模态方式显示对话框:
cpp
int QDialog::exec();
使用步骤(以 Qt Designer 创建的对话框为例):
- 在 Qt Creator 项目中通过 Designer 创建一个新的对话框界面(如添加
Dialog类,生成dialog.ui、dialog.h、dialog.cpp) - 在主窗口的某个触发函数(如菜单项点击的槽函数)中创建对话框对象并调用
exec()
cpp
// mainwindow.cpp 中的某个槽函数
void MainWindow::onActionTriggered()
{
// 创建对话框对象
Dialog dialog(this);
// 以模态方式显示,程序在此阻塞直到对话框关闭
int result = dialog.exec();
// 根据返回值判断用户的操作
if (result == QDialog::Accepted) {
// 用户点击了"确定"
qDebug() << "用户确认了操作";
} else if (result == QDialog::Rejected) {
// 用户点击了"取消"或关闭了对话框
qDebug() << "用户取消了操作";
}
}
exec()的返回值:
QDialog::Accepted(值为 1):用户通过"确定"按钮关闭对话框QDialog::Rejected(值为 0):用户通过"取消"按钮或右上角关闭按钮关闭对话框
模态对话框的执行流程:
主窗口处于活跃状态
→ 用户触发对话框(如点击菜单项)
→ exec() 被调用,对话框显示
→ 主窗口被阻塞,用户只能与对话框交互
→ 用户点击"确定"或"取消"
→ exec() 返回结果
→ 主窗口恢复活跃状态
6.2.2 非模态对话框
非模态对话框的特点:对话框弹出后,用户可以不关闭它而继续操作父窗口。 常用于工具箱、属性面板等需要持续显示的辅助窗口。
在 Qt 中,通过 QDialog::show() 方法以非模态方式显示对话框:
cpp
void QWidget::show();
关键注意事项 :使用 show() 显示非模态对话框时,必须设置 Qt::WA_DeleteOnClose 属性,确保对话框关闭时自动释放内存:
cpp
void MainWindow::onActionTriggered()
{
// ⚠️ 必须使用 new 创建在堆上!不能是局部变量
Dialog *dialog = new Dialog(this);
// 设置关闭时自动删除
dialog->setAttribute(Qt::WA_DeleteOnClose);
// 以非模态方式显示
dialog->show();
// ⚠️ 注意:此处不能使用 dialog 指针了,因为 show() 不阻塞,
// 函数会立即返回,dialog 可能在之后任意时刻被关闭并删除
}
为什么非模态对话框必须用
new创建?如果使用局部变量(栈对象),当槽函数执行完毕后,局部变量会被自动销毁,对话框也会随之消失,用户根本来不及看到它。使用
new在堆上创建,并配合Qt::WA_DeleteOnClose属性,可以让对话框在用户关闭它之前一直存在,关闭时自动释放内存,避免内存泄漏。
模态 vs 非模态对比:
| 特性 | 模态对话框 | 非模态对话框 |
|---|---|---|
| 显示方法 | exec() |
show() |
| 阻塞父窗口 | 是 | 否 |
| 创建方式 | 局部变量(栈)或 new(堆) |
必须 new(堆) |
| 内存管理 | 栈变量自动释放 | 需要 WA_DeleteOnClose |
| 返回值 | 有(Accepted/Rejected) | 无 |
| 典型应用场景 | 确认框、设置窗口 | 查找替换框、工具面板 |
6.2.3 使用 setModal() 方法
除了使用 exec() 和 show() 来区分模态与非模态,Qt 还提供了 setModal() 方法来设置对话框的模态属性:
cpp
void QDialog::setModal(bool modal);
cpp
// 创建对话框
Dialog *dialog = new Dialog(this);
dialog->setModal(true); // 设置为模态
dialog->show(); // 注意:虽然是 show(),但因为 setModal(true),行为类似 exec()
// 或者
Dialog *dialog = new Dialog(this);
dialog->setModal(false); // 设置为非模态(默认值)
dialog->show(); // 非模态显示
注意 :
setModal(true)+show()可以实现模态效果,但这种方式与exec()有一个重要区别:show()不会返回对话框的执行结果(Accepted/Rejected)。因此,通常推荐直接使用exec()来显示模态对话框。
6.3 Qt 内置对话框
除了自定义 QDialog 子类,Qt 还提供了一系列功能强大的内置对话框类,它们都是 QDialog 的子类,可以直接调用静态方法快速弹出。
6.3.1 QMessageBox ------ 消息对话框
QMessageBox(消息对话框)是最常用的对话框类型,用于向用户展示提示信息或询问用户的选择。
五种标准消息类型:
| 类型 | 图标 | 用途 |
|---|---|---|
| Question | ❓ 问号 | 向用户提问,通常配合 Yes/No 按钮 |
| Information | ℹ️ 信息图标 | 向用户展示普通提示信息 |
| Warning | ⚠️ 警告图标 | 向用户展示警告信息 |
| Critical | ❌ 错误图标 | 向用户报告严重错误 |
| About | 🛈 无特殊图标 | 关于对话框 |
QMessageBox 的核心静态方法:
cpp
// 标准消息框
QMessageBox::StandardButton QMessageBox::information(
QWidget *parent, const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton
);
QMessageBox::StandardButton QMessageBox::question(
QWidget *parent, const QString &title,
const QString &text,
StandardButtons buttons = Yes | No,
StandardButton defaultButton = NoButton
);
QMessageBox::StandardButton QMessageBox::warning(
QWidget *parent, const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton
);
QMessageBox::StandardButton QMessageBox::critical(
QWidget *parent, const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton
);
void QMessageBox::about(
QWidget *parent, const QString &title,
const QString &text
);
常用标准按钮常量:
| 按钮常量 | 显示的文本 |
|---|---|
QMessageBox::Ok |
确定 |
QMessageBox::Cancel |
取消 |
QMessageBox::Yes |
是 |
QMessageBox::No |
否 |
QMessageBox::Save |
保存 |
QMessageBox::Discard |
放弃 |
QMessageBox::Close |
关闭 |
实际使用示例 1:信息提示
cpp
QMessageBox::information(this, "提示", "文件保存成功!");
实际使用示例 2:询问用户
cpp
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认退出",
"您确定要退出程序吗?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
this->close();
}
实际使用示例 3:警告对话框
cpp
QMessageBox::warning(this, "警告", "未保存的更改将会丢失!");
实际使用示例 4:错误对话框
cpp
QMessageBox::critical(this, "错误", "无法连接到数据库!");
实际使用示例 5:多按钮自定义
cpp
QMessageBox msgBox(this);
msgBox.setWindowTitle("保存更改");
msgBox.setText("文档已被修改。");
msgBox.setInformativeText("是否保存更改?");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
// 保存
break;
case QMessageBox::Discard:
// 放弃
break;
case QMessageBox::Cancel:
// 取消
break;
}
6.3.2 QColorDialog ------ 颜色对话框
QColorDialog 提供了一个标准的颜色选择对话框,用户可以从调色板中选择颜色或自定义颜色。
核心 API:
cpp
// 构造函数
QColorDialog(QWidget *parent = nullptr);
QColorDialog(const QColor &initial, QWidget *parent = nullptr);
// 获取/设置当前颜色
QColor currentColor() const;
void setCurrentColor(const QColor &color);
// 静态方法:快速弹出颜色选择对话框
QColor QColorDialog::getColor(
const QColor &initial = Qt::white,
QWidget *parent = nullptr,
const QString &title = QString(),
QColorDialog::ColorDialogOptions options = ColorDialogOptions()
);
// 信号槽方式打开
void open(QObject *receiver, const char *member);
参数说明:
initial:初始选中的颜色(默认为白色)parent:父窗口指针title:对话框标题options:对话框选项(如是否显示 Alpha 通道等)
使用示例 1:使用静态方法
cpp
// 弹出颜色选择对话框,默认选中红色
QColor color = QColorDialog::getColor(Qt::red, this, "选择颜色");
// 判断用户是否选择了有效颜色
if (color.isValid()) {
// 用户选择了颜色,应用到目标控件
label->setStyleSheet(QString("background-color: %1").arg(color.name()));
qDebug() << "选中的颜色:" << color.name(); // 如 "#ff0000"
}
使用示例 2:使用 open() 信号槽方式
cpp
QColorDialog *colorDialog = new QColorDialog(Qt::blue, this);
colorDialog->setAttribute(Qt::WA_DeleteOnClose);
// 当用户选择颜色后,触发 colorSelected 信号
connect(colorDialog, &QColorDialog::colorSelected, this, [](const QColor &color) {
qDebug() << "用户选择了颜色:" << color.name();
});
// 打开对话框(非模态方式,需要 WA_DeleteOnClose)
colorDialog->open();
6.3.3 QFileDialog ------ 文件对话框
QFileDialog 是用于打开或保存文件的标准对话框,是使用频率最高的内置对话框之一。
三个核心静态方法:
cpp
// 1. 获取单个打开文件的路径
QString QFileDialog::getOpenFileName(
QWidget *parent = nullptr,
const QString &caption = QString(), // 对话框标题
const QString &dir = QString(), // 初始打开的目录
const QString &filter = QString(), // 文件过滤器
QString *selectedFilter = nullptr, // 返回用户选择的过滤器
QFileDialog::Options options = Options()
);
// 2. 获取多个打开文件的路径
QStringList QFileDialog::getOpenFileNames(
QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options()
);
// 3. 获取保存文件的路径
QString QFileDialog::getSaveFileName(
QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options()
);
参数详解:
| 参数 | 类型 | 说明 |
|---|---|---|
parent |
QWidget* |
父窗口,对话框将显示在父窗口中央 |
caption |
QString |
对话框标题栏的文字 |
dir |
QString |
对话框打开时默认显示的目录路径 |
filter |
QString |
文件过滤器,用于过滤显示的文件类型 |
selectedFilter |
QString* |
输出参数,返回用户最终选择的过滤器 |
options |
Options |
对话框的选项标志 |
filter 过滤器的格式:
过滤器有特定的格式要求,每组分号分隔:
"描述文字 (*.扩展名1 *.扩展名2);;描述文字2 (*.扩展名3)"
使用示例 1:打开单个文件
cpp
QString fileName = QFileDialog::getOpenFileName(
this,
"选择图片文件",
"C:/Users/Pictures/",
"图片文件 (*.png *.jpg *.bmp);;所有文件 (*.*)"
);
if (!fileName.isEmpty()) {
qDebug() << "用户选择的文件:" << fileName;
// 使用 QPixmap 加载图片
QPixmap pixmap(fileName);
imageLabel->setPixmap(pixmap);
}
使用示例 2:打开多个文件
cpp
QStringList files = QFileDialog::getOpenFileNames(
this,
"选择多个文本文件",
"C:/Users/Documents/",
"文本文件 (*.txt);;所有文件 (*.*)"
);
for (const QString &file : files) {
qDebug() << "选择的文件:" << file;
}
使用示例 3:保存文件
cpp
QString savePath = QFileDialog::getSaveFileName(
this,
"另存为",
"C:/Users/Documents/untitled.txt",
"文本文件 (*.txt);;所有文件 (*.*)"
);
if (!savePath.isEmpty()) {
// 执行文件保存逻辑
QFile file(savePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream stream(&file);
stream << edit->toPlainText();
file.close();
}
}
6.3.4 QFontDialog ------ 字体对话框
QFontDialog 提供了一个标准的字体选择对话框,用户可以从中选择字体、字号、样式等。
核心静态方法:
cpp
// 弹出字体选择对话框并返回用户选择的字体
QFont QFontDialog::getFont(
bool *ok,
const QFont &initial,
QWidget *parent = nullptr,
const QString &title = QString(),
QFontDialog::FontDialogOptions options = FontDialogOptions()
);
使用示例:
cpp
bool ok;
QFont font = QFontDialog::getFont(
&ok, // 输出参数:用户是否点击了"确定"
QFont("微软雅黑", 12), // 初始字体
this, // 父窗口
"选择字体" // 对话框标题
);
if (ok) {
// 用户点击了"确定",应用选择的字体
edit->setFont(font);
qDebug() << "字体:" << font.family() << "大小:" << font.pointSize();
} else {
// 用户点击了"取消"
qDebug() << "用户取消了字体选择";
}
6.3.5 QInputDialog ------ 输入对话框
QInputDialog 提供了从用户获取单一值(整数、浮点数、字符串、列表项等)的标准输入对话框。
三个核心静态方法:
cpp
// 1. 获取浮点数
double QInputDialog::getDouble(
QWidget *parent,
const QString &title, // 对话框标题
const QString &label, // 提示标签文字
double value = 0, // 默认值
double min = -2147483647, // 最小值
double max = 2147483647, // 最大值
int decimals = 1, // 小数位数
bool *ok = nullptr, // 输出:用户是否点击确定
Qt::WindowFlags flags = Qt::WindowFlags()
);
// 2. 获取整数
int QInputDialog::getInt(
QWidget *parent,
const QString &title, // 对话框标题
const QString &label, // 提示标签文字
int value = 0, // 默认值
int min = -2147483647, // 最小值
int max = 2147483647, // 最大值
int step = 1, // 步长(点击上下箭头时的增量)
bool *ok = nullptr, // 输出:用户是否点击确定
Qt::WindowFlags flags = Qt::WindowFlags()
);
// 3. 从列表中选择一项
QString QInputDialog::getItem(
QWidget *parent,
const QString &title, // 对话框标题
const QString &label, // 提示标签文字
const QStringList &items, // 可选择的下拉列表项
int current = 0, // 默认选中的索引
bool editable = true, // 是否允许用户自行输入
bool *ok = nullptr, // 输出:用户是否点击确定
Qt::WindowFlags flags = Qt::WindowFlags(),
Qt::InputMethodHints inputMethodHints = Qt::ImhNone
);
参数说明:
| 参数 | 说明 |
|---|---|
parent |
父窗口,对话框将显示在父窗口中央 |
title |
对话框标题栏的文字 |
label |
输入框上方的提示标签文字 |
items |
getItem() 特有:下拉列表的选项列表 |
value |
默认值 |
min / max |
允许输入的最小/最大值 |
step |
getInt() 特有:点击上下箭头时的增量 |
decimals |
getDouble() 特有:小数位数 |
editable |
getItem() 特有:列表是否可编辑 |
ok |
输出参数,标识用户点击了"确定"(true)还是"取消"(false) |
使用示例 1:获取整数
cpp
bool ok;
int age = QInputDialog::getInt(
this,
"输入年龄",
"请输入您的年龄:",
18, // 默认值
1, // 最小值
150, // 最大值
1, // 步长
&ok
);
if (ok) {
qDebug() << "用户输入的年龄:" << age;
}
使用示例 2:获取浮点数
cpp
bool ok;
double price = QInputDialog::getDouble(
this,
"输入价格",
"请输入商品价格:",
0.00, // 默认值
0.00, // 最小值
99999.99, // 最大值
2, // 小数位数
&ok
);
if (ok) {
qDebug() << "用户输入的价格:" << price;
}
使用示例 3:从列表中选择一项
cpp
bool ok;
QStringList items;
items << "北京" << "上海" << "广州" << "深圳" << "杭州";
QString city = QInputDialog::getItem(
this,
"选择城市",
"请选择您所在的城市:",
items,
0, // 默认选中第一项
false, // 不允许用户自行输入(只能从列表中选择)
&ok
);
if (ok && !city.isEmpty()) {
qDebug() << "用户选择的城市:" << city;
}
总结
本章系统讲解了 Qt 中主窗口(QMainWindow)的完整架构体系,核心知识点回顾如下:
| 模块 | 核心类 | 关键方法 |
|---|---|---|
| 菜单栏 | QMenuBar / QMenu / QAction | menuBar(), addMenu(), addAction(), addSeparator() |
| 工具栏 | QToolBar | addToolBar(), setAllowedAreas(), setFloatable(), setMovable() |
| 状态栏 | QStatusBar | statusBar(), showMessage(), addWidget(), addPermanentWidget() |
| 停靠部件 | QDockWidget | addDockWidget(), setAllowedAreas(), setWidget() |
| 模态对话框 | QDialog | exec() → 返回 Accepted / Rejected |
| 非模态对话框 | QDialog | show() + setAttribute(Qt::WA_DeleteOnClose) |
| 消息框 | QMessageBox | information(), question(), warning(), critical() |
| 颜色对话框 | QColorDialog | getColor(), open() |
| 文件对话框 | QFileDialog | getOpenFileName(), getOpenFileNames(), getSaveFileName() |
| 字体对话框 | QFontDialog | getFont() |
| 输入对话框 | QInputDialog | getInt(), getDouble(), getItem() |
学习建议:
-
理解 QMainWindow 的布局结构是本章的基础------菜单栏在上、状态栏在下、工具栏可停靠四周、中心部件占据剩余空间、停靠部件可浮动可停靠。
-
QAction 的"一处创建,多处使用" 是 Qt 设计中的重要思想------同一个 QAction 可以同时出现在菜单栏和工具栏中,保持状态同步。
-
模态与非模态对话框的选择直接影响用户体验------对于必须确认的操作使用模态对话框;对于辅助性工具窗口使用非模态对话框。
-
内置对话框的静态方法是快速开发的利器------大多数情况下调用一行代码就能实现功能,不必每次自定义 QDialog 子类。
-
建议动手按照 [2.5 节的完整案例](#2.5 节的完整案例) 搭建一个文本编辑器原型,在其中依次添加菜单栏、工具栏、状态栏和各类型对话框,通过实践加深理解。