QT入门第五天:对话框QDialog详解 | 零基础学QT

QT入门第五天:对话框QDialog详解 | 零基础学QT

前言

前四天我们学习了环境搭建、信号与槽、常用控件、布局管理器,今天我们来学习对话框(QDialog)

做软件的时候,我们经常需要弹出一些小窗口和用户交互,比如:

  • 弹出一个提示"操作成功"
  • 删除文件前问一句"确定要删除吗?"
  • 让用户输入一些文字
  • 选择一个文件打开或保存
  • 选择颜色、字体

这些小窗口就是对话框。QT已经帮我们做好了很多常用的对话框,直接调用就行,不用自己从零开始写。

今天我们学习:

  • 模态对话框 vs 非模态对话框
  • QMessageBox 消息框(最常用)
  • QInputDialog 输入对话框
  • QFileDialog 文件对话框
  • QColorDialog 颜色对话框
  • QFontDialog 字体对话框
  • 自定义对话框

一、模态对话框 vs 非模态对话框

在学习具体的对话框之前,先搞清楚两个重要概念:

1.1 模态对话框(Modal)

模态对话框弹出来之后,你不能操作后面的主窗口,必须先把这个对话框关掉,才能继续操作主窗口。

生活类比:就像你去银行办业务,柜员叫你填单子,你必须填完单子交给他,才能继续办下一个业务。

常见场景

  • 删除确认框(必须选是或否)
  • 登录对话框(必须登录才能用软件)
  • 重要的提示信息

1.2 非模态对话框(Modeless)

非模态对话框弹出来之后,你还可以操作后面的主窗口,两者互不影响。

生活类比:就像你一边看电视一边吃零食,电视还在播,你也可以吃零食,互不干扰。

常见场景

  • 查找替换对话框
  • 工具箱面板
  • 帮助窗口

1.3 怎么用

cpp 复制代码
// 模态对话框:用 exec()
QDialog dialog;
dialog.exec();  // 程序会停在这里,直到对话框关闭

// 非模态对话框:用 show()
QDialog *dialog = new QDialog(this);
dialog->show();  // 弹出来就完事,程序继续往下走

💡 记住:模态用 exec(),非模态用 show()。大部分常用对话框都是模态的。

二、QMessageBox 消息框

QMessageBox是最常用的对话框,用来显示提示信息,或者问用户一些简单的问题。

2.1 信息提示框

cpp 复制代码
#include <QMessageBox>

// 信息提示(蓝色i图标)
QMessageBox::information(this, "提示", "操作成功!");

运行效果:弹出一个对话框,标题是"提示",内容是"操作成功!",有一个"确定"按钮。

2.2 警告框

cpp 复制代码
// 警告提示(黄色感叹号图标)
QMessageBox::warning(this, "警告", "您的磁盘空间不足!");

2.3 错误框

cpp 复制代码
// 错误提示(红色叉号图标)
QMessageBox::critical(this, "错误", "文件打开失败!");

2.4 询问框

这个最实用,问用户"是"还是"否":

cpp 复制代码
// 询问(问号图标,有是和否两个按钮)
int ret = QMessageBox::question(this, "确认", "确定要删除吗?");

if (ret == QMessageBox::Yes) {
    qDebug() << "用户点了是,执行删除";
} else {
    qDebug() << "用户点了否,取消删除";
}

2.5 关于框

cpp 复制代码
// 关于对话框(显示软件信息)
QMessageBox::about(this, "关于", "我的软件 v1.0\n作者:小明");

2.6 自定义按钮

默认的按钮不够用?你还可以自定义按钮:

cpp 复制代码
// 自定义按钮:保存、不保存、取消
int ret = QMessageBox::question(
    this,
    "提示",
    "文件已修改,是否保存?",
    QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
    QMessageBox::Save  // 默认选中的按钮
);

if (ret == QMessageBox::Save) {
    qDebug() << "保存";
} else if (ret == QMessageBox::Discard) {
    qDebug() << "不保存";
} else {
    qDebug() << "取消";
}

常用按钮常量:

  • QMessageBox::Ok - 确定
  • QMessageBox::Cancel - 取消
  • QMessageBox::Yes - 是
  • QMessageBox::No - 否
  • QMessageBox::Save - 保存
  • QMessageBox::Discard - 不保存/丢弃
  • QMessageBox::Abort - 中止
  • QMessageBox::Retry - 重试
  • QMessageBox::Ignore - 忽略

💡 小技巧:多个按钮用 |(按位或)连起来。

三、QInputDialog 输入对话框

QInputDialog用来让用户输入一些内容,比如输入文字、数字、从列表中选一项。

3.1 输入文字

cpp 复制代码
#include <QInputDialog>

