Parameter Validation using SearchCursor

407
1
11-20-2019 09:36 AM
JohnMossing
New Contributor II

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?

Tags (2)
0 Kudos
1 Reply
RandyBurton
MVP Regular Contributor

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. 

0 Kudos