Hi @pim
Basically as explained in TreeView made simple – Part 1 you have to override GetDown to make it actually return the first child.
Then GetNext will be called to retrieve the next children.
So I would say it's more about how to structure your data that matters.
But since you seem to use the previous example I extended it to also support children.
So first we will extend our TextureObject to support children.
To do so we will add a list that will store all children and also a weakref of our parent. (don't forget to import weakref).
If you don't know what is a weakref I encourage you to read weakref – Garbage-collectable references to objects.
def __init__(self, texturePath):
self.texturePath = texturePath
self.otherData += texturePath
self.children = [] # This will store all children of the current TextureObject.
self._parent = None # This will store a weakreaf (so don't forget to import weakref) to the parent.
Then we will define some convenient functions in our TextureObject:
def AddChild(self, obj):
obj._parent = weakref.ref(self)
self.children.append(obj)
def GetChildren(self):
return self.children
def GetParent(self):
if self._parent:
return self._parent()
return None
Once it's done it's time to adapt our TreeViewFunctions implementation to add support for children.
So the first thing to override is GetDown() to retrieve the first child of a given TextureObject like so.
def GetDown(self, root, userdata, obj):
"""
Return a child of a node, since we only want a list, we return None everytime
"""
children = obj.GetChildren()
if children:
return children[0]
return None
The treeview will call GetDown to retrieve the first child, then to retrieve the next child it will call GetNext on it.
So we need to adapt GetNext and GetPref to also support children.
Typically we will use the same logic, the only difference is is the current obj have a parent, look for self in the parent list instead of the global one stored in our TreeViewFunctions implementation.
def GetNext(self, root, userdata, obj):
"""
Returns the next Object to display after arg:'obj'
"""
rValue = None
# If does have a child it means it's a child.
objParent = obj.GetParent()
listToSearch = objParent.GetChildren() if objParent is not None else self.listOfTexture
currentObjIndex = listToSearch.index(obj)
nextIndex = currentObjIndex + 1
if nextIndex < len(listToSearch):
rValue = listToSearch[nextIndex]
return rValue
def GetPred(self, root, userdata, obj):
"""
Returns the previous Object to display before arg:'obj'
"""
rValue = None
# If does have a child it means it's a child.
objParent = obj.GetParent()
listToSearch = objParent.GetChildren() if objParent is not None else self.listOfTexture
currentObjIndex = listToSearch.index(obj)
predIndex = currentObjIndex - 1
if 0 <= predIndex < len(listToSearch):
rValue = listToSearch[predIndex]
return rValue
We have everything needed now to display everything.
However, we also need to adapt DeletePressed to support children.
DeletePressed is a global event without a passed obj.
Previously we iterated listOfTexture so we need to support children. To do so let's make an iterator that will iterate all nodes and all children.
And let use it in the DeletePressed function.
# This is a global function which accept a list and will iterate over each TextureObject
# of the passed list to retrieve all TextureObject and all their children.
def TextureObjectIterator(lst):
for parentTex in lst:
yield parentTex
TextureObjectIterator(parentTex.GetChildren())
def DeletePressed(self, root, userdata):
"Called when a delete event is received."
# Convert the iterator to a list to be able to reverse it
for tex in reversed(list(TextureObjectIterator(self.listOfTexture))):
if tex.IsSelected:
objParent = tex.GetParent()
listToRemove = objParent.GetChildren() if objParent is not None else self.listOfTexture
listToRemove.remove(tex)
Now everything is done for the TreeViewFunctions implementation.
So let's add a new button in our GeDialog CreateLayout to add a child to the selected TextureObject.
And defines its behavior when clicked in the GeDialog Command.
# In CreateLayout
self.AddButton(1002, c4d.BFH_CENTER, name="Add Child to selected")
# In Command
if id == 1002:
for parentTex in TextureObjectIterator(self._listView.listOfTexture):
if not parentTex.IsSelected:
continue
newID = len(parentTex.GetChildren()) + 1
tex = TextureObject("T{0}.{1}".format(str(parentTex), newID))
parentTex.AddChild(tex)
# Refresh the TreeView
self._treegui.Refresh()
Now that we have everything we may also want to support the folding state to show/hide children in the treeview.
So let's enhance our TextureObject to support folding (very similar to selection state).
class TextureObject(object):
_open = True
@property
def IsOpened(self):
return self._open
def Open(self):
self._open = True
def Close(self):
self._open = False
Then in the TreeViewFunctions implementation we need to override IsOpened and Open.
def IsOpened(self, root, userdata, obj):
"""
Returns: (bool): Status If it's opened = True (folded) or closed = False.
"""
return obj.IsOpened()
def Open(self, root, userdata, obj, onoff):
"""
Called when the user clicks on a folding state of an object to display/hide its children
"""
if onoff:
obj.Open()
else:
obj.Close()
And here you are, find the full code in pastebin, keep in mind it's only one way to store data.
How you decide to store data is up to you.
In this case, it would make more sense to have a TextureObject as root, so this way all TextureObject will be handled in the same way and the first level is not stored in a specific one level the only list.
Maybe it will be for part 3 of TreeView Exploration! 
Cheers,
Maxime.