bool ok;
QString text = QInputDialog::getText(
    this,
    "输入",
    "请输入你的名字:",
    QLineEdit::Normal,  // 输入模式(正常/密码等)
    "默认文字",          // 默认值
    &ok                 // 用户是否点了确定
);

if (ok && !text.isEmpty()) {
    qDebug() << "用户输入了:" << text;
}

3.2 输入数字

cpp 复制代码
// 输入整数
int num = QInputDialog::getInt(
    this,
    "输入",
    "请输入一个数字:",
    10,     // 默认值
    0,      // 最小值
    100,    // 最大值
    1,      // 步长(点一下加减多少)
    &ok
);

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

3.3 下拉选择

让用户从一个列表中选一项:

cpp 复制代码
QStringList items;
items << "苹果" << "香蕉" << "橙子" << "葡萄";

QString item = QInputDialog::getItem(
    this,
    "选择",
    "请选择你喜欢的水果:",
    items,      // 选项列表
    0,          // 默认选中第几项
    false,      // 是否可编辑(false只能选,true还能自己输入)
    &ok
);

if (ok) {
    qDebug() << "用户选择了:" << item;
}

3.4 多行文本

cpp 复制代码
QString text = QInputDialog::getMultiLineText(
    this,
    "输入",
    "请输入备注:",
    "默认内容",
    &ok
);

四、QFileDialog 文件对话框

QFileDialog用来让用户选择文件或目录,比如打开文件、保存文件、选择文件夹。

4.1 打开单个文件

cpp 复制代码
#include <QFileDialog>

QString fileName = QFileDialog::getOpenFileName(
    this,
    "打开文件",           // 对话框标题
    "",                  // 默认打开的目录(空表示当前目录)
    "文本文件 (*.txt);;图片文件 (*.png *.jpg);;所有文件 (*.*)"  // 文件过滤器
);

if (!fileName.isEmpty()) {
    qDebug() << "选择的文件:" << fileName;
}

文件过滤器格式说明:

  • "文本文件 (*.txt)" - 显示txt文件
  • 多个类型用 ;; 分隔
  • 同一类型多个后缀用空格分隔,如 "图片文件 (*.png *.jpg)"

4.2 打开多个文件

cpp 复制代码
QStringList fileNames = QFileDialog::getOpenFileNames(
    this,
    "选择多个文件",
    "",
    "所有文件 (*.*)"
);

qDebug() << "选择了" << fileNames.size() << "个文件";

4.3 保存文件

cpp 复制代码
QString fileName = QFileDialog::getSaveFileName(
    this,
    "保存文件",
    "untitled.txt",      // 默认文件名
    "文本文件 (*.txt)"
);

if (!fileName.isEmpty()) {
    qDebug() << "保存到:" << fileName;
}

4.4 选择目录

cpp 复制代码
QString dirName = QFileDialog::getExistingDirectory(
    this,
    "选择文件夹",
    ""
);

if (!dirName.isEmpty()) {
    qDebug() << "选择的文件夹:" << dirName;
}

五、QColorDialog 颜色对话框

QColorDialog用来让用户选择颜色,比如设置文字颜色、背景颜色。

cpp 复制代码
#include <QColorDialog>

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

if (color.isValid()) {
    qDebug() << "选择的颜色:" << color.name();  // 比如 #ff0000
}

选完颜色后,你可以用这个颜色来设置控件的样式:

cpp 复制代码
// 比如设置按钮的背景色
QPushButton *btn = new QPushButton("按钮");
btn->setStyleSheet(QString("background-color: %1;").arg(color.name()));

六、QFontDialog 字体对话框

QFontDialog用来让用户选择字体,比如设置文字的字体、大小、加粗、斜体。

cpp 复制代码
#include <QFontDialog>

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

if (ok) {
    qDebug() << "字体:" << font.family();
    qDebug() << "大小:" << font.pointSize();
    qDebug() << "加粗:" << font.bold();
    qDebug() << "斜体:" << font.italic();
}

设置字体给控件:

cpp 复制代码
QLabel *label = new QLabel("Hello");
label->setFont(font);

七、QProgressDialog 进度对话框

QProgressDialog用来显示操作进度,比如复制文件、下载文件的时候显示进度条。

cpp 复制代码
#include <QProgressDialog>

QProgressDialog progress("正在处理...", "取消", 0, 100, this);
progress.setWindowTitle("进度");
progress.setWindowModality(Qt::WindowModal);  // 窗口模态

for (int i = 0; i <= 100; i++) {
    progress.setValue(i);  // 设置当前进度
    
    if (progress.wasCanceled()) {
        qDebug() << "用户取消了";
        break;
    }
    
    // 模拟耗时操作(实际项目中这里是你的业务逻辑)
    // 注意:实际开发不要用sleep,会卡住界面
    // 这里只是演示
}

progress.setValue(100);  // 完成

