A problem with document.close method

A problem with document.close method

nnikbin
Collaborator Collaborator
1,958 Views
7 Replies
Message 1 of 8

A problem with document.close method

nnikbin
Collaborator
Collaborator

Calling "document.close" from within the execute handler of commands with user interface will cause Fusion 360 to close unexpectedly. It will not cause any problem if it is called from within the run method or in response to an input change (like pressing an input button)

 

To reproduce the problem please follow the following steps:

  1. Run the following code
  2. Press "Test" button. A new document will be created and after pressing Ok button of a message box that appears, the created document will be closed without any problem
  3.  Press Ok button of the command dialog box.  A new document will be created and after pressing Ok button of a message box that appears, Fusion 360 will be closed unexpectedly after a few seconds.

It is worthwile to mention that if we create n new documents, we can close (n - 1) documents safely without any problem.

 

import adsk.core, adsk.fusion, traceback

_app = None
_ui  = None

_handlers = []

class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            testDocuments()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            inputs = eventArgs.inputs
            cmdInput = eventArgs.input
            
            if cmdInput.id == 'test':
                testDocuments()
          
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            cmd = adsk.core.Command.cast(args.command)

            onExecute = MyCommandExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)
            
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)
            
            onInputChanged = MyCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged) 

            inputs = cmd.commandInputs
            
            testCmdInput = inputs.addBoolValueInput('test', 'Test', False)

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def testDocuments():
    try:
        docs = _app.documents
        newDoc = docs.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        _ui.messageBox('"close" method will be called for this document after pressing Ok button')

        newDoc.close(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef = _ui.commandDefinitions.itemById('cmdDocsTest')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition('cmdDocsTest', 'Command Docs Test', 'Sample to test Document commands.')

        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes
Accepted solutions (2)
1,959 Views
7 Replies
Replies (7)
Message 2 of 8

BrianEkins
Mentor
Mentor
Accepted solution

I'm not surprised by this behavior.  The actions you're performing in a command execute are all wrapped within a single transaction so you have a single undo step to revert everything you did.  Closing a document doesn't play well with transactions because anything you did within that document is lost and cannot be undone.  It's not documented, but a general rule is that you should be opening and closing documents within a command.  It's ok in a script because then there's no transaction wrapping.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 3 of 8

nnikbin
Collaborator
Collaborator

Hi @BrianEkins ,

Many thanks for your quick reply and describing the roots of the problem. What is the reason that we can create for example 2 new documents and close one of them without any problem? Is it unsafe?

0 Likes
Message 4 of 8

BrianEkins
Mentor
Mentor

It probably is unsafe.  The API is mostly just a thin wrapper over the internal functionality.  In cases like this, through the API, you're able to do things that none of the standard Fusion commands do and its not likely that we ever tested it as part of the API testing so you're in uncharted territory.  Did you try an undo?

 

If you want the capability to have a command dialog but will be doing document creation and closing as part of the command, I think it would be best to just use the dialog to capture all of the input data and then wait for the command to finish (the command's destroy event) and then use the saved input to do whatever you wanted to do.  You won't have a single undo, but it should behave better.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 5 of 8

nnikbin
Collaborator
Collaborator

@BrianEkins wrote:

If you want the capability to have a command dialog but will be doing document creation and closing as part of the command, I think it would be best to just use the dialog to capture all of the input data and then wait for the command to finish (the command's destroy event) and then use the saved input to do whatever you wanted to do.  You won't have a single undo, but it should behave better.


Fantastic Solution! Thanks a lot @BrianEkins !

 


Did you try an undo?

Yes. When I create 2 new documents and close one of them, the other created document will become active, and trying an undo on this document will undo only the last change made on this document. Activating the main document and trying an undo will do nothing.

0 Likes
Message 6 of 8

nnikbin
Collaborator
Collaborator

Hi @BrianEkins,

I tested command's destroy event. Like command's execute event handler, creating and closing a document in command's destroy event handler will cause Fusion 360 to close unexpectedly. It is interesting that closing the main document fist and then closing the created document is safe, but it is not a practical solution. Do you have any other suggestion? Thanks!

0 Likes
Message 7 of 8

BrianEkins
Mentor
Mentor
Accepted solution

That's too bad.  I was hoping the timing of the destroy event would be after the command has completed but it must be just before it's finished.

 

The only other workaround I can think of is to somehow use the UserInterface.commandTerminated event and hopefully that fires after the command is truly finished.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 8 of 8

nnikbin
Collaborator
Collaborator

It works! Many thanks @BrianEkins !

The behaviour is a little odd for me. UserInterface.commandTerminated event fires 3 times with different "CommandTerminationReason"s:

  • First with  PreEmptedTerminationReason (before UI is shown)
  • Then with CompletedTerminationReason (after pressigng the Ok button)
  • Then with CancelledTerminationReason (I expected to receive this only by cancelling the command not by pressing the Ok button!).

Opening and closing a new document during the event with CompletedTerminationReason terminates Fusion 360 unexpectedly. But setting a flag during CompletedTerminationReason and then checking the flag with the next termination event with CancelledTerminationReason will do the job.

 

Also the action of opening and closing a new document fires more additional commandTerminated events that I  ignored them.

0 Likes