Solved create_nodematerial.py from Github not working

Hi,

Whenever I run create_nodematerial.py from Github in R25, it gives me an error of :

Traceback (most recent call last):
  File "C:\Program Files\Maxon Cinema 4D R25\resource\modules\python\libs\python39\maxon\data.py", line 75, in __getattribute__
    return Data_GetMember(self, name, o)
AttributeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "scriptmanager", line 111, in <module>
  File "scriptmanager", line 90, in main
  File "scriptmanager", line 56, in CreateMaterialForNodeSpace
  File "C:\Program Files\Maxon Cinema 4D R25\resource\modules\python\libs\python39\maxon\data.py", line 77, in __getattribute__
    return object.__getattribute__(self, name)
AttributeError: 'UnknownDataType' object has no attribute 'GetGraph'

Hi @bentraje sorry nodes examples were adapted to 26 but nothing mention it's 26+ only this is going to be fixed today. I would advice you to download the R25 python SDK which contain examples for the R25 version here.

Regarding your issue, as mentioned in the S26.0 Python Changelog frameworks are now by default imported so you don't need anymore to explicitly address the type from its framework but you can directly address it from the maxon module. Nevertheless any existent code (pre R25) where you need to import frameworks keep working.
So to make it works in R25 you need to import the relevant frameworks at the beginning and then use the correct type, instead of maxon.NODE_KIND.ALL_MASK use maxon.frameworks.graph.NODE_KIND.ALL_MASK.
So the next code will work from R24 up to 2023.0.0:

"""
Copyright: MAXON Computer GmbH
Author: Manuel Magalhaes

Description:
    Creates a NodeMaterial for the current node space and for Redshift node 
    space. 

    NodeMaterials inherit from BaseMaterial. To be able to use the functions 
    that are in the NodeMaterial class, the BaseMaterial must be cast to a 
    NodeMaterial. This is done with the function GetNodeMaterialReference. To 
    add a graph to an existing node space, one must use the function AddGraph.
    If one wants to have access to an already added node graph, one must 
    retrieve the nimbus reference which does store the graph. Once one has 
    retrieved the graph, one can access the root that contains all the nodes.
    
Class/method highlighted:
    - GetNodeMaterialReference
    - AddGraph
    - GetActiveNodeSpaceId
    - GetChildren
    - GetNimbusRef
"""
import c4d
import maxon
import maxon.frameworks.nodespace
import maxon.frameworks.nodes

# Define a function that will create the node material for the passed
# nodespace Id and return the root of the created Graph


def CreateMaterialForNodeSpace(nodespaceId):
    # Create a baseMaterial first
    mat = c4d.BaseMaterial(c4d.Mmaterial)
    if mat is None:
        raise ValueError("Cannot create a BaseMaterial.")

    # Retrieve the reference of the material as a node material.
    nodeMaterial = mat.GetNodeMaterialReference()
    if nodeMaterial is None:
        raise ValueError("Cannot retrieve nodeMaterial reference.")

    # Add a graph for the space Id
    addedGraph = nodeMaterial.AddGraph(nodespaceId)
    if addedGraph is None:
        raise ValueError("Cannot add a GraphNode for this NodeSpace.")

    # Retrieve the Nimbus reference for a specific NodeSpace
    nimbusRef = mat.GetNimbusRef(nodespaceId)
    if nimbusRef is None:
        raise ValueError(
            "Cannot retrieve the nimbus reference for that NodeSpace.")

    # Retrieve the graph corresponding to that NodeSpace.
    graph = nimbusRef.GetGraph()
    if graph is None:
        raise ValueError("Cannot retrieve the graph of this nimbus NodeSpace.")

    # Retrieve the root of the graph
    root = graph.GetRoot()

    # Insert the material in the document
    doc.InsertMaterial(mat)
    return root


# Define a function that will recursively print all the child of the root node.
def PrintChildren(node):
    if node is None:
        return None
    # Print the current Node
    print(node)

    # Call GetChildren on this node with the delegate function
    node.GetChildren(PrintChildren, maxon.frameworks.graph.NODE_KIND.ALL_MASK)

    # Important that the delegate return True or False
    return True


def main():

    # Retrieve the current node space Id
    nodespaceId = c4d.GetActiveNodeSpaceId()

    # Create the material and retrieve the root of the graph
    root = CreateMaterialForNodeSpace(nodespaceId)

    # Start the recursive process on the first node
    PrintChildren(root)

    # Do the same with the Redshift node space. The Redshift plugin is not 
    # installed by default, so we call the function in an exception handling
    # context.
    redShiftNodeSpPaceId = maxon.Id(
        'com.redshift3d.redshift4c4d.class.nodespace')
    try:
        root = CreateMaterialForNodeSpace(redShiftNodeSpPaceId)
        PrintChildren(root)
    except:
        print(f"The node space with id {redShiftNodeSpPaceId} does not exist")

    # Pushes an update event to Cinema 4D
    c4d.EventAdd()


if __name__ == "__main__":
    main()

Cheers,
Maxime.

