Qt-快速上手-QFileDialog

QFileDialog

Qt 文件选择对话框,提供打开/保存文件、选择目录等能力。

头文件:#include <QFileDialog> | qmake:QT += widgets


一、类概览

cpp 复制代码
/**
 * @class QFileDialog
 * @brief 提供一个允许用户选择文件或目录的对话框。
 *
 * QFileDialog 继承自 QDialog,是 Qt 中最常用的文件交互组件。
 * 它既可以通过静态方法快速弹出原生对话框,也可以实例化后
 * 精细定制外观与行为。
 *
 * @par 继承关系
 * QObject → QWidget → QDialog → QFileDialog
 *
 * @par 典型用法
 * @code
 *   // 方式一:静态便捷方法(最常用,一行搞定)
 *   QString path = QFileDialog::getOpenFileName(this, "打开文件");
 *
 *   // 方式二:实例化并精细配置
 *   QFileDialog dlg(this, "选择图片", QDir::homePath(),
 *                   "图片 (*.png *.jpg *.bmp)");
 *   dlg.setFileMode(QFileDialog::ExistingFiles);
 *   if (dlg.exec() == QDialog::Accepted) {
 *       QStringList files = dlg.selectedFiles();
 *   }
 * @endcode
 */

二、核心枚举

2.1 FileMode --- 文件选择模式

cpp 复制代码
/**
 * @enum QFileDialog::FileMode
 * @brief 控制用户可以选择什么类型的文件系统条目。
 *
 * @var QFileDialog::AnyFile
 *      文件名可以是任意内容,即使文件不存在也能返回。
 *      适用于"另存为"场景。
 * @var QFileDialog::ExistingFile
 *      只能选择单个已存在的文件。
 * @var QFileDialog::Directory
 *      只能选择目录。此时文件名也显示目录名。
 * @var QFileDialog::ExistingFiles
 *      可以选择零个或多个已存在的文件(多选模式)。
 */

2.2 AcceptMode --- 对话框用途

cpp 复制代码
/**
 * @enum QFileDialog::AcceptMode
 * @brief 区分对话框是用于"打开"还是"保存"。
 *
 * @var QFileDialog::AcceptOpen  打开模式(默认)
 * @var QFileDialog::AcceptSave  保存模式,会触发覆盖确认
 */

2.3 ViewMode --- 视图样式

cpp 复制代码
/**
 * @enum QFileDialog::ViewMode
 * @brief 文件列表的展示方式。
 *
 * @var QFileDialog::Detail  详细列表(含大小、日期等列,默认)
 * @var QFileDialog::List    仅图标+名称的紧凑列表
 */

2.4 Option --- 行为选项(可按位或组合)

cpp 复制代码
/**
 * @enum QFileDialog::Option
 * @brief 精细控制对话框行为的标志位,使用 setOptions() 设置。
 *
 * @var QFileDialog::ShowDirsOnly
 *      只显示目录,隐藏文件(配合 Directory 模式使用)。
 * @var QFileDialog::DontResolveSymlinks
 *      不解析符号链接,返回链接本身路径。
 * @var QFileDialog::DontConfirmOverwrite
 *      保存时若文件已存在,不弹确认框直接返回。
 * @var QFileDialog::DontUseNativeDialog
 *      强制使用 Qt 自绘对话框,而非系统原生对话框。
 *      (需要自定义信号/侧边栏时必须开启此选项)
 * @var QFileDialog::ReadOnly
 *      对话框只读,禁止在其中创建目录或重命名。
 * @var QFileDialog::HideNameFilterDetails
 *      过滤器下拉框中只显示描述,隐藏通配符细节。
 * @var QFileDialog::DontUseCustomDirectoryIcons
 *      不使用目录的自定义图标,提升性能。
 */

2.5 DialogLabel --- 可自定义的标签

cpp 复制代码
/**
 * @enum QFileDialog::DialogLabel
 * @brief setLabelText() 可以修改的界面文字元素。
 *
 * @var QFileDialog::LookIn   "查找范围"标签
 * @var QFileDialog::FileName "文件名"标签
 * @var QFileDialog::FileType "文件类型"标签
 * @var QFileDialog::Accept   确认按钮文字(默认"打开"/"保存")
 * @var QFileDialog::Reject   取消按钮文字
 */

三、静态便捷方法(最常用)

这些方法内部会创建一个模态对话框并阻塞等待用户操作,

返回选中的路径(取消时返回空字符串/空列表)。

3.1 getOpenFileName --- 选择单个文件打开

