系列介绍

这是系列文章的第六篇,我将带你全面学习Qt的对话框系统。对话框是与用户交互的重要方式,掌握它们能让你的程序更加友好和专业。

🎯 本篇你将学到:

  • 标准消息对话框的使用

  • 文件对话框和输入对话框

  • 颜色和字体对话框

  • 进度对话框

  • 自定义对话框的创建

  • 向导对话框的实现

🏆 系列进度:

  • ✅ 第1篇:Qt环境搭建与第一个Hello World

  • ✅ 第2篇:Qt信号槽机制详解

  • ✅ 第3篇:Qt布局管理器完全指南

  • ✅ 第4篇:Qt样式表QSS美化教程

  • ✅ 第5篇:Qt常用控件全解析

  • 📝 第6篇:Qt对话框系统(本篇)

  • 🔜 第7篇:QProcess调用外部程序

让我们开始吧!


一、对话框概述

1.1 什么是对话框?

对话框是用于与用户进行短期交互的窗口,通常用于:

  • 显示信息或警告

  • 获取用户输入

  • 确认操作

  • 进度展示

  • 设置选项

┌─────────────────────────────────────────────────────────────┐
│                        主窗口                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │                内容区域                              │   │
│  │                                                     │   │
│  │    [打开文件] ← 点击按钮                             │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                          │
                          ▼ 弹出对话框
┌─────────────────────────────────────────────────────────────┐
│  ┌─────────────────────────────────────────────────────┐   │
│  │  选择文件                                   [×]      │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  📁 桌面                                    │   │   │
│  │  │  📄 文档1.pdf                               │   │   │
│  │  │  📄 文档2.pdf                               │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  │                                           [取消] [打开] │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

1.2 对话框的分类

类型 说明 常用类
模态对话框 阻止与其它窗口交互 大部分对话框
非模态对话框 可同时操作其他窗口 QDialog::setModal(false)
标准对话框 Qt内置的常用对话框 QMessageBox, QFileDialog
自定义对话框 用户自己设计的对话框 QDialog子类

1.3 模态 vs 非模态

// 模态对话框(阻塞)
QDialog dialog(this);
dialog.exec();  // 代码会阻塞在这里,直到对话框关闭

// 非模态对话框(不阻塞)
QDialog *dialog = new QDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);  // 关闭时自动删除
dialog->show();  // 不阻塞,立即返回

模态效果:

┌─────────────────┐     ┌─────────────────┐
│   主窗口        │     │   主窗口        │
│                 │     │  (不可点击)     │
│  [按钮] 灰色    │  →  │                 │
│                 │     │   ┌─────────┐   │
│                 │     │   │ 对话框  │   │
│                 │     │   └─────────┘   │
└─────────────────┘     └─────────────────┘

二、标准消息对话框

2.1 QMessageBox基础用法

QMessageBox是最常用的对话框,用于显示信息、警告、错误或询问确认。

// 最简单的用法
QMessageBox::information(this, "标题", "这是一条信息");

// 带按钮的确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "是否保存更改?",
                               QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);

if (reply == QMessageBox::Yes) {
    // 保存逻辑
} else if (reply == QMessageBox::No) {
    // 不保存逻辑
} else {
    // 取消操作
}

2.2 四种标准对话框

// 1. 信息对话框
QMessageBox::information(this, "提示", "操作已完成!");

// 2. 警告对话框
QMessageBox::warning(this, "警告", "文件不存在,请检查路径!");

// 3. 错误对话框
QMessageBox::critical(this, "错误", "程序遇到未知错误,即将退出。");

// 4. 提问对话框
QMessageBox::StandardButton result;
result = QMessageBox::question(this, "确认删除", 
                                "确定要删除选中的文件吗?\n此操作不可撤销!",
                                QMessageBox::Yes | QMessageBox::No);

视觉效果:

┌─────────────────────────────────────┐
│ 提示                          [×]   │
├─────────────────────────────────────┤
│  ℹ️                                 │
│  操作已完成!                       │
│                                     │
│                    [确定]           │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 确认删除                      [×]   │
├─────────────────────────────────────┤
│  ❓                                 │
│  确定要删除选中的文件吗?           │
│  此操作不可撤销!                   │
│                                     │
│              [是] [否] [取消]       │
└─────────────────────────────────────┘

