解码Qt 对话框与文件操作

Qt 对话框(QDialog)

对话框是GUI程序中实现短期交互、功能配置的顶层/子窗口,Qt中通过QDialog类实现(继承自QWidget)。

  • QDialogparent参数特殊意义:
    • parent = nullptr:对话框为顶层窗口,任务栏有独立位置;
    • parent != nullptr:对话框为父组件的子对话框,默认显示在父组件中心,共享父组件的任务栏位置。

对话框分类

对话框核心分为模态对话框非模态对话框,核心区别是是否阻塞用户对其他窗口的操作。

模态对话框

定义:显示时阻塞用户输入,其他窗口无法获取焦点,直到对话框关闭。
模态对话框分两种级别:

  • 应用程序级模态:阻塞整个应用的所有窗口(最常用);
  • 窗口级模态:仅阻塞关联的父窗口,其他窗口可正常操作。
显示方式 模态级别 阻塞范围 返回值 关闭方式
exec() 应用级 整个应用 QDialog::Accepted(确定)/QDialog::Rejected(取消) accept()/reject()
open() 窗口级 仅父窗口 无(通过信号finished(int result)获取结果) accept()/reject()

示例(应用级模态对话框)

image

void MainWindow::showModalDialog()
{
    QDialog dlg(this); // 父窗口为MainWindow,子对话框
    dlg.setWindowTitle("应用级模态对话框");
    dlg.resize(300, 100);

    // 添加确定按钮
    QPushButton *okBtn = new QPushButton("确定", &dlg);
    QObject::connect(okBtn, &QPushButton::clicked, &dlg, &QDialog::accept);

    // 添加取消按钮
    QPushButton *cancelBtn = new QPushButton("取消", &dlg);
    QObject::connect(cancelBtn, &QPushButton::clicked, &dlg, &QDialog::reject);

    // 布局管理
    QHBoxLayout *layout = new QHBoxLayout(&dlg);
    layout->addWidget(okBtn);
    layout->addWidget(cancelBtn);

    /**
     * exec():显示应用级模态对话框,阻塞直到对话框关闭
     * @return int 关闭后返回QDialog::Accepted(1)或QDialog::Rejected(0)
     * @note 执行exec()时,当前线程阻塞,但Qt的事件循环仍在运行(界面不卡死)
     */
    int ret = dlg.exec();
    if (ret == QDialog::Accepted) {
        qDebug() << "用户点击了确定";
    } else if (ret == QDialog::Rejected) {
        qDebug() << "用户点击了取消";
    }
}

非模态对话框

定义:显示时不阻塞程序执行,用户可同时操作主窗口/其他窗口(如记事本的“查找”对话框)。

  • 显示方式:调用show()(非阻塞,立即返回);
  • 内存注意:非模态对话框若为局部对象,函数结束后会被销毁,需用new创建(可结合setAttribute(Qt::WA_DeleteOnClose)关闭时自动释放内存)。

示例(非模态对话框)

image

void MainWindow::showNonModalDialog()
{
    /**
     * 非模态对话框需动态创建,避免局部对象销毁导致窗口消失
     * setAttribute(Qt::WA_DeleteOnClose):对话框关闭时自动释放内存,防止内存泄漏
     */
    QDialog *dlg = new QDialog(this);
    dlg->setAttribute(Qt::WA_DeleteOnClose);
    dlg->setWindowTitle("非模态对话框");
    dlg->resize(300, 100);

    QPushButton *btn = new QPushButton("关闭", dlg);
    QObject::connect(btn, &QPushButton::clicked, dlg, &QDialog::close);

    QHBoxLayout *layout = new QHBoxLayout(dlg);
    layout->addWidget(btn);

    /**
     * show():显示非模态对话框,立即返回,不阻塞程序执行
     * @note 非模态对话框不会阻塞事件循环,用户可同时操作主窗口
     */
    dlg->show();
}

标准对话框(Qt内置)

Qt提供一系列封装好的标准对话框,无需自定义,直接调用即可实现常见交互。

文件对话框(QFileDialog)

用于选择文件/目录路径(打开/保存文件),支持两种使用方式:创建对象、调用静态函数(推荐)。

方式1:创建QFileDialog对象

image

