QFileSystemModel() / QTreeView():在视图扩展之前遍历模型文件系统树
QFileSystemModel() / QTreeView(): traverse model filesystem tree prior to view expansion
我想为使用 PyQt5 创建的应用程序创建一个小部件。我希望用户能够 select 指定目录下文件系统层次结构中文件的任何子集。我已经扩展了 QFileSystemModel 以允许大致按照此
检查模型中的元素
example.
我希望用户能够在检查目录时修改目录内容的检查状态——甚至在子目录展开之前。
这样:
...树视图中折叠节点的 'under the hood':
我面临的问题是,QTreeView - 似乎是 QFileSystemModel - 各自或一起通过仅报告已经查看过的模型项目来优化性能。直到我手动展开View中的子目录,才无法遍历Model中的数据。
为了说明,我添加了一个打印回调,并将其传递给我的(递归)tree-traversal 例程 - 以打印任何索引有多少 children。 (见附件代码。)在我通过 double-clicking 扩展树视图中的子目录之前,它报告没有 children: Image 1 当我报告以下内容时点击 'one':
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,那么我会看到所有 children,如下所示:
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_())
我在这里查看了几个链接
- 这并没有改变行为
[2] - 这提出了一个替代解决方案,但不适用于我的目的
[3] - 这处理了相同的 类,但不是同一个问题
根据设计 QFileSystemModel
不会加载所有项目,因为该任务非常繁重,另一方面 hasChildren()
表示它是否有子目录或文件,但仅 rowCount()
returns 由于设计问题可见的子项,已在 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。
我想为使用 PyQt5 创建的应用程序创建一个小部件。我希望用户能够 select 指定目录下文件系统层次结构中文件的任何子集。我已经扩展了 QFileSystemModel 以允许大致按照此
检查模型中的元素example.
我希望用户能够在检查目录时修改目录内容的检查状态——甚至在子目录展开之前。
这样:
...树视图中折叠节点的 'under the hood':
我面临的问题是,QTreeView - 似乎是 QFileSystemModel - 各自或一起通过仅报告已经查看过的模型项目来优化性能。直到我手动展开View中的子目录,才无法遍历Model中的数据。
为了说明,我添加了一个打印回调,并将其传递给我的(递归)tree-traversal 例程 - 以打印任何索引有多少 children。 (见附件代码。)在我通过 double-clicking 扩展树视图中的子目录之前,它报告没有 children: Image 1 当我报告以下内容时点击 'one':
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,那么我会看到所有 children,如下所示:
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_())
我在这里查看了几个链接
[2] - 这提出了一个替代解决方案,但不适用于我的目的
[3] - 这处理了相同的 类,但不是同一个问题
根据设计 QFileSystemModel
不会加载所有项目,因为该任务非常繁重,另一方面 hasChildren()
表示它是否有子目录或文件,但仅 rowCount()
returns 由于设计问题可见的子项,已在 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。