【问题标题】:QFileSystemModel() / QTreeView(): traverse model filesystem tree prior to view expansionQFileSystemModel() / QTreeView():在视图展开之前遍历模型文件系统树
【发布时间】:2018-12-22 14:21:49
【问题描述】:

我希望为使用 PyQt5 创建的应用创建一个小部件。我希望用户能够在指定目录下的文件系统层次结构中选择文件的任何子集。我已经扩展了 QFileSystemModel 以允许大致按照此检查模型中的元素

example.

我希望用户能够在检查目录时修改目录内容的检查状态 - 甚至在子目录展开之前。

这样:

...在树视图中“隐藏”折叠节点:

我面临的问题是 QTreeView - 以及看似 QFileSystemModel - 都是通过仅报告已经查看过的模型项来优化性能,或者一起优化性能。在我手动展开 View 中的子目录之前,我无法遍历 Model 中的数据。

为了说明,我添加了一个打印回调,并将其传递给我的(递归)树遍历例程 - 以打印任何索引有多少子节点。 (参见附加代码。)在我通过双击展开树视图中的子目录之前,它报告没有子目录:Image 1 在我单击“一个”时报告以下内容:

tree clicked: /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one
|children|: 0

...但是如果我展开视图 - 如 Image 2 所示,然后我会看到所有的孩子,就像这样:

tree clicked: /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one
|children|: 7
child[0]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_f
|children|: 0
child[1]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_e
|children|: 0
child[2]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_d
|children|: 0
child[3]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_c
|children|: 0
child[4]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_b
|children|: 0
child[5]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a
|children|: 6
child[0]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/tfd
|children|: 0
child[1]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/sgl
|children|: 0
child[2]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/kjh
|children|: 0
child[3]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/jyk
|children|: 0
child[4]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/dgj
|children|: 0
child[5]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/one_a/..
|children|: 0
child[6]: recursing
traverseDirectory():
model printIndex(): /Users/caleb/dev/ML/cloudburst-ml/data/test_dir/one/..
|children|: 0

如何诱导加载子目录,或扩展模型对实际文件系统的反映?如何在用户点击视图小部件之前遍历 QFileSystemModel 的根路径?

这是我的代码:

import sys
from PyQt5 import QtWidgets, QtCore, QtGui


class FileTreeSelectorModel(QtWidgets.QFileSystemModel):
    def __init__(self, parent=None, rootpath='/'):
        QtWidgets.QFileSystemModel.__init__(self, None)
        self.root_path      = rootpath
        self.checks         = {}
        self.nodestack      = []
        self.parent_index   = self.setRootPath(self.root_path)
        self.root_index     = self.index(self.root_path)

        self.setFilter(QtCore.QDir.AllEntries | QtCore.QDir.Hidden | QtCore.QDir.NoDot)
        self.directoryLoaded.connect(self._loaded)

    def _loaded(self, path):
        print('_loaded', self.root_path, self.rowCount(self.parent_index))

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.CheckStateRole:
            return QtWidgets.QFileSystemModel.data(self, index, role)
        else:
            if index.column() == 0:
                return self.checkState(index)

    def flags(self, index):
        return QtWidgets.QFileSystemModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable

    def checkState(self, index):
        if index in self.checks:
            return self.checks[index]
        else:
            return QtCore.Qt.Checked

    def setData(self, index, value, role):
        if (role == QtCore.Qt.CheckStateRole and index.column() == 0):
            self.checks[index] = value
            print('setData(): {}'.format(value))
            return True
        return QtWidgets.QFileSystemModel.setData(self, index, value, role)

    def traverseDirectory(self, parentindex, callback=None):
        print('traverseDirectory():')
        callback(parentindex)
        if self.hasChildren(parentindex):
            print('|children|: {}'.format(self.rowCount(parentindex)))
            for childRow in range(self.rowCount(parentindex)):
                childIndex = parentindex.child(childRow, 0)
                print('child[{}]: recursing'.format(childRow))
                self.traverseDirectory(childIndex, callback=callback)
        else:
            print('no children')

    def printIndex(self, index):
        print('model printIndex(): {}'.format(self.filePath(index)))