cpp 复制代码
/**
 * @brief 弹出"打开文件"对话框,返回用户选择的单个文件绝对路径。
 *
 * @param parent        父窗口,对话框居中于该窗口。
 * @param caption       对话框标题栏文字。
 * @param dir           初始目录(为空则使用当前工作目录)。
 * @param filter        文件过滤器,如 "文本文件 (*.txt);;所有文件 (*)"。
 * @param selectedFilter 输出参数,返回用户实际选中的过滤器。
 * @param options       行为选项组合。
 * @return QString      选中文件的绝对路径;用户取消则返回空 QString。
 *
 * @par 示例
 * @code
 *   QString file = QFileDialog::getOpenFileName(
 *       this, "打开配置", QDir::homePath(),
 *       "JSON (*.json);;所有文件 (*.*)");
 *   if (!file.isEmpty()) {
 *       // 处理 file
 *   }
 * @endcode
 */
QString QFileDialog::getOpenFileName(
    QWidget *parent = nullptr,
    const QString &caption = QString(),
    const QString &dir = QString(),
    const QString &filter = QString(),
    QString *selectedFilter = nullptr,
    Options options = Options());

3.2 getOpenFileNames --- 选择多个文件

cpp 复制代码
/**
 * @brief 弹出"打开文件"对话框,允许多选,返回文件路径列表。
 *
 * @return QStringList 选中文件的绝对路径列表;取消则为空列表。
 *
 * @par 示例
 * @code
 *   QStringList files = QFileDialog::getOpenFileNames(
 *       this, "选择图片", "", "图片 (*.png *.jpg *.jpeg)");
 *   for (const QString &f : files) {
 *       qDebug() << "选中:" << f;
 *   }
 * @endcode
 */
QStringList QFileDialog::getOpenFileNames(
    QWidget *parent = nullptr,
    const QString &caption = QString(),
    const QString &dir = QString(),
    const QString &filter = QString(),
    QString *selectedFilter = nullptr,
    Options options = Options());

3.3 getSaveFileName --- 选择保存路径

cpp 复制代码
/**
 * @brief 弹出"另存为"对话框,返回用户指定的保存路径。
 *
 * 若目标文件已存在,默认会弹出覆盖确认(可用 DontConfirmOverwrite 关闭)。
 *
 * @par 示例
 * @code
 *   QString savePath = QFileDialog::getSaveFileName(
 *       this, "保存报告", QDir::homePath() + "/report.txt",
 *       "文本文件 (*.txt)");
 * @endcode
 */
QString QFileDialog::getSaveFileName(
    QWidget *parent = nullptr,
    const QString &caption = QString(),
    const QString &dir = QString(),
    const QString &filter = QString(),
    QString *selectedFilter = nullptr,
    Options options = Options());

3.4 getExistingDirectory --- 选择已有目录

cpp 复制代码
/**
 * @brief 弹出目录选择对话框,返回选中的目录绝对路径。
 *
 * @param options 默认包含 ShowDirsOnly,即只显示目录。
 *
 * @par 示例
 * @code
 *   QString dir = QFileDialog::getExistingDirectory(
 *       this, "选择导出目录", QDir::homePath());
 * @endcode
 */
QString QFileDialog::getExistingDirectory(
    QWidget *parent = nullptr,
    const QString &caption = QString(),
    const QString &dir = QString(),
    Options options = ShowDirsOnly);

Qt 5.12+ 还提供了对应的 URL 版本:

getOpenFileUrl() / getOpenFileUrls() / getSaveFileUrl() /

getExistingDirectoryUrl(),用于支持远程文件系统。


四、实例化常用成员方法

当静态方法不够用(需要多选 + 自定义信号 + 侧边栏等)时,

先创建 QFileDialog 对象,配置后调用 exec()

4.1 选择模式与接受模式

cpp 复制代码
void setFileMode(FileMode mode);   ///< 设置文件选择模式
FileMode fileMode() const;         ///< 获取当前文件模式

void setAcceptMode(AcceptMode mode); ///< 设置打开/保存模式
AcceptMode acceptMode() const;       ///< 获取当前接受模式

4.2 文件过滤器

cpp 复制代码
/**
 * @brief 设置单个文件过滤器。
 * @param filter 格式为 "描述 (通配符)",多个用 ;; 分隔。
 *
 * 例:"C++源文件 (*.cpp *.h);;Python (*.py);;所有文件 (*)"
 */
void setNameFilter(const QString &filter);

void setNameFilters(const QStringList &filters); ///< 设置多个过滤器
QStringList nameFilters() const;                 ///< 获取所有过滤器

