SOLVED 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?

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

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