第十三章:QT对话框——让程序与用户优雅对话

对话框这东西,说白了就是程序跟用户「搭话」的窗口。你想想看,用户要打开文件、选个颜色、改个字体,总不能让人家去敲代码吧?这时候对话框就派上用场了。QT 给我们准备了一整套现成的对话框,用起来相当顺手。

我个人习惯把对话框分成两类:一类是系统自带的「标准对话框」,开箱即用;另一类是「自定义对话框」,满足特殊需求。今天咱们就把这两类都过一遍。

13.1 QMessageBox——消息提示的好帮手

QMessageBox 是我用得最频繁的对话框。不管是提示用户「保存成功」,还是警告「操作不可逆」,它都能搞定。

先看一个最简单的用法:

QMessageBox::information(this, "提示", "文件已保存");

这一行代码就能弹出一个信息提示框。QT 提供了几种常用类型:

静态方法 图标 适用场景
QMessageBox::information() i 图标 普通信息提示
QMessageBox::warning() ⚠ 图标 警告用户注意
QMessageBox::critical() ✖ 图标 严重错误提示
QMessageBox::question() ❓ 图标 询问用户选择
QMessageBox::about() 程序图标 关于对话框

我曾经在做一个数据导出功能时,用户点了导出按钮后程序直接卡死——因为没有加「正在处理」的提示。后来我加了个 QMessageBox,用户体验立马好了很多。

如果你需要用户做选择,用 question 最合适:

QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "确定要删除这条记录吗?",
                              QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
    // 执行删除操作
}
小技巧: 如果你想让对话框更「聪明」,可以用 QMessageBox 的对象方式创建,然后调用 setDetailedText() 添加详细错误信息。用户平时看不到,点开「显示详情」才展开,界面很干净。

13.2 QFileDialog——文件选择不求人

文件对话框,每个桌面应用几乎都离不开。QT 的 QFileDialog 封装得很完善,打开文件、保存文件、选择目录,一行代码搞定。

打开单个文件:

QString fileName = QFileDialog::getOpenFileName(this, "选择图片",
                                                "/home/user",
                                                "图片文件 (*.png *.jpg *.bmp)");
if (!fileName.isEmpty()) {
    // 加载图片
}

打开多个文件:

QStringList files = QFileDialog::getOpenFileNames(this, "选择多个文件",
                                                   "/home/user",
                                                   "所有文件 (*)");
foreach (QString file, files) {
    qDebug() << "选中了:" << file;
}

保存文件:

QString savePath = QFileDialog::getSaveFileName(this, "保存报告",
                                                 "report.txt",
                                                 "文本文件 (*.txt);;PDF文件 (*.pdf)");
if (!savePath.isEmpty()) {
    // 写入文件
}

选择目录:

QString dir = QFileDialog::getExistingDirectory(this, "选择工作目录",
                                                 "/home/user");
if (!dir.isEmpty()) {
    // 设置工作目录
}
注意: 文件对话框返回的路径是 QString 类型,记得检查是否为空。用户点了「取消」就会返回空字符串,这时候直接操作文件会出问题。我曾经踩过这个坑,后来养成了「先判空,再操作」的习惯。

13.3 QColorDialog——颜色选择器

如果你的应用需要让用户自定义颜色,QColorDialog 就是你的救星。它提供了一个完整的颜色选择界面,支持 RGB、HSV 等多种颜色模式。

QColor color = QColorDialog::getColor(Qt::white, this, "选择背景色");
if (color.isValid()) {
    // 使用选中的颜色
    ui->widget->setStyleSheet(QString("background-color: %1").arg(color.name()));
}

这里有个细节:getColor 的第一个参数是默认颜色,第二个是父窗口,第三个是对话框标题。返回的 QColor 对象用 isValid() 判断用户是否真的选了一个颜色。

我记得有一次做绘图工具,用户反馈说「选颜色时能不能记住上次选的颜色?」。解决方案很简单:用一个成员变量保存上次选中的颜色,每次打开对话框时传进去作为默认值。

13.4 QFontDialog——字体设置不求人

字体对话框和颜色对话框很像,都是让用户从预设选项里挑一个。不过字体对话框返回的是 QFont 对象,包含了字体名称、大小、粗体、斜体等信息。

bool ok;
QFont font = QFontDialog::getFont(&ok, QFont("微软雅黑", 12), this, "选择字体");
if (ok) {
    // 应用字体
    ui->textEdit->setFont(font);
}

