Qt 和 Python - QIdentityProxyModel 嵌套在 QSortFilterProxyModel 顶部时无法获得正确的列数

Qt and Python - QIdentityProxyModel does not get the right column count when nested on top of a QSortFilterProxyModel

我有三个模型,一个 QAbstractItemModel“sourceModel”,一个用于过滤的 QSortFilterProxyModel“proxyModel”,一个用于修改列和显示数据的 QIdentityProxyModel“dataModel”。

当我只对 QAbstractItemModel -> QIdentityProxyModel 进行分层时,一切正常。 我的源模型只给定了一个默认列,我可以在我的 QIdentifyProxyModel 上添加多个新列,并且我的视图显示所有正确的数据。这让我可以在具有不同数据的多个视图上重用相同的模型,太棒了。

但是,如果我对 QAbstractItemModel -> QSortFilterProxyModel -> QIdentityProxyModel 进行分层,那么我的视图只会在第一列中显示数据,并且选择模型会中断。 如果我在我的源模型中定义了相等或更多数量的列,那么所有 QIdentityProxyModel 列在我的视图中都会正确运行,并显示与我在我的源模型中定义的不同的数据。

当代理被嵌套时,QIdentityProxyModel class 仍然可以使用数据访问源项,但是传递给 QIdentityProxyModel 的数据函数的索引只查询源模型中定义的数量列。

知道怎么做吗?非常感谢任何帮助!

from PySide2 import QtCore, QtWidgets, QtGui

class Item(dict):
    def __init__(self, data=None):
        super(Item, self).__init__()

        self._children = list()
        self._parent = None
        if data:
            self.update(data)

    def childCount(self):
        return len(self._children)

    def child(self, row):
        if row >= len(self._children):
            return
        return self._children[row]

    def children(self):
        return self._children

    def parent(self):
        return self._parent

    def row(self):
        if self._parent is not None:
            siblings = self.parent().children()
            return siblings.index(self)

    def add_child(self, child):
        child._parent = self
        self._children.append(child)

class TreeModel(QtCore.QAbstractItemModel):
    Columns = list()
    ItemRole = QtCore.Qt.UserRole + 1

    def __init__(self, parent=None):
        super(TreeModel, self).__init__(parent)
        self._root_item = Item()

    def rowCount(self, parent):
        if parent.isValid():
            item = parent.internalPointer()
        else:
            item = self._root_item
        return item.childCount()

    def columnCount(self, parent):
        return len(self.Columns)

    def setColumns(self, keys):
        assert isinstance(keys, (list, tuple))
        self.Columns = keys

    def data(self, index, role):
        if not index.isValid():
            return None
        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            item = index.internalPointer()
            column = index.column()

            key = self.Columns[column]
            return item.get(key, None)

        if role == self.ItemRole:
            return index.internalPointer()

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if section < len(self.Columns):
                return self.Columns[section]
        super(TreeModel, self).headerData(section, orientation, role)


    def parent(self, index):
        item = index.internalPointer()
        parent_item = item.parent()

        if parent_item == self._root_item or not parent_item:
            return QtCore.QModelIndex()

        return self.createIndex(parent_item.row(), 0, parent_item)

    def index(self, row, column, parent):
        if not parent.isValid():
            parent_item = self._root_item
        else:
            parent_item = parent.internalPointer()

        child_item = parent_item.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
        else:
            return QtCore.QModelIndex()

    def add_child(self, item, parent=None):
        if parent is None:
            parent = self._root_item
        parent.add_child(item)

    def column_name(self, column):
        if column < len(self.Columns):
            return self.Columns[column]

    def clear(self):
        self.beginResetModel()
        self._root_item = Item()
        self.endResetModel()


