QTreeView 请求无效行的索引

QTreeView requests index for invalid row

看看下面的MWE。

这是一个简单的 QAbstractItemModel,只有一个级别,将其项目存储在列表中。我创建了一个 QTreeView 来显示模型,并创建了一个按钮来删除第二个项目。

from PyQt5.QtCore import QModelIndex, QAbstractItemModel, Qt
from PyQt5.QtWidgets import QTreeView, QApplication, QPushButton


class Item:
    def __init__(self, title):
        self.title = title


class TreeModel(QAbstractItemModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._items = []  # typing.List[Item]

    def addItem(self, item: Item):
        self.beginInsertRows(QModelIndex(), len(self._items), len(self._items))
        self._items.append(item)
        self.endInsertRows()

    def removeItem(self, item: Item):
        index = self._items.index(item)
        self.beginRemoveRows(QModelIndex(), index, index)
        self._items.remove(item)
        self.endRemoveRows()

    # ----- overridden methods from QAbstractItemModel -----

    # noinspection PyMethodOverriding
    def data(self, index: QModelIndex, role):
        item = index.internalPointer()
        if role == Qt.DisplayRole:
            return item.title

    # noinspection PyMethodOverriding
    def rowCount(self, parent=QModelIndex()):
        if not parent.isValid():
            return len(self._items)
        return 0

    # noinspection PyMethodOverriding
    def columnCount(self, parent=QModelIndex()):
        return 1

    # noinspection PyMethodOverriding
    def index(self, row: int, col: int, parent=QModelIndex()):
        assert not parent.isValid()
        return self.createIndex(row, 0, self._items[row])

    def parent(self, index=QModelIndex()):
        return QModelIndex()


def removeItem():
    model.removeItem(item2)


if __name__ == '__main__':
    app = QApplication([])
    model = TreeModel()
    button = QPushButton('Delete')
    button.clicked.connect(removeItem)
    button.show()
    item1 = Item('Item 1')
    model.addItem(item1)
    item2 = Item('Item 2')
    model.addItem(item2)
    treeView = QTreeView()
    treeView.setModel(model)
    treeView.show()

    app.exec()

据我所知,我的模型的实现是正确的(尽管非常基础)。特别是,它报告的行数和列数是正确的,并且它永远不会为无效的数据创建索引。

重现我的问题的步骤:

在我的系统上,应用程序在 beginRemoveRows() 中崩溃,因为视图请求第 2 行的 QModelIndex。当然,第 2 行不存在。

知道为什么 QTreeView 认为会有 3 行,而模型明确报告只有 2 行吗?

添加、移动、删除等项目时,模型所做的是验证 QPersistentModelIndex 是否有效,因此它会调用您不使用的 index() method of QAbstractItemModel. And in that method it is the developer's responsibility to verify if the row or column is valid, and for that the model provides the hasIndex() 方法,从而导致您指出的错误out,所以解决方案是:

def index(self, row: int, col: int, parent=QModelIndex()):
    if not self.hasIndex(row, col, parent):
        return QModelIndex()
    assert not parent.isValid()
    return self.createIndex(row, 0, self._items[row])