how to get all subobject(children) of active object?



  • Hi everyone~~
    I want to get all subobject(children) of active object.

    Snipaste_2019-11-07_23-20-42.png

    This is my idea :

    import c4d
    from c4d import gui
    # Welcome to the world of Python
    
    def all_child(obj):
        if not obj: return
        elif obj.GetDown():
            return obj.GetDown()
        elif obj.GetNext():
            return obj.GetNext()
        while obj.GetUp() and not obj.GetNext():
            obj = obj.GetUp()
        return obj.GetNext()
    
    def main():
        obj = doc.GetActiveObject()
        if not obj: return
        objs = []
        if obj.GetDown():
            while obj:
                objs.append(obj)
    
                obj = all_child(obj)
        else:
            objs.append(obj)
        
        print len(objs),objs
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    but something is wrong, i don't know how to fix it.
    somebody can help me?



  • Is anyone here?



  • Hi,

    there are basically two approaches how you can do this: iteratively and recursively. The recursive approach is somewhat easier to implement / more readable, but has the disadvantage that you actually might hit Python's recursion limit when dealing with a graph with 100's of objects. It might look something like this:

    import c4d
    
    def get_descendants(op):
        """ Returns all descendants of op.
        """
        if not isinstance(op, c4d.GeListNode):
            return []
        
        res = []
        
        for child in op.GetChildren():
            res.append(child)
            res += get_descendants(child) # recursion happens here
        return res
    
    # Main function
    def main():
        for node in get_descendants(op):
            print node
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    The iterative approach is a bit more complex in implementation but does not have this disadvantage. If you need an iterative approach - as your code does go into that direction -, just ask.

    Cheers
    zipit



  • @zipit
    Thank you zipit.
    I find your methods convenient, but I still need an iterative approach
    Can you give me some simple example?

    Cheers



  • Hi,

    I am not sure what you would consider a simple example. I have once written this here. It does a bit more than (then?, argh, the English language ;)) you are asking for, but should show the principle. Its actually not that hard, you only have to find "your" algorithm to navigate the graph. There is no one way to do it (you can do it for example depth first or breadth first). Building a simple example graph and doing it on paper by hand might help. That is also why it is not easy to debug your example for you.

    def iter_node(node, include_node=False, include_siblings=False):
        """Provides a non-recursive iterator for all descendants of a node.
    
        Args:
            node (c4d.GeListNode): The node to iterate over.
            include_node (bool, optional): If node itself should be included in
             the generator. Defaults to False. 
            include_siblings (bool, optional): If the siblings (and their
             descendants) of node should be included. Will implicitly include
             node (i.e. set include_node to True). Defaults to False. 
    
        Yields:
            c4d.GeListNode: A descendant of node.
    
        Example:
            For the following graph with object.2 as the input node.
    
            object.0
                object.1
                object.2
                    object.3
                    object.4
                        object.5
                object.6
                    object.7
                    object.8
    
            >> for node in iter_node(object_2, False, False):
            >>     print node.GetName()
            object.3
            object.4
            object.5
            >> for node in iter_node(object_2, True, False):
            >>     print node.GetName()
            object.2
            object.3
            object.4
            object.5
            >> for node in iter_node(object_2, True, True):
            >>     print node.GetName()
            object.1
            object.2
            object.3
            object.4
            object.5
            object.6
            object.7
            object.8
        """
        if not isinstance(node, c4d.GeListNode):
            msg = "The argument node has to be a c4d.GeListNode. Received: {}."
            raise TypeError(msg.format(type(node)))
    
        # Lookup lists
        input_node = node
        yielded_nodes = []
        top_nodes = []
    
        # Set top nodes (and set node to first sibling if siblings are included)
        if include_siblings:
            while node.GetNext():
                node = node.GetNext()
            top_nodes = [node]
            while node.GetPred():
                node = node.GetPred()
                top_nodes.append(node)
        else:
            top_nodes = [node]
    
        # Start of iterator
        while node:
            # Yield the current node if it has not been yielded yet
            if node not in yielded_nodes:
                yielded_nodes.append(node)
                if node is input_node and include_node:
                    yield node
                elif node is not input_node:
                    yield node
    
            # Get adjacent nodes
            is_top_node = node in top_nodes
            node_down = node.GetDown()
            node_next = node.GetNext()
            node_up = node.GetUp()
    
            if is_top_node:
                node_up = None
            if is_top_node and not include_siblings:
                node_next = None
    
            # Get the next node in the graph in a depth first fashion
            if node_down and node_down not in yielded_nodes:
                node = node_down
            elif node_next and node_next not in yielded_nodes:
                node = node_next
            elif node_up:
                node = node_up
            else:
                node = None
    

    Cheers
    zipit



  • Hi @gheyret, thanks for reaching out us.

    Although @zipit has already provide an exhaustive answer to the topic (thanks man!) , I warmly suggest to look also to this blog post from @fwilleke80 and also to have a look at Navigation in Lists and Trees section on the GeListNode Manual.

    Best, Riccardo


Log in to reply