I am developing a script to Add, Delete, and Update rows within a table. The script basic function works well using InsertCursor and UpdateCursor. My questions or troubles come when I attempt to use script validation to auto populate some of the parameters.
When the users choose to Update a Row, they pick a country from a list. Then the validation prepoulates 5 parameters from the existing row in the table. I am using SearchCursor within script validation to auto populate those fields. That generally works well.
The problem is the script validation never seems to stop. So after the user picks a country code, 5 parameters are populated in the tool dialog with data from the fields in the table. Ideally I would like the user to be able to type over those entries and then resubmit to the table for changes. However every time the user changes one of the parameters the script validation overwrites it with the original value from the table. Does anyone have any suggestions on how I can make the parameters populate with the field data in the table, and still allow the user to be able to manually change those parameters for update purposes?
Here is a code snippet:
if self.params[1].value == True or self.params[2].value == True:
self.params[4].filter.list = [str(val) for val in sorted(set(row[0] for row in arcpy.da.SearchCursor(Table,Field)))]
wClause = "COUNTRY_CODE ='{}'".format(self.params[4].value)
# SO FAR EVERYTHING WORKS FINE
# HERE IS THE VALIDATION THAT USES SEARCHCURSOR AND IT WORKS
if self.params[4].altered:
values = next(arcpy.da.SearchCursor(Table, Fields, where_clause=wClause))
self.params[5].value = values[0]
self.params[6].value = values[1]
self.params[7].value = values[2]
self.params[8].value = values[3]
self.params[9].value = values[4]
self.params[10].value = values[5]
So after the searchcursor populates the params, how can i stop the validation and let the user manually edit those params?
Working with script tools and Python toolboxes can be frustrating. The .altered property looks like it is always True if the parameter has a value, and False if it is None. The .hasBeenValidated property can be switched to False just by clicking on a drop-down without changing the selected value. Using global variables can also be tricky.
If I understand what you are trying to accomplish ( select a feature/table, a field in that table, and populate a drop-down with the field's attributes and allow a user to add to the list ), here are a couple of ideas using a Python toolbox ( with a .pyt extension ). One idea is to use some global variables to keep track of parameter value changes. The second is to allow the user typed data to be inserted at the top of the drop-down temporarily allowing the value to be used.
import arcpy
import pythonaddins
class Toolbox(object):
def __init__(self):
"""Define the toolbox (the name of the toolbox is the name of the
.pyt file)."""
self.label = "Select Value"
self.alias = "selection"
self.tools = [Tool]
class Tool(object):
def __init__(self):
"""Define the tool (tool name is the name of the class)."""
self.label = "Tool"
self.description = "Select field and attribute from feature"
self.canRunInBackground = False
def getParameterInfo(self):
# create global variables here as
# getParameterInfo is only called once
global fcValue # keep track of parameter changes
fcValue = None # inFeature
global fldValue
fldValue = None # field1
global attrValue
attrValue = None # attribute1
global attrList
attrList = None # attribute list
'''
# for debugging, display values in an annoying pop-up box
pythonaddins.MessageBox('fcValue: {}\nfldValue: {}\nattrValue: {}\nattrList: {}\nstatus: {}'.format(
fcValue,
fldValue,
attrValue,
attrList,
'initialized'
),'Get Parameters')
'''
# First parameter
inFeature = arcpy.Parameter(
displayName="Input Features",
name="inFeature",
datatype=["DEFeatureClass","GPFeatureLayer"],
parameterType="Required",
direction="Input")
# Second parameter
field1 = arcpy.Parameter(
displayName="Field 1",
name="field1",
datatype="Field",
parameterType="Required",
direction="Input")
field1.parameterDependencies = [inFeature.name]
field1.filter.list = ["Short", "Long", "Double", "Float", "Text"]
# Third parameter
attribute1 = arcpy.Parameter(
displayName="Value 1",
name="attribute1",
datatype="String",
parameterType="Required",
direction="Input")
attribute1.parameterDependencies = [inFeature.name, field1.name]
# Fourth parameter
field2 = arcpy.Parameter(
displayName="Field 2",
name="field2",
datatype="Field",
parameterType="Required",
direction="Input")
field2.parameterDependencies = [inFeature.name]
field2.filter.list = ["Short", "Long", "Double", "Float", "Text"]
# Fifth parameter
attribute2 = arcpy.Parameter(
displayName="Value 2",
name="attribute2",
datatype="String",
parameterType="Required",
direction="Input")
attribute2.parameterDependencies = [inFeature.name, field1.name]
params = [inFeature, field1, attribute1, field2, attribute2]
return params
def isLicensed(self):
"""Set whether tool is licensed to execute."""
return True
def updateParameters(self, parameters):
# globals are initalized in getParameterInfo to keep track of parameter changes
global fcValue
global fldValue
global attrValue
global attrList
'''
# for debugging, display annoying popup box everytime updateParameters is called to show parameter values
# popup box is not always on top, so you may need to move some windows
pythonaddins.MessageBox('0: {}\n1: {}\n2: {}\n3: {}\n4: {}'.format(
parameters[0].value,
parameters[1].value,
parameters[2].value,
parameters[3].value,
parameters[4].value
),'Update Parameters (in)')
'''
# this section uses globals to track changes in the parameters
if parameters[0].value and not parameters[1].value:
fcValue = parameters[0].value # save value to global
if parameters[0].value and parameters[1].value: # get first field and value list
fc, col = parameters[0].valueAsText, parameters[1].valueAsText
desc = arcpy.Describe(parameters[0])
if desc.dataType == 'FeatureLayer': # if feature layer, use catalog path
fc = desc.catalogPath
if parameters[0].valueAsText != fcValue or parameters[1].valueAsText != fldValue: # feature or field has changed
fcValue = parameters[0].valueAsText # save value to global
fldValue = parameters[1].valueAsText
# get the field's attributes
attrList = [str(val) for val in sorted(set(row[0] for row in arcpy.da.SearchCursor(fc,col)))]
parameters[2].filter.list = attrList # save attribute list to global
if parameters[2].value is None: # default to first item in list
parameters[2].value = parameters[2].filter.list[0]
else:
if parameters[2].valueAsText != attrValue: # attribute selection changed
if parameters[2].value not in attrList:
attrList.insert(0,parameters[2].value)
attrList = sorted(attrList)
parameters[2].filter.list = attrList
attrValue = parameters[2].valueAsText # save selected value to global
# this section only allows 1 change to be added to top of attribute list temporarly
if parameters[0].value and parameters[3].value: # get second field and value list
fc, col = parameters[0].valueAsText, parameters[3].valueAsText
desc = arcpy.Describe(parameters[0])
if desc.dataType == 'FeatureLayer': # if feature layer, use catalog path
fc = desc.catalogPath
filter2 = [str(val) for val in sorted(set(row[0] for row in arcpy.da.SearchCursor(fc,col)))]
if parameters[4].value is None:
parameters[4].filter.list = filter2 # default to first item in list
parameters[4].value = parameters[4].filter.list[0]
else:
if parameters[4].value not in filter2:
filter2.insert(0,parameters[4].value)
parameters[4].filter.list = filter2
'''
# for debugging, display annoying popup box everytime updateParameters is called
pythonaddins.MessageBox('0: {}\n1: {}\n2: {}\n3: {}\n4: {}'.format(
parameters[0].value,
parameters[1].value,
parameters[2].value,
parameters[3].value,
parameters[4].value
),'Update Parameters (out)')
'''
return
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."""
inFeature = parameters[0].valueAsText
field1 = parameters[1].valueAsText
attribute1 = parameters[2].valueAsText
field2 = parameters[3].valueAsText
attribute2 = parameters[4].valueAsText
messages.addMessage(
"\nInput Feature: {}".format(inFeature))
messages.addMessage(
"\nSelected Field: {}".format(field1))
messages.addMessage(
"\nField Property: {}".format(attribute1))
messages.addMessage(
"\nSelected Field: {}".format(field2))
messages.addMessage(
"\nField Property: {}".format(attribute2))
desc = arcpy.Describe(parameters[0])
messages.addMessage(
"\nDataType: {}\nPath: {}".format(desc.dataType, desc.catalogPath))
return
I use pythonaddins.MessageBox() to display some annoying pop-up message boxes that can show the parameter values at various times. Global variables seem to work if they are declared in the getParameterInfo() section. They can then be used in the updateParameters() validation section. My testing was with Desktop 10.5.
Hopefully, this will give you some ideas.