@m_adam Hi Maxime, thanks for your detailed reply. I had a feeling that EventAdd()
performed an asynchronous operation (i.e., placed an event on the event queue) and therefore it didn't matter, since control of the main thread would not be relinquished until both EndUndo()
were called (regardless of sequence). However, as C4DS and you yourself pointed out, it makes more sense to call EndUndo()
prior to EventAdd()
.
This is not only true from a safety perspective, but the calls to StartUndo()
and EndUndo()
can be wrapped in a Python Context Manager, along exception handling and cleanup to be done automatically and correctly when a scope is exited. If one is going to undo whatever operation in case of an error, anyways (e.g., as part of said exception handling), it makes sense to call c4d.EventAdd()
after we know that the entire action was successfully performed.
I am going on the assumption that if my code performs some action inside of a StartUndo()
/ EndUndo()
sequence, possibly consisting of multiple sub-actions and changes resulting in multiple AddUndo()
calls, then sees mid-action that there is an and it cannot continue, and assuming the Start/End undo sequence is wrapped in a Python context manager class. Python will call EndUndo()
on my behalf, as part of exiting the block governed by the context manager and then in the exception handler, I can perform an undo of the last action, undoing whatever sub-action did perform successfully (and added their AddUndo() pieces). Since this will hopefully leave everything in the same state that it was before the action was started, I am going to assume that there is no point in calling c4d.EventAdd()
after the undo of the action is performed in my exception handler.
To summarize, here is an example scenario:
try:
with MyUndoContextManager(doc) as undo_manager: # Calls StartUndo() as part of its Context Manager __enter__() method
# undo_manager will make the calls to AddUndo() based on our actions
DoSomethingAndCallAddUndoOnTheDoc(undo_manager);
DoSomethingElseAndCallAddUndoOnTheDoc(undo_manager);
DoOneLastThingAndCallAddUndoOnTheDoc(undo_manager); # Oh, oh, fails and throws exception
# Note: EndUndo() will get called as part of the implicit call to MyUndoContextManager's __exit__() method
# which will automatically get called when we exit this block due to the exception being thrown
c4d.EventAdd(); # This code will not get reached due to exception being thrown above
except:
# An error occurred, get back to the initial state prior to the action
# Undo whichever action sub-steps were performed successfully and AddUndo()s got called for, if any
# Since the undo_manager object no longer exists, call DoUndo() on the doc object, directly
doc.DoUndo()
# Since, we reverted to the initial state before the action, there is no need to call c4d.EventAdd(), right?