问题
I am trying to load child items in a treeview
+top
parent1
parent2
parent3
To start off I populate the model with parent node information only and attach it to a treeview, the tree looks like above.
self.mytreeview=QtGui.Qtreeview()
self.model=QtGui.QStandardItemModel(self.mytreeview)
def initialise_model(self):
'''Populate the model with parent nodes only'''
#open file and process each line, each file line has the parent node info
....
for file_name_entry in fileobject:
parent_item=QtGui.QStandardItem(str(file_name_entry).strip())
parent_item.setData("this is a parent",QtCore.Qt.TooltipRole)
self.model.appendRow(parent_item)
self.mytreeview.setModel(self.model)
At this point none of the parents have any child information. What I want to do next is to update a parent node, when the user clicks on the parent node(just in time or lazy loading of a parents child information).
So when a parent node is clicked, and the signal is emitted
QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked(QModelIndex)'), self.update_model)
update_model code should go fetch the children of that parent, update the model, refresh the tree and expand the parent node.
def update_model(self,index):
'''Update the model by adding children to selected/clicked parent node only'''
parent_node=QtGui.QStandardItem(self.model.itemFromIndex(index))
parent_item_index=index.row()
#open the file called parent_node.txt and get all the child entries from the file...
child_index=0
for child_name_entry in parent_text_fileobject:
child_item=QtGui.QStandardItem(str(child_name_entry).strip())
child_item.setData("this is a child",QtCore.Qt.TooltipRole)
parent_item.setChild(child_index,child_item)
child_index=child_index+1
#Once all the children have been added update the model, by inserting a new row where the parent was
self.model.removeRow(parent_item_index)#remove old parent
self.model.insertRow(parent_item_index,parent_item)#insert new parent with its children
When I try to remove the selected parent node (the one with no children) and try inserting a new parent item (with children) it ends up addding the new parent(with children) above the parent node with no children so I end up with duplicated parent nodes
+top
parent1
parent2
+parent2
child_a
child_b
child_c
parent3
So just to recap, removing and inserting i.e.
#Once all the children have been added update the model, by inserting a new row where the parent was
self.model.removeRow(parent_item_index)#remove old parent
self.model.insertRow(parent_item_index,parent_item)#insert new parent with its children
is a bad idea. Looking at the comments its been suggested that AppendRow() might be the answer, but I am not so certain I believe that
self.model.appendRow(parent_item)#new parent with its children
Would simply add the parent to the bottom of my model and therefore the tree would still have duplicate parent nodes i.e
+top
parent1
**parent2**
parent3
**+parent2**
child_a
child_b
child_c
I think what I am looking for is a way to update an existing node in the model, so that the selected (parent node) can be updated with its children. e.g.
...
self.model.UpdateExisitingNode(node_index)
...
Any suggestions on how to update an node in model? Note I am using Windows and PyQt
Regards
回答1:
You are duplicating items because you are creating another parent item, you should only create new child items. For example:
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.mytreeview = QtGui.QTreeView(self)
self.setLayout(QtGui.QVBoxLayout())
self.layout().addWidget(self.mytreeview)
self.model = QtGui.QStandardItemModel(self.mytreeview)
self.mytreeview.setModel(self.model)
self.mytreeview.clicked.connect(self.update_model)
self.initialise_model()
def initialise_model(self):
for text in ["parent1", "parent2", "parent3"]:
item = QtGui.QStandardItem(text)
self.model.appendRow(item)
def update_model(self, index):
parent = self.model.itemFromIndex(index)
for text in ["children1", "children2", "children3"]:
children = QtGui.QStandardItem("{}_{}".format(parent.text(), text))
parent.appendRow(children)
self.mytreeview.expand(index)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
回答2:
To get this to work nicely, the parent items should show expansion arrows before they are populated with child items. It would also be nice to populate the top-level items when expaned by whatever means (i.e. via the mouse, the keyboard, and also programmatically), whilst at the same time ensuring that this only happens when required (i.e. only if the parent is currently empty).
This can all be achieved quite simply by setting an "Expandable" flag on the relevant items and then reimplementing the hasChildren
method of the QStandardItemModel
. With that in place, it is then very easy to write methods to populate the top-level items on demand.
Here is a simple demo based on your example code that implements all that:
import sys
from PyQt4 import QtCore, QtGui
class StandardItemModel(QtGui.QStandardItemModel):
ExpandableRole = QtCore.Qt.UserRole + 500
def hasChildren(self, index):
if self.data(index, StandardItemModel.ExpandableRole):
return True
return super(StandardItemModel, self).hasChildren(index)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.mytreeview = QtGui.QTreeView()
self.model = StandardItemModel(self.mytreeview)
self.mytreeview.setModel(self.model)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.mytreeview)
self.mytreeview.expanded.connect(self.update_model)
self.initialise_model()
def initialise_model(self):
self.model.clear()
for file_name_entry in fileobject:
parent_item = QtGui.QStandardItem(file_name_entry.strip())
parent_item.setData(True, StandardItemModel.ExpandableRole)
parent_item.setData("this is a parent", QtCore.Qt.ToolTipRole)
self.model.appendRow(parent_item)
def update_model(self, index):
parent_item = self.model.itemFromIndex(index)
if not parent_item.rowCount():
for child_name_entry in parent_text_fileobject:
child_item = QtGui.QStandardItem(child_name_entry.strip())
child_item.setData("this is a child", QtCore.Qt.ToolTipRole)
parent_item.appendRow(child_item)
# dummy test data
fileobject = 'parent1 parent2 parent3'.split()
parent_text_fileobject = 'child_a child_b child_c'.split()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/46778623/lazy-loading-child-items-qtreeview