2.3 高级定制

// 创建自定义消息框
QMessageBox msgBox(this);
msgBox.setWindowTitle("关于");
msgBox.setIcon(QMessageBox::Information);
msgBox.setText("<b>PDF全能工具箱</b>");
msgBox.setInformativeText("版本: 1.0.0\n作者: Your Name\n\n一个免费的PDF处理工具。");

// 添加自定义按钮
QPushButton *githubBtn = msgBox.addButton("访问GitHub", QMessageBox::ActionRole);
QPushButton *closeBtn = msgBox.addButton(QMessageBox::Close);

msgBox.exec();

if (msgBox.clickedButton() == githubBtn) {
    QDesktopServices::openUrl(QUrl("https://github.com/..."));
}

// 设置详细文本(错误详情)
QMessageBox errorBox;
errorBox.setIcon(QMessageBox::Critical);
errorBox.setWindowTitle("错误");
errorBox.setText("无法打开文件");
errorBox.setDetailedText("错误详情:\n文件路径:C:/test.pdf\n错误代码:0x80070002\n文件不存在。");
errorBox.exec();

带详细信息的错误框:

┌─────────────────────────────────────────────────┐
│ 错误                                      [×]   │
├─────────────────────────────────────────────────┤
│  ❌                                             │
│  无法打开文件                                   │
│                                                 │
│  详细信息 ▼                                     │
│  ┌───────────────────────────────────────────┐ │
│  │ 错误详情:                                 │ │
│  │ 文件路径:C:/test.pdf                     │ │
│  │ 错误代码:0x80070002                      │ │
│  │ 文件不存在。                              │ │
│  └───────────────────────────────────────────┘ │
│                                     [确定]      │
└─────────────────────────────────────────────────┘

2.4 可用的标准按钮

QMessageBox::StandardButton 类型:
- QMessageBox::Ok          // 确定
- QMessageBox::Cancel      // 取消
- QMessageBox::Yes         // 是
- QMessageBox::No          // 否
- QMessageBox::Save        // 保存
- QMessageBox::Discard     // 丢弃
- QMessageBox::Apply       // 应用
- QMessageBox::Reset       // 重置
- QMessageBox::Close       // 关闭
- QMessageBox::Abort       // 中止
- QMessageBox::Retry       // 重试
- QMessageBox::Ignore      // 忽略

2.5 辅助函数封装

// 在实际项目中,可以封装成方便使用的函数
class MessageBoxHelper {
public:
    static void showInfo(QWidget *parent, const QString &title, const QString &msg) {
        QMessageBox::information(parent, title, msg);
    }
    
    static void showError(QWidget *parent, const QString &title, const QString &msg) {
        QMessageBox::critical(parent, title, msg);
    }
    
    static bool confirm(QWidget *parent, const QString &title, const QString &msg) {
        return QMessageBox::question(parent, title, msg, 
                                     QMessageBox::Yes | QMessageBox::No) 
               == QMessageBox::Yes;
    }
    
    static bool confirmDelete(QWidget *parent, const QString &itemName) {
        return QMessageBox::question(parent, "确认删除",
                                     QString("确定要删除「%1」吗?\n此操作不可撤销!").arg(itemName),
                                     QMessageBox::Yes | QMessageBox::No)
               == QMessageBox::Yes;
    }
};

// 使用示例
MessageBoxHelper::showInfo(this, "提示", "操作成功!");

if (MessageBoxHelper::confirm(this, "确认", "是否保存更改?")) {
    // 保存逻辑
}

if (MessageBoxHelper::confirmDelete(this, filePath)) {
    QFile::remove(filePath);
}

三、文件对话框

3.1 QFileDialog基础用法

#include <QFileDialog>

// 打开单个文件
QString filePath = QFileDialog::getOpenFileName(this, 
                                                  "选择文件",
                                                  QDir::homePath(),
                                                  "PDF文件 (*.pdf);;所有文件 (*.*)");
