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:基础版 --- 文本编辑器的打开与保存
目标 :掌握 getOpenFileName 和 getSaveFileName 的基本用法。
文件: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;
}
要点:
- 静态方法返回空字符串表示用户取消,必须判断。
getSaveFileName的dir参数可以直接包含默认文件名。- 过滤器格式:
"描述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可以获取文件大小、修改时间、是否目录等元信息。- 信号在用户高亮(单击/键盘移动)时就触发,不是确认后才触发。