filterAcceptsRow() 究竟做了什么?

What does filterAcceptsRow() exactly do?

一个目录下有3个文件夹:

f:/root_folder/folder1
f:/root_folder/_folder2
f:/root_folder/folder3.asset

我尝试自定义 QSortFilterProxyModel.filterAcceptsRow() 以仅显示带有 .asset 后缀的文件夹。

** 我知道我可以使用 QFileSystemModel.setNameFilters([*.asset]) 来做到这一点。但有时它不起作用。

我有 Python3.7 + PySide2 5.13.0.

# first inherit a QFileSystemModel instance:
listModel = QFileSystemModel()

# let the instance display only folders, except for '.' and '..':
listModel.setFilter(QDir.NoDotAndDotDot | QDir.Dirs)

# assgin a root path. i just want the model to search the 'f:/root_folder':
listModel.setRootPath("f:/root_folder")

# add a custom QSortFilterProxyModel:
myProxy = myProxyModel()
myProxy.setSourceModel(listModel)

# finally show result in a QListView:
# 'ui' is a QWidget object that contain a listView widget.
ui.listView.setModel(myProxy)
ui.listView.setRootIndex(myProxy.mapFromSource(listModel.index("f:/root_folder")))

这里是自定义 QSortFilterProxyModel:

# test:
class myProxyModel(QSortFilterProxyModel):
    def filterAcceptsRow(self, source_row, source_parent):
        return True

此时,脚本按预期工作:列表中有 3 个文件夹,没有过滤器。

如果我理解正确,'source_parent' 应该是 'listModel' 的 QModelIndex,它指向目录 'f:/root_folder'。并且 'source_row' 应该是 'f:/root_folder' 中项目的 "ordinal number",三个文件夹之一。对吗?

然后我添加了自己的过滤器:

# first try:
class myProxyModel(QSortFilterProxyModel):
    def filterAcceptsRow(self, source_row, source_parent):
        source_model = self.sourceModel() 
        # 'source_model' should be the 'listModel', right?

        source_index = source_model.index(source_row, 0, source_parent)
        # 'source_index' is a QModelIndex, pointing to 'folder1' or '_folder2' or 'folder3.asset'.

        # start filtering
        filename = source_index.data(Qt.DisplayRole)
        print(filename) # check
        if filename[-6:] == ".asset": return True
        else: return False

它应该在控制台上显示 3 个文件夹名称,在列表中显示 1 个文件夹(folder3.asset)。但我得到了非常奇怪的结果!这是控制台的结果:** 它多次列出我所有的硬盘

HDD (F:)
root_folder
HDD (F:)
HDD (E:)
HDD (D:)
C:
HDD (F:)
HDD (E:)
HDD (D:)
C:

并且 listView 是空的。

'source_parent'无效吗?然后我试试这个:

class myProxyModel(QSortFilterProxyModel):
    def filterAcceptsRow(self, source_row, source_parent):
        if not source_parent.isValid():
            print("index invalid")
            return False
        else: return True

在控制台上得到这个:

index invalid
index invalid

和 listView 中的 3 个文件夹:

现在我完全糊涂了。

filterAcceptsRow() 到底做了什么?

您只需要过滤与 QFileSystemModel 的 rootPath() 关联的索引的子项:

from PySide2 import QtCore, QtGui, QtWidgets


class SuffixDirProxyModel(QtCore.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._suffix = ""

    def filterAcceptsRow(self, source_row, source_parent):
        source_model = self.sourceModel()
        if (
            self._suffix
            and isinstance(source_model, QtWidgets.QFileSystemModel)
            and source_parent == source_model.index(source_model.rootPath())
        ):
            index = source_model.index(source_row, 0, source_parent)
            name = index.data(QtWidgets.QFileSystemModel.FileNameRole)
            file_info = source_model.fileInfo(index)
            return name.split(".")[-1] == self._suffix and file_info.isDir()
        return True

    @property
    def suffix(self):
        return self._suffix

    @suffix.setter
    def suffix(self, s):
        self._suffix = s
        self.invalidateFilter()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    model = QtWidgets.QFileSystemModel()
    model.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)

    path = # "f:/root_folder"
    model.setRootPath(path)

    proxy = SuffixDirProxyModel()
    proxy.suffix = "asset"
    proxy.setSourceModel(model)

    w = QtWidgets.QListView()
    w.setViewMode(QtWidgets.QListView.IconMode)
    w.setModel(proxy)
    w.setRootIndex(proxy.mapFromSource(model.index(path)))
    w.show()
    sys.exit(app.exec_())

如果你不超载就什么都没有。

只有当您使用的 class 中提供的过滤器(通常 QSortFilterProxyModel)不能满足您的需求时,您才需要重载它。