if (!filePath.isEmpty()) {
    qDebug() << "选择的文件:" << filePath;
}

// 打开多个文件
QStringList files = QFileDialog::getOpenFileNames(this,
                                                   "选择多个文件",
                                                   QDir::homePath(),
                                                   "PDF文件 (*.pdf)");
foreach (QString file, files) {
    qDebug() << file;
}

// 保存文件对话框
QString savePath = QFileDialog::getSaveFileName(this,
                                                 "保存文件",
                                                 QDir::homePath() + "/未命名.pdf",
                                                 "PDF文件 (*.pdf)");

// 选择目录
QString dirPath = QFileDialog::getExistingDirectory(this,
                                                     "选择目录",
                                                     QDir::homePath());

3.2 文件过滤器详解

// 单个过滤器
QString filter = "图片文件 (*.png *.jpg *.bmp)";

// 多个过滤器(用;;分隔)
QString filter = "PDF文件 (*.pdf);;Word文件 (*.doc *.docx);;Excel文件 (*.xls *.xlsx);;所有文件 (*.*)";

// 使用示例
QString filePath = QFileDialog::getOpenFileName(this, "打开文件", 
                                                  QDir::homePath(),
                                                  filter,
                                                  &selectedFilter);  // 返回用户选择的过滤器

3.3 高级选项

QFileDialog dialog(this);
dialog.setWindowTitle("选择文件");
dialog.setFileMode(QFileDialog::ExistingFiles);  // 多选模式
dialog.setNameFilter("PDF文件 (*.pdf)");
dialog.setViewMode(QFileDialog::Detail);  // 详细视图(列表+详情)
dialog.setOption(QFileDialog::DontUseNativeDialog, false);  // 使用系统原生对话框

// 设置默认后缀
dialog.setDefaultSuffix("pdf");

if (dialog.exec()) {
    QStringList files = dialog.selectedFiles();
    // 处理文件...
}

// 可用的文件模式
// QFileDialog::AnyFile         任意文件(保存时)
// QFileDialog::ExistingFile    已存在的文件(打开时)
// QFileDialog::ExistingFiles   多个文件
// QFileDialog::Directory       目录
// QFileDialog::DirectoryOnly   仅目录

四、输入对话框

4.1 QInputDialog基础用法

#include <QInputDialog>

// 文本输入
bool ok;
QString text = QInputDialog::getText(this, "输入姓名", 
                                      "请输入您的姓名:",
                                      QLineEdit::Normal,
                                      "默认值",
                                      &ok);
if (ok && !text.isEmpty()) {
    qDebug() << "输入的姓名:" << text;
}

// 整数输入
int age = QInputDialog::getInt(this, "年龄输入",
                                "请输入年龄:",
                                18,    // 默认值
                                1,     // 最小值
                                150,   // 最大值
                                1,     // 步长
                                &ok);
if (ok) {
    qDebug() << "年龄:" << age;
}

// 浮点数输入
double price = QInputDialog::getDouble(this, "价格输入",
                                        "请输入价格:",
                                        99.99,  // 默认值
                                        0,      // 最小值
                                        9999,   // 最大值
                                        2,      // 小数位数
                                        &ok);

// 下拉选择
QStringList items = {"北京", "上海", "广州", "深圳"};
QString city = QInputDialog::getItem(this, "选择城市",
                                      "请选择所在城市:",
                                      items,
                                      0,    // 当前选中项索引
                                      false, // 是否可编辑
                                      &ok);

// 多行文本输入
QString comments = QInputDialog::getMultiLineText(this, "输入备注",
                                                   "请输入备注信息:",
                                                   "默认备注内容",
                                                   &ok);

视觉效果:

┌─────────────────────────────────────┐
│ 输入姓名                      [×]   │
├─────────────────────────────────────┤
│  请输入您的姓名:                     │
│  ┌─────────────────────────────┐    │
│  │ 张三                        │    │
│  └─────────────────────────────┘    │
│                                     │
│                    [取消] [确定]    │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 选择城市                      [×]   │
├─────────────────────────────────────┤
│  请选择所在城市:                     │
│  ┌─────────────────────────────┐    │
│  │ 北京                  ▼     │    │
│  └─────────────────────────────┘    │
│                                     │
│                    [取消] [确定]    │
└─────────────────────────────────────┘

