SelectionInput allows multiple selection of same object even with setSelectionLimits(1,1)

SelectionInput allows multiple selection of same object even with setSelectionLimits(1,1)

Jorge_Jaramillo
Collaborator Collaborator
786 Views
3 Replies
Message 1 of 4

SelectionInput allows multiple selection of same object even with setSelectionLimits(1,1)

Jorge_Jaramillo
Collaborator
Collaborator

I'd like a script to split a body in multiples slices. For it I need to select single a body, a single plane and a single edge.

What I found in the implementation is that it allows me to select the same item (body, plane or edge) multiples time even tough each commandInput has setSelectionLimits(1,1) which means only one.

The step to reproduce the issue is:

1. Run the script; the focus is automatically set to choose a body.

2. Select any body you have in the design; after choosing it the focus will be set to choose a plane.

3. Select any plane you have in the design; after choosing it the focus will be set to choose an edge.

4. Select any edge you have in the design.

5. Click on the command input to select the body (with label "1 selected")

6. At this point in the select, the edge you selected is highlighted.

7. Select the same body as in step 2 and now the command input to select the body has the label "2 selected"

The same situation happen with the command input for the plane and the edge.

 

What I'm missing to avoid it to the let the user select the same item more than once? 

I also expect the body to be highlighted when the body command input get the focus, as it happen in the command inputs of the application.

You can use any simple design with two bodies on it to test this script.  In my case I used a box and a cilinder.

 

The following image shows the status of the command input with multiple objects selected on each command input:

Captura de pantalla 2022-06-17 124507.png

 

The following is the python source code:


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

# Globals
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
_handlers = []

_cmdDefGeneral   = 'cmdDefGeneral'
_BODY_SELECTION  = 'bodySelection'
_BODY_SELECTED   = 'bodySelected'
_PLANE_SELECTION = 'planeSelection'
_PLANE_SELECTED  = 'planeSelected'
_EDGE_SELECTION  = 'edgeSelection'
_EDGE_SELECTED   = 'edgeSelected'
_ERR_MESSAGE     = 'errMessage'

_bodySelection = adsk.core.SelectionCommandInput.cast(None)

