pyqt treeview 删除最后一行时报索引错误

1 投票
2 回答
1620 浏览
提问于 2025-04-30 22:03

我有一个两列的树形视图,结构很简单,只有两层:根节点 > 父节点 > 子节点。当我按下删除键时,我在树形视图的子类中有一个删除行的调用。删除任何行时,这个功能都能正常工作,但删除最后一行时就出问题了。

我遇到了一个错误,提示 IndexError: list index out of range,这个错误似乎是从节点类的 child 方法中产生的。这个问题发生在模型的 removeRows 方法中调用 self.beginRemoveRows 时。奇怪的是,操作的顺序似乎是反的,或者是线程的问题导致在模型还不知道最近的更新时就发生了刷新。

我希望通过这些代码片段,能有人提供一些可能的解释或者一些调试的建议。

来自 QTreeView 子类的代码,

def keyPressEvent(self, event):
    if event.key() == Qt.Key_Delete:
        index = self.currentIndex()
        self.model().removeRow(index.row())
    else:
        # call base class keyPressEvent
        QTreeView.keyPressEvent(self, event)  

来自 QAbstractItemModel 子类的代码,

def index(self, row, column, parent=QModelIndex()):
    parent_node = self.getNode(parent)
    child_item = parent_node.child(row)
    if child_item:
        return self.createIndex(row, column, child_item)
    else:
        return QModelIndex()

def getNode(self, index):
    if index.isValid():
        node = index.internalPointer()
        if node:
            return node
    return self._root

def removeRows(self, position, rows, parent=QModelIndex()):

    parent_node = self.getNode(parent)
    self.beginRemoveRows(parent, position, position + rows - 1)
    parent_node.removeChild(position)
    self.endRemoveRows()
    return True

来自 节点类 的代码,

def child(self, row):
    return self._children[row]

def removeChild(self, position):

    if position < 0 or position >= len(self._children):
        return False

    child = self._children.pop(position)
    child._parent = None

    return True
暂无标签

2 个回答

0

对QTreeView的这个修改似乎解决了问题。不过,我也不完全确定问题是不是由其他原因引起的。

def keyPressEvent(self, event):
    if event.key() == Qt.Key_Delete:
        index = self.currentIndex()
        if not index.isValid(): return

        parent = index.parent()

        # adjust selection so refresh does not trigger IndexError 
        self.selectionModel().setCurrentIndex(self.indexAbove(index), QItemSelectionModel.ClearAndSelect)

        # remove selected
        self.model().removeRow(row, parent=parent)

        return

    # call base class keyPressEvent
    QTreeView.keyPressEvent(self, event)
4

我花了一些时间,终于找到了这个bug的原因:根据这封邮件,在使用index方法时,你应该先检查一下索引是否存在再继续操作。让人烦的是,这一点在方法说明里没有写,也没有在qt4或qt5的文档中提到。

正确的index方法实现应该是这样的:

def index(self, row, column, parent=QModelIndex()):
    if self.hasIndex(row, column, parentIndex):
        parent_node = self.getNode(parent) 
        child_item = parent_node.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
    else:
        return QModelIndex()

在我的应用中,这样做解决了问题。

撰写回答