八、自定义对话框

内置的对话框不够用?没关系,我们可以自己做对话框!

8.1 创建自定义对话框

第一步,新建一个QT设计师界面类(或者纯代码写),继承自QDialog。

我们用纯代码来演示:

cpp 复制代码
// 自定义对话框类
class MyDialog : public QDialog {
    Q_OBJECT

public:
    MyDialog(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("自定义对话框");
        resize(300, 200);

        // 布局
        QVBoxLayout *layout = new QVBoxLayout(this);

        // 输入框
        m_edit = new QLineEdit();
        m_edit->setPlaceholderText("请输入内容");
        layout->addWidget(m_edit);

        // 按钮(水平布局)
        QHBoxLayout *btnLayout = new QHBoxLayout();
        QPushButton *okBtn = new QPushButton("确定");
        QPushButton *cancelBtn = new QPushButton("取消");
        btnLayout->addStretch();
        btnLayout->addWidget(okBtn);
        btnLayout->addWidget(cancelBtn);
        layout->addLayout(btnLayout);

        // 连接信号
        connect(okBtn, &QPushButton::clicked, this, &QDialog::accept);   // 确定,返回Accepted
        connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); // 取消,返回Rejected
    }

    // 获取输入的内容
    QString getText() const {
        return m_edit->text();
    }

private:
    QLineEdit *m_edit;
};

8.2 使用自定义对话框

cpp 复制代码
// 在主窗口中调用
MyDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
    QString text = dlg.getText();
    qDebug() << "用户输入了:" << text;
} else {
    qDebug() << "用户取消了";
}

8.3 对话框返回值

  • accept() - 确定,返回 QDialog::Accepted(值为1)
  • reject() - 取消,返回 QDialog::Rejected(值为0)
  • done(int r) - 自定义返回值

你也可以自己定义返回值:

cpp 复制代码
done(123);  // exec()就会返回123

8.4 主窗口和对话框传值

主窗口 → 对话框:通过构造函数或set方法传进去

cpp 复制代码
// 对话框加一个set方法
void setText(const QString &text) {
    m_edit->setText(text);
}

// 主窗口调用
MyDialog dlg(this);
dlg.setText("初始内容");  // 把主窗口的数据传给对话框
dlg.exec();

对话框 → 主窗口:通过get方法拿出来

cpp 复制代码
// 就是我们上面写的getText()
QString text = dlg.getText();

九、综合实战:记事本的菜单功能

我们来做一个简单的记事本界面,把今天学的对话框都用上:

cpp 复制代码
#include "mainwindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QTextEdit>
#include <QMessageBox>
#include <QInputDialog>
#include <QFileDialog>
#include <QColorDialog>
#include <QFontDialog>
#include <QFile>
#include <QTextStream>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle("简易记事本");
    resize(600, 400);

    // 文本编辑区
    m_textEdit = new QTextEdit(this);
    setCentralWidget(m_textEdit);

    // ===== 菜单栏 =====
    QMenuBar *menuBar = this->menuBar();

    // 文件菜单
    QMenu *fileMenu = menuBar->addMenu("文件(&F)");

    QAction *openAct = fileMenu->addAction("打开(&O)");
    openAct->setShortcut(QKeySequence::Open);  // Ctrl+O
    connect(openAct, &QAction::triggered, this, &MainWindow::openFile);

    QAction *saveAct = fileMenu->addAction("保存(&S)");
    saveAct->setShortcut(QKeySequence::Save);  // Ctrl+S
    connect(saveAct, &QAction::triggered, this, &MainWindow::saveFile);

    fileMenu->addSeparator();  // 分隔线

    QAction *exitAct = fileMenu->addAction("退出(&X)");
    exitAct->setShortcut(QKeySequence::Quit);
    connect(exitAct, &QAction::triggered, this, &QWidget::close);

    // 编辑菜单
    QMenu *editMenu = menuBar->addMenu("编辑(&E)");

    QAction *findAct = editMenu->addAction("查找(&F)");
    findAct->setShortcut(QKeySequence::Find);  // Ctrl+F
    connect(findAct, &QAction::triggered, this, &MainWindow::findText);

    // 格式菜单
    QMenu *formatMenu = menuBar->addMenu("格式(&M)");

    QAction *colorAct = formatMenu->addAction("文字颜色(&C)");
    connect(colorAct, &QAction::triggered, this, &MainWindow::setColor);

    QAction *fontAct = formatMenu->addAction("字体(&F)");
    connect(fontAct, &QAction::triggered, this, &MainWindow::setFont);

    // 帮助菜单
    QMenu *helpMenu = menuBar->addMenu("帮助(&H)");

    QAction *aboutAct = helpMenu->addAction("关于(&A)");
    connect(aboutAct, &QAction::triggered, this, &MainWindow::showAbout);
}

