文章

通用对话框:QErrorMessage、QFileDialog、QProgressDialog

QErrorMessage 用于显示错误提示;QFileDialog 用于选择文件或目录;QProgressDialog 显示任务进度,支持用户取消,常用于耗时操作中。

通用对话框:QErrorMessage、QFileDialog、QProgressDialog

通用对话框:QErrorMessage、QFileDialog、QProgressDialog

QErrorMessage

QErrorMessage 是 Qt 提供的一个专用对话框类,用于显示错误消息(通常是运行时错误),并带有“不再显示此消息”的功能。这对于向用户提示非致命性错误(如输入错误、文件加载失败等)非常有用,能够有效防止同一个错误提示反复打扰用户。

  • 提供一个标准的错误提示窗口。

  • 自动维护“消息记录”,对于重复错误可以勾选“不再显示此消息”。

  • 支持 showMessage() 动态显示错误文本。

  • 模态对话框(默认),会阻塞用户对其他窗口的操作。

常用函数说明

函数名说明
void showMessage(const QString &message)显示错误信息;重复信息会自动合并。
static QErrorMessage *qtHandler()获取 Qt 全局的错误处理器(单例模式)。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <QApplication>
#include <QPushButton>
#include <QErrorMessage>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QPushButton button("触发错误");
    QErrorMessage *errorDialog = new QErrorMessage();

    QObject::connect(&button, &QPushButton::clicked, [&]() {
        errorDialog->showMessage("文件加载失败:文件不存在。");
        errorDialog->exec(); // 模态显示对话框(非必须)
    });

    button.show();
    return app.exec();
}

QMessageBox 区别

特性QErrorMessageQMessageBox
用途显示可忽略的错误信息显示各种提示(信息、警告、问题等)
特点内建“下次不再提示”选项无记录功能
是否模态默认模态可选模态
适合场景经常性错误、重复错误单次确认性提示

高级用法:使用 Qt 全局错误处理器

1
QErrorMessage::qtHandler()->showMessage("这是一个全局错误提示!");

在大型应用中,可以通过 qtHandler() 实现统一的错误提示策略。

常见注意事项

  • showMessage()非阻塞 的,如果希望阻塞用户操作,需调用 exec()
  • 多次调用 showMessage()合并相同内容,除非关闭对话框或内容不同。
  • 适合用在用户可以选择忽略的错误提示上,而不是强制必须处理的错误。

QFileDialog

QFileDialog 是 Qt 提供的标准文件选择对话框,用于让用户浏览文件系统,选择文件或目录。它支持多种文件类型过滤、选择单个或多个文件、选择目录等功能。

主要功能

  • 打开文件选择对话框(Open File)
  • 保存文件选择对话框(Save File)
  • 选择文件夹对话框(Select Directory)
  • 支持过滤文件类型(比如只显示图片文件)
  • 支持多文件选择
  • 支持自定义对话框标题、初始路径
  • 支持本地文件系统和虚拟文件系统

构造函数

1
2
QFileDialog(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
QFileDialog(const QString &parent, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString());
  • parent:父窗口

  • caption:对话框标题

  • directory:初始打开路径

  • filter:过滤文件类型字符串,比如 "Images (*.png *.xpm *.jpg);;Text files (*.txt);;All files (*.*)"

常用静态函数(简易使用)

函数功能备注
QString getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = 0)打开单文件选择对话框,返回选中的文件路径。 
QStringList getOpenFileNames(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = 0)打开多文件选择对话框,返回选中的文件路径列表。 
QString getSaveFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = 0)打开保存文件对话框,返回用户输入的文件路径。 
QString getExistingDirectory(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), Options options = 0)打开目录选择对话框,返回选中的目录路径。 

使用示例

打开单文件选择
1
2
3
4
5
6
7
8
QString fileName = QFileDialog::getOpenFileName(this,
    tr("打开文件"),
    "/home/user",
    tr("文本文件 (*.txt);;所有文件 (*.*)"));

if (!fileName.isEmpty()) {
    // 使用 fileName 做进一步处理
}
选择多个文件
1
2
3
4
5
6
7
8
QStringList files = QFileDialog::getOpenFileNames(this,
    tr("选择多个文件"),
    "/home/user",
    tr("图片文件 (*.png *.jpg);;所有文件 (*.*)"));

