如何使用 QAbstractItemModel 从 QTreeView 中删除行?

How to remove row from QTreeView using QAbstractItemModel?

我正在尝试从模型中删除 QTreeView 项目 "Node 6 (delete me)"。

我无法让它工作,因为没有删除任何内容。我做错了什么?
删除代码见MainWindow.init方法。

import sys

try:
    from PySide2 import QtWidgets
except ImportError:
    from PyQt5 import QtWidgets

try:
    from PySide2 import QtCore
except ImportError:
    from PyQt5 import QtCore


class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent

        if parent is not None:
            parent.addChild(self)

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

    def name(self):
        return self._name

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

    def insertChild(self, position, child):
        if position < 0 or position > len(self._children):
            return False

        self._children.insert(position, child)
        child._parent = self
        return True

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

    def parent(self):
        return self._parent

    def setParent(self, new_parent):
        self._parent._children.remove(self)
        self._parent = new_parent
        new_parent._children.append(self)

    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)

    def removeChild(self, position):
        if position < 0 or position > len(self._children):
            return False
        child = self._children.pop(position)
        child._parent = None
        return True

    def __repr__(self):
        return self._name


class TreeModel(QtCore.QAbstractItemModel):
    def __init__(self, root, parent=None):
        super(TreeModel, self).__init__(parent)
        self._rootNode = root

    def rowCount(self, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()

        return parentNode.childCount()

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        """Return whatever the view should display"""

        if not index.isValid():
            return None

        node = index.internalPointer()

        if role == QtCore.Qt.DisplayRole:
            if index.column() == 0:
                return node.name()

    def index(self, row, column, parent):
        if not parent.isValid():
            # parent is not valid when it is the root node, since the "parent"
            # method returns an empty QModelIndex
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()  # the node

        childItem = parentNode.child(row)

        return self.createIndex(row, column, childItem)

    def parent(self, index):
        node = index.internalPointer()

        parentNode = node.parent()

        if parentNode == self._rootNode:
            return QtCore.QModelIndex()

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

    def flags(self, index):

        # Original, inherited flags:
        original_flags = super(TreeModel, self).flags(index)

        return (original_flags | QtCore.Qt.ItemIsEnabled
                | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled
                | QtCore.Qt.ItemIsDropEnabled)

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if section == 0:
                return 'Node name'


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)

        self.tree = QtWidgets.QTreeView()
        self.layout = QtWidgets.QGridLayout()
        self.main_widget = QtWidgets.QWidget()

        self.layout.addWidget(self.tree, 0, 0)
        self.main_widget.setLayout(self.layout)
        self.setCentralWidget(self.main_widget)

        # Note how these are added in a non-sorted fashion
        root_node = Node('Hidden root')
        Node(name='Node 3', parent=root_node)  # add using parent param
        n2 = Node(name='Node 2', parent=root_node)  # add using parent param
        Node(name='Node 1', parent=root_node)  # add using parent param
        n2.addChild(Node(name='Node 5'))  # add using addChild
        n2.addChild(Node(name='Node 6 (delete me)'))  # add using addChild
        n2.addChild(Node(name='Node 4'))  # add using addChild

        model = TreeModel(root=root_node)
        self.tree.setModel(model)
        self.tree.expandAll()

        # Attempt to remove a node from within the model
        node2 = model.index(1, 0, QtCore.QModelIndex())  # "Node 2" index
        index = model.index(1, 0, parent=node2)
        index_node = index.internalPointer()
        print('index represents node "%s" (its parent node is "%s")' %
            (index_node.name(), index.parent().internalPointer().name()))
        print('Removing %s on row %s' % (index_node.name(), index.row()))
        model.beginRemoveRows(index.parent(), index.row(), index.row())
        success = model.removeRow(index.row(), parent=index.parent())
        print('Removal was a succes?:', success)
        model.endRemoveRows()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

@Jaa-c 如果你愿意,可以把它复制粘贴到你自己的答案中,我会把它标记为已接受的答案。

import sys

try:
    from PySide2 import QtWidgets
except ImportError:
    from PyQt5 import QtWidgets

try:
    from PySide2 import QtCore
except ImportError:
    from PyQt5 import QtCore


class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent

        if parent is not None:
            parent.addChild(self)

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

    def name(self):
        return self._name

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

    def insertChild(self, position, child):
        if position < 0 or position > len(self._children):
            return False

        self._children.insert(position, child)
        child._parent = self
        return True

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

    def parent(self):
        return self._parent

    def setParent(self, new_parent):
        self._parent._children.remove(self)
        self._parent = new_parent
        new_parent._children.append(self)

    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)

    def removeChild(self, position):
        if position < 0 or position > len(self._children):
            return False
        child = self._children.pop(position)
        child._parent = None
        return True

    def __repr__(self):
        return self._name


class TreeModel(QtCore.QAbstractItemModel):
    def __init__(self, root, parent=None):
        super(TreeModel, self).__init__(parent)
        self._rootNode = root

    def rowCount(self, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()

        return parentNode.childCount()

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        """Return whatever the view should display"""

        if not index.isValid():
            return None

        node = index.internalPointer()

        if role == QtCore.Qt.DisplayRole:
            if index.column() == 0:
                return node.name()

    def index(self, row, column, parent):
        if not parent.isValid():
            # parent is not valid when it is the root node, since the "parent"
            # method returns an empty QModelIndex
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()  # the node

        childItem = parentNode.child(row)

        return self.createIndex(row, column, childItem)

    def parent(self, index):
        node = index.internalPointer()

        parentNode = node.parent()

        if parentNode == self._rootNode:
            return QtCore.QModelIndex()

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

    def flags(self, index):

        # Original, inherited flags:
        original_flags = super(TreeModel, self).flags(index)

        return (original_flags | QtCore.Qt.ItemIsEnabled
                | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled
                | QtCore.Qt.ItemIsDropEnabled)

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if section == 0:
                return 'Node name'

    def removeRow(self, row, parent):
        if not parent.isValid():
            # parent is not valid when it is the root node, since the "parent"
            # method returns an empty QModelIndex
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()  # the node

        parentNode.removeChild(row)
        return True


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)

        self.tree = QtWidgets.QTreeView()
        self.layout = QtWidgets.QGridLayout()
        self.main_widget = QtWidgets.QWidget()

        self.layout.addWidget(self.tree, 0, 0)
        self.main_widget.setLayout(self.layout)
        self.setCentralWidget(self.main_widget)

        # Note how these are added in a non-sorted fashion
        root_node = Node('Hidden root')
        Node(name='Node 3', parent=root_node)  # add using parent param
        n2 = Node(name='Node 2', parent=root_node)  # add using parent param
        Node(name='Node 1', parent=root_node)  # add using parent param
        n2.addChild(Node(name='Node 5'))  # add using addChild
        n2.addChild(Node(name='Node 6 (delete me)'))  # add using addChild
        n2.addChild(Node(name='Node 4'))  # add using addChild

        model = TreeModel(root=root_node)
        self.tree.setModel(model)
        self.tree.expandAll()

        # Attempt to remove a node from within the model
        node2 = model.index(1, 0, QtCore.QModelIndex())  # "Node 2" index
        index = model.index(1, 0, parent=node2)
        index_node = index.internalPointer()
        print('index represents node "%s" (its parent node is "%s")' %
            (index_node.name(), index.parent().internalPointer().name()))
        print('Removing %s on row %s' % (index_node.name(), index.row()))
        model.beginRemoveRows(index.parent(), index.row(), index.row())
        success = model.removeRow(index.row(), parent=index.parent())
        print('Removal was a succes?:', success)
        model.endRemoveRows()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())