How to force a command to terminate
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hello, I am trying to build a copy paste function in my addon. The user clicks the button and selects some components. Then when the user clicks the button again, the addon will copy then and make them follow the mouse, and when the user clicks it will place the copied part.
It is working, but I do not know how to terminate the command and commit the changes in fusion. The button stays highlighted and the destroy event is never called. Is there a way to force the command to terminate?
I have tried the .doExecute(True) but this gives an error that it is not allowed to be executed during a command event.
import adsk.core,adsk.fusion,adsk.cam,os,math,traceback
from ...lib import fusion360utils as futil
from datetime import datetime
app = adsk.core.Application.get()
ui = app.userInterface
selectedItems = [] #list of list of dataFile and occurence names to keep when inserting
def __import():
global selectedItems
design:adsk.fusion.Design = app.activeDocument.products.itemByProductType("DesignProductType")
rootComp = design.rootComponent
placerOccurrences = []
for item in selectedItems:
name = item[0]
transform = item[1]
occ = rootComp.occurrences.itemByName(name)
newOcc = rootComp.occurrences.addNewComponentCopy(occ.component,transform)
component = occ.component
component.name = f'{component.name} ({datetime.now().strftime("%b-%d-%Y %H:%M:%S")})'
placerOccurrences.append(occ)
return placerOccurrences
def command_created(args: adsk.core.CommandCreatedEventArgs):
'''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.'''
try:
global selectedItems
design:adsk.fusion.Design = app.activeDocument.products.itemByProductType("DesignProductType")
rootComp:adsk.fusion.Occurrences = design.rootComponent.occurrences
cmd = args.command
cmdID = cmd.parentCommandDefinition.id
inputs = cmd.commandInputs
ui.activeSelections.clear()
# futil.add_handler(args.command.destroy, command_destroy)
if selectedItems == []:
for i,occ in enumerate(rootComp):
newButton = inputs.addBoolValueInput(f'Occurence{i}',occ.name,True,"",False)
selectAll = inputs.addBoolValueInput("selectAll","Select All",False,"",False)
selectAll.isFullWidth = True
futil.add_handler(args.command.execute, command_copy)
futil.add_handler(args.command.inputChanged, command_input_changed)
futil.add_handler(args.command.validateInputs, command_validate_input)
futil.add_handler(args.command.destroy, command_destroy)
else:
#if we press it again
confirm = ui.messageBox(f'Place {len(selectedItems)} parts?',"",adsk.core.MessageBoxButtonTypes.YesNoButtonType)
if confirm == adsk.core.DialogResults.DialogNo:
selectedItems = []
else:
cmd.isAutoExecute = False
futil.add_handler(args.command.execute, command_paste)
futil.add_handler(args.command.destroy, command_destroy)
placerOccurrences = __import()
placer = Placer(cmd,placerOccurrences)
except:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def command_copy(args: adsk.core.CommandEventArgs):
'''This event handler is called when the user clicks the OK button in the command dialog or is immediately called after the created event if no command inputs were created for the dialog.'''
global selectedItems
cmd = args.command
inputs = cmd.commandInputs
design:adsk.fusion.Design = app.activeDocument.products.itemByProductType("DesignProductType")
rootComp:adsk.fusion.Occurrences = design.rootComponent.occurrences
selectAll:adsk.core.BoolValueCommandInput = inputs.itemById("selectAll")
for button in inputs:
if button != selectAll:
if button.value:
occurence = rootComp.itemByName(button.name)
selectedItems.append([occurence.name,occurence.transform2])
def command_input_changed(args: adsk.core.InputChangedEventArgs):
"controls for the buttons being selected or not"
changed_input = args.input
inputs = args.inputs
design:adsk.fusion.Design = app.activeDocument.products.itemByProductType("DesignProductType")
rootComp:adsk.fusion.Occurrences = design.rootComponent.occurrences
selectAll:adsk.core.BoolValueCommandInput = inputs.itemById("selectAll")
#if select all was pressed apply its value to all buttons
if changed_input == selectAll:
for button in args.inputs:
button.value = selectAll.value
#change the button if the user manually selected all buttons
allTrue = True
for button in args.inputs:
if button != selectAll:
if button.value == False:
allTrue = False
break
if allTrue:
selectAll.text = "Unselect All"
selectAll.value = True
else:
selectAll.text = "Select All"
selectAll.value = False
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
'''if at least one item is true then it is valid'''
invalid = True
for button in args.inputs:
if button.value == True:
invalid = False
break
if invalid:
args.areInputsValid = False
else:
args.areInputsValid = True
def command_paste(args:adsk.core.CommandEventArgs):
global selectedItems
app.log("imported")
selectedItems = []
def command_destroy(args: adsk.core.CommandEventArgs):
app.log("destroyed")
local_handlers=[]
class Placer:
def __init__(self,cmd,occurenceList) -> None:
self.occurenceList:list = occurenceList
self.occurence:adsk.fusion.Occurrence = occurenceList[0]
self.cmd:adsk.core.Command = cmd
#make occurences invisible
for occ in occurenceList:
occ.isLightBulbOn = False
self.occurence.isLightBulbOn = True
# Add Mouse movement Handlers
mouseMove = self.MyMouseMoveHandler(self)
self.cmd.mouseMove.add(mouseMove)
self.mouseMove = mouseMove
mouseScroll = self.MyMouseScrollHandler(self)
self.cmd.mouseWheel.add(mouseScroll)
self.mouseScroll = mouseScroll
mouseClick = self.MyMouseClickHandler(self)
self.cmd.mouseClick.add(mouseClick)
self.mouseClick = mouseClick
self.fixCamera()
app.log("PLACER INITIALIZED")
def destroy(self):
# Remove Mouse movement Handlers
handlersCleared = self.cmd.mouseMove.remove(self.mouseMove)
app.log(f'mouseMove cleared: {handlersCleared}')
handlersCleared = self.cmd.mouseWheel.remove(self.mouseScroll)
app.log(f'mouseScroll cleared: {handlersCleared}')
handlersCleared = self.cmd.keyUp.remove(self.spaceBar)
app.log(f'spaceBar cleared: {handlersCleared}')
try:
self.cmd.isAutoExecute = True
terminate = self.cmd.doExecute(False)
except:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def activateNewOccurence(self, occurence:adsk.fusion.Occurrence,placing=False):
if placing: #we need to place the current occurence and update
self.occurenceList.pop(self.occurenceList.index(self.occurence))
self.occurence = self.occurenceList[0]
self.occurence.isLightBulbOn = True
else: #otherwise we are just cycling through the occurencelist
self.occurence.isLightBulbOn = False
self.occurence = occurence
self.occurence.isLightBulbOn = True
def fixCamera(self):
vp = app.activeViewport
camera:adsk.core.Camera = vp.camera
camera.isSmoothTransition = False
camera.cameraType = adsk.core.CameraTypes.OrthographicCameraType
camera.target = adsk.core.Point3D.create(0,0,0)
camera.eye = adsk.core.Point3D.create(0,0,1)
camera.setExtents(11,11)
camera.upVector = adsk.core.Vector3D.create(0,1,0)
vp.camera = camera
class MyMouseMoveHandler(adsk.core.MouseEventHandler):
def __init__(self,placer):
super().__init__()
self.placer = placer
def notify(self, args: adsk.core.MouseEventArgs):
app.log(args.firingEvent.name)
self.placer.fixCamera()
vp = args.viewport
# Get the point3D of the mouse
cursor2D: adsk.core.Point2D = args.viewportPosition
pos3d: adsk.core.Point3D = vp.viewToModelSpace(cursor2D)
#Maybe if we transform pos3d by some matrix of the camera to the world position we dont have to lock the camera?
mousePoint = adsk.core.Point3D.create(pos3d.x,pos3d.y,0)
# Set the position to be the mouse position
mat = self.placer.occurence.transform2
origin,x,y,z = mat.getAsCoordinateSystem()
trans = adsk.core.Matrix3D.create()
trans.setWithCoordinateSystem(mousePoint,x,y,z)
self.placer.occurence.transform2 = trans
class MyMouseScrollHandler(adsk.core.MouseEventHandler):
def __init__(self,placer):
super().__init__()
self.placer = placer
def __setPosition(self,args: adsk.core.MouseEventArgs, occurence:adsk.fusion.Occurrence):
app.log(args.firingEvent.name)
self.placer.fixCamera()
vp = args.viewport
# Get the point3D of the mouse
cursor2D: adsk.core.Point2D = args.viewportPosition
pos3d: adsk.core.Point3D = vp.viewToModelSpace(cursor2D)
#Maybe if we transform pos3d by some matrix of the camera to the world position we dont have to lock the camera?
mousePoint = adsk.core.Point3D.create(pos3d.x,pos3d.y,0)
# Set the position to be the mouse position
mat = occurence.transform2
origin,x,y,z = mat.getAsCoordinateSystem()
trans = adsk.core.Matrix3D.create()
trans.setWithCoordinateSystem(mousePoint,x,y,z)
occurence.transform2 = trans
def notify(self, args: adsk.core.MouseEventArgs):
app.log(args.firingEvent.name)
key = args.keyboardModifiers
vp = args.viewport
scroll = args.wheelDelta
shiftKey = adsk.core.KeyboardModifiers.ShiftKeyboardModifier
ctrlKey = adsk.core.KeyboardModifiers.CtrlKeyboardModifier
occurence = self.placer.occurence
occurenceList = self.placer.occurenceList
self.placer.fixCamera()
# Rotate positive or negative
if scroll > 0:
rotation = 1
else:
rotation = -1
#If shift is pressed cycle through the part list and toggle visability and activate the occurence for transformation
if key == shiftKey:
app.log("SHIFT KEY")
#check if there are more then one items in the list
if len(occurenceList) > 1:
#if we are at the first item in the list and want to go backwards - activate the last item
if occurenceList.index(occurence) == 0 and rotation == -1:
newOccurence = occurenceList[-1]
#if we are at the last item in the list and want to go forwards - activate the first item
elif occurenceList.index(occurence) == len(occurenceList)-1 and rotation == 1:
newOccurence = occurenceList[0]
else:
occurenceIndex = occurenceList.index(occurence)+rotation
newOccurence = occurenceList[occurenceIndex]
self.__setPosition(args,newOccurence)
self.placer.activateNewOccurence(newOccurence)
#Otherwise Rotate the part
else:
rotationMatrix = adsk.core.Matrix3D.create()
origin,x,y,z = occurence.transform2.getAsCoordinateSystem()
# rotation.setToRotation(math.radians(5*rotation),z,origin)
# rotation.setToRotation(math.radians(5*rotation),adsk.core.Vector3D.create(0,0,1),origin)
rotationMatrix.setToRotation(math.radians(5*rotation),adsk.core.Vector3D.create(0,0,1),origin)
mat:adsk.core.Matrix3D = occurence.transform2
mat.transformBy(rotationMatrix)
occurence.transform2 = mat
class MyMouseClickHandler(adsk.core.MouseEventHandler):
def __init__(self,placer):
super().__init__()
self.placer = placer
def notify(self, args: adsk.core.MouseEventArgs):
app.log(args.firingEvent.name)
if args.button == adsk.core.MouseButtons.LeftMouseButton:
if len(self.placer.occurenceList) == 1:
self.placer.destroy()
else:
self.placer.activateNewOccurence(self.placer.occurence,True)