def run(context😞
    try:
        global _app, _ui

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

        cmdDef = _ui.commandDefinitions.itemById(_cmdDefGeneral)
        if not cmdDef:
            # Create a command definition.
            cmdDef = _ui.commandDefinitions.addButtonDefinition(_cmdDefGeneral, 'body splitter', 'Splits a body in multiple slices')

        # Connect to the command created event.
        onCommandCreated = splitterCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        # Execute the command.
        cmdDef.execute()
        adsk.autoTerminate(False)

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

# Event handler that reacts to any changes the user makes to any of the command inputs.
class splitterCommandInputChangedHandler(adsk.core.InputChangedEventHandler😞
    def __init__(self😞
        super().__init__()
    def notify(self, args😞
        global _bodySelection
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            if eventArgs.input.id == _BODY_SELECTION:
                textBody = adsk.core.TextBoxCommandInput.cast(eventArgs.inputs.itemById(_BODY_SELECTED))
                if textBody:
                    if eventArgs.input.selectionCount:
                        body = adsk.fusion.BRepBody.cast(eventArgs.input.selection(0).entity)
                        bbox = adsk.core.BoundingBox3D.cast(body.boundingBox)
                        mn = [f'{n:.4f}' for n in bbox.minPoint.asArray()]
                        mx = [f'{n:.4f}' for n in bbox.maxPoint.asArray()]
                        textBody.text = f'{body.name}\n{mn}\n{mx}'
                        adsk.core.SelectionCommandInput.cast(eventArgs.inputs.itemById(_PLANE_SELECTION)).hasFocus = True
                    else:
                        textBody.text = ''
            elif eventArgs.input.id == _PLANE_SELECTION:
                textPlane = adsk.core.TextBoxCommandInput.cast(eventArgs.inputs.itemById(_PLANE_SELECTED))
                if textPlane:
                    if eventArgs.input.selectionCount:
                        if isinstance(eventArgs.input.selection(0).entity,adsk.fusion.ConstructionPlane😞
                            geom = adsk.fusion.ConstructionPlane.cast(eventArgs.input.selection(0).entity).geometry
                        else:
                            geom = adsk.fusion.BRepFace.cast(eventArgs.input.selection(0).entity).geometry
                        if isinstance(geom,adsk.core.Plane😞
                            plane = adsk.core.Plane.cast(geom)
                            textPlane.text = f'{plane.uDirection.x}|{plane.uDirection.y}|{plane.uDirection.z}  ~  {plane.vDirection.x}|{plane.vDirection.y}|{plane.vDirection.z}  |  {plane.origin.x}|{plane.origin.y}|{plane.origin.z}'
                            adsk.core.SelectionCommandInput.cast(eventArgs.inputs.itemById(_EDGE_SELECTION)).hasFocus = True
                        else:
                            adsk.core.SelectionCommandInput.cast(eventArgs.inputs.itemById(_PLANE_SELECTION)).clearSelection()
                            textPlane.text = 'invalid plane'
                    else:
                        textPlane.text = ''
            elif eventArgs.input.id == _EDGE_SELECTION:
                textEdge = adsk.core.TextBoxCommandInput.cast(eventArgs.inputs.itemById(_EDGE_SELECTED))
                if textEdge:
                    if eventArgs.input.selectionCount:
                        geom = eventArgs.input.selection(0).entity.geometry
                        if isinstance(geom,adsk.core.Line3D😞
                            op = geom.startPoint
                            dp = geom.endPoint
                        else:
                            op = geom.origin
                            dp = geom.direction
                        axis = 'x' if op.x-dp.x != 0.0 else 'y' if op.y-dp.y != 0.0 else 'z'
                        textEdge.text = f'{op.x}|{op.y}|{op.z}  ~  {dp.x}|{dp.y}|{dp.z}    |    {axis}'
                    else:
                        textEdge.text = ''
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

# Event handler that reacts when the command definition is executed which
# results in the command being created and this event being fired.
class splitterCommandCreatedHandler(adsk.core.CommandCreatedEventHandler😞
    def __init__(self😞
        super().__init__()
    def notify(self, args😞
        # global _bodySelection, _handlers
        try:
            # Get the command that was created.
            cmd = adsk.core.Command.cast(args.command)

            #-------------------------------------------------------------
            # Connect to the command destroyed event.
            onDestroy = splitterCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # Connect to the input changed event.          
            onInputChanged = splitterCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)    

            #-------------------------------------------------------------
            # Get the CommandInputs collection associated with the command.
            inputs = cmd.commandInputs

            # Create a selection input.
            bodySelection = inputs.addSelectionInput(_BODY_SELECTION, 'Body', 'Select body to split')
            bodySelection.hasFocus = True
            bodySelection.setSelectionLimits(1,1)
            bodySelection.addSelectionFilter("Bodies")

            # Create a read only textbox input.
            inputs.addTextBoxCommandInput(_BODY_SELECTED, 'Body selected:', '', 3, True)

            # Create a plane input.
            planeSelection = inputs.addSelectionInput(_PLANE_SELECTION, 'Plane', 'Select tool plane')
            planeSelection.setSelectionLimits(1,1)
            planeSelection.addSelectionFilter("ConstructionPlanes")
            planeSelection.addSelectionFilter("Faces")

            # Create a read only textbox input.
            inputs.addTextBoxCommandInput(_PLANE_SELECTED, 'Plane selected:', '', 3, True)

            # Create a plane input.
            edgeSelection = inputs.addSelectionInput(_EDGE_SELECTION, 'Edge', 'Select direction')
            edgeSelection.setSelectionLimits(1,1)
            edgeSelection.addSelectionFilter("LinearEdges")
            edgeSelection.addSelectionFilter("SketchLines")
            edgeSelection.addSelectionFilter("ConstructionLines")

            # Create a read only textbox input.
            inputs.addTextBoxCommandInput(_EDGE_SELECTED, 'Plane selected:', '', 3, True)

            # Create a read only textbox input.
            inputs.addTextBoxCommandInput(_ERR_MESSAGE, '', '', 1, True)

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

# Event handler that reacts to when the command is destroyed. This terminates the script.            
class splitterCommandDestroyHandler(adsk.core.CommandEventHandler😞
    def __init__(self😞
        super().__init__()
    def notify(self, args😞
        try:
            # When the command is done, terminate the script
            # This will release all globals which will remove all event handlers
            adsk.terminate()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes
Accepted solutions (1)
787 Views
3 Replies
Replies (3)
Message 2 of 4

j.han97
Advocate
Advocate

Hi @Jorge_Jaramillo ,

 

The selectionCommandInput seems buggy on my side. I changed a section of the code to this:

_app.log(f'Body selection limit: {bodySelection.getSelectionLimits()[1:]}')
ret = bodySelection.setSelectionLimits(1,1)
_app.log(f'Successfully set selection limits: {ret}')
_app.log(f'Body selection limit: {bodySelection.getSelectionLimits()[1:]}')

and I got this output which is very weird:

Body selection limit: [1, 1]
Successfully set selection limits: True
Body selection limit: [1, 0]

 

Could you test this on your side?

0 Likes
Message 3 of 4

Jorge_Jaramillo
Collaborator
Collaborator
Accepted solution

Hi @j.han97 ,

Based on your answer I made some more testings.

This is what I found out:

  • Everytime you set limits to (1,1) it will change them to (1,0), and for me this is when the problem arises, meaning there is not maximum limit.
  • I tried with limits set to (2,1) and it worked, meaning like the maximum set to 1 take precedence over the minimum to 2 (with no error raised).  Other values like (3,1), (4,1) and so on didn't work for me (same behavior as before).
  • Since the initial values are set to (1,1), you don't need to set them and it work perfect.

For the bodySelection part this how I left it:

# Create a selection input.
_bodySelection = inputs.addSelectionInput(_BODY_SELECTION, 'Body', 'Select body to split')
_bodySelection.hasFocus = True
_bodySelection.addSelectionFilter("Bodies")

 

I wonder if someone from Fusion360 team could investigate further.

 

Thank you @j.han97 for your time and feedback.

 

Best regards,

Jorge

 

Message 4 of 4

BrianEkins
Mentor
Mentor

I'm able to reproduce this and will work with the team to get a bug logged.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes