You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在QML中递归遍历目录及子目录并在ListView展示指定扩展名文件?

嘿,这个需求我刚好折腾过,给你两种靠谱的实现方案——推荐用QML结合Qt C的方式,功能更灵活稳定;如果不想碰C,也可以试试纯QML的方案(虽然有一些局限性)。下面一步步给你拆解:

方案一:QML + Qt C++(推荐,适合复杂场景)

这个方案的核心是用C处理文件扫描逻辑(递归遍历文件这类操作,C的性能和跨平台性更优),再把结果传递给QML的ListView展示。

1. 编写C++文件扫描类

先写一个继承自QObject的类,负责递归扫描目录、筛选扩展名,然后通过信号把结果传给QML。

头文件(FileScanner.h)

#ifndef FILESCANNER_H
#define FILESCANNER_H

#include <QObject>
#include <QStringList>
#include <QDir>

class FileScanner : public QObject
{
    Q_OBJECT
public:
    explicit FileScanner(QObject *parent = nullptr);

    // 暴露给QML调用的扫描方法:参数为目标目录、要筛选的扩展名(比如"jpg,png")
    Q_INVOKABLE void scanDirectory(const QString& dirPath, const QString& extensions = "");

signals:
    // 扫描完成后发送结果信号
    void scanFinished(const QStringList& filePaths);
    // 可选:实时发送扫描到的文件(适合大目录)
    void fileFound(const QString& filePath);

private:
    void recursiveScan(const QDir& dir, const QStringList& filter);
    QStringList m_results;
};

#endif // FILESCANNER_H

源文件(FileScanner.cpp)

#include "FileScanner.h"

FileScanner::FileScanner(QObject *parent) : QObject(parent)
{
}

void FileScanner::scanDirectory(const QString &dirPath, const QString &extensions)
{
    m_results.clear();
    QDir dir(dirPath);
    if (!dir.exists()) {
        emit scanFinished(m_results);
        return;
    }

    // 处理扩展名筛选,转成QDir需要的格式,比如["*.jpg", "*.png"]
    QStringList filter;
    if (!extensions.isEmpty()) {
        QStringList extList = extensions.split(",", Qt::SkipEmptyParts);
        for (const QString& ext : extList) {
            filter.append(QString("*.%1").arg(ext.trimmed()));
        }
    } else {
        // 无指定扩展名则扫描所有文件
        filter.append("*");
    }

    recursiveScan(dir, filter);
    emit scanFinished(m_results);
}

void FileScanner::recursiveScan(const QDir &dir, const QStringList &filter)
{
    // 获取当前目录下的所有文件
    QFileInfoList fileInfos = dir.entryInfoList(filter, QDir::Files | QDir::NoDotAndDotDot);
    for (const QFileInfo& info : fileInfos) {
        m_results.append(info.absoluteFilePath());
        emit fileFound(info.absoluteFilePath()); // 实时发送文件路径
    }

    // 递归遍历子目录
    QFileInfoList dirInfos = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo& dirInfo : dirInfos) {
        recursiveScan(QDir(dirInfo.absoluteFilePath()), filter);
    }
}

2. 注册C++类到QML

在主函数(main.cpp)里,把FileScanner注册到QML上下文,让QML能调用它的方法:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "FileScanner.h"

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

    QQmlApplicationEngine engine;
    // 注册FileScanner类到QML,命名为FileScanner
    qmlRegisterType<FileScanner>("com.example", 1, 0, "FileScanner");

    const QUrl url(u"qrc:/main.qml"_qs);
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

3. QML端实现UI和逻辑

写一个包含触发按钮、结果展示ListView的QML文件:

import QtQuick 2.15
import QtQuick.Controls 2.15
import com.example 1.0