class FileTreeSelectorDialog(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.root_path      = '/Users/caleb/dev/ML/cloudburst-ml/data/test_dir/'

        # Widget
        self.title          = "Application Window"
        self.left           = 10
        self.top            = 10
        self.width          = 1080
        self.height         = 640

        self.setWindowTitle(self.title)         #TODO:  Whilch title?
        self.setGeometry(self.left, self.top, self.width, self.height)

        # Model
        self.model          = FileTreeSelectorModel(rootpath=self.root_path)
        # self.model          = QtWidgets.QFileSystemModel()

        # View
        self.view           = QtWidgets.QTreeView()

        self.view.setObjectName('treeView_fileTreeSelector')
        self.view.setWindowTitle("Dir View")    #TODO:  Which title?
        self.view.setAnimated(False)
        self.view.setIndentation(20)
        self.view.setSortingEnabled(True)
        self.view.setColumnWidth(0,150)
        self.view.resize(1080, 640)

        # Attach Model to View
        self.view.setModel(self.model)
        self.view.setRootIndex(self.model.parent_index)

        # Misc
        self.node_stack     = []

        # GUI
        windowlayout = QtWidgets.QVBoxLayout()
        windowlayout.addWidget(self.view)
        self.setLayout(windowlayout)

        QtCore.QMetaObject.connectSlotsByName(self)

        self.show()

    @QtCore.pyqtSlot(QtCore.QModelIndex)
    def on_treeView_fileTreeSelector_clicked(self, index):
        print('tree clicked: {}'.format(self.model.filePath(index)))
        self.model.traverseDirectory(index, callback=self.model.printIndex)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = FileTreeSelectorDialog()
    sys.exit(app.exec_())

我在这里查看了几个链接

[1] - 这并没有改变行为

[2] - 这提出了一种替代解决方案,但不适用于我的目的

[3] - 这涉及相同的类,但不是相同的问题

【问题讨论】:

    标签: python pyqt pyqt5 qtreeview qfilesystemmodel


    【解决方案1】:

    根据设计,QFileSystemModel 不会加载所有项目,因为该任务非常繁重,另一方面,hasChildren() 指示它是否有子目录或文件,但 rowCount() 仅返回可见的子项,因为设计问题,在this report 中讨论。

    因此,您不应该使用rowCount(),而是使用QDirIterator 执行遍历目录的任务:

    def traverseDirectory(self, parentindex, callback=None):
        print('traverseDirectory():')
        callback(parentindex)
        if self.hasChildren(parentindex):
            path = self.filePath(parentindex)
            it = QtCore.QDirIterator(path, self.filter()  | QtCore.QDir.NoDotAndDotDot)
            while it.hasNext():
                childIndex =  self.index(it.next())
                self.traverseDirectory(childIndex, callback=callback)
        else:
            print('no children')
    

    如果您的模型有很多级别,我建议您在另一个线程中实现该任务,因为它可以冻结 GUI。

    【讨论】:

    • 谢谢您,eyllanesc,我已经阅读了有关此设计功能的信息,您建议的答案与上面链接 ([2]) 中提供的答案没有什么不同。我需要在 QFileSystemModel 中执行此操作的原因是因为它是在我基于 QFileSystemModel (FileTreeSelectorModel) 构建的扩展类中定义了 Checked 状态,因此我需要遍历目录的全部内容并在模型。我想我会尝试在 QDirIterator() 循环中为 idx 做一个 QFileSystemModel.index(idx) 。那是你认为我应该做的吗?谢谢!
    • 虽然我完全同意仅从文件系统加载所需节点的设计功能,但如果 QFileSystemModel() 有一种方法可以查询文件系统的单个节点并填充到模型中。这与当前类的工作方式相同,并且还允许对由 QFileSystemModel 建模的文件系统的完整编程知识。我假设通过用户使用鼠标输入以编程方式提供等效功能。以编程方式扩展视图也不会像我预期的那样更新模型。
    • @CalebHoward 如果您检查我的解决方案,我所做的是遍历文件夹的子项并获取他们的 qmodelindex,这就是您所问的,您期望什么答案?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    相关资源
    最近更新 更多