Update Slider Min/Max Error in Dialog

Update Slider Min/Max Error in Dialog

ebunn3
Advocate Advocate
1,248 Views
5 Replies
Message 1 of 6

Update Slider Min/Max Error in Dialog

ebunn3
Advocate
Advocate

Hi,

 

I have a float slider command input in a dialog (see code example below) that I am trying to update based on the user selection in a drop down box.  When 'Material2' is selected it crashes the macro at line 34 where it is attempting to reset the valueOne property to a new value based on a list.  Any help here would be greatly appreciated.  Just run the attached code and change the drop drown to 'Material2' to throw the error.

 

Thanks, 

 

Eric

ebunn3_0-1632835038863.pngebunn3_1-1632835052804.png

#Author-Autodesk Inc.
#Description-Demo command input examples
import adsk.core, adsk.fusion, traceback

_app = None
_ui  = None
_rowNumber = 0
matProps = None

# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []

# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            changedInputs = eventArgs.input
            inputs = eventArgs.firingEvent.sender.commandInputs

            if changedInputs.id == 'dropdown':
                for i in range(1,len(matProps)):
                    #find material
                    mater_choice = inputs.itemById('dropdown')
                    if matProps[i][0] == mater_choice.selectedItem.name:
                        min = float(matProps[i][2])
                        max = float(matProps[i][3])
                        break
                SS_Slider = inputs.itemById('floatSlider')
                SS_Slider.minimumValue = min
                SS_Slider.maximumValue = max
                SS_Slider.valueOne = min
                SS_Slider.valueTwo = max  
                print('')  

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


# Event handler that reacts to when the command is destroyed. This terminates the script.            
class MyCommandDestroyHandler(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()))


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

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

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

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

            #material property list used to change slider min/maxs
            matProps = [['Material1', '12', '1.05', '2'],['Material2', '12', '3', '5']]

            dropdownInput = inputs.addDropDownCommandInput('dropdown', 'Drop Down', adsk.core.DropDownStyles.TextListDropDownStyle)
            dropdownItems = dropdownInput.listItems
            dropdownItems.add(matProps[0][0], True, '')
            dropdownItems.add(matProps[1][0], False, '')

            # Create float slider input with two sliders.
            min = float(matProps[0][2])
            max = float(matProps[0][3])
            SS_Slider = inputs.addFloatSliderCommandInput('floatSlider', 'Float Slider', '', min , max , True)
            SS_Slider.spinStep = 0.10


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


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        # Get the existing command definition or create it if it doesn't already exist.
        cmdDef = _ui.commandDefinitions.itemById('cmdInputsSample')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition('cmdInputsSample', 'Command Inputs Sample', 'Sample to demonstrate various command inputs.')

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

        # Execute the command definition.
        cmdDef.execute()

        # Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes
Accepted solutions (1)
1,249 Views
5 Replies
Replies (5)
Message 2 of 6

kandennti
Mentor
Mentor

Hi @ebunn3 .

 

The following avoids the error when changing 'Material1' -> 'Material2'.
However, changing 'Material2' -> 'Material1' resulted in a different error.

・・・
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
・・・
                SS_Slider = inputs.itemById('floatSlider')
                SS_Slider.minimumValue = min
                SS_Slider.maximumValue = max
                SS_Slider.valueOne = SS_Slider.minimumValue
                SS_Slider.valueTwo = SS_Slider.maximumValue
・・・

This may be a bug, but I don't think it is intended for this kind of usage.

 

So I prepared two sliders and switched the slider to be displayed according to the dropdown changes.

・・・
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
・・・
            if changedInputs.id == 'dropdown':
                # sliderIds = ['Material1_Slider', 'Material2_Slider']
                sliderIds = [f'{prop[0]}_Slider' for prop in matProps]

                for prop in matProps:
                    if prop[0] != changedInputs.selectedItem.name:
                        continue
                    showId = f'{prop[0]}_Slider'
                    sliderIds.remove(showId)
                    hideId = sliderIds[0]

                slider: adsk.core.FloatSliderCommandInput = inputs.itemById(showId)
                slider.isVisible = True
                slider.valueOne = slider.minimumValue
                slider.valueTwo = slider.maximumValue

                slider = inputs.itemById(hideId)
                slider.isVisible = False