void MainWindow::openFileDialogByObj()
{
    QFileDialog fileDlg(this);
    /**
     * QFileDialog构造函数:QFileDialog(QWidget *parent = nullptr, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString())
     * @param parent 父窗口,决定对话框的模态范围和显示位置
     * @param caption 对话框标题(默认空)
     * @param directory 初始打开的目录(默认当前程序目录)
     * @param filter 文件过滤器,格式:"文本文件 (*.txt);;图片文件 (*.png *.jpg)"
     */
    fileDlg.setWindowTitle("选择文件(对象方式)");
    fileDlg.setDirectory("E:\\Project\\QT"); // 设置初始目录
    fileDlg.setNameFilter("文本文件 (*.txt);;所有文件 (*.*)"); // 过滤仅显示txt和所有文件

    /**
     * exec():模态显示文件对话框
     * fileSelected信号:用户选择文件并确认后触发,参数为选中的文件绝对路径
     */
    QObject::connect(&fileDlg, &QFileDialog::fileSelected, [](const QString &fileName) {
        qDebug() << "选中的文件路径:" << fileName;
    });
    fileDlg.exec();
}

方式2:调用静态函数(推荐)

image

void MainWindow::openFileDialogByStatic()
{
    /**
     * QFileDialog::getOpenFileName:打开“选择单个文件”对话框(应用级模态)
     * @param parent 父窗口
     * @param caption 对话框标题(默认"Open File")
     * @param dir 初始目录(默认当前程序目录)
     * @param filter 文件过滤器(默认空)
     * @param selectedFilter 输出参数,返回用户选中的过滤器(可选)
     * @param options 对话框选项(如QFileDialog::DontUseNativeDialog,可选)
     * @return QString 选中的文件绝对路径;用户取消则返回空字符串
     */
    QString fileName = QFileDialog::getOpenFileName(
        this,                  // parent
        "选择文件(静态函数)", // caption
        "E:\\Project\\QT",                  // dir
        "文本文件 (*.txt);;所有文件 (*.*)" // filter
    );
    if (!fileName.isEmpty()) {
        qDebug() << "选中的文件路径:" << fileName;
    } else {
        qDebug() << "用户取消了选择";
    }

    // 其他常用静态函数:
    // getOpenFileNames():选择多个文件,返回QStringList
    // getSaveFileName():选择保存文件路径,自动提示覆盖已存在文件
    // getExistingDirectory():选择目录路径,返回目录绝对路径
}

颜色对话框(QColorDialog)

用于选择颜色,返回QColor对象,可设置控件的前景/背景色。

image

void Widget::on_pushButton_clicked()
{
		/**
     * QColorDialog::getColor:打开颜色选择对话框
     * @param initial 初始颜色(默认白色)
     * @param parent 父窗口
     * @param title 对话框标题(默认"Select Color")
     * @param options 对话框选项(可选)
     * @return QColor 选中的颜色;用户取消则返回无效颜色(可通过isValid()判断)
     */
    QColor color = QColorDialog::getColor(
        Qt::white,          // 初始颜色
        this,               // parent
        "选择背景颜色"      // title
    );

    if (color.isValid()) { // 判断是否选中有效颜色(用户未取消)
        // 设置标签背景色:StyleSheet语法 "控件{属性:值}"
        ui->label->setStyleSheet(QString("QLabel{background-color:%1;}").arg(color.name()));
    }
}

输入对话框(QInputDialog)

用于获取用户输入的文本、数字、下拉选项等,常用静态函数getText()/getInt()/getDouble()

image

void Widget::on_pushButton_clicked()
{
    /**
     * QInputDialog::getText:打开文本输入对话框
     * @param parent 父窗口
     * @param title 对话框标题
     * @param label 输入提示文本
     * @param echoMode 输入框模式(QLineEdit::Normal/Password等)
     * @param text 初始输入文本(默认空)
     * @param ok 输出参数,返回true表示用户确认,false表示取消(可选)
     * @param flags 窗口标志(可选)
     * @return QString 用户输入的文本;取消则返回空字符串
     */
    bool ok;
    QString inputText = QInputDialog::getText(
        this,               // parent
        "密码输入",         // title
        "请输入密码:",     // label
        QLineEdit::Password,// 密码模式(输入内容隐藏)
        "",                 // 初始文本
        &ok                 // 输出参数:是否确认
    );

    if (ok && !inputText.isEmpty()) {
        ui->label->setText(QString("输入的密码:%1").arg(inputText));
    } else {
        ui->label->setText("用户取消了输入或输入为空");
    }
    // 其他常用静态函数:
    // getInt():输入整数,可指定范围(min/max)和步长
    // getDouble():输入浮点数,可指定范围和小数位数
    // getItem():下拉选项选择,返回选中的选项文本
}