void selectNameFilter(const QString &filter);    ///< 默认选中某个过滤器
QString selectedNameFilter() const;              ///< 获取当前选中的过滤器

4.3 目录与默认后缀

cpp 复制代码
void setDirectory(const QString &directory); ///< 设置初始目录
void setDirectory(const QDir &directory);    ///< 重载,接受 QDir
QDir directory() const;                      ///< 获取当前目录

/**
 * @brief 设置默认后缀。
 *
 * 当用户输入的文件名没有后缀时,自动追加此后缀。
 * 保存对话框中非常实用。
 */
void setDefaultSuffix(const QString &suffix);
QString defaultSuffix() const;

4.4 获取选中结果

cpp 复制代码
/**
 * @brief 返回用户选中的文件/目录的本地绝对路径列表。
 *
 * 应在 exec() 返回 Accepted 之后调用。
 * 单选模式下列表长度为 1。
 */
QStringList selectedFiles() const;

QList<QUrl> selectedUrls() const; ///< URL 版本,支持远程路径

4.5 视图与外观

cpp 复制代码
void setViewMode(ViewMode mode);  ///< Detail 或 List
ViewMode viewMode() const;

/**
 * @brief 修改对话框中指定标签的显示文字。
 *
 * 常用于把"打开"按钮改成"导入"、"选择"等。
 */
void setLabelText(DialogLabel label, const QString &text);
QString labelText(DialogLabel label) const;

4.6 选项与侧边栏

cpp 复制代码
void setOptions(Options options);       ///< 批量设置选项
Options options() const;                ///< 获取当前选项
void setOption(Option option, bool on = true); ///< 单独开关一个选项
bool testOption(Option option) const;   ///< 检测某个选项是否开启

/**
 * @brief 设置左侧"常用位置"侧边栏。
 * @note 必须同时设置 DontUseNativeDialog 才会生效。
 */
void setSidebarUrls(const QList<QUrl> &urls);
QList<QUrl> sidebarUrls() const;

4.7 预选中文件

cpp 复制代码
/**
 * @brief 在对话框打开时预先选中/填入指定文件名。
 *
 * 常用于"另存为"时给出默认文件名。
 */
void selectFile(const QString &filename);

五、常用信号

使用信号时必须设置 DontUseNativeDialog

因为原生对话框不会发出这些 Qt 信号。

cpp 复制代码
void currentChanged(const QString &path);     ///< 当前高亮条目变化
void currentUrlChanged(const QUrl &url);      ///< URL 版本
void fileSelected(const QString &file);       ///< 确认选择单个文件
void filesSelected(const QStringList &files); ///< 确认选择多个文件
void urlSelected(const QUrl &url);            ///< URL 版本
void urlsSelected(const QList<QUrl> &urls);   ///< URL 版本

六、练习

所有练习均使用 Qt Widgets,QT += widgets

编译命令示例:qmake && make 或在 Qt Creator 中直接运行。


练习 1:基础版 --- 文本编辑器的打开与保存

目标 :掌握 getOpenFileNamegetSaveFileName 的基本用法。

文件:mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QTextEdit>
#include <QFileDialog>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowTitle("练习1 - 简易文本编辑器");
    resize(600, 400);

    auto *edit = new QTextEdit(this);
    setCentralWidget(edit);

    auto *fileMenu = menuBar()->addMenu("文件(&F)");

    auto *openAct = fileMenu->addAction("打开(&O)");
    openAct->setShortcut(QKeySequence::Open);
    connect(openAct, &QAction::triggered, this, [this, edit](){
        QString path = QFileDialog::getOpenFileName(
            this, "打开文本文件", QDir::homePath(),
            "文本文件 (*.txt *.md);;所有文件 (*.*)");
        if (path.isEmpty()) return;

        QFile file(path);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QMessageBox::warning(this, "错误", "无法打开文件");
            return ;
        }
        QTextStream stream(&file);
        edit->setText(stream.readAll());
        setWindowTitle("练习1 - " + path);
        file.close();
    });

    auto *saveAct = fileMenu->addAction("保存(&S)");
    saveAct->setShortcut(QKeySequence::Save);
    connect(saveAct, &QAction::triggered, this, [this, edit](){
        QString path = QFileDialog::getSaveFileName(
            this, "保存文本文件", QDir::homePath() + "/untitled.txt",
            "文本文件 (*.txt);;Markdown (*.md)");
        if (path.isEmpty()) return;

        QFile file(path);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            QMessageBox::warning(this, "错误", "保存文件失败");
            return ;
        }

        QTextStream stream(&file);
        stream << edit->toPlainText();
        setWindowTitle("练习1 - " + path);
        file.close();
    });

    fileMenu->addSeparator();
    auto *quitAct = fileMenu->addAction("退出(&Q)");
    quitAct->setShortcut(QKeySequence::Quit);
    connect(quitAct, &QAction::triggered, this, &QWidget::close);
}