class CustomSortFilterProxyModel(QtCore.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super(CustomSortFilterProxyModel, self).__init__(parent)

    def filterAcceptsRow(self, row, parent):
        model = self.sourceModel()
        index = model.index(row, self.filterKeyColumn(), parent)
        item = index.internalPointer()
        if item.get('name'):
            return True
        else:
            return False


class IdentityProxyModel(QtCore.QIdentityProxyModel):
    def __init__(self, *args, **kwargs):
        super(IdentityProxyModel, self).__init__(*args, **kwargs)
        self.Columns = []
        self._root_item = Item()

    def setColumns(self, keys):
        assert isinstance(keys, (list, tuple))
        self.Columns = keys
    #
    def columnCount(self, parent):
        # return 3
        return len(self.Columns)

    def column_name(self, column):
        if column < len(self.Columns):
            return self.Columns[column]

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            if section < len(self.Columns):
                return self.Columns[section]
        super(IdentityProxyModel, self).headerData(section, orientation, role)

    def data(self, index, role):
        if not index.isValid():
            return
        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            item = self.mapToSource(index).data(TreeModel.ItemRole)
            column = index.column()

            key = self.Columns[column]
            return item.get(key, None)

        return super(IdentityProxyModel, self).data(index, role)


if __name__ == '__main__':
    import sys

    sourceModel = TreeModel()
    sourceModel.setColumns(['name'])
    sourceModel.add_child(Item({'name': 'itemA', 'number': '1', 'info': 'A'}))
    sourceModel.add_child(Item({'name': 'itemB', 'number': '2', 'info': 'B'}))

    proxyModel = CustomSortFilterProxyModel()
    proxyModel.setSourceModel(sourceModel)

    dataModel = IdentityProxyModel()
    dataModel.setSourceModel(proxyModel)
    dataModel.setColumns(['name', 'info'])

    app = QtWidgets.QApplication(sys.argv)
    view = QtWidgets.QTreeView()
    view.setModel(dataModel)
    view.show()
    sys.exit(app.exec_())

树模型很棘手。 很多.

无论何时你必须处理它们,你都必须真正理解递归。对于 Qt 模型(在某种程度上很复杂),这变得更加困难,并且常常是导致严重头痛和熬夜的原因。

我将以下代码基于 ,这是非常基本的,没有考虑子列数量不一致的可能性。

概念是基于树结构的代理模型需要跟踪层次结构(通常基于父索引内部指针或 id),并且当“幽灵”列出现时必须考虑这一点已创建。

以上所有总是 非常重要。项目视图需要所有实现才能正确更新其内容并允许有效的用户交互。

class IdentityProxyModel(QtCore.QIdentityProxyModel):
    def __init__(self, *args, **kwargs):
        super(IdentityProxyModel, self).__init__(*args, **kwargs)
        self.Columns = []
        self._parents = {}
        self._root_item = Item()

    # ...
    def _isInvalid(self, column):
        # we assume that the model always has the same column count
        return column > self.sourceModel().columnCount() - 1

    def mapToSource(self, index):
        if self._isInvalid(index.column()):
            index = index.sibling(index.row(), 0)
        return super().mapToSource(index)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        if self._isInvalid(column):
            index = self.createIndex(row, column, parent.internalId())
            self._parents[index] = parent
            return index
        return super().index(row, column, parent)

    def parent(self, index):
        if self._isInvalid(index.column()):
            return self._parents[index]
        return super().parent(index)

    def flags(self, index):
        if self._isInvalid(index.column()):
            return self.flags(index.sibling(index.row(), 0))
        return super().flags(index)

    def sibling(self, row, column, idx):
        if self._isInvalid(column):
            return self.index(row, column, idx.parent())
        elif self._isInvalid(idx.column()):
            idx = self.index(idx.row(), 0, idx.parent())
        return super().sibling(row, column, idx)

注意:覆盖应始终使用其基本实现的签名。例如,rowCount()columnCount() 必须 接受无效的关键字父参数,not 用于“快速在模型的顶层调用”。通常的做法是使用基本的 QModelIndex 实例,因为它基本上是一个不可变的对象(但 None 也被普遍接受):

    def rowCount(self, parent=QtCore.QModelIndex()):
        # ...

    def columnCount(self, parent=QtCore.QModelIndex()):
        # ...