字体对话框(QFontDialog)

用于选择字体(字号、样式、粗细等),返回QFont对象。

image

void Widget::on_pushButton_clicked()
{
    /**
     * QFontDialog::getFont:打开字体选择对话框
     * @param ok 输出参数,返回true表示用户确认,false表示取消
     * @param initial 初始字体(默认应用程序字体)
     * @param parent 父窗口
     * @param title 对话框标题(默认"Select Font")
     * @param options 对话框选项(可选)
     * @return QFont 选中的字体;取消则返回初始字体(需通过ok判断是否有效)
     */
    bool ok;
    QFont font = QFontDialog::getFont(
        &ok,                // 输出参数:是否确认
        ui->label->font(),  // 初始字体(使用标签当前字体)
        this,               // parent
        "选择字体"          // title
    );

    if (ok) { // 仅当用户确认时,才设置字体
        ui->label->setFont(font);
    }
}

消息对话框(QMessageBox)

用于提示信息、询问确认(如删除确认),模态显示,返回用户点击的按钮类型。

image

void Widget::on_pushButton_clicked()
{
    /**
     * QMessageBox::warning:打开警告类型消息框(其他类型:info/critical/question)
     * @param parent 父窗口
     * @param title 对话框标题
     * @param text 主要提示文本
     * @param buttons 显示的按钮(默认Ok),如QMessageBox::Yes|QMessageBox::No
     * @param defaultButton 默认选中的按钮(可选)
     * @return QMessageBox::StandardButton 用户点击的按钮类型;取消则返回QMessageBox::NoButton
     */
    QMessageBox::StandardButton ret = QMessageBox::warning(
        this,               // parent
        "删除确认",         // title
        "确定要删除该文件吗?", // text
        QMessageBox::Yes | QMessageBox::No, // 显示“是”和“否”按钮
        QMessageBox::No     // 默认选中“否”按钮
    );

    if (ret == QMessageBox::Yes) {
        qDebug() << "用户确认删除";
        // 执行删除逻辑
    } else if (ret == QMessageBox::No) {
        qDebug() << "用户取消删除";
    }
    // 其他常用静态函数:
    // QMessageBox::information():提示普通信息(如操作成功)
    // QMessageBox::critical():提示错误信息(如文件打开失败)
    // QMessageBox::question():询问用户(如是否保存更改)
}

自定义对话框

当标准对话框无法满足需求时,可自定义对话框(继承QDialog或直接创建QDialog对象)。
示例(自定义对话框,继承QDialog)

image

// 自定义对话框类头文件(CustomDialog.h)
#include <QDialog>
#include <QPushButton>
#include <QHBoxLayout>

class CustomDialog : public QDialog
{
    Q_OBJECT
public:
    explicit CustomDialog(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("自定义对话框");
        resize(300, 100);

        // 创建按钮
        QPushButton *okBtn = new QPushButton("确定", this);
        QPushButton *cancelBtn = new QPushButton("取消", this);

        // 布局
        QHBoxLayout *layout = new QHBoxLayout(this);
        layout->addWidget(okBtn);
        layout->addWidget(cancelBtn);

        // 信号槽:点击按钮关闭对话框
        connect(okBtn, &QPushButton::clicked, this, &QDialog::accept);
        connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
    }
};

// 主窗口中调用
void MainWindow::openCustomDialog()
{
    CustomDialog dlg(this);
    /**
     * exec():模态显示自定义对话框
     * @return int QDialog::Accepted(确定)/QDialog::Rejected(取消)
     */
    int ret = dlg.exec();
    if (ret == QDialog::Accepted) {
        qDebug() << "用户点击了自定义对话框的确定";
    } else {
        qDebug() << "用户点击了自定义对话框的取消";
    }
}

Qt 文件操作

Qt提供QFile(文件读写)、QDir(目录操作)、QTextStream(文本文件便捷读写)等类,简化文件/目录操作。

QFile(文件基础操作)

