Problem with pre-populating multiple selection inputs

Problem with pre-populating multiple selection inputs

j.han97
Advocate Advocate
1,831 Views
7 Replies
Message 1 of 8

Problem with pre-populating multiple selection inputs

j.han97
Advocate
Advocate

Hi all,

 

I have multiple selection inputs for my addin. They are designed to hold different selection filters (body & face) and must be separated.

 

command = adsk.core.CommandCreatedEventArgs.cast(args).command
            inputs = command.commandInputs
            body_selection = inputs.addSelectionInput('body_selection', 'Select body', 'Click on body to select')
            body_selection.addSelectionFilter('Bodies')
            body_selection.setSelectionLimits(0)
            
            face_selection = inputs.addSelectionInput('face_selection', 'Select face', 'Click on face to select')
            face_selection.addSelectionFilter('Faces')
            face_selection.setSelectionLimits(0)

 

 The selections made by users have to be remembered and re-applied next time when users use this command again. Therefore, I stored the selections in two global lists, and used the activate event of command to pre-populate the selections.

 

global _sel_body, _sel_face
            ipts = adsk.core.CommandEventArgs.cast(args).command.commandInputs
            #Pre-populate body selections if any
            if len(_sel_body) > 0:
                body_selection = ipts.itemById('body_selection')
                for body in _sel_body:
                    body_selection.addSelection(body)
            #Pre-populate face selections if any
            if len(_sel_face) > 0:
                face_selection = ipts.itemById('face_selection')
                for face in _sel_face:
                    face_selection.addSelection(face)

 

To record the selections, I used inputChanged event to detect changes in the selection inputs.

 

global _sel_body, _sel_face
            ipt = adsk.core.InputChangedEventArgs.cast(args).input
            if ipt.id == 'body_selection':
                _sel_body = [ipt.selection(s).entity for s in range(ipt.selectionCount)]
            elif ipt.id == 'face_selection':
                _sel_face = [ipt.selection(s).entity for s in range(ipt.selectionCount)]

 

 

However, when I tested my code, it did not work as expected (in the pre-populating step, to be precise). First I selected a body and a face for the two selection inputs respectively. Note that the selected face did not belong to the selected body.

jhan97_0-1637890546814.png

After clicking OK, I opened the command again, expecting the same selection (1 body, 1 face) would be made. But instead, 2 bodies & 1 face were selected.

jhan97_1-1637890705850.png

The body of the selected face was selected, although I never selected it. For my understanding, it is because the pre-populating action triggers the inputChanged event.

 

How can I avoid this behavior? If anyone wants to reproduce this situation, I have attached the addin & the test model in a zip file. Many thanks!

 

 

0 Likes
Accepted solutions (1)
1,832 Views
7 Replies
Replies (7)
Message 2 of 8

tykapl.breuil
Advocate
Advocate

If I understood correcly, you need to remember with what selections the last commands were executed with right ? In that case, why not put the 'memory' step in the command execute handler rather than the input changed handler ?

0 Likes
Message 3 of 8

kandennti
Mentor
Mentor

Hi @j.han97 .

 

I was able to reproduce the phenomenon.

I tried de-focusing the body_selection and reselecting the face_selection first, but the result was the same.

It seems to be a bug, but I'm not sure if the cause is the SelectionCommandInput or the activate event.

0 Likes
Message 4 of 8

j.han97
Advocate
Advocate

Hi all,

 

I am guessing that the command input is focusing on the first selection input, therefore in the pre-population step, the selected faces are added to the body_selection (which is the first selection input). To verify this, I used only activate & execute handler to store & pre-populate the selections.

class selections_activateHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    
    def notify(self, args):
        try:
            global _sel_body, _sel_face
            ipts = adsk.core.CommandEventArgs.cast(args).command.commandInputs
            _app.log('\tPrepopulate started')
            if len(_sel_body) > 0:
                body_selection = ipts.itemById('body_selection')
                for body in _sel_body:
                    body_selection.addSelection(body)
            
            if len(_sel_face) > 0:
                face_selection = ipts.itemById('face_selection')
                for face in _sel_face:
                    face_selection.addSelection(face)
                    
        except:
            if _ui:
                _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class selections_executeHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    
    def notify(self, args):
        try:
            global _sel_body, _sel_face
            _sel_body = []; _sel_face = []
            
            command = adsk.core.CommandEventArgs.cast(args).command
            inputs = command.commandInputs
            body_selection = inputs.itemById('body_selection')
            for i in range(body_selection.selectionCount):
                _sel_body.append(body_selection.selection(i).entity)
            face_selection = inputs.itemById('face_selection')
            for i in range(face_selection.selectionCount):
                _sel_face.append(face_selection.selection(i).entity)
            
            _ui.messageBox(f'{len(_sel_body)} bodies, {len(_sel_face)} faces')
        except:
            if _ui:
                _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 The situation remained the same. Now it seems that the the problem comes from either the activate handler or the selectionCommandInput as suggested by @kandennti .