注意那个 &ok 参数。它是一个输出参数,用户点了「确定」就设为 true,点了「取消」就是 false。这样你就能区分用户是「选好了」还是「不想选了」。

核心要点: QFontDialog 的 getFont 静态方法返回的是 QFont,不是 QString。如果你需要字体名称字符串,用 font.family() 获取。

13.5 QInputDialog——简单输入场景

有时候你只需要用户输入一个数字、一段文字或者从列表里选一项。这时候用 QInputDialog 最合适,它不会像 QDialog 那样「大动干戈」。

获取文本输入:

bool ok;
QString text = QInputDialog::getText(this, "输入姓名", "请输入您的姓名:",
                                     QLineEdit::Normal, "", &ok);
if (ok && !text.isEmpty()) {
    // 处理输入
}

获取整数输入:

int value = QInputDialog::getInt(this, "设置数量", "请输入数量:",
                                 10, 0, 100, 1, &ok);
if (ok) {
    // 使用数值
}

从列表中选择:

QStringList items;
items << "选项A" << "选项B" << "选项C";
QString item = QInputDialog::getItem(this, "选择模式", "请选择一种模式:",
                                      items, 0, false, &ok);
if (ok) {
    // 处理选择
}

getInt 的参数依次是:父窗口、标题、标签、默认值、最小值、最大值、步长、是否成功。这个函数我特别喜欢,因为它自带输入验证——用户想输入「abc」都输不进去。

13.6 自定义对话框——当标准对话框不够用

标准对话框虽然方便,但总有满足不了需求的时候。比如你要做一个「用户登录」对话框,总不能指望 QInputDialog 能搞定吧?这时候就需要自己动手了。

自定义对话框的核心思路:继承 QDialog,在构造函数里布局控件,然后通过信号或成员函数获取用户输入。

// LoginDialog.h
class LoginDialog : public QDialog {
    Q_OBJECT
public:
    explicit LoginDialog(QWidget *parent = nullptr);
    QString username() const;
    QString password() const;

private:
    QLineEdit *m_usernameEdit;
    QLineEdit *m_passwordEdit;
    QPushButton *m_okBtn;
    QPushButton *m_cancelBtn;
};

// LoginDialog.cpp
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent) {
    setWindowTitle("用户登录");
    setFixedSize(300, 200);

    m_usernameEdit = new QLineEdit(this);
    m_passwordEdit = new QLineEdit(this);
    m_passwordEdit->setEchoMode(QLineEdit::Password);

    m_okBtn = new QPushButton("登录", this);
    m_cancelBtn = new QPushButton("取消", this);

    // 布局
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(new QLabel("用户名:"));
    mainLayout->addWidget(m_usernameEdit);
    mainLayout->addWidget(new QLabel("密码:"));
    mainLayout->addWidget(m_passwordEdit);

    QHBoxLayout *btnLayout = new QHBoxLayout();
    btnLayout->addStretch();
    btnLayout->addWidget(m_okBtn);
    btnLayout->addWidget(m_cancelBtn);
    mainLayout->addLayout(btnLayout);

    connect(m_okBtn, &QPushButton::clicked, this, &QDialog::accept);
    connect(m_cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
}

QString LoginDialog::username() const {
    return m_usernameEdit->text();
}

QString LoginDialog::password() const {
    return m_passwordEdit->text();
}

使用的时候:

LoginDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
    QString user = dlg.username();
    QString pwd = dlg.password();
    // 验证登录
}
设计建议: 自定义对话框的布局尽量简洁。我见过有些新手把对话框塞得满满当当,用户一打开就懵了。记住:对话框是「一次性交互」,不是「主界面」。能用标准对话框解决的,就别自己造轮子。

13.7 知识体系总览

下面这张图把本章的对话框体系梳理了一下,方便你对照着看:

QT 对话框体系 QDialog 标准对话框 自定义对话框 QMessageBox QFileDialog QColorDialog QFontDialog QInputDialog QProgressDialog 继承 QDialog + 布局控件 exec() / accept() / reject() 核心原则:能用标准对话框就用,不够用再自定义

嗯,这张图把今天讲的内容串起来了。你可以看到,标准对话框覆盖了大部分常见场景,自定义对话框则用来处理那些「特殊需求」。两者结合,基本能应对所有用户交互场景。

最后说一句:对话框的设计要「轻量」。用户打开对话框是为了快速完成一个操作,不是来参观你的 UI 设计的。保持简洁,保持清晰,这就是好对话框的标准。


公众号:蓝海资料掘金营,微信deep3321