Alternative way to iterate list other than GetNext()?
-
I am currently using this approach to iterate the objects in a document
doc = c4d.documents.GetActiveDocument()
obj = doc.GetFirstObject()
next_obj = obj.GetNext()
while next_obj:
# Do stuff
next_obj = obj.GetNext()Is there some equivalent of doing this via e.g. indexing in a for-loop ?
Cheers
-
I hope that's not really your code because it will hang. In the last line, you retrieve next_obj always from obj.GetNext() so you get the same object forever. Also, you're skipping the first object before you Do Stuff. And next_obj is not needed anyway:
doc = c4d.documents.GetActiveDocument() obj = doc.GetFirstObject() while obj: # Do stuff obj = obj.GetNext()
Also, doc is a predefined variable that is already set to the current document.
And you are not traversing into the child objects, but I guess you know that.As per your question, you can use
GeListNode.GetChildren()
to retrieve a list of child objects which you can address by index, but there is noGetSiblings()
built in to achieve the same on the top level. Nevertheless, you can create your own function easily which assembles all siblings into a Python list, or alternatively a function that yields the next sibling, depending on how you want to use it.The following script shows all siblings on top level (still not traversing the children) by assembling them into a list:
import c4d from c4d import gui def GetSiblings(obj): while obj.GetPred(): obj = obj.GetPred() # go to first sibling retlist = [] while obj: retlist.append(obj) obj = obj.GetNext() return retlist def main(): obj = doc.GetFirstObject() print GetSiblings(obj) if __name__=='__main__': main()
Learn more about Python for C4D scripting:
https://www.patreon.com/cairyn -
Hi @nicholas_yue,
thank you for reaching out to us. And thanks at @Cairyn for jumping in. @Cairyn answer is perfectly fine, but I would like to point out of some stuff.
- Cinema's objects are organized in something called a Graph, which is basically just a fancy word for a tree in this case. There is also something called Graph Traversal which is the process of navigating within a graph.
- There are multiple ways to do this, apart from the general categories of depth-first and breadth-first traversal, and custom tailored traversal to your specific task can be very advantageous.
- You usually also want to avoid traversing the whole graph without having to do so, because this can be quite expensive for complex scenes (@Cairyn's example does this as it stores the whole traversal in a
list
). Instead you should design aniterator
, i.e. useyield
like I have in my example, which will let you break out of the traversal when you have "found what you were looking for".
Below you will find an example for such iterator, which intentionally does something slightly different than @Cairyn example, as it only will only return children and grand-children and no siblings, i.e. it will only traverse a partial graph marked by an input node as the root of that partial graph. As said in 2. it really depends on what you want to do and you should design your own on an as-needed basis.
Cheers,
Ferdinandimport c4d def get_next(op, stop_node): """Returns the "next" node in a graph in a depth-first traversal. Args: op (c4d.GeListNode): The node from which to traverse. stop_node (c4d.GeListNode): A node not to exceed in the horizontal hierarchical traversal. Returns: c4d.GeListNode or None: A child or grandchild of op. Or `None` when the graph has been exhausted. """ if op is None: return None elif op.GetDown(): return op.GetDown() while (op.GetUp() and op.GetUp() != stop_node and not op.GetNext()): op = op.GetUp() return op.GetNext() def iter_partial_graph(node): """Yields all children and grand-children of the passed node. """ stop_node = node while node is not None: yield node node = get_next(node, stop_node) if __name__=='__main__': # Go over all children and grand-children of the currently selected node. for node in iter_partial_graph(op): print (node)
-
Hi,
without further feedback, we will consider this thread as solved by Monday and flag it accordingly.
Cheers,
Ferdinand