Hello @bentraje,
Thank you for reaching out to us. You must pass a settings dictionary to your transaction, telling it that you do not want a separate undo step for the transaction.
# Define a settings dictionary for the graph transactions. This one says, "no undo steps please".
settings: maxon.DataDictionaryInterface = maxon.DataDictionary()
settings.Set(maxon.nodes.UndoMode, maxon.nodes.UNDO_MODE.NONE)
and then later use that in your transaction(s):
# Start a transaction using our transaction settings.
with graph.BeginTransaction(settings) as transaction:
diffuseIPort.SetDefaultValue(diffuseColor) # Set the diffuse color of the material.
transaction. Commit()
Your code is also a bit lengthy in some parts. When you add a graph yourself, .AddGraph
will already return the maxon.GraphModelRef
for that graph. No need to go over the active space and the nimbus graph handler. Also - you first add the RS node space graph to a material, but then retrieve the graph for the active node space. Which at least somehow implies that you assume them to be always the same. But that is only true when RS is the active render engine. Nothing prevents you from adding and modifying material node graphs for node spaces which are not the active render engine (as long as you have the render engine installed and therefore the node space registered).
Find an example below.
Cheers,
Ferdinand
Result:

Code:
"""Demonstrates how to mute undo steps for graph transactions.
When run as a Script Manager script, #n Redshift node materials will be created, with each of
their diffuse color being set to the color #c. The creation of all materials as well as their
graph transactions will be wrapped into a singular undo. I.e., the modifications applied by running
the script once can be reversed with a singular [CTRL/CMD + Z].
The number of materials, their colors, and their names depends on #MATERIAL_DATA as defined below.
"""
import typing
import c4d
import maxon
__version__ = "2023.0.0"
doc: c4d.documents.BaseDocument # The active document.
# Defines materials as dictionaries of names and diffuse colors.
MATERIAL_DATA: list[dict[str, typing.Any]] = [
{"name": "red", "diffuse_color": maxon.Color64(1, 0, 0)},
{"name": "green", "diffuse_color": maxon.Color64(0, 1, 0)},
{"name": "blue", "diffuse_color": maxon.Color64(0, 0, 1)}
]
# The Redshift node space ID.
ID_RS_NODESPACE: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
def main():
"""Runs the example.
"""
# Not really necessary to do, but since you implied tying the script to the active space being
# Redshift, I did so too.
if c4d.GetActiveNodeSpaceId() != ID_RS_NODESPACE:
print ("Error: Active node space is not Redshift.")
return
# Define a settings dictionary for the graph transactions. This one says, "no undo steps please".
settings: maxon.DataDictionaryInterface = maxon.DataDictionary()
settings.Set(maxon.nodes.UndoMode, maxon.nodes.UNDO_MODE.NONE)
# Open an undo stack for the active document, since we do this outside of the loop, all three
# materials will be wrapped into a single undo.
if not doc.StartUndo():
raise MemoryError(f"{doc = }")
# Iterate over the abstract material data and get the name and diffuse color for each.
for data in MATERIAL_DATA:
name: str = data.get("name", None)
diffuseColor: maxon.Color64 = data.get("diffuse_color", None)
# Allocate a new material, get the node material, and add a RS node space graph.
material: c4d.BaseMaterial = c4d.BaseMaterial(c4d.Mmaterial)
material.SetName(name)
nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
graph: maxon.GraphModelRef = nodeMaterial.AddGraph(ID_RS_NODESPACE)
if graph.IsNullValue():
raise RuntimeError(f"Could not allocate '{ID_RS_NODESPACE}' graph.")
# Try to find the standard material node inside this graph.
result: list[maxon.GraphNode] = []
maxon.GraphModelHelper.FindNodesByAssetId(
graph, "com.redshift3d.redshift4c4d.nodes.core.material", False, result)
if len(result) < 1 or result[0].IsNullValue():
raise RuntimeError(f"{graph} does not contain a standard material node.")
# Get diffuse color input port of that node.
materialNode: maxon.GraphNode = result[0]
diffuseIPort: maxon.GraphNode = materialNode.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.material.diffuse_color")
if diffuseIPort.IsNullValue():
raise RuntimeError("Could not find diffuse color port.")
# Start a transaction using our transaction settings.
with graph.BeginTransaction(settings) as transaction:
diffuseIPort.SetDefaultValue(diffuseColor) # Set the diffuse color of the material.
transaction.Commit()
# Nothing went horribly wrong, we can actually add the material.
doc.InsertMaterial(material)
doc.AddUndo(c4d.UNDOTYPE_NEW, material)
# Close the undo and raise an update event.
if not doc.EndUndo():
raise RuntimeError(f"{doc = }")
c4d.EventAdd()
if __name__ == '__main__':
main()