QFile是Qt文件IO的核心类,支持文件的打开、关闭、读写、删除等操作。

打开/关闭文件

void MainWindow::fileOpenClose()
{
    QFile file("example.txt"); // 关联文件(相对路径:程序当前目录;绝对路径:如"C:/test/example.txt")
    /**
     * QFile::open:打开文件
     * @param mode 打开模式(组合使用,如ReadOnly | Text)
     *        - QIODevice::ReadOnly:只读模式(文件不存在则打开失败)
     *        - QIODevice::WriteOnly:只写模式(文件不存在则创建;存在则清空内容)
     *        - QIODevice::ReadWrite:读写模式
     *        - QIODevice::Append:追加模式(WriteOnly/ReadWrite时,写入内容追加到文件末尾)
     *        - QIODevice::Text:文本模式(自动转换换行符:Windows \\r\\n ↔ Linux \\n)
     * @return bool 打开成功返回true,失败返回false(可通过error()获取错误码)
     */
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "文件打开成功";

        // 操作完成后关闭文件(必须调用,释放文件句柄)
        file.close();
    } else {
        qDebug() << "文件打开失败:" << file.errorString(); // 输出错误信息
    }
}

image

读写文件

void MainWindow::fileReadWrite()
{
    // 1. 写入文件(WriteOnly + Text)
    QFile writeFile("example.txt");
    if (writeFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        /**
         * QFile::write:写入数据
         * @param data 要写入的字节数据(QByteArray/char*)
         * @return qint64 实际写入的字节数;失败返回-1
         */
        QString writeContent = "Hello Qt!\n你好 Qt!"; // 用QString保证中文编码正确
        qint64 writeBytes = writeFile.write(writeContent.toUtf8()); // 转为UTF-8字节写入
        if (writeBytes > 0) {
            qDebug() << "写入成功,字节数:" << writeBytes;
        }
        writeFile.close();
    }

    // 2. 读取文件(ReadOnly + Text)
    QFile readFile("example.txt");
    if (readFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        /**
         * QFile::read:读取指定字节数;readAll():读取全部内容
         * @param maxSize 最大读取字节数(默认-1,读取全部)
         * @return QByteArray 读取的内容;失败返回空
         */
        QByteArray allData = readFile.readAll();
        // 方案1:直接转QString(推荐,指定UTF-8编码)
        qDebug() << "文件全部内容(中文正常显示):" << QString::fromUtf8(allData);

        readFile.seek(0);

        // 逐行读取:转为QString显示中文
        while (!readFile.atEnd()) {
            QByteArray lineData = readFile.readLine();
            // 关键:fromUtf8 解析字节为UTF-8字符串,trimmed去换行/空格
            QString lineStr = QString::fromUtf8(lineData).trimmed();
            qDebug() << "逐行读取(中文正常):" << lineStr;
        }

        readFile.close();
    }
}

image

获取文件信息(QFileInfo)

QFileInfo封装文件的元信息(文件名、路径、大小、修改时间等)。

void MainWindow::fileInfo()
{
    QFileInfo fileInfo("example.txt");
    if (fileInfo.exists()) { // 判断文件是否存在
        qDebug() << "文件名:" << fileInfo.fileName();          // 示例:example.txt
        qDebug() << "绝对路径:" << fileInfo.absoluteFilePath(); // 示例:C:/build/example.txt
        qDebug() << "文件大小:" << fileInfo.size() << "字节";   // 字节数
        qDebug() << "创建时间:" << fileInfo.birthTime().toString("yyyy-MM-dd hh:mm:ss");
        qDebug() << "修改时间:" << fileInfo.lastModified().toString("yyyy-MM-dd hh:mm:ss");
        qDebug() << "是否为文件:" << fileInfo.isFile();        // true(区分文件/目录)
        qDebug() << "是否为目录:" << fileInfo.isDir();         // false
    } else {
        qDebug() << "文件不存在";
    }
}

image

QDir(目录操作)

QDir用于目录的遍历、创建、删除、重命名等操作,支持绝对/相对路径。

枚举目录内容

