Thank you for reaching out to us. The likely reason why your script is not working is the position of your
AddUndo call. As the Python documentation states,
always has to be called before a change is made. In the case of the creation of a new object the call is done afterwards,
There is also a slight ambiguity with how to treat hierarchy modifications: as a
UNDOTYPE_CHANGE event or as an
UNDOTYPE_NEWOBJ event. You can do both, but as hinted at in the
UNDOTYPE_CHANGE description, hierarchy modifications also count as changes. Find a script example below which works fine for me, i.e., undo does revert to the state before running the script.
Scene state before the script ran (and also the state after an undo):
Scene state after the script ran:
"""Provides an example for creating undo items for hierarchy modifications.
doc: c4d.documents.BaseDocument # The active document
def main() -> None:
# Get the active selection as you did.
selection: list[c4d.BaseObject] = doc.GetActiveObjects(
c4d.GETACTIVEOBJECTFLAGS_CHILDREN | c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER)
if len(selection) < 2:
# We declare the first item in the list our root to which we are going to parent things.
root: c4d.BaseObject = selection.pop(0)
# Start an undo stack.
for item in selection:
# Here you got things mixed up a bit. An undo item must be added before the action is
# carried out, e.g., UNDO_CHANGE, and only after when a new item is being added, i.e.,
# UNDOTYPE_NEWOBJ. While the situation might be ambiguous here - is moving an item adding
# an item or not, your symbol UNDO_CHANGE did not line up with place where you called
# I decided to go the easy route and treat this just as a change event and not as a removal
# and new object event.
# A node can only be attached to one thing. In the Python layer, GeListNode.Remove() is
# called automatically when one calls one of the insertion methods, in C++ not. So, this line
# is only for verbosity.
# Insert the item under the root.
# Close the undo stack and push an update event to Cinema 4D.
if __name__ == '__main__':