Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Issues using app.executeTextCommand() in add-ins

2 REPLIES 2
Reply
Message 1 of 3
en9y37
177 Views, 2 Replies

Issues using app.executeTextCommand() in add-ins

Hi there! 

 

Several days ago I started a thread in this forum because I had troubles implementing dropdown menus in my add-ins. After hours of research, tests and the help from @Jorge_Jaramillo , we found that all the problems had a quite weird origin. I'll try to explain.

 

I have created several add-ins in which I use many times the command "app.executeTextCommand()". Some of this add-ins behaves as a sort of macros in which I sequence several commands, so this seems to me a great option to be used. Thanks @kandennti for your help!

 

The problem is, if I call a Fusion native commmand with "app.executeTextCommand()" in my add-ins, everything works properly, but if I call a non-native command, then the execution begins an endless loop and the program crashes. This behaviour happens calling my own command add-ins and others such as the add-in JoinerCAD.

 

I've developed a couple of simple codes to reproduce this weird behaviour. In this example, when the selection is done in the dropdown, the function "command_execute" is run, but while debugging, I can see that once the line which execute the non-native add-in (line 108) is executed, the function "command_execute" is restarted, and so on.

 

In case this gave you more information about this issue, if I make this simple change in line 37, the command dialog is bypassed, but the add-in works completely properly.

 

futil.add_handler(cmd_def.commandCreated, command_created)
#Changed command_created to command_execute
futil.add_handler(cmd_def.commandCreated, command_execute)

 

Thanks in advance to everyone for the help.

 

 

Here is the code of the add-in from which I call others:

 

import adsk.core, adsk.fusion, traceback
import os
from ...lib import fusion360utils as futil
app = adsk.core.Application.get()
ui = app.userInterface

# TODO *** Specify the command identity information. ***
CMD_ID = f'actualizaTodoPostprocesa'
CMD_NAME = 'Actualiza todo y postprocesa' 
CMD_Description = 'Actualiza y postprocessa el diseño y sus subensamblajes con los datos de Google Sheets'

# Specify that the command will be promoted to the panel.
IS_PROMOTED = True

# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the 
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'

# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')

# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []


# Executed when add-in is run.
def start():
    # Create a command Definition.
    cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)

    # Define an event handler for the command created event. It will be called when the button is clicked.
    futil.add_handler(cmd_def.commandCreated, command_created)
    
    # ******** Add a button into the UI so the user can run the command. ********
    # Get the target workspace the button will be created in.
    workspace = ui.workspaces.itemById(WORKSPACE_ID)

    # Get the panel the button will be created in.
    panel = workspace.toolbarPanels.itemById(PANEL_ID)

    # Create the button command control in the UI after the specified existing command.
    control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)

    # Specify if the command is promoted to the main toolbar. 
    control.isPromoted = IS_PROMOTED


# Executed when add-in is stopped.
def stop():
    # Get the various UI elements for this command
    workspace = ui.workspaces.itemById(WORKSPACE_ID)
    panel = workspace.toolbarPanels.itemById(PANEL_ID)
    command_control = panel.controls.itemById(CMD_ID)
    command_definition = ui.commandDefinitions.itemById(CMD_ID)

    # Delete the button command control
    if command_control:
        command_control.deleteMe()

    # Delete the command definition
    if command_definition:
        command_definition.deleteMe()

# Function that is called when a user clicks the corresponding button in the UI.
# This defines the contents of the command dialog and connects to the command related events.

def command_created(args: adsk.core.CommandCreatedEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Created Event')

    # https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
    inputs = args.command.commandInputs

    # TODO Define the dialog for your command by adding different inputs to the command.
    listaItems = inputs.addDropDownCommandInput('elegirPost','Postprocesador',0).listItems
    listaItems.add("Mucobal",True)
    listaItems.add("Zoroa",False)

    # TODO Connect to the events that are needed by this command.
    futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
    futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
    futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
    futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
    futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)

# This event handler is called when the user clicks the OK button in the command dialog or 
# is immediately called after the created event not command inputs were created for the dialog.
def command_execute(args: adsk.core.CommandEventArgs):

     
    # TODO ******************************** Your code here ********************************
    try:  
        inputs = args.command.commandInputs
        listaItems = inputs.addDropDownCommandInput('elegirPost','Postprocesador',0).listItems
        listaItems.add("Mucobal",True)
        listaItems.add("Zoroa",False)     
        
        postUsuario: adsk.core.DropDownCommandInput = inputs.itemById('elegirPost')
        nombrePost = postUsuario.selectedItem.name
        
              
        app.executeTextCommand(u'Commands.Start ContextUpdateAllFromParentCmd') #Native command
        app.executeTextCommand(u'Commands.Start actualizarGsheet') #Non-native command
               
        ui.messageBox("If you read this, everything works fine" + nombrePost )

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

