c++qtqtreeviewqstandarditemmodel

How to make a QStandardItemModel from QStringList of file paths


i'm new to C++ and Qt and can't seem to find the right solution for this. I want to create a QTreeView using a QStandardItemModel. I have a list of file/folder paths (within a QStringList) structured in a format like this:

Arena/Main/00078/a.txt
Arena/Main/00080/b.txt
Arena/Main/00080/collision/c.txt
Arena/Main/00080/light/d.txt
Arena/Main/00081/e.txt
Characters/f.txt
Characters/Main/g.txt
Characters/Main/someFolder/h.txt

I previously used a QFileSystemModel and made actual temporary directories, iterating through the list as a workaround:

   QDir dir(temp_path);
    if (!dir.exists(dir_string)){
         dir.mkpath(dir_string);
    }
        
    QFile file(filepath_str);
    file.open(QIODevice::WriteOnly);

Which gave me a working result like this: https://i.sstatic.net/ITzJz.png

However, the list can reach up to 10,000+ files making it a longer backwards workaround. I want to populate a QTreeView in a similar way using a QStandardItemModel created from the path list.


Solution

  • You can store the created paths in a map like this:

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QMap>
    #include <QStandardItemModel>
    
    class QStandardItem;
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        void addPathRecusively(const QStringList &path_parts, QStandardItem *parent, int layer = 1);
    
        QMap<QString, QStandardItem*> path_item_map_;
        QStandardItemModel model_;
    };
    #endif // MAINWINDOW_H
    

    mainwindow.cpp

    #include "mainwindow.h"
    
    #include <QStringList>
    #include <QTreeView>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        QStringList filenames =
        {
            "Arena/Main/00078/a1.txt",
            "Arena/Main/00078/a2.txt",
            "Arena/Main/00080/b.txt",
            "Arena/Main/00080/collision/c.txt",
            "Arena/Main/00080/light/d.txt",
            "Arena/Main/00081/e1.txt",
            "Arena/Main/00081/e2.txt",
            "Arena/Main/00081/e3.txt",
            "Characters/f.txt",
            "Characters/Main/g.txt",
            "Characters/Main/someFolder/h.txt",
        };
    
        for (auto &filename : filenames)
        {
            QStringList parts = filename.split("/");
            parts.pop_back();
            addPathRecusively(parts, model_.invisibleRootItem());
        }
    
        QTreeView *view = new QTreeView{this};
        view->setModel(&model_);
        this->setCentralWidget(view);
        this->resize(500, 500);
    }
    
    MainWindow::~MainWindow()
    {
    }
    
    void MainWindow::addPathRecusively(const QStringList &path_parts, QStandardItem *parent, int layer)
    {
        QStringList path_parts_to_layer;
        std::copy(path_parts.begin(), path_parts.begin() + layer, std::back_inserter(path_parts_to_layer));
        QString path = path_parts_to_layer.join("/");
        auto item = path_item_map_.value(path);
        if (item == nullptr)
        {
            item = new QStandardItem{path_parts_to_layer.last()};
            path_item_map_.insert(path, item);
            parent->appendRow({item, new QStandardItem});
        }
    
        if (path_parts.length() == layer)
        {
            auto file_count_index = model_.indexFromItem(item).siblingAtColumn(1);
            model_.setData(file_count_index, file_count_index.data().toInt() + 1, Qt::DisplayRole);
        }
        else
            addPathRecusively(path_parts, item, layer + 1);
    }