One day i'll get around to putting my framework on GitHub, but for now here's my black-magic abstraction layer.
class FusionEventHandler(object):
"""Makes wiering up to Fusion events dead simple and friendly!
Usage:
Inherit from this class and call self._auto_wire(command) at some point.
Annotate the methods you wish to handle events using the return
annotation to indicate which event to wire to.
Example Annotation:
def on_execute(self, args) -> 'execute':
self.ui.messageBox('EXECUTE')
NOTICE:
Only one subscribing method per event source, per class instance, is supported.
If the same event source is subscribed to multiple times only one method will
receive events.
Supported Event Names:
command_created (may not be autowired)
destroy
execute
activate
deactivate
preview
input_changed
validate_inputs
key_down
key_up
mouse_click
mouse_double_click
mouse_down
mouse_move
mouse_up
mouse_wheel
mouse_drag_begin
mouse_drag
mouse_drag_end
selecting
"""
def __init__(self):
self.__event_handlers = dict()
def __handler_factory(self, event, callback, handler_cls ):
"""Factory method to create handler classes and bind them to Fusion event sources.
Args:
event: object - on which to call .add passing the handler instance
callback: function - which will be called when the event fires
handler_cls: type - one of the adsk.core.*EventHandler types
Returns:
Instance of a handler if subscription to event was successfull, None otherwise.
"""
class _Handler(handler_cls):
def __init__(self):
super().__init__()
def notify(self, *args):
try:
callback(*args)
except Exception as ex:
#adsk.core.Application.get().userInterface.messageBox('Failed:\n{}'.format(traceback.format_exc()))
adsk.core.Application.get().userInterface.messageBox(
'{}\n\n--------\n{}'.format(ex, traceback.format_exc()),
'Error')
h = _Handler()
return h if event.add(h) else None
def _wire_event(self, command, event, callback ):
"""Subscribes a listener to an event trigger.
See core.py near line 2977
Args:
command: adsk.core.Command or adsk.core.CommandDefinitions
event: string
callback: Function - which will be called when the event fires
"""
_wire_handler = self.__handler_factory
events = {
'command_created': lambda command, callback: _wire_handler(command.commandCreated, callback, adsk.core.CommandCreatedEventHandler),
'destroy': lambda command, callback: _wire_handler(command.destroy, callback, adsk.core.CommandEventHandler),
'execute': lambda command, callback: _wire_handler(command.execute, callback, adsk.core.CommandEventHandler),
'activate': lambda command, callback: _wire_handler(command.activate, callback, adsk.core.CommandEventHandler),
'deactivate': lambda command, callback: _wire_handler(command.deactivate, callback, adsk.core.CommandEventHandler),
'preview': lambda command, callback: _wire_handler(command.executePreview, callback, adsk.core.CommandEventHandler),
'input_changed': lambda command, callback: _wire_handler(command.inputChanged, callback, adsk.core.InputChangedEventHandler),
'validate_inputs': lambda command, callback: _wire_handler(command.validateInputs, callback, adsk.core.ValidateInputsEventHandler),
'key_down': lambda command, callback: _wire_handler(command.keyDown, callback, adsk.core.KeyboardEventHandler),
'key_up': lambda command, callback: _wire_handler(command.keyUp, callback, adsk.core.KeyboardEventHandler),
'mouse_click': lambda command, callback: _wire_handler(command.mouseClick, callback, adsk.core.MouseEventHandler),
'mouse_double_click': lambda command, callback: _wire_handler(command.mouseDoubleClick, callback, adsk.core.MouseEventHandler),
'mouse_down': lambda command, callback: _wire_handler(command.mouseDown, callback, adsk.core.MouseEventHandler),
'mouse_move': lambda command, callback: _wire_handler(command.mouseMove, callback, adsk.core.MouseEventHandler),
'mouse_up': lambda command, callback: _wire_handler(command.mouseUp, callback, adsk.core.MouseEventHandler),
'mouse_wheel': lambda command, callback: _wire_handler(command.mouseWheel, callback, adsk.core.MouseEventHandler),
'mouse_drag_begin': lambda command, callback: _wire_handler(command.mouseDragBegin, callback, adsk.core.MouseEventHandler),
'mouse_drag': lambda command, callback: _wire_handler(command.mouseDrag, callback, adsk.core.MouseEventHandler),
'mouse_drag_end': lambda command, callback: _wire_handler(command.mouseDragEnd, callback, adsk.core.MouseEventHandler),
'selecting': lambda command, callback: _wire_handler(command.selectionEvent, callback, adsk.core.SelectionEventHandler)}
h = events[event](command, callback) if event in events else None
if h: self.__event_handlers[event] = h
def _auto_wire(self, command):
for name in dir(self):
attr = getattr(self, name)
if callable(attr) and hasattr(attr,'__annotations__') and 'return' in attr.__annotations__:
self._wire_event(command, attr.__annotations__['return'], attr)
Inherit from this class to make use of it. Here's a mostly complete example...
class MyAddin(FusionEventHandler):
def __init__(self):
super().__init__()
@property
def app(self):
return adsk.core.Application.get()
@property
def ui(self):
return self.app.userInterface
def run(self, context):
try:
self._command_definition = self.ui.commandDefinitions.itemById(self.command_id)
if not self._command_definition:
self._command_definition = self.add_button()
assert self._command_definition, 'Script/Addin failed to produce a \'button\''
self._wire_event(self._command_definition, 'command_created', self.__on_create)
except:
self.ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def stop(self, context):
if self._command_definition:
self._command_definition.deleteMe()
self.remove_button()
def add_button(self):
button = self.ui.commandDefinitions.addButtonDefinition(
self.command_id,
self.command_name,
self.command_description,
self.resource_dir)
panel = self.ui.allToolbarPanels.itemById('SketchPanel')
panel.controls.addCommand(button)
button.isPromotedByDefault = True
button.isPromoted = True
return button
def remove_button(self):
# cleanup self._command_definition
pass
def __on_create(self, _args):
args = adsk.core.CommandCreatedEventArgs.cast(_args)
self._command = args.command
self._auto_wire(self._command)
self.initialize_inputs(self._command)
def initialize_inputs(self, command):
"""Perform all UI initialization here."""
pass
def on_execute(self, args) -> 'execute':
pass
def on_preview(self, args) -> 'preview':
pass
Then somewhere you'll need the standard run/stop code
def run(context):
global __addin
__addin = MyAddin(context)
__addin.run()
def stop(context):
global __addin
if __addin:
__addin.stop(context)