python-3.xqtreeviewos.walkpyqt6qstandarditemmodel

How can I append a folder directory with many sub directories to a QTreeView using QStandardItemModel in PyQt6


I am building a script that takes a path and gets the folders. Then appends those folder names as items to another item that I am calling "Root". I am using QStandardItemModel and not QFileSystemModel because I intend to replace "Root" with a name that the user will provide and I am using QTreeView as a preview to the entire structure of the folder.

Current issue:

Photo with current issue

I would like to add the entire directory and include the sub directories but have it all appended under "Root". In my attempt below I am only getting the first set of folder.

In this example, I created a folder, with many sub folders and am trying to add those folders in correct order to my "Root" item.

import os
from PyQt6.QtWidgets import QApplication, QTreeView
from PyQt6.QtGui import QStandardItemModel, QStandardItem

app = QApplication([])

# Create the model
model = QStandardItemModel()

# Set the path to the directory containing the folders you want to add to the tree view
path = r"replace\with\path\to\folder"

# Create an Item for everything to parent under
root_item = QStandardItem("Root")
model.appendRow(root_item)


#iterate through the directory provided
for index,(root, folders, files) in enumerate(os.walk(path)):
    folder_root_name = (str(root))
    folder_root_name = folder_root_name.split('\\')[-1]

    for folder in folders:
        folder_item = QStandardItem(folder)
        
        if index == 0:
            root_item.appendRow(folder_item)
        else:
            folder_root_name_item = QStandardItem(folder_root_name)
            folder_root_name_item.appendRow(folder_item)


tree_view = QTreeView()
tree_view.setModel(model)
tree_view.show()
app.exec()

Here is a photo of what I am trying to accomplish:

Photo of desired goal


Solution

  • Whenever you have to deal with tree/directory structures, you have to consider that you're using a 3-dimensional model, which automatically calls for a recursive behavior.

    While, normally, such a structure would require a relative 3D "model" as a reference, a basic string-based dictionary can suffice with a simple directory-based model.

    The assumption is based on the fact that os.walk will always walk through sub directories, even if they are empty.

    The trick is to use a dictionary that has keys as full directory paths, and items as their values.

    root_item = QStandardItem("Root")
    parents = {path: root_item}
    model.appendRow(root_item)
    
    def getParent(path):
        parent = parents.get(path)
        if parent:
            return parent
        grandParentPath, parentName = path.rsplit(os.sep, 1)
        parent = QStandardItem(parentName)
        parents[path] = parent
        getParent(grandParentPath).appendRow(parent)
        return parent
    
    for root, folders, files in os.walk(path):
        getParent(root)