SOLVED How can I get a Layer's objects with Python?

Hello,
I have 450 takes. Every take activates only one layer (make editor and render visibility true) of the document. (Other layers are disabled by default)

I want to modify objects that belongs to that layer which is activated by a take.

After getting Layer, I need to get list of objects of that Layer by Python.

Simply I want to make:
after I activated a take I will execute a script. That script will make some modifications about objects and their materials. After that it will updates take overrides. To make those actions I need to find Active Take > Layer > Objects

how can I make this? could anyone help me about this please?

thank you

Hello @delizade,

thank you for reaching out to us. Please remember to tag your topics also with the version(s) of Cinema 4D and OS you are targeting.

About your question: The answer to that is unfortunately not trivial. The take system of Cinema 4D is powerful, but that makes it sometimes hard to find general solutions for working with it programmatically. There is no general solution for "find all classic nodes that are somehow part of the active take". The reason for that is that classic nodes can be manipulated in many ways by takes. In your example it is the layers which are being manipulated and not the objects themselves you are after. So, you will need to find the layer(s) which are part of the active take and then the objects that are part of that layer.

We cannot provide full solutions, but I wrote a little example which goes into the direction you want to go. Please note that this is an example and that we can only answer specific follow up questions. We cannot improve the script so that it will fit your requirements, you will have to do that yourself.

I hope this helps and cheers,
Ferdinand

The result:
1.gif

The code:

"""Example for iterating over all objects that are attached to a layer.

The script retrieves the layer to be tested for via the take system, looking
for a layer which is part of the overrides of the currently active take.

As discussed in:
    plugincafe.maxon.net/topic/13640
"""

import c4d

def Iterate(document, callback, **kwargs):
    """The iteration function, walking a document object graph depth first.
    
    Args:
        document (c4d.documents.BaseDocument): The document to iterate over.
        callback (callable): A callback to evaluate if a certain node should
         be yielded or not.
        **kwargs: The additional kw arguments for the callback.
    
    Yields:
        c4d.BaseObject: An object node within the document graph that did
         satisfy the callback.
    """
    node = document.GetFirstObject()
    visisted = []
    terminal = node.GetUp() if isinstance(node, c4d.GeListNode) else None

    while node:
        if node not in visisted:
            if callback(node, document, **kwargs):
                yield node
            visisted.append(node)

        if node.GetDown() and node.GetDown() not in visisted:
            node = node.GetDown()
        elif node.GetNext():
            node = node.GetNext()
        else:
            node = node.GetUp()

        if node == terminal:
            break

def MyCallback(node, document, **kwargs):
    """A little callback function which does test if a node should be yielded
    or not.

    We are testing here if the node is attached to the same layer as passed
    down via activeLayer to `Iterate`.
    """
    if not isinstance(node, c4d.BaseList2D):
        return False
    if "activeLayer" not in kwargs:
        return False
    return kwargs["activeLayer"] == node.GetLayerObject(document)

def main():
    """Entry point.
    """
    # Get the active take 
    takeData = doc.GetTakeData()
    baseTake = takeData.GetCurrentTake()

    # Iterate over all overrides to find the first override for a layer 
    # object. Bail the script when we cannot find any overrides for a layer.
    layer = None
    for item in baseTake.GetOverrides():
        if isinstance(item.GetSceneNode(), c4d.documents.LayerObject):
            layer = item.GetSceneNode()
            break
    if layer is None:
        raise RuntimeError("Could not find any layer object overrides.")

    # Iterate over all objects in the scene which are attached to that layer.
    for node in Iterate(doc, MyCallback, activeLayer=layer):
        print (node)


if __name__ == '__main__':
    main()

Hello @delizade,

thank you for reaching out to us. Please remember to tag your topics also with the version(s) of Cinema 4D and OS you are targeting.

About your question: The answer to that is unfortunately not trivial. The take system of Cinema 4D is powerful, but that makes it sometimes hard to find general solutions for working with it programmatically. There is no general solution for "find all classic nodes that are somehow part of the active take". The reason for that is that classic nodes can be manipulated in many ways by takes. In your example it is the layers which are being manipulated and not the objects themselves you are after. So, you will need to find the layer(s) which are part of the active take and then the objects that are part of that layer.

We cannot provide full solutions, but I wrote a little example which goes into the direction you want to go. Please note that this is an example and that we can only answer specific follow up questions. We cannot improve the script so that it will fit your requirements, you will have to do that yourself.

I hope this helps and cheers,
Ferdinand

The result:
1.gif

The code:

"""Example for iterating over all objects that are attached to a layer.

The script retrieves the layer to be tested for via the take system, looking
for a layer which is part of the overrides of the currently active take.

As discussed in:
    plugincafe.maxon.net/topic/13640
"""

import c4d

def Iterate(document, callback, **kwargs):
    """The iteration function, walking a document object graph depth first.
    
    Args:
        document (c4d.documents.BaseDocument): The document to iterate over.
        callback (callable): A callback to evaluate if a certain node should
         be yielded or not.
        **kwargs: The additional kw arguments for the callback.
    
    Yields:
        c4d.BaseObject: An object node within the document graph that did
         satisfy the callback.
    """
    node = document.GetFirstObject()
    visisted = []
    terminal = node.GetUp() if isinstance(node, c4d.GeListNode) else None

    while node:
        if node not in visisted:
            if callback(node, document, **kwargs):
                yield node
            visisted.append(node)

        if node.GetDown() and node.GetDown() not in visisted:
            node = node.GetDown()
        elif node.GetNext():
            node = node.GetNext()
        else:
            node = node.GetUp()

        if node == terminal:
            break

def MyCallback(node, document, **kwargs):
    """A little callback function which does test if a node should be yielded
    or not.

    We are testing here if the node is attached to the same layer as passed
    down via activeLayer to `Iterate`.
    """
    if not isinstance(node, c4d.BaseList2D):
        return False
    if "activeLayer" not in kwargs:
        return False
    return kwargs["activeLayer"] == node.GetLayerObject(document)

def main():
    """Entry point.
    """
    # Get the active take 
    takeData = doc.GetTakeData()
    baseTake = takeData.GetCurrentTake()

    # Iterate over all overrides to find the first override for a layer 
    # object. Bail the script when we cannot find any overrides for a layer.
    layer = None
    for item in baseTake.GetOverrides():
        if isinstance(item.GetSceneNode(), c4d.documents.LayerObject):
            layer = item.GetSceneNode()
            break
    if layer is None:
        raise RuntimeError("Could not find any layer object overrides.")

    # Iterate over all objects in the scene which are attached to that layer.
    for node in Iterate(doc, MyCallback, activeLayer=layer):
        print (node)


if __name__ == '__main__':
    main()

Thank you so much for your time and help.

Hello @delizade,

we will set this topic to 'Solved' when there are no further questions or replies until Monday, November the 22th.

Thank you for your understanding,
Ferdinand