如何使用 QAbstractItemModel 从 QTreeView 中删除行?
Posted
技术标签:
【中文标题】如何使用 QAbstractItemModel 从 QTreeView 中删除行?【英文标题】:How to remove row from QTreeView using QAbstractItemModel? 【发布时间】:2018-05-17 12:05:34 【问题描述】:我正在尝试从模型中删除 QTreeView 项目“节点 6(删除我)”。
我无法让它工作,因为没有任何内容被删除。我做错了什么?
删除代码见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_())
【问题讨论】:
您没有实现removeRow
方法,模型不知道如何从您的数据中删除一行...
啊,是的 - 谢谢!!
【参考方案1】:
@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_())
【讨论】:
如果你有时间,我也实现了moveRow
,但虽然移动行工作但出现错误:***.com/questions/50392221/…以上是关于如何使用 QAbstractItemModel 从 QTreeView 中删除行?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 QAbstractItemModel 和 QTreeView 类中找到子项?
如何在 QAbstractItemModel 中为 QTreeView 创建人工节点
应用于自定义 QAbstractItemModel 的外部选择
QAbstractItemModel data() 永远不会被调用