for (const QString &file : files) {
    // 处理每个文件
}
保存文件
1
2
3
4
5
6
7
8
QString fileName = QFileDialog::getSaveFileName(this,
    tr("保存文件"),
    "/home/user/untitled.txt",
    tr("文本文件 (*.txt);;所有文件 (*.*)"));

if (!fileName.isEmpty()) {
    // 保存文件
}
选择目录
1
2
3
4
5
6
7
8
QString dir = QFileDialog::getExistingDirectory(this,
    tr("选择目录"),
    "/home/user",
    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

if (!dir.isEmpty()) {
    // 使用目录路径
}

常用成员函数(非静态)

  • void setDirectory(const QString &directory):设置默认目录。
  • void setNameFilter(const QString &filter):设置文件过滤器。
  • void setFileMode(QFileDialog::FileMode mode):设置文件选择模式,如单文件、多文件、目录等。 主要枚举值:
    • QFileDialog::AnyFile:允许选择任何文件(即使不存在)
    • QFileDialog::ExistingFile:只能选择已存在的单个文件
    • QFileDialog::ExistingFiles:可以选择多个已存在的文件
    • QFileDialog::Directory:选择目录
  • void setViewMode(QFileDialog::ViewMode mode):设置视图模式(列表或详细信息)。
  • QStringList selectedFiles() const:获取用户选中的文件路径列表。

文件过滤器语法

过滤器字符串由一组过滤条件组成,格式类似:

1
"Images (*.png *.jpg *.bmp);;Text files (*.txt);;All files (*.*)"
  • ;; 分隔不同过滤条件

  • 用括号内定义通配符,支持 *?

  • 用户在对话框中可以切换过滤器

自定义和信号

QFileDialog 作为 QDialog,支持信号和槽,可以监听用户操作:

  • void fileSelected(const QString &file) — 用户选择文件后触发(单文件)
  • void filesSelected(const QStringList &files) — 多文件选择后触发
  • void directoryEntered(const QString &directory) — 用户进入某目录时触发

可以连接这些信号实现更细粒度的处理。

示例:自定义对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
QFileDialog dialog(this);
dialog.setWindowTitle("选择图片");
dialog.setDirectory("/home/user/Pictures");
dialog.setNameFilter("Images (*.png *.xpm *.jpg)");
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setViewMode(QFileDialog::Detail);

if (dialog.exec() == QDialog::Accepted) {
    QStringList files = dialog.selectedFiles();
    for (const QString &file : files) {
        qDebug() << "选择文件:" << file;
    }
}

QProgressDialog

QProgressDialog 是 Qt 提供的用于显示任务进度的对话框,带有进度条、文本提示和取消按钮,适合耗时操作时向用户反馈进度并允许用户取消操作。

主要功能

  • 显示任务当前进度(通过进度条)
  • 显示文字信息(如“正在处理中…”)
  • 允许用户点击“取消”按钮中断任务
  • 支持设置最小和最大进度值
  • 支持自动显示和隐藏,避免无意义的闪烁

构造函数

1
2
QProgressDialog(QWidget *parent = nullptr);
QProgressDialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
  • labelText:对话框上显示的文字描述
  • cancelButtonText:取消按钮文本,如“取消”
  • minimum:进度条最小值,通常是0
  • maximum:进度条最大值,任务完成时的值
  • parent:父窗口
  • f:窗口标志

常用成员函数

函数说明
void setLabelText(const QString &text)设置显示的提示文本
void setRange(int minimum, int maximum)设置进度条范围
void setValue(int value)设置当前进度值,进度条前进
int value() const获取当前进度值
void setCancelButtonText(const QString &text)设置取消按钮文本
void setMinimumDuration(int ms)设置延迟显示对话框的时间,避免短任务闪烁
bool wasCanceled() const返回用户是否点击了取消按钮
QPushButton* cancelButton() const获取取消按钮指针,可用于连接信号
void reset()重置对话框,进度条回到初始状态
void setAutoClose(bool)任务完成时自动关闭对话框(默认 true)
void setAutoReset(bool)任务完成时自动重置进度条(默认 true)

信号

信号说明
void canceled()用户点击取消按钮时发出

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <QApplication>
#include <QProgressDialog>
#include <QTimer>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QProgressDialog progressDialog("正在处理中...", "取消", 0, 100);
    progressDialog.setWindowModality(Qt::WindowModal);
    progressDialog.setMinimumDuration(500); // 0.5秒后才显示对话框
    progressDialog.setValue(0);

    // 模拟长时间任务
    for (int i = 0; i <= 100; ++i) {
        QThread::msleep(50); // 模拟耗时操作

        progressDialog.setValue(i);
        QApplication::processEvents(); // 保持界面响应

        if (progressDialog.wasCanceled()) {
            qDebug("任务被用户取消");
            break;
        }
    }

    if (!progressDialog.wasCanceled()) {
        qDebug("任务完成");
    }

    return 0;
}

