In PyQt4 Model-View tutorial the author
designs Node()
Class to be used with QAbstractItemModel
linked to QTreeView
:
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 typeInfo(self):
return "NODE"
def addChild(self, child):
self._children.append(child)
def name(self):
return self._name
def setName(self, name):
self._name = name
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
Then he proceeds with declaring a data variable itself.
First he declares a rootNode
variable to be used as a parent to all top level item-variables:
rootNode = Node("Root")
Then he declares two top level item-variables A and B: childNodeA0
and childNodeB0
.
And for each of these two top-level item-variables two child item-variables are declared
(I renamed the variables to make things look more generic):
childNodeA0 = Node("childNodeA0", rootNode)
childNodeA1 = Node("childNodeA1", childNodeA0)
childNodeA2 = Node("childNodeA2", childNodeA1)
childNodeB0 = Node("childNodeB0", rootNode)
childNodeB1 = Node("childNodeB1", childNodeB0)
childNodeB3 = Node("childNodeB3", childNodeB1)
Here is the schematic representation of data stored in rootNode variable:
|------Root
|------childNodeA0
|------childNodeA1
|------childNodeA2
|------childNodeB0
|------childNodeB1
|------childNodeB3
Now I declare a third item variable called childNodeC0
:
childNodeC0 = Node("childNodeC0", rootNode)
I pass rootNode
as its parent. Here is the schematics:
|------Root
|------childNodeA0
|------childNodeA1
|------childNodeA2
|------childNodeB0
|------childNodeB1
|------childNodeB3
|------childNodeC0
Now using .addChild()
method I append childNodeC0
variable as a child of childNodeB0
.
So childNodeC0
is a child of two variables: it is a child of rootNode
and childNodeB0
:
childNodeB0.addChild(childNodeC0)
Here is the schematics:
|------Root
|------childNodeA0
|------childNodeA1
|------childNodeA2
|------childNodeB0
|------childNodeB1
|------childNodeB3
|------childNodeC0
|------childNodeC0
All goes as expected. But when this data variable is assigned to a "real" Model the childNodeC0
item-variable only shows up as a child of Root
and not as a child of childNodeB0
. Aside from it the childNodeB0
item-variable is shown in TreeView twice: as a child of Root (as it is supposed to be). And it is also shown as a child of itself!:
Question:
How to make TreeView to show a data stored in a root variable properly?
from PyQt4 import QtCore, QtGui
import sys
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 typeInfo(self):
return "NODE"
def addChild(self, child):
self._children.append(child)
def name(self):
return self._name
def setName(self, name):
self._name = name
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
def log(self, tabLevel=-1):
output = ""
tabLevel += 1
for i in range(tabLevel):
output += "\t"
output += "|------" + self._name + "\n"
for child in self._children:
output += child.log(tabLevel)
tabLevel -= 1
output += "\n"
return output
def __repr__(self):
return self.log()
class SceneGraphModel(QtCore.QAbstractItemModel):
def __init__(self, root, parent=None):
super(SceneGraphModel, self).__init__(parent)
self._rootNode = root
def rowCount(self, parent):
if not parent.isValid():
parentNode = self._rootNode
else:
parentNode = parent.internalPointer()
# print 'VALID: ', type(parent), parent.row(), parent.column()
return parentNode.childCount()
def columnCount(self, parent):
return 1
def data(self, index, role):
if not index.isValid():
return None
node = index.internalPointer()
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
if index.column() == 0:
return node.name()
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.isValid():
if role == QtCore.Qt.EditRole:
node = index.internalPointer()
node.setName(value)
return True
return False
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if section == 0:
return "Column #0"
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def parent(self, index):
node = self.getNode(index)
parentNode = node.parent()
if parentNode == self._rootNode:
return QtCore.QModelIndex()
return self.createIndex(parentNode.row(), 0, parentNode)
def index(self, row, column, parent):
parentNode = self.getNode(parent)
childItem = parentNode.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def getNode(self, index):
if index.isValid():
node = index.internalPointer()
if node:
return node
return self._rootNode
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
rootNode = Node("Root")
childNodeA0 = Node("childNodeA0", rootNode)
childNodeA1 = Node("childNodeA1", childNodeA0)
childNodeA2 = Node("childNodeA2", childNodeA1)
childNodeB0 = Node("childNodeB0", rootNode)
childNodeB1 = Node("childNodeB1", childNodeB0)
childNodeB3 = Node("childNodeB3", childNodeB1)
childNodeC0 = Node("childNodeC0", rootNode)
childNodeB0.addChild(childNodeC0)
treeView=QtGui.QTreeView()
model = SceneGraphModel(rootNode)
treeView.setModel(model)
treeView.show()
print rootNode
sys.exit(app.exec_())
I think that with this data model you can't have a Node having 2 different parents.
The addChild
is confusing because it seems that you can do it.
If you look in the scene graph model, it will ask the Node
for it's parent (in parent(self, index)
):
parentNode = node.parent()
and Node.parent()
only returns the parent defined at the construction of the item:
return self._parent
If you want to have the same child at 2 different locations you may want to copy it or to improve the data model.