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
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:
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...
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:
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