Qt窗口详解

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 的三大用途

  1. 菜单项 :通过 QMenu::addAction() 添加到菜单
  2. 工具栏按钮 :通过 QToolBar::addAction() 添加到工具栏
  3. 独立快捷键 :通过 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);
}

要点总结

  1. QMainWindow 通过 setMenuBar() 设置菜单栏
  2. QMenuBar 通过 addMenu() 添加菜单
  3. QMenu 通过 addAction() 添加菜单项
  4. QActiontriggered() 信号用于响应菜单项点击
  5. setCentralWidget() 设置窗口中心部件,占据菜单栏、工具栏和状态栏之外的全部剩余空间
  6. 本案例中的"保存"和"打开"功能使用了 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);

setFloatablesetMovable 的区别

  • 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 创建的对话框为例):

  1. 在 Qt Creator 项目中通过 Designer 创建一个新的对话框界面(如添加 Dialog 类,生成 dialog.uidialog.hdialog.cpp
  2. 在主窗口的某个触发函数(如菜单项点击的槽函数)中创建对话框对象并调用 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()

学习建议

  1. 理解 QMainWindow 的布局结构是本章的基础------菜单栏在上、状态栏在下、工具栏可停靠四周、中心部件占据剩余空间、停靠部件可浮动可停靠。

  2. QAction 的"一处创建,多处使用" 是 Qt 设计中的重要思想------同一个 QAction 可以同时出现在菜单栏和工具栏中,保持状态同步。

  3. 模态与非模态对话框的选择直接影响用户体验------对于必须确认的操作使用模态对话框;对于辅助性工具窗口使用非模态对话框。

  4. 内置对话框的静态方法是快速开发的利器------大多数情况下调用一行代码就能实现功能,不必每次自定义 QDialog 子类。

  5. 建议动手按照 [2.5 节的完整案例](#2.5 节的完整案例) 搭建一个文本编辑器原型,在其中依次添加菜单栏、工具栏、状态栏和各类型对话框,通过实践加深理解。


相关推荐
这个DBA有点耶3 小时前
COUNT进阶:超大表的近似计数与HyperLogLog
数据库·sql·程序人生·学习方法·dba·改行学it
武子康3 小时前
调查研究-138 全球机器人产业深度调研报告【01 篇】:市场规模、竞争格局与商业化成熟 2026
服务器·数据库·ai·chatgpt·机器人·具身智能
zhojiew3 小时前
在本地PostgreSQL使用pgvector构建生成式 AI 应用的实践
数据库·人工智能·postgresql
likerhood4 小时前
Java static 关键字从浅入深
java·开发语言
Yushan Bai4 小时前
EXADATA X5数据库一体机节点login: failure forking: Cannot allocate memory问题处理
数据库·oracle·vr
zh_xuan4 小时前
解决VS Code 控制台中文乱码
c++·vscode·乱码
郭涤生4 小时前
飞凌 RK3588 开发板同显 / 异显模式切换
c++·rk3588
KaMeidebaby4 小时前
卡梅德生物技术快报|噬菌体肽库展示技术构建 Mhp168‑Hsp70 定向随机肽库:流程、质控与数据结果
前端·数据库·其他·百度·新浪微博
猫猫的小茶馆4 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32