4.2 实际应用场景

// PDF工具箱中的实际使用
void MainWindow::onSplitButtonClicked()
{
    int pageCount = pdfCore->getPageCount(currentFiles.first());
    
    bool ok;
    QString rangeStr = QInputDialog::getText(this, "分割PDF",
        QString("页码范围(如: 1-5,6-10,11-15)\n总页数: %1").arg(pageCount),
        QLineEdit::Normal, 
        "1-10",
        &ok);
    
    if (!ok || rangeStr.isEmpty()) {
        return;  // 用户取消了
    }
    
    // 解析并执行分割...
}

void MainWindow::onCompressButtonClicked()
{
    bool ok;
    int quality = QInputDialog::getInt(this, "压缩PDF",
                                        "压缩质量 (1-100):\n值越小文件越小,质量越低",
                                        70,
                                        1,
                                        100,
                                        10,
                                        &ok);
    if (ok) {
        // 执行压缩...
    }
}

五、颜色和字体对话框

5.1 QColorDialog(颜色选择)

#include <QColorDialog>

// 选择颜色
QColor color = QColorDialog::getColor(Qt::white, this, "选择颜色");

if (color.isValid()) {
    // 应用颜色
    label->setStyleSheet(QString("color: %1").arg(color.name()));
    qDebug() << "选择的颜色:" << color.name();  // #RRGGBB格式
    qDebug() << "RGB:" << color.red() << color.green() << color.blue();
}

// 高级用法
QColorDialog dialog(this);
dialog.setCurrentColor(Qt::blue);
dialog.setOptions(QColorDialog::ShowAlphaChannel);  // 显示透明度
if (dialog.exec()) {
    QColor color = dialog.currentColor();
    // 使用颜色...
}

5.2 QFontDialog(字体选择)

#include <QFontDialog>

// 选择字体
bool ok;
QFont font = QFontDialog::getFont(&ok, QFont("微软雅黑", 12), this, "选择字体");

if (ok) {
    textEdit->setFont(font);
    qDebug() << "字体:" << font.family() << "大小:" << font.pointSize();
}

// 高级用法
QFontDialog dialog(this);
dialog.setCurrentFont(QFont("Consolas", 14));
dialog.setOptions(QFontDialog::MonospacedFonts);  // 只显示等宽字体
if (dialog.exec()) {
    QFont font = dialog.currentFont();
    // 应用字体...
}

六、进度对话框

6.1 QProgressDialog(带取消的进度条)

#include <QProgressDialog>

void longRunningOperation()
{
    int totalSteps = 100;
    
    QProgressDialog progress("正在处理...", "取消", 0, totalSteps, this);
    progress.setWindowTitle("进度");
    progress.setWindowModality(Qt::WindowModal);  // 模态
    progress.setMinimumDuration(0);  // 立即显示
    progress.setValue(0);
    
    for (int i = 0; i <= totalSteps; i++) {
        if (progress.wasCanceled()) {
            // 用户取消了操作
            QMessageBox::information(this, "提示", "操作已取消");
            break;
        }
        
        // 执行耗时操作
        doWork(i);
        
        // 更新进度
        progress.setValue(i);
        progress.setLabelText(QString("正在处理... %1%").arg(i));
        
        // 让界面保持响应
        QCoreApplication::processEvents();
    }
    
    progress.setValue(totalSteps);
}

6.2 自定义进度对话框

// 实际项目中更优雅的进度处理方式
class ProgressHelper : public QObject
{
    Q_OBJECT
    
public:
    ProgressHelper(QWidget *parent = nullptr) : QObject(parent)
    {
        m_progress = new QProgressDialog("正在处理...", "取消", 0, 100, parent);
        m_progress->setWindowTitle("进度");
        m_progress->setWindowModality(Qt::WindowModal);
        m_progress->setMinimumDuration(0);
        m_progress->setAutoClose(false);  // 完成后不自动关闭
        m_progress->setAutoReset(false);
    }
    