Hmm. Okay. I guess there is a change in API in R25 and R26.
Used the R25 code and it now works (under the GitHub releases)

Why does this simple create material task needed a change code anyway?
Checked also the changes and the changes are trivial.

maxon.frameworks.graph.NODE_KIND.ALL_MASK to maxon.NODE_KIND.ALL_MASK

So trivial. So I guess we need a lot of this if else statement for a script/plug in to work for R25 and R26/R27 at the same time?

Hi @bentraje sorry nodes examples were adapted to 26 but nothing mention it's 26+ only this is going to be fixed today. I would advice you to download the R25 python SDK which contain examples for the R25 version here.

Regarding your issue, as mentioned in the S26.0 Python Changelog frameworks are now by default imported so you don't need anymore to explicitly address the type from its framework but you can directly address it from the maxon module. Nevertheless any existent code (pre R25) where you need to import frameworks keep working.
So to make it works in R25 you need to import the relevant frameworks at the beginning and then use the correct type, instead of maxon.NODE_KIND.ALL_MASK use maxon.frameworks.graph.NODE_KIND.ALL_MASK.
So the next code will work from R24 up to 2023.0.0:

"""
Copyright: MAXON Computer GmbH
Author: Manuel Magalhaes

Description:
    Creates a NodeMaterial for the current node space and for Redshift node 
    space. 

    NodeMaterials inherit from BaseMaterial. To be able to use the functions 
    that are in the NodeMaterial class, the BaseMaterial must be cast to a 
    NodeMaterial. This is done with the function GetNodeMaterialReference. To 
    add a graph to an existing node space, one must use the function AddGraph.
    If one wants to have access to an already added node graph, one must 
    retrieve the nimbus reference which does store the graph. Once one has 
    retrieved the graph, one can access the root that contains all the nodes.
    
Class/method highlighted:
    - GetNodeMaterialReference
    - AddGraph
    - GetActiveNodeSpaceId
    - GetChildren
    - GetNimbusRef
"""
import c4d
import maxon
import maxon.frameworks.nodespace
import maxon.frameworks.nodes

# Define a function that will create the node material for the passed
# nodespace Id and return the root of the created Graph


def CreateMaterialForNodeSpace(nodespaceId):
    # Create a baseMaterial first
    mat = c4d.BaseMaterial(c4d.Mmaterial)
    if mat is None:
        raise ValueError("Cannot create a BaseMaterial.")

    # Retrieve the reference of the material as a node material.
    nodeMaterial = mat.GetNodeMaterialReference()
    if nodeMaterial is None:
        raise ValueError("Cannot retrieve nodeMaterial reference.")

    # Add a graph for the space Id
    addedGraph = nodeMaterial.AddGraph(nodespaceId)
    if addedGraph is None:
        raise ValueError("Cannot add a GraphNode for this NodeSpace.")

    # Retrieve the Nimbus reference for a specific NodeSpace
    nimbusRef = mat.GetNimbusRef(nodespaceId)
    if nimbusRef is None:
        raise ValueError(
            "Cannot retrieve the nimbus reference for that NodeSpace.")

    # Retrieve the graph corresponding to that NodeSpace.
    graph = nimbusRef.GetGraph()
    if graph is None:
        raise ValueError("Cannot retrieve the graph of this nimbus NodeSpace.")

    # Retrieve the root of the graph
    root = graph.GetRoot()

    # Insert the material in the document
    doc.InsertMaterial(mat)
    return root


# Define a function that will recursively print all the child of the root node.
def PrintChildren(node):
    if node is None:
        return None
    # Print the current Node
    print(node)

    # Call GetChildren on this node with the delegate function
    node.GetChildren(PrintChildren, maxon.frameworks.graph.NODE_KIND.ALL_MASK)

    # Important that the delegate return True or False
    return True


def main():

    # Retrieve the current node space Id
    nodespaceId = c4d.GetActiveNodeSpaceId()

    # Create the material and retrieve the root of the graph
    root = CreateMaterialForNodeSpace(nodespaceId)

    # Start the recursive process on the first node
    PrintChildren(root)

    # Do the same with the Redshift node space. The Redshift plugin is not 
    # installed by default, so we call the function in an exception handling
    # context.
    redShiftNodeSpPaceId = maxon.Id(
        'com.redshift3d.redshift4c4d.class.nodespace')
    try:
        root = CreateMaterialForNodeSpace(redShiftNodeSpPaceId)
        PrintChildren(root)
    except:
        print(f"The node space with id {redShiftNodeSpPaceId} does not exist")

    # Pushes an update event to Cinema 4D
    c4d.EventAdd()


if __name__ == "__main__":
    main()

Cheers,
Maxime.

@m_adam

Gotcha. Thansk for confirmation.
Looking forward for the update.

Hi @bentraje I think there was a small misscommunication what was fixed on Github was the fact that the file does not contain the information of which Cinema 4D version they are working. We are not going to adapt our GitHub script to make it work for R25.
If you want code that is going to work for R25, the only solution is to download the python SDK for R25.

Cheers,
Maxime.