Spencer
Spencer

Reputation: 2110

Qt custom QFileSystemModel to group sequential files

I am trying to create a file browser which will group sequential files in the same director together in a tree view. For example if you had the following files in a directory: img.001.png img.002.png img.003.png, images 2 & 3 would appear as children of image 1 in a QTreeView. They would simply be made children of the first sequential item in the directory.

I have tried a number of ways to accomplish this but so far performance has been a huge issue. Doing it all manually with a QStandardItemModel and python os module was very slow and cumbersome. The QFileSystemModel is surprisingly fast and includes a ton of convenient functions for renaming files, etc, so I am trying to work with that as my base. I tried creating a QStandardItemModel as a sort of "proxy model" which iterated over the QFileSystemModel when the root dir changed. This allowed me to create all of the functionality I need, but was shockingly slow (I guess there is a lot of signaling under the hood or something?). Setting a root dir in the FileSystemModel takes virtually no time, but with a custom model iterating over rows and re-interpreting data it can take 5-10 seconds for one folder with 1000+ files.

So I am thinking that the best solution would be to subclass the QFileSystemModel directly because it has methods built in I want and is lightning fast. However I do not know very much about models and I'm not quite sure where to start. I see that there are quite a few signals that can notify me when rows are going to be inserted, have been inserted, are being moved, etc, etc. But at what point would I be able to check if a row was a sequential file, and if so "re-direct" it to be a child of another index?

Hopefully that is all clear, here is a snippet to get up and running. I do not usually post such general questions on SO but I have put a lot of time into this problem and need some direction as to where to go. I don't need you to write my code for me, but rather pointing me in the right direction would be very helpful!

import sys, os, re
from PyQt5 import QtWidgets

class TestApp(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()

        self.tree = QtWidgets.QTreeView(self)

        self.model = QtWidgets.QFileSystemModel()
        self.proxyModel = ProxyModel(self.model)
        self.tree.setModel(self.proxyModel)
        self.model.setRootPath("test/dir/with/lots/of/files")
        self.model.directoryLoaded.connect(self.update)

    def update(self):
        self.proxyModel.update_model_from_source()

class ProxyModel(QtGui.QStandardItemModel):
    def __init__(self, sourceModel):
        super().__init__()
        self.sourceModel = sourceModel

    def update_model_from_source(self):
        self.removeRows(0, self.rowCount())

        self.sourceModel.sort(0, QtCore.Qt.AscendingOrder)

        candidates = {}

        parent = self.sourceModel.index(self.sourceModel.rootPath())
        for row in range(self.sourceModel.rowCount(parent)):
            index = self.sourceModel.index(row, 0, parent)
            filename = self.sourceModel.fileName(index)

            file_item = QtGui.QStandardItem(filename)
            file_item.setIcon(self.sourceModel.fileIcon(index))
            filetype = QtGui.QStandardItem(self.sourceModel.type(index))
            date = QtGui.QStandardItem(self.sourceModel.lastModified(index).toString("yyyy-MM-dd h:mm.ss")))
            size = QtGui.QStandardItem(str(self.sourceModel.size(indes)))

            # check for sequences
            sequence = False
            if not self.sourceModel.isDir(index):
                search_str = os.path.splitext(filename)[0]
                matches = re.search(r'([0-9]+)$', search_str)
                if matches:
                    candidate = filename[0:matches.start(0)]

                    if candidate in candidates:
                        parent_item = candidates[candidate]
                        sequence = True
                    else:
                        candidates[candidate] = file_item

            row = [file_item, filetype, date, size]
            if sequence:
                parent_item.appendRow(row)
            else:
                self.appendRow(row)


if __name__ == "__main__":
    app = QtWidgets.QApplication()
    ex = TestApp()
    ex.show()
    sys.exit(app.exec_())

Upvotes: 0

Views: 401

Answers (0)

Related Questions