    bool isCanceled() const { return m_progress->wasCanceled(); }
    
    void setRange(int min, int max) {
        m_progress->setRange(min, max);
    }
    
    void setValue(int value) {
        m_progress->setValue(value);
        QCoreApplication::processEvents();
    }
    
    void setText(const QString &text) {
        m_progress->setLabelText(text);
    }
    
    void finish() {
        m_progress->setValue(m_progress->maximum());
        m_progress->close();
    }
    
private:
    QProgressDialog *m_progress;
};

七、自定义对话框

7.1 创建自定义对话框类

// aboutdialog.h
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>

class AboutDialog : public QDialog
{
    Q_OBJECT

public:
    explicit AboutDialog(QWidget *parent = nullptr);
    
private:
    QLabel *m_titleLabel;
    QLabel *m_versionLabel;
    QLabel *m_descriptionLabel;
    QPushButton *m_closeButton;
};

#endif
// aboutdialog.cpp
#include "aboutdialog.h"

AboutDialog::AboutDialog(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle("关于");
    setFixedSize(400, 300);
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
    
    // 创建控件
    m_titleLabel = new QLabel("PDF全能工具箱");
    QFont titleFont = m_titleLabel->font();
    titleFont.setPointSize(18);
    titleFont.setBold(true);
    m_titleLabel->setFont(titleFont);
    m_titleLabel->setAlignment(Qt::AlignCenter);
    
    m_versionLabel = new QLabel("版本 1.0.0");
    m_versionLabel->setAlignment(Qt::AlignCenter);
    
    m_descriptionLabel = new QLabel(
        "一款完全免费的PDF处理工具\n\n"
        "功能:\n"
        "• 合并/分割PDF\n"
        "• 提取/旋转页面\n"
        "• 插入/删除页面\n"
        "• 压缩/加密/解密\n\n"
        "基于Qt和qpdf开发\n"
        "版权所有 © 2026"
    );
    m_descriptionLabel->setAlignment(Qt::AlignCenter);
    m_descriptionLabel->setWordWrap(true);
    
    m_closeButton = new QPushButton("关闭");
    
    // 布局
    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->addWidget(m_titleLabel);
    layout->addWidget(m_versionLabel);
    layout->addSpacing(20);
    layout->addWidget(m_descriptionLabel);
    layout->addSpacing(20);
    layout->addWidget(m_closeButton);
    
    // 连接信号
    connect(m_closeButton, &QPushButton::clicked, this, &QDialog::accept);
}

7.2 更复杂的自定义对话框

// inputdialog.h - 获取用户名和密码的自定义对话框
class LoginDialog : public QDialog
{
    Q_OBJECT

public:
    explicit LoginDialog(QWidget *parent = nullptr);
    
    QString getUsername() const { return m_usernameEdit->text(); }
    QString getPassword() const { return m_passwordEdit->text(); }
    
private:
    QLineEdit *m_usernameEdit;
    QLineEdit *m_passwordEdit;
    QPushButton *m_loginButton;
    QPushButton *m_cancelButton;
};