Until this step I am completely confused because this pre-population method is suggested in the API reference manual (If you want to pre-populate the selection when the command is starting, you can use this method in the activate method of the Command). I am hoping someone from the development team can provide some guidance or confirm that this is a bug (so it won't trouble me anymore!).

 

To @tykapl.breuil , the real code is slightly more complex than this sample script: I wanted to 'extend' the selection from user, for example, include similar bodies (in terms of volume, mass, etc) when a body is selected. Therefore I had to monitor the new selection and do the calculations in the inputChanged handler. I didn't realize that this is actually kind of stupid here (:D).

 

Thank you all for your comments!

0 Likes
Message 5 of 8

j.han97
Advocate
Advocate

Small update: I thought I found the solution by setting the focus to face_selection before adding selections to it:

 

class selections_activateHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    
    def notify(self, args):
        try:
            global _sel_body, _sel_face
            ipts = adsk.core.CommandEventArgs.cast(args).command.commandInputs
            _app.log('\tPrepopulate started')
            if len(_sel_body) > 0:
                body_selection = ipts.itemById('body_selection')
                for body in _sel_body:
                    body_selection.addSelection(body)
            
            
            if len(_sel_face) > 0:  
                face_selection = ipts.itemById('face_selection') 
                #Set focus to face_selection
                face_selection.hasFocus = True
                for face in _sel_face:
                    face_selection.addSelection(face)
            
                    
        except:
            if _ui:
                _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

But it still failed. 

0 Likes
Message 6 of 8

kandennti
Mentor
Mentor
Accepted solution

@j.han97 .

 

If it's just "face_selection", it works correctly.

 

It is true that if there are multiple SelectionCommandInputs, setSelectionLimits will not work correctly and the OK button will function even though the condition is not met.

It's possible that SelectionCommandInput is not designed to be used more than once.

0 Likes
Message 7 of 8

j.han97
Advocate
Advocate

Hi @kandennti ,

 

I see. But since it is not practical for me to mix up the selection inputs, I guess I will have to create separate buttons for users for different selection types.

 

Thank you.

Message 8 of 8

tykapl.breuil
Advocate
Advocate

This addin only adds a face to the face selection input and still displays the bug :

#Author-Juan
#Description-Testing for addins

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

class selections_commandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    
    def notify(self, args):
        try:
            product = _app.activeProduct
            design = adsk.fusion.Design.cast(product)
            rootComp = design.rootComponent
            command = adsk.core.CommandCreatedEventArgs.cast(args).command
            inputs = command.commandInputs
            body_selection = inputs.addSelectionInput('body_selection', 'Select body', 'Click on body to select')
            body_selection.addSelectionFilter('Bodies')
            body_selection.setSelectionLimits(0)
            
            face_selection = inputs.addSelectionInput('face_selection', 'Select face', 'Click on face to select')
            face_selection.addSelectionFilter('Faces')
            face_selection.setSelectionLimits(0)

            face_selection = inputs.itemById('face_selection')
            face_selection.hasFocus = True
            face_selection.addSelection(rootComp.bRepBodies.item(0).faces.item(0))

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


_app = adsk.core.Application.get()
_ui  = _app.userInterface
_handlers = []


def run(context):
    try:
        
        #Get command definitions & design workspace
        cmd_def = _ui.commandDefinitions
        des_wp = _ui.workspaces.itemById('FusionSolidEnvironment')
        product = _app.activeProduct
        design = adsk.fusion.Design.cast(product)

        #Get tools tab
        tools_tab = des_wp.toolbarTabs.itemById('ToolsTab')
        
        #Get add-in panel
        addin_panel = tools_tab.toolbarPanels.itemById('SolidScriptsAddinsPanel')
            
        
        #Add selections command definition
        selections_cmdDef = cmd_def.itemById('selections_cmd')
        if not selections_cmdDef:
            selections_cmdDef = cmd_def.addButtonDefinition('selections_cmd', 'Multiple selections', 'Sample addin to test multiple selections', './resources/selections')
        
        #Add to panel
        selections_ctrl = addin_panel.controls.itemById('selections_cmd')
        if not selections_ctrl:
            selections_ctrl = addin_panel.controls.addCommand(selections_cmdDef)
        
        #Set visibility of button
        selections_ctrl.isVisible = True; selections_ctrl.isPromoted = True; selections_ctrl.isPromotedByDefault = True
        
        #Connect to commandCreatedEvent
        selections_commandCreated = selections_commandCreatedEventHandler()
        selections_cmdDef.commandCreated.add(selections_commandCreated)
        _handlers.append(selections_commandCreated)
        
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def stop(context):
    try:
        pass
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))