QTreeview 迭代节点
QTreeview iterating nodes
我使用 PyQt5 和 QTreeview 来显示从文件加载的 xml 树(及其一些属性作为列)。这按预期工作。但我现在正在为两件事而苦苦挣扎:
itemChange
事件。树节点应该是可编辑的,我需要在编辑后做一些检查。调用了itemChange
但是在编辑一个节点时调用了N次
- 从编辑的项目开始,我需要在树中上下移动并检查子节点和父节点。我希望这很简单,比如一个
getParent()
和另一个方向的递归 getChildren()
。但是如何从 itemChange
事件 QStandardItem
获取父项和子项?
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import xml.etree.ElementTree as ET
tree = ET.ElementTree(file='data.xml')
class MainFrame(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.tree = QtWidgets.QTreeView(self)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(self.tree)
root_model = QtGui.QStandardItemModel()
root_model.setHorizontalHeaderLabels(['Label', 'privilege', 'uid', 'docId'])
self.tree.setModel(root_model)
self.tree.setUniformRowHeights(True)
root = tree.getroot()
self._populateTree(root, root_model.invisibleRootItem())
self.tree.expandAll()
self.tree.resizeColumnToContents(0)
self.tree.resizeColumnToContents(1)
self.tree.resizeColumnToContents(2)
self.move(0, 0)
self.resize(800, 1000)
#self.tree.itemChanged.connect(self.edit)
def _populateTree(self, root, parent):
child_item = QtGui.QStandardItem(root.tag)
child_item.setData(root)
privilege = ''
uid = ''
docId = ''
privilege_item = QtGui.QStandardItem(privilege)
uid_item = QtGui.QStandardItem(uid)
docId_item = QtGui.QStandardItem(docId)
parent.appendRow([child_item, privilege_item, uid_item, docId_item])
privilege_item.model().itemChanged.connect(self.change_privilege)
child_item.model().itemChanged.connect(self.change_privilege)
for elem in root.getchildren():
self._populateTree(elem, child_item)
def change_privilege(self, item):
print(item)
print(item.row(), item.column())
def check_parent(self, item):
# get parent of item and check value
# check_parent(...)
def check_child(self, item):
# get children of item and check value
# for child in item.children():
# check_child(child)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main = MainFrame()
main.show()
sys.exit(app.exec_())
回答第一个问题:每次调用 MainFrame._populateTree
时,都会在信号 child_item.model().itemChanged
和 privilege_item.model().itemChanged
与槽 MainFrame.change_privilege
之间创建一个信号槽连接。但是,由于所有子项和特权项都属于同一模型(即 root_model
),您实际上是在同一信号和插槽之间创建多个连接。这意味着当模型中的任何项目发生变化时,插槽也会被多次调用。解决此问题的最简单方法是在 __init__
左右创建一个连接。
要访问项目的父项,您确实可以使用 item.parent()
。可以通过 item.child(row, column)
逐一访问项目的子项,其中 row
在 range(item.rowCount())
范围内,column
在 range(item.columnCount())
范围内,例如
def change_privilege(self, item):
print(item.row(), item.column(), item.text())
self.check_parent(item)
self.check_child(item)
def check_parent(self, item):
if item.parent():
print('Parent:', item.parent().text())
else:
print('No parent')
def check_child(self, item):
if item.hasChildren():
print('Children:')
for row in range(item.rowCount()):
print('\t', row, end='')
for col in range(item.columnCount()):
print('\t', item.child(row, col).text(), end='')
print()
else:
print('No children')
我使用 PyQt5 和 QTreeview 来显示从文件加载的 xml 树(及其一些属性作为列)。这按预期工作。但我现在正在为两件事而苦苦挣扎:
itemChange
事件。树节点应该是可编辑的,我需要在编辑后做一些检查。调用了itemChange
但是在编辑一个节点时调用了N次- 从编辑的项目开始,我需要在树中上下移动并检查子节点和父节点。我希望这很简单,比如一个
getParent()
和另一个方向的递归getChildren()
。但是如何从itemChange
事件QStandardItem
获取父项和子项?
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import xml.etree.ElementTree as ET
tree = ET.ElementTree(file='data.xml')
class MainFrame(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.tree = QtWidgets.QTreeView(self)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(self.tree)
root_model = QtGui.QStandardItemModel()
root_model.setHorizontalHeaderLabels(['Label', 'privilege', 'uid', 'docId'])
self.tree.setModel(root_model)
self.tree.setUniformRowHeights(True)
root = tree.getroot()
self._populateTree(root, root_model.invisibleRootItem())
self.tree.expandAll()
self.tree.resizeColumnToContents(0)
self.tree.resizeColumnToContents(1)
self.tree.resizeColumnToContents(2)
self.move(0, 0)
self.resize(800, 1000)
#self.tree.itemChanged.connect(self.edit)
def _populateTree(self, root, parent):
child_item = QtGui.QStandardItem(root.tag)
child_item.setData(root)
privilege = ''
uid = ''
docId = ''
privilege_item = QtGui.QStandardItem(privilege)
uid_item = QtGui.QStandardItem(uid)
docId_item = QtGui.QStandardItem(docId)
parent.appendRow([child_item, privilege_item, uid_item, docId_item])
privilege_item.model().itemChanged.connect(self.change_privilege)
child_item.model().itemChanged.connect(self.change_privilege)
for elem in root.getchildren():
self._populateTree(elem, child_item)
def change_privilege(self, item):
print(item)
print(item.row(), item.column())
def check_parent(self, item):
# get parent of item and check value
# check_parent(...)
def check_child(self, item):
# get children of item and check value
# for child in item.children():
# check_child(child)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main = MainFrame()
main.show()
sys.exit(app.exec_())
回答第一个问题:每次调用 MainFrame._populateTree
时,都会在信号 child_item.model().itemChanged
和 privilege_item.model().itemChanged
与槽 MainFrame.change_privilege
之间创建一个信号槽连接。但是,由于所有子项和特权项都属于同一模型(即 root_model
),您实际上是在同一信号和插槽之间创建多个连接。这意味着当模型中的任何项目发生变化时,插槽也会被多次调用。解决此问题的最简单方法是在 __init__
左右创建一个连接。
要访问项目的父项,您确实可以使用 item.parent()
。可以通过 item.child(row, column)
逐一访问项目的子项,其中 row
在 range(item.rowCount())
范围内,column
在 range(item.columnCount())
范围内,例如
def change_privilege(self, item):
print(item.row(), item.column(), item.text())
self.check_parent(item)
self.check_child(item)
def check_parent(self, item):
if item.parent():
print('Parent:', item.parent().text())
else:
print('No parent')
def check_child(self, item):
if item.hasChildren():
print('Children:')
for row in range(item.rowCount()):
print('\t', row, end='')
for col in range(item.columnCount()):
print('\t', item.child(row, col).text(), end='')
print()
else:
print('No children')