// inputdialog.cpp
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
{
    setWindowTitle("登录");
    setFixedSize(350, 200);
    setModal(true);
    
    // 创建控件
    QLabel *usernameLabel = new QLabel("用户名:");
    QLabel *passwordLabel = new QLabel("密码:");
    
    m_usernameEdit = new QLineEdit;
    m_usernameEdit->setPlaceholderText("请输入用户名");
    
    m_passwordEdit = new QLineEdit;
    m_passwordEdit->setEchoMode(QLineEdit::Password);
    m_passwordEdit->setPlaceholderText("请输入密码");
    
    m_loginButton = new QPushButton("登录");
    m_loginButton->setDefault(true);  // 回车键触发
    m_cancelButton = new QPushButton("取消");
    
    // 布局
    QFormLayout *formLayout = new QFormLayout;
    formLayout->addRow(usernameLabel, m_usernameEdit);
    formLayout->addRow(passwordLabel, m_passwordEdit);
    
    QHBoxLayout *buttonLayout = new QHBoxLayout;
    buttonLayout->addStretch();
    buttonLayout->addWidget(m_loginButton);
    buttonLayout->addWidget(m_cancelButton);
    
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addLayout(formLayout);
    mainLayout->addLayout(buttonLayout);
    
    // 信号连接
    connect(m_loginButton, &QPushButton::clicked, this, &QDialog::accept);
    connect(m_cancelButton, &QPushButton::clicked, this, &QDialog::reject);
    
    // 输入验证:用户名和密码不能为空
    auto validate = [this]() {
        bool ok = !m_usernameEdit->text().isEmpty() && 
                  !m_passwordEdit->text().isEmpty();
        m_loginButton->setEnabled(ok);
    };
    
    connect(m_usernameEdit, &QLineEdit::textChanged, validate);
    connect(m_passwordEdit, &QLineEdit::textChanged, validate);
    
    validate();  // 初始状态
}

// 使用
void MainWindow::onLogin()
{
    LoginDialog dialog(this);
    if (dialog.exec() == QDialog::Accepted) {
        QString username = dialog.getUsername();
        QString password = dialog.getPassword();
        // 处理登录逻辑...
        qDebug() << "登录用户名:" << username;
    }
}

八、向导对话框

8.1 使用QWizard创建安装向导

#include <QWizard>
#include <QWizardPage>

// 第一个页面:欢迎页
class WelcomePage : public QWizardPage
{
public:
    WelcomePage(QWidget *parent = nullptr) : QWizardPage(parent)
    {
        setTitle("欢迎使用PDF工具箱");
        
        QLabel *label = new QLabel(
            "本向导将帮助您完成PDF工具箱的安装。\n\n"
            "PDF工具箱是一款免费的PDF处理工具,"
            "支持合并、分割、压缩、加密等功能。\n\n"
            "点击「下一步」继续。"
        );
        label->setWordWrap(true);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(label);
    }
};

// 第二个页面:安装路径选择
class InstallPathPage : public QWizardPage
{
public:
    InstallPathPage(QWidget *parent = nullptr) : QWizardPage(parent)
    {
        setTitle("选择安装路径");
        
        m_pathEdit = new QLineEdit;
        m_pathEdit->setText("C:/Program Files/PDFToolbox");
        
        QPushButton *browseBtn = new QPushButton("浏览...");
        
        QHBoxLayout *pathLayout = new QHBoxLayout;
        pathLayout->addWidget(m_pathEdit);
        pathLayout->addWidget(browseBtn);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(new QLabel("请选择安装目录:"));
        layout->addLayout(pathLayout);
        
        registerField("installPath*", m_pathEdit);  // *表示必填
        
        connect(browseBtn, &QPushButton::clicked, this, &InstallPathPage::browse);
    }
    
    QString getInstallPath() const { return m_pathEdit->text(); }
    
private:
    void browse()
    {
        QString dir = QFileDialog::getExistingDirectory(this, "选择安装目录",
                                                        m_pathEdit->text());
        if (!dir.isEmpty()) {
            m_pathEdit->setText(dir);
        }
    }
    
    QLineEdit *m_pathEdit;
};

// 第三个页面:组件选择
class ComponentsPage : public QWizardPage
{
public:
    ComponentsPage(QWidget *parent = nullptr) : QWizardPage(parent)
    {
        setTitle("选择组件");
        
        m_coreCheck = new QCheckBox("核心程序(必需)");
        m_coreCheck->setChecked(true);
        m_coreCheck->setEnabled(false);
        
        m_shortcutCheck = new QCheckBox("创建桌面快捷方式");
        m_shortcutCheck->setChecked(true);
        
        m_startMenuCheck = new QCheckBox("添加到开始菜单");
        m_startMenuCheck->setChecked(true);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(new QLabel("请选择要安装的组件:"));
        layout->addWidget(m_coreCheck);
        layout->addWidget(m_shortcutCheck);
        layout->addWidget(m_startMenuCheck);
        layout->addStretch();
        
        registerField("createShortcut", m_shortcutCheck);
        registerField("addToStartMenu", m_startMenuCheck);
    }
    
private:
    QCheckBox *m_coreCheck;
    QCheckBox *m_shortcutCheck;
    QCheckBox *m_startMenuCheck;
};