MainWindow::~MainWindow()
{
    delete ui;
}

要点

  • 静态方法返回空字符串表示用户取消,必须判断。
  • getSaveFileNamedir 参数可以直接包含默认文件名。
  • 过滤器格式:"描述1 (*.ext1);;描述2 (*.ext2)",用 ;; 分隔。

练习 2:多选文件 + 目录选择

目标 :掌握 getOpenFileNames 多选、getExistingDirectory 选目录,

以及 selectedFiles() 的使用。

cpp 复制代码
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QTextEdit>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QListWidget>
#include <QString>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowTitle("练习2 - 多选与目录选择");
    resize(500, 400);

    auto *layout = new QVBoxLayout(ui->centralwidget);

    // 界面组件
    auto *btnFiles = new QPushButton("选择多个图片文件", this);
    auto *btnDir   = new QPushButton("选择一个目录", this);
    auto *label    = new QLabel("尚未选择", this);
    auto *list     = new QListWidget(this);

    // 添加进layout
    layout->addWidget(btnFiles);
    layout->addWidget(btnDir);
    layout->addWidget(label);
    layout->addWidget(list);

    // 多选文件
    connect(btnFiles, &QPushButton::clicked, this, [this, list, label](){
        QStringList files = QFileDialog::getOpenFileNames(
            this, "选择图片", QDir::homePath(),
            "图片 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)");
        list->clear();
        list->addItems(files);
        label->setText(QString("共选中 %1 个文件").arg(files.size()));
    });

    // 选择目录
    connect(btnDir, &QPushButton::clicked, this, [this, list, label](){
        QString dir = QFileDialog::getExistingDirectory(
            this, "选择目录", QDir::homePath(),
            QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
        if (dir.isEmpty()) return ;

        list->clear();
        QDir d(dir);
        list->addItems(d.entryList(QDir::Files | QDir::NoDotAndDotDot));
        label->setText(QString("目录: %1 (含 %2 个文件)").arg(dir).arg(list->count()));
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}

要点

  • getOpenFileNames 返回 QStringList,空列表表示取消。
  • getExistingDirectory 默认带 ShowDirsOnly,可追加其他 Option。
  • QDir::entryList() 可列出目录内容,与文件对话框配合做预览。

练习 3:实例化精细定制 --- 带默认后缀、自定义按钮文字、侧边栏

目标:掌握实例化 QFileDialog 后的高级配置。

cpp 复制代码
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QTextEdit>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QListWidget>
#include <QString>
#include <QPlainTextEdit>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowTitle("练习3 - 精细定制保存对话框");
    resize(500, 300);

    auto *layout = new QVBoxLayout(ui->centralwidget);
    auto *btn    = new QPushButton("导出为 JSON", this);
    auto *log    = new QPlainTextEdit(this);
    log->setReadOnly(true);
    layout->addWidget(btn);
    layout->addWidget(log);

    connect(btn, &QPushButton::clicked, this, [this, log](){
        QFileDialog dlg(this, "导出配置", QDir::homePath());
        dlg.setFileMode(QFileDialog::AnyFile);
        dlg.setAcceptMode(QFileDialog::AcceptSave);

        // 默认后缀(用户只输文件名时自动补 .json)
        dlg.setDefaultSuffix("json");

        // 过滤器
        dlg.setNameFilters({
            "JSON 配置 (*.json)",
            "所有文件 (*)"
        });
        dlg.selectNameFilter("JSON 配置 (*.json)");

        // 自定义按钮文字
        dlg.setLabelText(QFileDialog::Accept, "导出");
        dlg.setLabelText(QFileDialog::Reject, "取消");
        dlg.setLabelText(QFileDialog::FileName, "配置名称");

        // 预填默认文件名
        dlg.selectFile("my_config");

        // 自定义侧边栏(必须 DontUseNativeDialog)
        dlg.setOption(QFileDialog::DontUseNativeDialog, true);
        dlg.setSidebarUrls({
                            QUrl::fromLocalFile(QDir::homePath()),
                            QUrl::fromLocalFile(QDir::homePath() + "/Desktop"),
                            QUrl::fromLocalFile("/tmp"),
        });

        if (dlg.exec() == QDialog::Accepted) {
            QStringList files = dlg.selectedFiles();
            if (!files.isEmpty()) {
                log->appendPlainText("导出路径: " + files.first());
                log->appendPlainText("使用过滤器: " + dlg.selectedNameFilter());
            }
        } else {
            log->appendPlainText("用户取消了导出");
        }
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

要点

  • setDefaultSuffix("json") 让用户输入 my_config 自动变成 my_config.json
  • setLabelText(Accept, "导出") 把确认按钮从"保存"改成"导出"。
  • setSidebarUrls() 必须配合 DontUseNativeDialog 才生效。
  • selectFile() 用于预填文件名。

练习 4:实时预览 --- 利用 currentChanged 信号

目标:掌握 QFileDialog 的信号机制,实现选中文件时实时显示信息。

mainwindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QDir>
#include <QFileDialog>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;

private:
    Ui::MainWindow *ui;
    QLabel *infoLable_ = nullptr;

private slots:
    void openDialog() {
        // 注意:使用信号必须设置 DontUseNativeDialog
        QFileDialog dlg(this, "选择文件", QDir::homePath());
        dlg.setOption(QFileDialog::DontUseNativeDialog, true);
        dlg.setFileMode(QFileDialog::ExistingFile);
        dlg.setNameFilters({
                            "所有文件 (*)",
                            "文本 (*.txt *.md)",
                            "图片 (*.png *.jpg)"
        });

        // 实时预览:当前高亮条目变化时更新信息
        connect(&dlg, &QFileDialog::currentChanged, this, &MainWindow::onCurrentChanged);

        if (dlg.exec() == QDialog::Accepted) {
            infoLable_->setText("最终选择: " + dlg.selectedFiles().first());
        }
    }


    void onCurrentChanged(const QString &path) {
        QFileInfo fi(path);
        if (!fi.exists()) {
            infoLable_->setText("路径不存在: " + path);
            return ;
        }
        QString text;
        text += QString("名称: %1\n").arg(fi.fileName());
        text += QString("类型: %1\n").arg(fi.isDir() ? "目录" : "文件");
        if (fi.isFile()) {
            text += QString("大小: %1 KB\n").arg(fi.size()/1024.0, 0, 'f', 2);
        }
        text += QString("修改时间: %1\n").arg(fi.lastModified().toString("yyyy-MM-dd hh:mm:ss"));
        text += QString("绝对路径: %1").arg(fi.absoluteFilePath());
        infoLable_->setText(text);
    }
};
#endif // MAINWINDOW_H

mainwindow.cpp

c++ 复制代码
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QVBoxLayout>
#include <QPushButton>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowTitle("练习4 - 实时文件预览");
    resize(450, 200);

    auto *layout = new QVBoxLayout(ui->centralwidget);
    auto *btn = new QPushButton("打开文件对话框(带实时预览)", this);
    infoLable_ = new QLabel("请选择文件...", this);
    infoLable_->setWordWrap(true);
    infoLable_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    infoLable_->setFrameShape(QFrame::StyledPanel);
    infoLable_->setMinimumHeight(120);

    layout->addWidget(btn);
    layout->addWidget(infoLable_);

    connect(btn, &QPushButton::clicked, this, &MainWindow::openDialog);
}

MainWindow::~MainWindow()
{
    delete ui;
}

要点

  • 使用 currentChanged 信号必须设置 DontUseNativeDialog
  • QFileInfo 可以获取文件大小、修改时间、是否目录等元信息。
  • 信号在用户高亮(单击/键盘移动)时就触发,不是确认后才触发。
相关推荐
请你吃div1 小时前
Electron 从零开始的新手开发教程
vue.js·windows·electron
小小龙学IT1 小时前
Qt Graphics View Framework(图形视图框架)深度解析:从 Scene/View/Item 到工业级 2D 场景
开发语言·数据库·qt
程与留1 小时前
09_Qt 布局管理系统——QHBoxLayout、QVBoxLayout、QGridLayout
c++·qt
神仙别闹1 小时前
基于QT(C++)实现旅游线路规划系统
c++·qt·旅游
影寂ldy1 小时前
C# WinForm TCP聊天室(服务端+客户端)
windows·tcp/ip·c#
≮傷£≯√1 小时前
音量调节弹窗 自定义组件qt
开发语言·qt
多弗朗皮卡丘2 小时前
C语言梦开始的地方16:结构体
c语言·开发语言·windows
polarislove02142 小时前
Windows安全中心网络盘打开文件提示“打开这些文件可能会对你的计算机有害”的解决方法
网络·windows·安全
李少兄2 小时前
Windows 系统 Hosts 文件完全指南
windows