# This event handler is called when the command needs to compute a new preview in the graphics window.
def command_preview(args: adsk.core.CommandEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Preview Event')
    inputs = args.command.commandInputs


# This event handler is called when the user changes anything in the command dialog
# allowing you to modify values of other inputs based on that change.
def command_input_changed(args: adsk.core.InputChangedEventArgs):
    changed_input = args.input
    inputs = args.inputs

    # General logging for debug.
    futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')


# This event handler is called when the user interacts with any of the inputs in the dialog
# which allows you to verify that all of the inputs are valid and enables the OK button.
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Validate Input Event')

    inputs = args.inputs
    
    # Verify the validity of the input values. This controls if the OK button is enabled or not.
        

# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Destroy Event')

    global local_handlers
    local_handlers = []

 

The code for the add-in to be called, just in case you could find anything wrong in it:

 

import adsk.core, adsk.fusion, traceback
import os
from ...lib import fusion360utils as futil


app = adsk.core.Application.get()
ui = app.userInterface


# TODO *** Specify the command identity information. ***
CMD_ID = f'actualizarGsheet'
CMD_NAME = 'Actualizar de Google Sheets'
CMD_Description = 'Actualiza el estado del diseño con los datos de Google Sheets, solo en el diseño actual'

# Specify that the command will be promoted to the panel.
IS_PROMOTED = False

# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the 
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'

# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')

# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []


# Executed when add-in is run.
def start():
    # Create a command Definition.
    cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)

    # Define an event handler for the command created event. It will be called when the button is clicked.
    futil.add_handler(cmd_def.commandCreated, command_created) #command_execute ejecuta la secuencia sin pasar por dialogo previo

    # ******** Add a button into the UI so the user can run the command. ********
    # Get the target workspace the button will be created in.
    workspace = ui.workspaces.itemById(WORKSPACE_ID)

    # Get the panel the button will be created in.
    panel = workspace.toolbarPanels.itemById(PANEL_ID)

    # Create the button command control in the UI after the specified existing command.
    control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)

    # Specify if the command is promoted to the main toolbar. 
    control.isPromoted = IS_PROMOTED


# Executed when add-in is stopped.
def stop():
    # Get the various UI elements for this command
    workspace = ui.workspaces.itemById(WORKSPACE_ID)
    panel = workspace.toolbarPanels.itemById(PANEL_ID)
    command_control = panel.controls.itemById(CMD_ID)
    command_definition = ui.commandDefinitions.itemById(CMD_ID)

    # Delete the button command control
    if command_control:
        command_control.deleteMe()

    # Delete the command definition
    if command_definition:
        command_definition.deleteMe()

# Function that is called when a user clicks the corresponding button in the UI.
# This defines the contents of the command dialog and connects to the command related events.

def command_created(args: adsk.core.CommandCreatedEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Created Event')

    # https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
    inputs = args.command.commandInputs

    # TODO Define the dialog for your command by adding different inputs to the command.

    # TODO Connect to the events that are needed by this command.
    futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
    futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
    futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
    futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
    futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)

# This event handler is called when the user clicks the OK button in the command dialog or 
# is immediately called after the created event not command inputs were created for the dialog.
def command_execute(args: adsk.core.CommandEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Execute Event')

    # TODO ******************************** Your code here ********************************
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        ui.messageBox("Se ha ejecutado actualizar Gsheet")
   
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
       


# This event handler is called when the command needs to compute a new preview in the graphics window.
def command_preview(args: adsk.core.CommandEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Preview Event')
    inputs = args.command.commandInputs


# This event handler is called when the user changes anything in the command dialog
# allowing you to modify values of other inputs based on that change.
def command_input_changed(args: adsk.core.InputChangedEventArgs):
    changed_input = args.input
    inputs = args.inputs

    # General logging for debug.
    futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')


# This event handler is called when the user interacts with any of the inputs in the dialog
# which allows you to verify that all of the inputs are valid and enables the OK button.
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Validate Input Event')

    inputs = args.inputs
    
    # Verify the validity of the input values. This controls if the OK button is enabled or not.
    valueInput = inputs.itemById('value_input')
    if valueInput.value >= 0:
        inputs.areInputsValid = True
    else:
        inputs.areInputsValid = False
        

# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
    # General logging for debug.
    futil.log(f'{CMD_NAME} Command Destroy Event')

    global local_handlers
    local_handlers = []

 

 

 

 

 

 

 

2 REPLIES 2
Message 2 of 3
Jorge_Jaramillo
in reply to: en9y37

Hi @en9y37 ,

 

I believe the source of the problem is when you chain the execution of the commands.

In Fusion, only one command is active at a time; when you start a new one while other is active, it will fire the command_execute handler just as the new one is being created.  This is the case on your code when you start actualizarGsheet from actualizaTodoPostprocesa's commmand_execute.

 

I'll suggest to use a custom event to process the Command.Start's instructions.

Take a look a this example on how to implement the custom event: https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-B38361DE-1B38-432D-BAE6-ABC3B0E7AB35 

When the code in the custom event handler runs, the command_execute had ended and you won't have the problems you had before.

 

Regards,

Jorge

 

Message 3 of 3
en9y37
in reply to: Jorge_Jaramillo

Hi again @Jorge_Jaramillo .

 

I tried something with your response but I was not able to work something out, mainly because the event handlers thing is something really hard to understand to me.

 

Finally, I've decided to aim the solution from another point of view, and instead of using execute text commands, I'm coding my own functions, some of theme replicating the commands I called with execute text command. 

 

I recognize everything makes much more sense this way, I get much more control about what I want to do and my add-ins work properly.

 

Thanks anyway for your help, this issue keeps without solution by now, but it's been a great chance to learn tones of things this last weeks.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report