// 第四个页面:完成页
class FinishPage : public QWizardPage
{
public:
    FinishPage(QWidget *parent = nullptr) : QWizardPage(parent)
    {
        setTitle("安装完成");
        
        QLabel *label = new QLabel(
            "PDF工具箱已成功安装!\n\n"
            "点击「完成」退出安装向导。"
        );
        label->setWordWrap(true);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(label);
    }
};

// 创建向导
void showInstallWizard()
{
    QWizard wizard;
    wizard.setWindowTitle("PDF工具箱安装向导");
    wizard.setWizardStyle(QWizard::ModernStyle);
    
    wizard.addPage(new WelcomePage);
    wizard.addPage(new InstallPathPage);
    wizard.addPage(new ComponentsPage);
    wizard.addPage(new FinishPage);
    
    if (wizard.exec() == QWizard::Accepted) {
        // 获取用户选择
        QString installPath = wizard.field("installPath").toString();
        bool createShortcut = wizard.field("createShortcut").toBool();
        bool addToStartMenu = wizard.field("addToStartMenu").toBool();
        
        qDebug() << "安装路径:" << installPath;
        qDebug() << "创建快捷方式:" << createShortcut;
        qDebug() << "添加到开始菜单:" << addToStartMenu;
        
        // 执行安装逻辑...
    }
}

九、对话框最佳实践

9.1 内存管理

// 模态对话框:可以栈上创建
{
    QMessageBox::information(this, "提示", "消息");
}

// 非模态对话框:堆上创建,设置关闭时删除
QDialog *dialog = new QDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();

// 或者使用智能指针
std::unique_ptr<QDialog> dialog(new QDialog(this));
dialog->show();

9.2 中心位置设置

void showDialogCentered(QDialog *dialog)
{
    // 移动到父窗口中心
    if (dialog->parentWidget()) {
        int x = dialog->parentWidget()->x() + 
                (dialog->parentWidget()->width() - dialog->width()) / 2;
        int y = dialog->parentWidget()->y() + 
                (dialog->parentWidget()->height() - dialog->height()) / 2;
        dialog->move(x, y);
    }
    dialog->show();
}

9.3 防止多个对话框

class MainWindow : public QMainWindow
{
private:
    QDialog *m_settingsDialog = nullptr;
    
    void showSettings()
    {
        // 避免重复创建
        if (m_settingsDialog && m_settingsDialog->isVisible()) {
            m_settingsDialog->raise();
            m_settingsDialog->activateWindow();
            return;
        }
        
        m_settingsDialog = new SettingsDialog(this);
        m_settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
        m_settingsDialog->show();
    }
};

十、本课小结

通过本篇学习,你掌握了:

✅ 标准消息对话框(QMessageBox)
✅ 文件对话框(QFileDialog)
✅ 输入对话框(QInputDialog)
✅ 颜色和字体对话框
✅ 进度对话框(QProgressDialog)
✅ 自定义对话框的创建
✅ 向导对话框(QWizard)

下一课预告:

第7篇「QProcess调用外部程序」——我们将学习如何在Qt程序中调用外部命令行程序,获取输出,并实现PDF工具箱背后的qpdf调用!


系列进度

状态 篇数 文章标题
✅ 已完成 第1篇 Qt环境搭建与第一个Hello World
✅ 已完成 第2篇 Qt信号槽机制详解
✅ 已完成 第3篇 Qt布局管理器完全指南
✅ 已完成 第4篇 Qt样式表QSS美化教程
✅ 已完成 第5篇 Qt常用控件全解析
✅ 已完成 第6篇 Qt对话框系统
🔜 待发布 第7篇 QProcess调用外部程序

如果觉得有用,欢迎点赞、收藏、关注

下期见!👋

更多推荐