// 打开文件
void MainWindow::openFile()
{
    QString fileName = QFileDialog::getOpenFileName(
        this, "打开文件", "", "文本文件 (*.txt);;所有文件 (*.*)"
    );

    if (fileName.isEmpty()) return;

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "无法打开文件!");
        return;
    }

    QTextStream in(&file);
    m_textEdit->setText(in.readAll());
    file.close();

    m_currentFile = fileName;
    setWindowTitle(fileName + " - 简易记事本");
}

// 保存文件
void MainWindow::saveFile()
{
    if (m_currentFile.isEmpty()) {
        // 还没有文件名,弹出保存对话框
        m_currentFile = QFileDialog::getSaveFileName(
            this, "保存文件", "未命名.txt", "文本文件 (*.txt)"
        );
        if (m_currentFile.isEmpty()) return;
    }

    QFile file(m_currentFile);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "无法保存文件!");
        return;
    }

    QTextStream out(&file);
    out << m_textEdit->toPlainText();
    file.close();

    setWindowTitle(m_currentFile + " - 简易记事本");
    QMessageBox::information(this, "提示", "保存成功!");
}

// 查找文字
void MainWindow::findText()
{
    bool ok;
    QString text = QInputDialog::getText(
        this, "查找", "请输入要查找的内容:", QLineEdit::Normal, "", &ok
    );

    if (!ok || text.isEmpty()) return;

    // 查找并选中
    if (!m_textEdit->find(text)) {
        QMessageBox::information(this, "提示", "找不到 \"" + text + "\"");
    }
}

// 设置文字颜色
void MainWindow::setColor()
{
    QColor color = QColorDialog::getColor(Qt::black, this, "选择文字颜色");
    if (color.isValid()) {
        m_textEdit->setTextColor(color);
    }
}

// 设置字体
void MainWindow::setFont()
{
    bool ok;
    QFont font = QFontDialog::getFont(&ok, m_textEdit->font(), this, "选择字体");
    if (ok) {
        m_textEdit->setFont(font);
    }
}

// 关于对话框
void MainWindow::showAbout()
{
    QMessageBox::about(
        this,
        "关于简易记事本",
        "简易记事本 v1.0\n\n"
        "一个用QT写的简单记事本程序\n"
        "作者:零基础学QT"
    );
}

运行一下,你就有了一个功能完整的简易记事本!可以打开文件、保存文件、查找文字、设置颜色和字体。

十、今日总结

今天我们学习了QT中的各种对话框,这些都是日常开发中经常用到的!

对话框汇总

对话框类 用途 常用静态方法
QMessageBox 消息提示/询问 information, warning, critical, question, about
QInputDialog 输入内容 getText, getInt, getDouble, getItem, getMultiLineText
QFileDialog 选择文件/目录 getOpenFileName, getSaveFileName, getExistingDirectory
QColorDialog 选择颜色 getColor
QFontDialog 选择字体 getFont
QProgressDialog 显示进度 直接创建对象,setValue
QDialog 自定义对话框 继承它,自己加控件

重要概念

  • 模态对话框 :用 exec(),必须关掉才能操作主窗口
  • 非模态对话框 :用 show(),可以和主窗口同时操作
  • 对话框返回值Accepted(确定)、Rejected(取消)
  • 传值方式:主窗口→对话框用set方法,对话框→主窗口用get方法

经验分享

  1. 优先用内置对话框:QT提供的对话框已经很完善了,能满足90%的需求
  2. 静态方法最方便:大部分对话框都有静态方法,一行代码就能调用
  3. 文件过滤器要写好:让用户更容易找到想要的文件
  4. 自定义对话框继承QDialog:不要自己从零写,继承QDialog省很多事
  5. 模态 vs 非模态:重要操作、必须等用户回复的用模态;辅助工具用非模态

十一、明日预告

明天我们将学习菜单栏、工具栏、状态栏(QMenuBar、QToolBar、QStatusBar)。

一个完整的桌面应用,通常都有:

  • 顶部的菜单栏(文件、编辑、帮助...)
  • 工具栏(一排图标按钮)
  • 底部的状态栏(显示状态信息)

这些都是QMainWindow自带的功能,我们会学习怎么添加菜单、怎么加工具栏按钮、怎么在状态栏显示信息。

学会这些,你就能做出像模像样的桌面软件了!


📝 学习建议:对话框是QT开发中非常实用的内容,建议每个对话框都自己试一遍。

练习建议:

  • 把今天的简易记事本代码敲一遍
  • 试试加一个"另存为"功能
  • 试试加一个"替换"功能(用QInputDialog输入要替换的内容)
  • 自己设计一个登录对话框(用户名+密码)

对话框掌握了,和用户交互就不成问题了!明天见,继续加油!💪