void MainWindow::dirEnum()
{
    QDir dir("./"); // 关联当前目录(绝对路径:如"C:/test")
    /**
     * QDir::entryList:获取目录下的文件/子目录列表
     * @param filters 过滤规则(组合使用)
     *        - QDir::Files:仅显示文件
     *        - QDir::Dirs:仅显示子目录(包含"."当前目录、".."上级目录)
     *        - QDir::NoDotAndDotDot:排除"."和".."
     *        - QDir::Hidden:显示隐藏文件/目录
     * @param sort 排序规则(如QDir::Name:按名称排序)
     * @return QStringList 符合条件的文件/目录名称列表
     */
    // 获取目录下所有文件(排除隐藏文件)
    QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
    qDebug() << "目录下的文件:" << files;

    // 获取目录下所有子目录(排除.和..)
    QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    qDebug() << "目录下的子目录:" << subDirs;
}

image

创建/删除目录

void MainWindow::dirCreateDelete()
{
    QDir dir("./");

    // 1. 创建目录:mkpath() 递归创建(如"test/subdir",父目录不存在则自动创建)
    QString newDir = "test/subdir";
    /**
     * QDir::mkpath:递归创建目录
     * @param path 要创建的目录路径
     * @return bool 创建成功(或已存在)返回true,失败返回false
     */
    if (dir.mkpath(newDir)) {
        qDebug() << "目录创建成功:" << newDir;
    } else {
        qDebug() << "目录创建失败:" << newDir;
    }

    // 2. 删除目录:rmdir() 删除空目录;rmdir()仅删除最后一级空目录
    /**
     * QDir::rmdir:删除空目录
     * @param dirName 要删除的目录名称
     * @return bool 删除成功返回true,失败返回false(目录非空/不存在则失败)
     */
    if (dir.rmdir("test/subdir")) { // 先删除子目录
        qDebug() << "子目录删除成功";
        if (dir.rmdir("test")) {    // 再删除空的父目录
            qDebug() << "父目录删除成功";
        }
    } else {
        qDebug() << "目录删除失败(目录非空或不存在)";
    }

    // 注:删除非空目录需先遍历删除所有文件/子目录,或使用QDir::removeRecursively()(Qt 5.15+)
    // dir.removeRecursively("test"); // 递归删除目录及所有内容
}

image

QTextStream(文本文件便捷读写)

QTextStream封装了文本文件的读写逻辑,支持字符串、数值、换行符等便捷操作,无需手动处理字节转换。

读取文本文件

void MainWindow::textStreamRead()
{
    QFile file("example.txt");
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        /**
         * QTextStream:文本流对象,关联QFile
         * @param device 关联的IO设备(如QFile)
         * @note 文本流自动处理编码(默认UTF-8,可通过setCodec()设置,如setCodec("GBK"))
         */
        QTextStream in(&file);
        in.setCodec("UTF-8"); // 设置读取编码(解决中文乱码)

        /**
         * QTextStream::readLine:读取一行文本(自动去除换行符)
         * @return QString 读取的一行内容;到末尾返回空字符串
         * @note atEnd():判断是否到文件末尾
         */
        while (!in.atEnd()) {
            QString line = in.readLine();
            qDebug() << "文本流逐行读取:" << line;
        }

        file.close();
    }
}

image

写入文本文件

void MainWindow::textStreamWrite()
{
    QFile file("example.txt");
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out.setCodec("UTF-8"); // 设置写入编码

        /**
         * QTextStream::operator<<:写入文本(支持字符串、数值、endl等)
         * @param data 要写入的内容(QString/int/double等)
         * @return QTextStream& 流对象本身,支持链式调用
         * @note endl:插入换行符并刷新缓冲区(确保内容立即写入文件)
         */
        out << QByteArray("Qt 文本流写入") << endl;
        out << QByteArray("整数:") << 123 << QByteArray(" 浮点数:") << 3.14 << endl;
        out << QByteArray("中文测试:你好 Qt!") << endl;

        file.close();
    }
}

image

常见注意事项

  • 路径问题:Windows路径用/\\(如C:/test.txtC:\\test.txt),Linux用/
  • 编码问题:QTextStream默认UTF-8,读取GBK文件需设置setCodec("GBK")
  • 大文件读写:避免readAll(),改用readLine()read(n)分块读取;
  • 内存管理:非模态对话框、动态创建的文件/目录对象需注意释放,避免内存泄漏;
  • 权限问题:写入系统目录(如C:/Windows)需管理员权限,否则打开失败。
posted @ 2025-12-17 19:04  YouEmbedded  阅读(27)  评论(0)    收藏  举报