ApplicationWindow {
    width: 600
    height: 400
    visible: true
    title: "文件扫描器"

    // 实例化C++扫描类
    FileScanner {
        id: scanner
        onScanFinished: {
            // 扫描完成后更新ListModel
            fileModel.clear()
            for (let path of filePaths) {
                fileModel.append({filePath: path})
            }
        }
        // 可选:实时添加文件到Model
        onFileFound: {
            fileModel.append({filePath: filePath})
        }
    }

    // 存储文件路径的ListModel
    ListModel {
        id: fileModel
    }

    Column {
        anchors.fill: parent
        spacing: 10
        padding: 10

        Row {
            spacing: 10
            TextField {
                id: dirPathInput
                width: 300
                text: "/home/your/test_dir" // 替换成你的目标目录
                placeholderText: "输入目标目录路径"
            }

            TextField {
                id: extInput
                width: 150
                text: "jpg,png"
                placeholderText: "输入扩展名(逗号分隔)"
            }

            Button {
                text: "开始扫描"
                onClicked: {
                    scanner.scanDirectory(dirPathInput.text, extInput.text)
                }
            }
        }

        ListView {
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.topMargin: 10

            model: fileModel
            delegate: Text {
                text: filePath
                padding: 5
                wrapMode: Text.Wrap
                width: parent.width
            }

            ScrollBar.vertical: ScrollBar {}
        }
    }
}
方案二:纯QML实现(适合简单场景)

如果不想写C++,可以用QML的FolderListModel结合JavaScript递归逻辑,但要注意:这种方式扫描大目录时性能可能不佳,且部分平台(如iOS)的文件系统权限限制可能影响功能。

import QtQuick 2.15
import QtQuick.Controls 2.15
import Qt.labs.folderlistmodel 2.15

ApplicationWindow {
    width: 600
    height: 400
    visible: true
    title: "纯QML文件扫描器"

    ListModel {
        id: fileModel
    }

    Column {
        anchors.fill: parent
        spacing: 10
        padding: 10

        Row {
            spacing: 10
            TextField {
                id: dirPathInput
                width: 300
                text: "/home/your/test_dir"
                placeholderText: "输入目标目录路径"
            }

            TextField {
                id: extInput
                width: 150
                text: "jpg"
                placeholderText: "输入扩展名"
            }

            Button {
                text: "开始扫描"
                onClicked: {
                    fileModel.clear()
                    recursiveScan(dirPathInput.text, extInput.text)
                }
            }
        }

        ListView {
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.topMargin: 10

            model: fileModel
            delegate: Text {
                text: filePath
                padding: 5
                wrapMode: Text.Wrap
                width: parent.width
            }

            ScrollBar.vertical: ScrollBar {}
        }
    }

    // JavaScript递归扫描函数
    function recursiveScan(dirPath, extension) {
        let folderModel = FolderListModel {}
        folderModel.folder = dirPath
        folderModel.nameFilters = extension ? ["*." + extension] : ["*"]
        folderModel.showFiles = true
        folderModel.showDirs = false

        // 添加当前目录的文件
        for (let i = 0; i < folderModel.count; i++) {
            fileModel.append({filePath: folderModel.get(i).filePath})
        }

        // 遍历子目录,递归扫描
        folderModel.nameFilters = ["*"]
        folderModel.showFiles = false
        folderModel.showDirs = true
        for (let i = 0; i < folderModel.count; i++) {
            let subDir = folderModel.get(i).filePath
            recursiveScan(subDir, extension)
        }
    }
}
一些注意事项
  • 权限问题:无论哪种方案,都要确保程序有目标目录的访问权限,比如Android/iOS上需要申请文件访问权限。
  • 性能优化:扫描大目录时,C++方案记得把扫描逻辑放到子线程(用QThread),避免卡住UI;纯QML方案尽量避开超大规模目录。
  • 扩展名匹配:注意大小写问题,比如JPG和jpg,C++里可以用QDir::IgnoreCase选项忽略大小写,QML则需要手动处理。

内容的提问来源于stack exchange,提问作者md612

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:36:04