Creating a custom command dialog then running a text command causes Fusion 360 to crash

Creating a custom command dialog then running a text command causes Fusion 360 to crash

jphalip
Enthusiast Enthusiast
1,083 Views
9 Replies
Message 1 of 10

Creating a custom command dialog then running a text command causes Fusion 360 to crash

jphalip
Enthusiast
Enthusiast

Hi,

 

I'm writing a script that creates a custom command dialog to prompt the user to enter some information. Then when the user clicks OK, the script needs to run a text command to create a Coil based on the information entered by the user in the previous dialog. The issue is that this workflow causes Fusion 360 to crash.

 

It looks like maybe there is some issue with the second command dialog (the created with a "Commands.Start" text command) clashes with the previous command dialog. Should the first command maybe cleaned up somehow before running the second one?

 

I've created the following bare-bone script to reproduce the issue. Warning: if you run this as-is then it'll likely crash Fusion 360 as it does for me. It is specifically the last line ("app.executeTextCommand(u'Commands.Start Extrude')") that causes the crash.

 

Do you know what's causing this and how to fix it or work around it?

 

Thanks!

 

Julien

 

import adsk.core, adsk.fusion, traceback

app = adsk.core.Application.get()
ui = app.userInterface
handlers = []


def run(context):
    cmdDef = ui.commandDefinitions.itemById('myCmd')
    if not cmdDef:
        cmdDef = ui.commandDefinitions.addButtonDefinition('myCmd', 'myCmd', 'myCmd')
    onCommandCreated = MyCommandCreatedHandler()
    cmdDef.commandCreated.add(onCommandCreated)
    handlers.append(onCommandCreated)
    cmdDef.execute()
    adsk.autoTerminate(False)


class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        adsk.terminate()


class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def notify(self, args):
        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)
        inputs: adsk.core.CommandInputs = cmd.commandInputs
        inputs.addTextBoxCommandInput('my_text', 'Some text', 'abcd', 1, False)


class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        # WARNING: Starting a new text command causes Fusion 360 to crash
        app.executeTextCommand(u'Commands.Start Coil')

  

0 Likes
1,084 Views
9 Replies
Replies (9)
Message 2 of 10

kandennti
Mentor
Mentor

Hi @jphalip .

 

Perhaps there is no one to answer the text command question.
There is no other way but to try various things that come to mind.

 

I have a similar experience with text commands. At that time, I gave up using dialogs.

 

When I run the coil command in the GUI, several commands are executed before "Coil" is executed.

1.png

 

When we tried it here, we were able to handle it by drawing a sketch circle in advance and making it selected, but when we used the dialog, it crashed as well.

 

The only way to deal with this may be to use sweeps.

0 Likes
Message 3 of 10

jphalip
Enthusiast
Enthusiast

Thanks @kandennti. I think I managed to get it to work and avoid the crash by registering another handler (ui.commandTerminated) to wait until my custom command is fully terminated before calling the Coil text command. See the bare-bone code sample below. I'll now try to transfer that to my real code and hopefully it'll work as I need it to.


 

import adsk.core, adsk.fusion, traceback

app = adsk.core.Application.get()
ui = app.userInterface
handlers = {}
results: dict = {}
commandId: str = 'myCmd'
command: adsk.core.Command = None


def run(context):
    cmdDef = ui.commandDefinitions.itemById(commandId)
    if not cmdDef:
        cmdDef = ui.commandDefinitions.addButtonDefinition(commandId, '', '')
    handlers['commandCreated'] = MyCommandCreatedHandler()
    cmdDef.commandCreated.add(handlers['commandCreated'])
    cmdDef.execute()
    adsk.autoTerminate(False)


class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        adsk.terminate()


class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def notify(self, args):
        global command
        command = args.command

        # Create the event handlers
        handlers['execute'] = MyCommandExecuteHandler()
        command.execute.add(handlers['execute'])
        handlers['destroy'] = MyCommandDestroyHandler()
        command.destroy.add(handlers['destroy'])
        handlers['terminate'] = MyCommandTerminateHandler()
        ui.commandTerminated.add(handlers['terminate'])

        # Create the command inputs
        inputs: adsk.core.CommandInputs = command.commandInputs
        inputs.addTextBoxCommandInput('my_text', 'Some text', 'abcd', 1, False)


class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def notify(self, args: adsk.core.CommandEventArgs):
        # This gets called after the user clicks OK in the custom command
        command: adsk.core.Command = args.firingEvent.sender
        inputs = command.commandInputs
        # Store the command results
        results['my_text'] = inputs.itemById('my_text').text


class MyCommandTerminateHandler(adsk.core.ApplicationCommandEventHandler):
    def notify(self, args:adsk.core.ApplicationCommandEventArgs):
        # This gets called after the command has been fully terminated
        if args.commandId == commandId and 'my_text' in results:
            # Deactivate all the event handlers
            command.execute.remove(handlers['execute'])
            command.destroy.remove(handlers['destroy'])
            ui.commandTerminated.remove(handlers['terminate'])
            ui.commandDefinitions.itemById(commandId).commandCreated.remove(handlers['commandCreated'])
            handlers.clear()

            # Call another text command
            app.executeTextCommand(u'Commands.Start Coil')

 

0 Likes
Message 4 of 10

jphalip
Enthusiast
Enthusiast

Quick update: My script now works all the way to the end... except that If I then try to modify any of the created features by double-clicking it in the timeline, then Fusion 360 crashes. Seems like a bug to me. This is so frustrating 😞

0 Likes
Message 5 of 10

kandennti
Mentor
Mentor

@jphalip .

 

