Select to view content in your preferred language

Python Toolbox Use Cases - altered, hasBeenValidated, valueAsText

1074
2
09-10-2023 11:04 AM
ZacharyUhlmann1
Frequent Contributor

1) I've read the docs ( customize tool behavior )

2) I've received excellent feedback in a related post ( related post )

3) And followed similar workflow examples ( Arcpy paramaters example )

My observation is that people use `param.valueAsText for some reason as validation that a parameter has been entered.  Others mix and match param.altered for a similiar purpose.  While others use hasBeenValidated.  Reposting this example from the ESRI docs - 1) above:

 

def updateParameters(self, parameters):
    # Set the default distance threshold to 1/100 of the larger of the width
    #  or height of the extent of the input features.  Do not set if there is no 
    #  input dataset yet, or the user has set a specific distance (Altered is true).
    #
    if parameters[0].valueAsText:
        if not parameters[6].altered:
            extent = arcpy.Describe(parameters[0]).extent
        if extent.width > extent.height:
            parameters[6].value = extent.width / 100
        else:
            parameters[6].value = extent.height / 100

    return

 

Why not do this:

 

def updateParameters(self, parameters):
    if parameters[0].altered:
        if not parameters[6].altered:
            #continue

 

My questions in relation to this example are:

1. What is the difference between altered and hasBeenValidated and can a paramaters be UNaltered and validated and altered and UNvalidated.  

2. When does validation occur? 

3. Is valueAsText/value just a syntactically awkward way of checking if a paramater has been set?  (I understand we can assign new values with this property)  

3. when to use value vs. valueAsText - i.e. 

 

if param[0].<value or valueAsText:
    # do something

 

My guess is that valueAsText/value is way overused and due to sparse documentation people made their Python Toolboxes work by misusing these properties.  Hence my attempt to clarify my understanding.

Thanks,

Zach

Tags (1)
0 Kudos
2 Replies
Luke_Pinner
MVP Regular Contributor

The way I do it is to check if parameter.altered and not parameter.hasBeenValidated. From the help:

altered

altered is true if the value of a parameter is changed... Once a parameter has been altered, it remains altered until the user empties (blanks out) the value, in which case it returns to being unaltered.

hasBeenValidated

hasBeenValidated is false if a parameter's value has been modified by the user since the last time updateParameters and internal validate were called. Once internal validate has been called, geoprocessing automatically sets hasBeenValidated to true for every parameter.

hasBeenValidated is used to determine whether the user has changed a value since the last call to updateParameters.

Re your questions:

  1. Altered is has the user changed the value (see above) and yes you can have altered and UNvalidated
  2. After a value is changed, internal (basic ArcGIS) validation happens which doesn't change "hasBeenValidated", then UpdateParameters is called.
  3. valueAsText/value is if you need to access the actual value of the parameter (as an object or as text) e.g. to check it or to use it for something else - for example to populate a filter list

 

Something like:

 

  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 and not self.params[0].hasBeenValidated:
          etc... 

 

 

0 Kudos
ZacharyUhlmann1
Frequent Contributor

Hi Luke,

I almost tagged you on this post, as you seem to be the Resident Python Toolbox Expert on the forum.  That makes...more sense.  If you get time (no rush) perhaps you can use my below example as an example to point out anything I did wrong, inefficient, redundant, etc.  It's based off of your Python Toolbox sample code chunk on this post - arcpy-parameter-for-arcgis-pro-document .  There the guy was accessing layouts from a project via arcpy.mp.ArcGISProject(<project_name>). Mine has one extra wrinkle.  I am cascading the variable project_name to map_name to layer_name, ultimately updating the CIM of a layer in the execute method.  It works (took awhile). Note that I am truncating the execute codeblock for brevity. But first here is the toolbox look:

Screenshot 2023-09-11 172734.jpg

import pandas as pd

class ToolBox(object):
    def __init__(self):
        '''
        Define the toolbox (name of the toolbox is name of .pyt file).
        '''
        self.label = "Color Ramp Toolbox"
        self.alias = "Color Ramp Toolbox"
        self.tools = [color_ramp_from_csv]
class color_ramp_from_csv(object):
    '''
    classifies color ramp from csv.  Note that user must classify color ramp simply with number \
    of classes initially prior to running
    '''

    def __init__(self):
        self.label = "format_color_ramp"
        self.description = "specify breaks, colors and labels for color ramps of rasters"
        self.canRunInBackground = False

    def getParameterInfo(self):
        '''
        Define parameter defitions
        '''
        param0 = arcpy.Parameter(
            displayName="Current Pro-Document",
            name="currentProDocument",
            datatype="GPBoolean",
            parameterType="Optional",
            direction="Input")

        param1 = arcpy.Parameter(
            displayName="Other Pro-Document",
            name="otherProDocument",
            datatype="DEFile",
            parameterType="Optional",
            direction="Input")
        param1.filter.list = ['aprx']
        param2 = arcpy.Parameter(
            displayName="map",
            name="map",
            datatype="GPString",
            parameterType="Required",
            direction="Input")
        param3 = arcpy.Parameter(
            displayName="layer",
            name="layer",
            datatype="GPString",
            parameterType="Required",
            direction="Input")
        parameters = [param0,param1,param2,param3]
        return parameters
    def isLicensed(self):
        '''
        set whether tool is licensed to execute.
        '''
        return True
    def updateParameters(self, parameters):
        '''
        Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed.
        '''
        if parameters[1].altered and not parameters[1].hasBeenValidated:
            parameters[0].value=False
            parameters[2].value=None
            parameters[2].filter.list=[]
            prodoc = parameters[1].valueAsText
            prodoc_selected = True
        elif parameters[0].altered and not parameters[0].hasBeenValidated:
            parameters[1].value=None
            parameters[2].value=None
            parameters[2].filter.list=[]
            prodoc='current'
            prodoc_selected=True
        else:
            prodoc_selected  = False
        if prodoc_selected:
            project=arcpy.mp.ArcGISProject(prodoc)
            map_list = [m.name for m in project.listMaps()]
            parameters[2].value=None
            parameters[2].filter.list=map_list
            parameters[3].value = None
            parameters[3].filter.list = []
        if parameters[2].altered and not parameters[2].hasBeenValidated:
            if parameters[0].value is not None:
                prodoc = 'current'
            elif parameters[1].value is not None:
                prodoc = parameters[1].valueAsText
            project = arcpy.mp.ArcGISProject(prodoc)
            map_name = parameters[2].valueAsText
            map_object = project.listMaps(map_name)[0]
            layer_list = [l.name for l in map_object.listLayers()]
            parameters[3].filter.list=layer_list
        return parameters

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return
    def execute(self, parameters, messages):
        """The source code of the tool."""
        if parameters[0].value is not None:
            prodoc = 'current'
        elif parameters[1].value is not None:
            prodoc = parameters[1].valueAsText
        project_name=prodoc
        layer_name = parameters[3].valueAsText
        map_name = parameters[2].valueAsText

        p = arcpy.mp.ArcGISProject(project_name)
        m = p.listMaps(map_name)[0]
        l = m.listLayers(layer_name)[0]

 As I said, it functions well but perhaps I missed or misused some conditions in my if statements.  I understand that altered property is re-assigned every time a parameter is changed and that hasBeenValidated relates to non-internal ArcGIS validation related to updateParameters method.  However I still don't understand WHEN hasBeenValidated is checked.  ex) Drop down, select parameter1, altered is assigned but nothing is validated?  So when does validation occur?

In regards to the value and valueAsText confusion, it seems that there are loads of examples of people using it INSTEAD OF hasBeenValidated or altered.  For instance the OP in the post I referenced above, does this (line 44):

 def updateParameters(self, parameters):
        if parameters[0].value:
            p0Val = parameters[0].valueAsText

 Couldn't it be

 def updateParameters(self, parameters):
        if parameters[0].altered:
            p0Val = parameters[0].valueAsText

?   Thanks for sharing your knowledge...

Zach

0 Kudos