・・・

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
・・・
            #material property list used to change slider min/maxs
            matProps = [['Material1', '12', '1.05', '2'],['Material2', '12', '3', '5']]

            dropdownInput = inputs.addDropDownCommandInput('dropdown', 'Drop Down', adsk.core.DropDownStyles.TextListDropDownStyle)
            dropdownItems = dropdownInput.listItems
            dropdownItems.add(matProps[0][0], True, '')
            dropdownItems.add(matProps[1][0], False, '')

            # Create float slider input with two sliders.
            for prop in matProps:
                min = float(prop[2])
                max = float(prop[3])
                slider = inputs.addFloatSliderCommandInput(
                    f'{prop[0]}_Slider',
                    'Float Slider',
                    '',
                    min,
                    max,
                    True
                )
                slider.spinStep = 0.10
            slider.isVisible = False

I think it is working the way I wanted it to.
You should not notice that there are two sliders.

 

0 Likes
Message 3 of 6

ebunn3
Advocate
Advocate

@kandennti 

 

Thank you.  This appears to work but I am having trouble wrapping my head around what is happening.  The code is creating 2 sliders, one for each material?  Then depending on the value of the drop down it is showing the appropriate slider?  I see the slider.isVisible = False which sets the last created slider to False?  I assume that if I have more than two materials I would need to loop through and find the one that matches the drop down?  Is this what is happening in MyCommandInputChangedHandler?

 

Thanks in advance for the explanation.

 

Eric

0 Likes
Message 4 of 6

kandennti
Mentor
Mentor
Accepted solution

@ebunn3 .

 

The ID of the FloatSliderCommandInput is created by using the value of matProps[x][0] and using For.
The reason for doing so is that it is easier to find it in inputs.itemById from the DropDown value.

 

I have created samples for the three cases.
Since it is difficult to explain in words, please try executing it while checking with breakpoints.

import adsk.core, adsk.fusion, traceback

_app = None
_ui  = None
_rowNumber = 0

# Material Information List
matProps = [
    ['Material1', '12', '1.05', '2'],
    ['Material2', '12', '3', '5'],
    ['Material3', '12', '5', '10'],
]

# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []

# Get the Slider based on the DropDown value and display only what is needed.
def updateSlider(
    dropdownValue: str,
    inputs: adsk.core.CommandInputs):

    global matProps
    for prop in matProps:
        slider: adsk.core.FloatSliderCommandInput = inputs.itemById(
            f'{prop[0]}_Slider'
        )

        if dropdownValue == prop[0]:
            slider.isVisible = True
            slider.valueOne = slider.minimumValue
            slider.valueTwo = slider.maximumValue
        else:
            slider.isVisible = False


# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            changedInputs = eventArgs.input
            inputs = eventArgs.firingEvent.sender.commandInputs

            if changedInputs.id == 'dropdown':
                updateSlider(
                    changedInputs.selectedItem.name,
                    inputs
                )

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


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

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

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

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

            #material property list used to change slider min/maxs
            global matProps

            dropdownInput = inputs.addDropDownCommandInput(
                'dropdown',
                'Drop Down',
                adsk.core.DropDownStyles.TextListDropDownStyle
            )
            # The dropdownItems are created using a list of matProps.
            dropdownItems = dropdownInput.listItems
            for idx, prop in enumerate(matProps):
                selected = True if idx == 0 else False # Make the first one selected.
                dropdownItems.add(
                    prop[0],
                    selected,
                    ''
                )

            # Create float slider input with two sliders.
            for idx, prop in enumerate(matProps):
                min = float(prop[2])
                max = float(prop[3])
                slider = inputs.addFloatSliderCommandInput(
                    f'{prop[0]}_Slider',
                    'Float Slider',
                    '',
                    min,
                    max,
                    True
                )
                slider.spinStep = 0.10
                slider.isVisible = True if idx == 0 else False # Show only the first.

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

The run function and MyCommandDestroyHandler class have not been changed.

 

If you have more than four, you can increase the number of matProps to handle them.

0 Likes
Message 5 of 6

ebunn3
Advocate
Advocate

