CommandCreatedEvent not triggering for ApplicationCommands (such as Extrude, Fillet, ...)

tomveuskens
Participant
Participant

CommandCreatedEvent not triggering for ApplicationCommands (such as Extrude, Fillet, ...)

tomveuskens
Participant
Participant

I'm writing a logger plugin to write UI interactions to a text file. To get fine-grained information, I'd like to include the values that the user enters in native Commands such as extrusion values etc. However, in order to get access to the Command object to define the appropriate listeners such as KeyboardEvents, I need to listen for the CommandCreatedEvent on the native CommandDefinitions. The code below does exactly that (and returns True for every .add() call). However, the notify function in the CommandCreatedHandler is never triggered.

Any help or input would be greatly appreciated.

 

 

 

import adsk.core, adsk.fusion, adsk.cam, traceback
handlers = []

def run(context):
    ui = adsk.core.Application.get().userInterface
    try:
        for i in range(ui.commandDefinitions.count):
            command_definition = ui.commandDefinitions.item(i)
            onCommandCreated = CommandCreatedHandler(print_text)
            success = command_definition.commandCreated.add(onCommandCreated) #success is true in every iteration.
            handlers.append(onCommandCreated)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def stop(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        ui.messageBox('Stop addin')

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

class CommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self, callback):
        super().__init__()

    def notify(self, args): #this is never triggered
        eventArgs = adsk.core.CommandCreatedEventArgs(args)
        command = eventArgs.command #this is what I need for defining the listeners
        adsk.core.Application.get().userInterface.messageBox("TEST")

 

 

According to the documentation, the above should be possible:

Any command (standard Fusion 360 or API created commands) can be run by the user clicking a button or by a program calling the command definitions execute method. In either case, Fusion 360 creates a new Command object and fires the commandCreated event where it passes the Command object to your add-in. Your add-in reacts to this event by connecting to other command related events and defining the contents of the command dialog, if it has one.

0 Likes
Reply
Accepted solutions (1)
485 Views
4 Replies
Replies (4)

kandennti
Mentor
Mentor

Hi @tomveuskens .

 

I don't know why CommandCreatedEvent is not called for native commands, but if you want to create a logger of commands used, I think it would be easier to combine commandTerminated Event and activeCommand Property.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-E381414E-1CFF-4007-B1DF-C413D7E6841A 

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-635C6C94-BA41-44D6-A97D-61BFA56C6688 

 

I have created one that uses commandStarting and published it here.

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

0 Likes

tomveuskens
Participant
Participant

Hi @kandennti 

 

Thanks for your quick reply. Unfortunately, the activeCommand property returns a string with the command identifier instead of the command itself. As a result, the question of how to get access to the specifics on each command (e.g. the entered values, the deleted entities, etc.) remains unsolved sadly. Thanks for sharing your logger plugin as well, however, if I'm not mistaken this implementation also does not access the values of the commands that are started.

0 Likes

kandennti
Mentor
Mentor
Accepted solution

I tried a few things.

 

Since there is no event immediately after pressing the OK button of the dialog, I made CustomEvent to always monitor the dialog.

 

It is possible to get some information, but it does not accept keyboard input.
Therefore, I don't think it will be useful.

# Fusion360API Python add-inns
import adsk.core, adsk.fusion, traceback
import re, threading, json

_app = None
_ui  = None
_handlers = []
_stopFlag = None
_myCustomEvent = 'MyCustomEventId'
_customEvent = None
_dialogInfos = []

class MyCommandTerminatedHandler(adsk.core.ApplicationCommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        blackList = [
            'SelectCommand',
            'CommitCommand'
        ]
        try:
            # command
            global _ui
            cmdId :str = args.commandId
            if cmdId in blackList:
                return

            cmdDef :adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(cmdId)
            cmdName :str = cmdDef.name if cmdDef else '(unknown)'
            infos = [f'---{cmdName}---']

            # dialog
            global _dialogInfos
            infos.extend(_dialogInfos)

            dumpMsg('\n'.join(infos))

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

class ThreadEventHandler(adsk.core.CustomEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        def getDialogInfo() -> list:
            app = adsk.core.Application.get()

            txtCmdRes = app.executeTextCommand(u'Toolkit.cmdDialog')
            infos =[]
            for tmpInfo in txtCmdRes.split('MenuItems:\n')[:-1]:
                stateInfo = tmpInfo.split('\n')[-2]
                lst = [s.split('\n')[0] for s in stateInfo.split(',')]
                label = re.sub(r"\s+|Label=", "", lst[-2])
                value = re.sub(r"\s+", "", lst[-1])
                infos.append(f'{label}:{value}')
            
            app.executeTextCommand(u'Toolkit.hud')
            return infos

        try:
            global _dialogInfos
            _dialogInfos = getDialogInfo() #It's interfering with keyboard input.
        except:
            if _ui:
                _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class MyThread(threading.Thread):
    def __init__(self, event):
        threading.Thread.__init__(self)
        self.stopped = event

    def run(self):
        global _app, _myCustomEvent
        while not self.stopped.wait(0.1):
            args = {'dmy': ''}
            _app.fireCustomEvent(_myCustomEvent, json.dumps(args)) 

def dumpMsg(msg :str):
    adsk.core.Application.get().userInterface.palettes.itemById('TextCommands').writeText(str(msg))

def run(context):
    try:
        dumpMsg('-- start add-ins --')

        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        global _myCustomEvent, _customEvent, _handlers
        _customEvent = _app.registerCustomEvent(_myCustomEvent)
        onThreadEvent = ThreadEventHandler()
        _customEvent.add(onThreadEvent)
        _handlers.append(onThreadEvent)

        global _stopFlag
        _stopFlag = threading.Event()
        myThread = MyThread(_stopFlag)
        myThread.start()

        onCommandTerminated = MyCommandTerminatedHandler()
        _ui.commandTerminated.add(onCommandTerminated)
        _handlers.append(onCommandTerminated)

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

def stop(context):
    try:
        dumpMsg('-- stop add-ins --')

        global _handlers, _customEvent
        _handlers.clear()
        _customEvent = None

        global  _stopFlag, _app
        _stopFlag.set() 
        _app.unregisterCustomEvent(_myCustomEvent)

    except:
        if _ui:
            _ui.messageBox(_('AddIn Stop Failed: {}').format(traceback.format_exc()))

1.png

0 Likes

tomveuskens
Participant
Participant

That's a very creative solution, @kandennti! Thank you very much for your help, greatly appreciated.

0 Likes