Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Should the call to c4d.EventAdd() come before or after the call to doc.EndUndo() ?

    Cinema 4D SDK
    3
    4
    735
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • M
      mikegold10
      last edited by mikegold10

      I'd like to hear what the canonical way of doing the following sequence, is:

      # Start a chain of undo actions
      doc.StartUndo()
      
      # ... Whatever actions that need undo and their associated calls to doc.AddUndo
      
      # Call EventAdd() before ending the undo chain
      c4d.EventAdd()
      
      # ..., then end the undo chain
      doc.EndUndo()
      

      or:

      # Start a chain of undo actions  
      doc.StartUndo()   
      
      # ... Whatever actions that need undo and their associated calls to doc.AddUndo()
      
      # End the undo chain first
      doc.EndUndo()
      
      # ..., and then call c4d.EventAdd()
      c4d.EventAdd()
      

      In other words, should the call to c4d.EventAdd() come before or after the call to EndUndo() on the document object. I've seen code doing it both ways and am wondering if makes any difference or if there is some advantage to one of the two orderings of calls.

      1 Reply Last reply Reply Quote 0
      • C4DSC
        C4DS
        last edited by

        If the two calls are done one right after the other I don't think it really matters.

        However, from point of view of scope it would make more sense to first perform doc.EndUndo(), in order to "close" the do/undo as a whole, and then inform the application that something has changed, calling c4d.EventAdd().

        1 Reply Last reply Reply Quote 1
        • M
          m_adam
          last edited by

          Hi @mikegold10 as @C4DS in reality it doesn't matter that much.
          First of all, remember that both calls can only be done from the main thread, so that means the main thread will be blocked until your script is executed completely.

          doc.EndUndo, will set the current state of doc to be the final state of the undo handling, this is a synchronous call when this line is executed, this is at this time that the state is saved.
          EventAdd, while being also an asynchronous call, will only have the duties to put an update event on the core event stack.
          This core event stack is processed by the main thread when the main thread is free, and since you are also in the main thread, its most likely that your doc.EndUndo will be in any case executed and processed previously.

          But to avoid any issue, it's safer to call EndUndo before.
          Cheers,
          Maxime.

          MAXON SDK Specialist

          Development Blog, MAXON Registered Developer

          M 1 Reply Last reply Reply Quote 0
          • M
            mikegold10 @m_adam
            last edited by mikegold10

            @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?
            
            1 Reply Last reply Reply Quote 0
            • First post
              Last post