@kandennti 

 

Thank you for doing all of this for me.  I appreciate it.  I'll try out the code that you suggest and let you know how it works.  One thing I did discover on my own was when I was trying to update the minimum and maximum values of an existing slider, the macro would crash if I updated the minimum first and then tried to update the maximum value with a number greater than its previous maximum value.  For example if the min were 1 and the max were 2, and I then tried to change the min to  3, it would not change the value since it exceeded the pre-existing maximum value of 2.  If I reversed it and changed the maximum value first to 5, I could then change the min value to 3 since the new min value no longer exceeded the previous max value which was changed to 5 first.  Hope that makes sense.  I reversed the assignments to this and it now works:

 

 

#previous min max was 1 and 2.  set max first and then min
SS_Spiner.maximumValue = 5
SS_Spiner.minimumValue = 3
SS_Spiner.valueTwo = SS_Spiner.maximumValue
SS_Spiner.valueOne = SS_Spiner.minimumValue 

 

 

I still may want to pursue your method because of other problems with setText.  I've found if I use setText initially to set the text to the min max values, instead of a spinner box which you cannot read because of all of the decimal places, you get text boxes right and left that just show the min max values for the slider.  The slider also ticks off the setting when you slide it which is a nice feature as well.  When I reset the slider to new min max values using the above code and then set the text to the new values, the setText function returns True but the text boxes do not update to the new values.  I'll try to repost the original example with the changes so you can see this for yourself.

 

Eric

Message 6 of 6

ebunn3
Advocate
Advocate

Here is the modified code showing the setText behavior.

ebunn3_1-1633005944858.pngebunn3_2-1633005958617.png

 

 

 

 

Eric

 

#Author-Autodesk Inc.
#Description-Demo command input examples
import adsk.core, adsk.fusion, traceback

_app = None
_ui  = None
_rowNumber = 0
matProps = None

# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []

# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            changedInputs = eventArgs.input
            inputs = eventArgs.firingEvent.sender.commandInputs

            if changedInputs.id == 'dropdown':
                for i in range(1,len(matProps)):
                    #find material
                    mater_choice = inputs.itemById('dropdown')
                    if matProps[i][0] == mater_choice.selectedItem.name:
                        min = float(matProps[i][2])
                        max = float(matProps[i][3])
                        break
                SS_Slider = inputs.itemById('floatSlider')
                #set max value first followed by min values
                SS_Slider.maximumValue = max
                SS_Slider.minimumValue = min
                SS_Slider.valueTwo = SS_Slider.maximumValue  
                SS_Slider.valueOne = SS_Slider.minimumValue
                #SET TEXT FAILS TO UPDATE TEXT ON FORM.  return value is True but form does not update.
                ret = SS_Slider.setText(str(min),str(max))
                #getting text to verify it was changed.  
                txtL = SS_Slider.getText(isLeft = True)
                txtR = SS_Slider.getText(isLeft = False)
                print(txtL,txtR)  

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


# Event handler that reacts to when the command is destroyed. This terminates the script.            
class MyCommandDestroyHandler(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()))


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

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

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

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

            #material property list used to change slider min/maxs
            matProps = [['Material1', '12', '1.05', '2'],['Material2', '12', '3', '5']]

            dropdownInput = inputs.addDropDownCommandInput('dropdown', 'Drop Down', adsk.core.DropDownStyles.TextListDropDownStyle)
            dropdownItems = dropdownInput.listItems
            dropdownItems.add(matProps[0][0], True, '')
            dropdownItems.add(matProps[1][0], False, '')

            # Create float slider input with two sliders.
            min = float(matProps[0][2])
            max = float(matProps[0][3])
            SS_Slider = inputs.addFloatSliderCommandInput('floatSlider', 'Float Slider', '', min , max , True)
            SS_Slider.spinStep = 0.10
            SS_Slider.setText(str(min),str(max))


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


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        # Get the existing command definition or create it if it doesn't already exist.
        cmdDef = _ui.commandDefinitions.itemById('cmdInputsSample')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition('cmdInputsSample', 'Command Inputs Sample', 'Sample to demonstrate various command inputs.')

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

        # Execute the command definition.
        cmdDef.execute()

        # Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))