使用建议

  • 避免闪烁:使用 setMinimumDuration(),只有任务超过指定时间才显示对话框。
  • 任务取消:检查 wasCanceled(),支持用户中断任务。
  • 自动关闭:开启 setAutoClose(true),任务完成自动关闭对话框。
  • 模态显示:通常设置为模态对话框,避免用户误操作。

示例:计算 MD5

widgetcalcmd5.ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>WidgetCalcMD5</class>
 <widget class="QWidget" name="WidgetCalcMD5">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>WidgetCalcMD5</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QLabel" name="label">
       <property name="text">
        <string>文件名</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QLineEdit" name="lineEditFileName"/>
     </item>
     <item>
      <widget class="QPushButton" name="pushButtonBrowser">
       <property name="text">
        <string>浏览</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButtonCalcMD5">
       <property name="text">
        <string>计算MD5</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QTextBrowser" name="textBrowserInfo"/>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

widgetcalcmd5.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#ifndef WIDGETCALCMD5_H
#define WIDGETCALCMD5_H

// 用于计算 MD5 值
#include <QCryptographicHash>
// 用于显示错误信息弹窗
#include <QErrorMessage>
// 用于文件操作
#include <QFile>
// 用于弹出文件选择对话框
#include <QFileDialog>
// 用于显示进度对话框
#include <QProgressDialog>
// Qt 窗口基类
#include <QWidget>

// 命名空间 Ui,自动由 Qt Designer 生成,用于访问界面控件
QT_BEGIN_NAMESPACE
namespace Ui {
class WidgetCalcMD5;
}
QT_END_NAMESPACE

// 继承自 QWidget 的主窗口类,用于实现文件 MD5 值计算功能
class WidgetCalcMD5 : public QWidget {
    Q_OBJECT

  public:
    // 构造函数,父对象默认为空
    WidgetCalcMD5(QWidget* parent = nullptr);

    // 析构函数
    ~WidgetCalcMD5();

  private slots:
    // 槽函数:当“浏览文件”按钮被点击时执行
    void on_pushButtonBrowser_clicked();

    // 槽函数:当“计算 MD5”按钮被点击时执行
    void on_pushButtonCalcMD5_clicked();

  private:
    // 指向 UI 界面对象的指针,用于访问界面上的控件
    Ui::WidgetCalcMD5* ui;

    // 错误提示对话框,用于统一显示错误信息
    QErrorMessage m_dlgErrorMsg;

    // 当前选择的文件名(含完整路径)
    QString m_strFileName;

    // 计算传入文件的 MD5 值
    // 参数:
    //   fileIn:输入文件对象(已打开)
    //   nFileSize:文件大小(用于显示进度)
    // 返回值:
    //   计算得到的 MD5 值(16 字节的 QByteArray)
    QByteArray calcFileMD5(QFile& fileIn, qint64 nFileSize);
};

#endif // WIDGETCALCMD5_H

widgetcalcmd5.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "widgetcalcmd5.h"
#include "./ui_widgetcalcmd5.h"

// 构造函数,初始化 UI 界面
WidgetCalcMD5::WidgetCalcMD5(QWidget* parent) : QWidget(parent), ui(new Ui::WidgetCalcMD5) {
    ui->setupUi(this); // 绑定 UI 界面
}

// 析构函数,释放 UI 内存
WidgetCalcMD5::~WidgetCalcMD5() {
    delete ui;
}