I don't know what the end goal is, but if creating the coil is the last process and it is ok to display the dialog, it would be easier to use the CommandDefinition.execute method.

class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        coilCmdDef: adsk.core.CommandDefinition = ui.commandDefinitions.itemById(
            'Coil'
        )

        if not coilCmdDef:
            return

        coilCmdDef.execute()
0 Likes
Message 6 of 10

jphalip
Enthusiast
Enthusiast

@kandennti Thank you. Unfortunately, I need to create more features after the coil.

 

I've added the Coil creation script you had provided here to the bare-bone example. Running this script reliably crashes Fusion 360 for me...

 

import adsk.core, adsk.fusion, traceback
import math

app = adsk.core.Application.get()
ui = app.userInterface
design: adsk.fusion.Design = app.activeProduct
root: adsk.fusion.Component = design.rootComponent
handlers = {}
results: dict = {}
commandId: str = 'myCmd'
command: adsk.core.Command = None


def run(context):
    cmdDef = ui.commandDefinitions.itemById(commandId)
    if not cmdDef:
        cmdDef = ui.commandDefinitions.addButtonDefinition(commandId, '', '')
    handlers['commandCreated'] = MyCommandCreatedHandler()
    cmdDef.commandCreated.add(handlers['commandCreated'])
    cmdDef.execute()
    adsk.autoTerminate(False)


class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def notify(self, args):
        adsk.terminate()


class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def notify(self, args):
        global command
        command = args.command

        # Create the event handlers
        handlers['execute'] = MyCommandExecuteHandler()
        command.execute.add(handlers['execute'])
        handlers['destroy'] = MyCommandDestroyHandler()
        command.destroy.add(handlers['destroy'])
        handlers['terminate'] = MyCommandTerminateHandler()
        ui.commandTerminated.add(handlers['terminate'])

        # Create the command inputs
        inputs: adsk.core.CommandInputs = command.commandInputs
        inputs.addTextBoxCommandInput('my_text', 'Some text', 'abcd', 1, False)


class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def notify(self, args: adsk.core.CommandEventArgs):
        # This gets called after the user clicks OK in the custom command
        command: adsk.core.Command = args.firingEvent.sender
        inputs = command.commandInputs
        # Store the command results
        results['my_text'] = inputs.itemById('my_text').text


class MyCommandTerminateHandler(adsk.core.ApplicationCommandEventHandler):
    def notify(self, args:adsk.core.ApplicationCommandEventArgs):
        # This gets called after the command has been fully terminated
        if args.commandId == commandId and 'my_text' in results:
            # Deactivate all the event handlers
            command.execute.remove(handlers['execute'])
            command.destroy.remove(handlers['destroy'])
            ui.commandTerminated.remove(handlers['terminate'])
            ui.commandDefinitions.itemById(commandId).commandCreated.remove(handlers['commandCreated'])
            handlers.clear()

            coilFeat: adsk.fusion.CoilFeature = create_coil(
                root,
                adsk.core.Point3D.create(2, 1, 0),
                1.0,
                4.0,
                3.0,
                15.0,
                0.5
            )

def create_coil(
    comp: adsk.fusion.Component,
    center: adsk.core.Point3D,
    coilDiameter: float,
    coilRevolutions: float,
    coilHeight: float,
    coilTaperAngle_deg: float,
    sectionSize: float):

    skt: adsk.fusion.Sketch = comp.sketches.add(
        comp.xYConstructionPlane
    )
    sktCircles: adsk.fusion.SketchCircles = skt.sketchCurves.sketchCircles

    circle: adsk.fusion.SketchCircle = sktCircles.addByCenterRadius(
        center,
        coilDiameter * 0.5
    )

    comp: adsk.fusion.Component = circle.parentSketch.parentComponent

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

    sels: adsk.core.Selections = ui.activeSelections
    sels.clear()
    sels.add(circle)

    app.executeTextCommand(u'Commands.Start Coil')
    app.executeTextCommand(u'Commands.SetString infoSizeType infoRevolutionAndHeight')
    app.executeTextCommand(u'Commands.SetDouble CoilRevolutions {}'.format(coilRevolutions))
    app.executeTextCommand(u'Commands.SetDouble CoilHeight {}'.format(coilHeight))
    app.executeTextCommand(u'Commands.SetDouble CoilTaperAngle {}'.format(math.radians(coilTaperAngle_deg)))
    app.executeTextCommand(u'Commands.SetString infoSectionType infoCircular')
    app.executeTextCommand(u'Commands.SetString infoSectionPosition infoOutside')
    app.executeTextCommand(u'Commands.SetDouble SectionSize {}'.format(sectionSize))
    app.executeTextCommand(u'Commands.SetString infoBooleanType infoNewBodyType')
    app.executeTextCommand(u'NuCommands.CommitCmd')

    skt.deleteMe()

    return comp.features.coilFeatures[-1]

 

0 Likes
Message 7 of 10

kandennti
Mentor
Mentor

@jphalip .

 

Unfortunately, I can't think of a way to avoid the crash.

0 Likes
Message 8 of 10

jphalip
Enthusiast
Enthusiast

@kandennti No worries, I appreciate your help. By chance, do you know what's the best way to report this to the development team as a potential bug?

0 Likes
Message 9 of 10

kandennti
Mentor
Mentor

@jphalip .

 

As I mentioned here, my understanding is that Autodesk does not recommend the use of text commands.

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/tree/master/TextCommands 

 

It is only to be used at your own risk and I don't think it is something that can be directed as a bug.

0 Likes
Message 10 of 10

jphalip
Enthusiast
Enthusiast

@kandennti Ok I see. And if I understand correctly, there is no way to create a coil feature other than with text commands, is that right?