How to force a command to terminate

Joshua.mursic
Advocate

How to force a command to terminate

Joshua.mursic
Advocate
Advocate

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)

 

0 Likes
Reply
188 Views
0 Replies
Replies (0)