Announcements
Autodesk Community will be read-only between April 26 and April 27 as we complete essential maintenance. We will remove this banner once completed. Thanks for your understanding

KeyboardEventHandler: script help request with basic keyUp notification

Anonymous
1,060 Views
4 Replies
Message 1 of 5

KeyboardEventHandler: script help request with basic keyUp notification

Anonymous
Not applicable

Hi Fusion360 peeps, 

I'm struggling with getting the keyboard input handler examples to work, I'm trying to trigger a message box in this test script, simply while the command is running, any key press should trigger 'MyKeyUpHandler' in a message box.

The script doesn't seem to error out but no Msgbox is triggered, please advise? I've tried everything I can think off but to no avail. 

I'm just trying to get this example to work:

http://help.autodesk.com/view/fusion360/ENU/?guid=GUID-8c89cd36-7d1b-4747-b643-c1784929427c


Eventually, while my command is running, I would like to switch the "active command" to various other commands like "line" "3 point arc" etc, while keeping this command running in the background. Hence "adsk.autoTerminate(False)". Any tips?

 

import adsk.core, adsk.fusion, adsk.cam, traceback, sys

# global mapping list of event handlers to keep them referenced for the duration of the command
handlers = []
cmdDefs = []
app = None
ui = None

# Event handler for the keyUp event.
class MyKeyUpHandler(adsk.core.KeyboardEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.KeyboardEventArgs.cast(args)
        app = adsk.core.Application.get()
        ui = app.userInterface
        # Code to react to the event.
        ui.messageBox('In MyKeyUpHandler event handler')
        ui.messageBox(eventArgs.keyCode)

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            global app, ui
            app = adsk.core.Application.get()
            ui = app.userInterface
            # Get the command that was created.
            cmd = adsk.core.Command.cast(args.command)
            onKeyUp = MyKeyUpHandler()
            # ui.messageBox(test)
            cmd.keyUp.add(onKeyUp)
            handlers.append(onKeyUp)
            adsk.autoTerminate(False)
        except:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def run(context):
    global app, ui,handlers
    handlers.clear()
    try:
        adsk.autoTerminate(False)
        app = adsk.core.Application.get()
        ui = app.userInterface

        cmdDef = ui.commandDefinitions.itemById('cmdKeyboardEvent')
        if not cmdDef:
            # cmdDef = ui.commandDefinitions.addButtonDefinition('cmdInputsSample', 'Command Inputs Sample', 'Sample to demonstrate various command inputs.')
            cmdDef = ui.commandDefinitions.addButtonDefinition('cmdKeyboardEvent', 'Command Inputs Sample', 'Sample to demonstrate various command inputs.')

        # Connect to the command created event.
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        handlers.append(onCommandCreated)
        cmdDef.execute()

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

def stop(context):
    try:
        global app, ui
        app = adsk.core.Application.get()
        ui  = app.userInterface

        for obj in cmdDefs:
            if obj.isValid:
                obj.deleteMe()
            else:
                ui.messageBox(str(obj) + ' is not a valid object')

        handlers.clear()

        ui.messageBox('Stop addin')

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

 

 

0 Likes
Accepted solutions (1)
1,061 Views
4 Replies
Replies (4)
Message 2 of 5

kandennti
Mentor
Mentor

Hi @Anonymous .

 

I tried it, but the trigger did not occur unless I made it to the state of displaying the dialog (put some Command Inputs).

 

Also, even if the dialog is displayed, HotKey will function preferentially, so the command will end.

 

I don't know how to temporarily turn off all Hotkey functionality.

1 Like
Message 3 of 5

Anonymous
Not applicable

Hi @kandennti ,

Thanks for the reply. Yeah I seem to be running into the same issue. 

It seems that command change, even changing the current active command based on keyboard input has to occur in the main thread, so setting up custom shortcuts that function in a way similar to how edit form has context alt+ q,w,e,r,a,s,d,f,z,x,c,v is not possible?

@BrianEtkins
Perhaps Brian Etkins or Someone from autodesk can chime in on this. 

I tried to implment an alternate method based on the Robot Arm example on Brian's blog. But from my understanding keyboard input and active commands have to be running in the main thread. So it is not possible to have a "backgorund command" that detects keyboard input, and based on that input changes the active command. (Similar to how the keyboard shortcuts in edit form work).

NOTE: Yes you can get the trigger a new command see "torus" in the commented code below... but once you do that this current command (Aka the custom context like "edit form") is terminated. I do not know how to make this context remain persistent even after an active command change.....



Can someone please confirm this? Thank you.

 

 

 

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
    sys.path.append(PARENT_DIR)

import adsk.core, adsk.fusion, adsk.cam, traceback, sys
sys.path.insert(1, "C:/Users/<UserName>/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs/")

# Global variable used to maintain a reference to all event handlers.
handlers = []
cmdDefs = []

# Other global variables
commandName = "KeyDownEvent"
app = adsk.core.Application.get()
if app:
    ui = app.userInterface
    design = app.activeProduct

# class KeyboardModifiers():
#     """
#     Keyboard modifier values.
#     """
#     def __init__(self):
#         pass
#     NoKeyboardModifier = 0
#     ShiftKeyboardModifier = 1
#     CtrlKeyboardModifier = 2
#     AltKeyboardModifier = 3
#     MetaKeyboardModifier = 4

# Event handler for the keyUp event.
class MyKeyUpHandler(adsk.core.KeyboardEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.KeyboardEventArgs.cast(args)
            keyCode = eventArgs.keyCode
            keyModifiers = eventArgs.modifierMask
            if (keyCode == adsk.core.KeyCodes.RKeyCode
                    and keyModifiers == adsk.core.KeyboardModifiers.NoKeyboardModifier):
                ui.messageBox('R key pressed')
                # ui.commandDefinitions.itemById('PrimitiveTorus').execute()
                # ui.activeCommand ==
            elif (keyCode == adsk.core.KeyCodes.RKeyCode
                    and keyModifiers == adsk.core.KeyboardModifiers.AltKeyboardModifier):
                ui.messageBox('Alt + R key pressed')

            # elif keyCode == adsk.core.KeyCodes.UpKeyCode:
            #     pass
            # elif keyCode == adsk.core.KeyCodes.LeftKeyCode:
            #     pass
            # elif keyCode == adsk.core.KeyCodes.RightKeyCode:
            #     pass

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

# Event handler for the executePreview event.
class MyExecutePreviewHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)
        # Make it accept the changes whatever happens
        eventArgs.isValidResult = True

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            command = adsk.core.Command.cast(args.command)
            onExecutePreview = MyExecutePreviewHandler()
            command.executePreview.add(onExecutePreview)
            handlers.append(onExecutePreview)
            onKeyUp = MyKeyUpHandler()
            command.keyUp.add(onKeyUp)
            handlers.append(onKeyUp)
            onDestroy = MyCommandDestroyHandler()
            command.destroy.add(onDestroy)
            handlers.append(onDestroy)
            inputs = command.commandInputs
            inputs.addTextBoxCommandInput(
                commandName + '_textBox', 'Usage:',
                'Use the arrow buttons to drive the robot arm', 2,
                True);
        except:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            commandDefinitions = ui.commandDefinitions
            # Check the command exists or not
            cmdDef = commandDefinitions.itemById(commandName)
            if cmdDef:
                cmdDef.deleteMe
                # cmdDef.execute()
                # pass
            # When the command is done, terminate the script
            # this will release all globals which will remove all event handlers
            # adsk.autoTerminate(False)
            adsk.terminate()
        except:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def run(context):
    try:
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)
        commandDefinitions = ui.commandDefinitions
        # Check the command exists or not
        cmdDef = commandDefinitions.itemById(commandName)
        if not cmdDef:
            cmdDef = commandDefinitions.addButtonDefinition(
                commandName, commandName, commandName, '')
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        # Keep the handler referenced beyond this function
        handlers.append(onCommandCreated)

        inputs = adsk.core.NamedValues.create()
        cmdDef.execute(inputs)
        # Prevent this module from being terminated when the script returns,
        adsk.autoTerminate(False)
    except:
        ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def stop(context):
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        for obj in cmdDefs:
            if obj.isValid:
                obj.deleteMe()
            else:
                ui.messageBox(str(obj) + ' is not a valid object')
        handlers.clear()
        ui.messageBox('Stop addin')
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

 



0 Likes
Message 4 of 5

prainsberry
Autodesk
Autodesk
Accepted solution

What exactly is it that you want to be able to do? 

 

In Fusion only one "Command" can be active at a time.   Thus triggering the torus command or any other command will stop the current command and execute the next.   There is actually a way to check for this in the API.  In the command destroyed handler you can check: CommandTerminationReason and see if the command was "pre-empted."

 

Also the key-up event you are creating is only "alive" for the duration of that command being active.  This is why you would see nothing if there were no inputs your command just executes instantly and then is over, this the key-up handler is no longer active. 

 

So if what you are trying to do os implement a sort of global keyboard short-cut add-in, this is not really possible.  What is it you are trying to accomplish ultimately?

 

 



Patrick Rainsberry
Developer Advocate, Fusion 360
0 Likes
Message 5 of 5

Anonymous
Not applicable

a. Trying to learn the API
b. Implement something similar to the "Edit Form" commands "Alt" + "Key" style shortcuts that are only active in specific contexts. 

Yeah, I gave up 🙂 It's way to complicated and with the threading issue it's not possible to have a "background" command active. Thanks for the confirmation.

0 Likes