Using altered property in ArcGIS Toolbox Validator Class

457
1
08-19-2020 02:33 PM
MikeMacRae
Regular Contributor

I am trying to script some behavior into the ArcGIS Toolbox validator class. Specifically, I am creating a dropdown parameter so the user can choose a predefined option and then a second parameter will update a list of items. The issue I am having is, when I change the option in the first parameter, I can get the options to change in the second parameter, but when I toggle the 'Unselect All' button in the second parameter, the items will not uncheck (they remained checked). I have a feeling it has to do with my script in the Validator.

Here are the details (see image below as well):

  1. First parameter is a string type. It is required, Input Direction, Has a default value. Multivalue = No and uses a value list so that I can have a dropdown with 3 options to choose from
  2. Second parameter is a string type. It is required. Contains Default values. Multivalue = Yes and uses a value list so that the items populated in this parameter (a list of items) can be toggled on or off.

https://i.stack.imgur.com/My4J7.png

So, in the Validator, my script is as follows. Can anyone see something obvious I ma misisng here to get the 'Select All' and 'Unselect All' buttons to work?

import arcpy
class ToolValidator(object):
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  def __init__(self):
    """Setup arcpy and the list of tool parameters."""
    self.params = arcpy.GetParameterInfo()

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""

    self.params[1].filter.list = [1,2,3,4]
    self.params[1].values = self.params[1].filter.list    
    return

  def updateParameters(self):
    """Modify the values and properties of parameters before internal
    validation is performed.  This method is called whenever a parameter
    has been changed."""
    if self.params[0].altered == True:
      if self.params[0].value == 'Option 1':     
        self.params[1].filter.list = [1,2,3,4]
        self.params[1].values = self.params[1].filter.list

      elif self.params[0].value == 'Option 2':  
        self.params[1].filter.list = ['a', 'b', 'c']
        self.params[1].values = self.params[1].filter.list

      elif self.params[0].value == 'Option 3':  
        self.params[1].filter.list = ['dr', 'bht', 'cjjjyy']
        self.params[1].values = self.params[1].filter.list
    
    return

  def updateMessages(self):
    """Modify the messages created by internal validation for each tool
    parameter.  This method is called after internal validation."""
    return
0 Kudos
1 Reply
RandyBurton
MVP Regular Contributor

I avoid using the "altered" property.  Once a parameter has been changed, altered always checks true.  In addition, the script tool seems to create new instances of the tool validator every time a parameter is checked.  So I check the values of the parameters and take the appropriate actions.  Here is my suggestion for your validator code:

import arcpy

class ToolValidator(object):
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  def __init__(self):
    """Setup arcpy and the list of tool parameters."""
    self.params = arcpy.GetParameterInfo()

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""
    return

  def updateParameters(self):
    """Modify the values and properties of parameters before internal
    validation is performed.  This method is called whenever a parameter
    has been changed."""

    options = ['Option 1', 'Option 2', 'Option 3']
    filters = [['1','2','3','4'], ['a', 'b', 'c'], ['dr', 'bht', 'cjjjyy']]

    if self.params[0].value == options[2]:
        if self.params[1].filter.list != filters[2]:  
            self.params[1].filter.list = filters[2] # update filter.list
            self.params[1].values = filters[2] # select all
            # self.params[1].values = None # clear all selections

    elif self.params[0].value == options[1]:  
        if self.params[1].filter.list != filters[1]:     
            self.params[1].filter.list = filters[1] # update filter.list
            self.params[1].values = filters[1] # select all
            # self.params[1].values = None # clear all selections

    else:
        self.params[0].value = options[0] # set to default value
        if self.params[1].filter.list != filters[0]:  
            self.params[1].filter.list = filters[0] # update filter.list
            self.params[1].values = filters[0] # select all
            # self.params[1].values = None # clear all selections

    return

  def updateMessages(self):
    """Modify the messages created by internal validation for each tool
    parameter.  This method is called after internal validation."""
    return

EDIT: I updated my code above after discovering that a user could edit the text for "Option 1", etc., and enter an "Option 4".  The code now will set "Option 1" as the default.

In your code, you were changing the filter list and selecting all values to match the "Option n" setting even if the list in use already matched the option.  That was causing select/unselect buttons to freeze.

As an alternative to using the "altered" property, you may wish to use the "hasBeenValidated" property.  When a parameter has been checked by updateParameters, this value is set to true.  When the user changes the parameter, the value is set to false.  A code example:

if self.params[0].value and not self.params[0].hasBeenValidated:

Hope this helps.