// 点击“浏览”按钮:弹出文件选择对话框
void WidgetCalcMD5::on_pushButtonBrowser_clicked() {
    // 打开文件选择对话框
    QString strFileName = QFileDialog::getOpenFileName(this, tr("选择文件"), "", "All files (*)");

    // 用户未选择文件
    if (strFileName.isEmpty()) {
        m_dlgErrorMsg.showMessage(tr("文件名为空,未选择文件!"));
        return;
    }

    // 记录文件名并显示在界面上
    m_strFileName = strFileName;
    ui->lineEditFileName->setText(m_strFileName);

    // 清空之前的显示信息
    ui->textBrowserInfo->clear();
}

// 点击“计算MD5”按钮,执行校验计算
void WidgetCalcMD5::on_pushButtonCalcMD5_clicked() {
    // 获取用户输入的文件路径
    QString strFileName = ui->lineEditFileName->text().trimmed();

    // 检查是否为空
    if (strFileName.isEmpty()) {
        m_dlgErrorMsg.showMessage(tr("文件名编辑框内容为空!"));
        return;
    }

    m_strFileName = strFileName;

    // 创建 QFile 对象
    QFile fileIn(m_strFileName);

    // 尝试打开文件
    if (!fileIn.open(QIODevice::ReadOnly)) {
        m_dlgErrorMsg.showMessage(tr("打开指定文件失败!"));
        return;
    }

    // 获取文件大小
    qint64 nFileSize = fileIn.size();
    if (nFileSize < 1) {
        m_dlgErrorMsg.showMessage(tr("文件大小为 0,没有数据!"));
        fileIn.close();
        return;
    }

    // 调用函数计算 MD5 值
    QByteArray baMD5 = calcFileMD5(fileIn, nFileSize);

    // 构造显示信息
    QString strInfo = tr("文件名:") + m_strFileName;
    strInfo += tr("\n文件大小:%1 字节").arg(nFileSize);
    strInfo += tr("\nMD5校验值:\n");
    strInfo += baMD5.toHex().toUpper(); // 转为十六进制并大写

    // 显示信息到文本框
    ui->textBrowserInfo->setText(strInfo);

    fileIn.close(); // 关闭文件
}

// 实际计算 MD5 值的函数,支持大文件分块读取 + 进度显示
QByteArray WidgetCalcMD5::calcFileMD5(QFile& fileIn, qint64 nFileSize) {
    QByteArray baRet;                                   // 返回值:最终的 MD5 校验结果
    QCryptographicHash algMD5(QCryptographicHash::Md5); // MD5 计算器
    QByteArray baCurData;                               // 用于临时存储读取的数据块

    // 小文件(<100KB)直接一次性读取计算
    if (nFileSize < 100 * 1000) {
        baCurData = fileIn.readAll(); // 一次读完
        algMD5.addData(baCurData);    // 添加到哈希器中
        baRet = algMD5.result();      // 获取最终结果
        return baRet;
    }

    // 对于大文件,进行分块读取
    qint64 oneBlockSize = nFileSize / 100; // 每块大小(大概分成 100 块)
    int nBlocksCount = 100;
    if (nFileSize % oneBlockSize != 0) {
        // 如果不能整除,块数 +1
        nBlocksCount += 1;
    }

    // 初始化进度对话框
    QProgressDialog dlgProgress(tr("正在计算MD5 ..."), tr("取消计算"), 0, nBlocksCount, this);
    dlgProgress.setWindowModality(Qt::WindowModal); // 模态,阻止用户其他操作
    dlgProgress.setMinimumDuration(0);              // 立即显示

    // 开始分块读取 + 更新进度条
    for (int i = 0; i < nBlocksCount; i++) {
        dlgProgress.setValue(i); // 设置当前进度

        // 判断用户是否取消
        if (dlgProgress.wasCanceled()) break;

        // 读取一个数据块并添加到 MD5 计算中
        baCurData = fileIn.read(oneBlockSize);
        algMD5.addData(baCurData);
    }

    // 如果没有被取消,则取出计算结果
    if (!dlgProgress.wasCanceled()) baRet = algMD5.result();

    dlgProgress.setValue(nBlocksCount); // 完成时设置为最大值,关闭进度框
    return baRet;
}
本文由作者按照 CC BY 4.0 进行授权