Using Validator Class updateParameter Function to Change Parameter Datatype

649
3
04-02-2014 01:20 PM
MikeMacRae
Occasional Contributor III
I am building an ArcGIS gui that takes 4 parameters:


  1. An input feature class or shapefile

  2. A parameter to query on the input feature class or shapefile

  3. A predefinied multivalue list of layer names that the user can choose from

  4. An output folder location to print out an excel file



The tool will clip data from any of the layers choosen in Parameter #3 based on the input feature class or shapefile (clip feature) from Parameter #1

If the user chooses a feature class as an input, it is know that there are only 2 to choose from ("AREA1", "AREA2"). They will use Parameter 2 to Query a record from which ever feature class they choose. (eg "SITE_NUMBER" = 10023) Also, I know that these 2 feature classes contain the same list of fields, so I can code this into the underlying python script to write the names of particular ones I want to the resultant excel spreasheet.

If the user chooses a shapefile, it could be one of several hundred. Each shapefile only has one record (polygon) so I do not need them to query on a feature using an SQL Query. However, each shapefile does not have the same list of fields from one to the next. In this case, instead of having Parameter #2 of datatype "SQL Expression", I would like to dynamically change the datatype, using the updateParameter function in the Validator Class, to string and a multivalue filter of field names from the shapefile to which the user can choose from.

In the following script, I have attempted to set the some default values for Parameters #1 and 2. dayatype on Parameter #2 if the user changes the input value for Parameter #1. For the programmers out there, this obviously doesn't work as it is a read only parameter according to the help docs:

http://resources.arcgis.com/en/help/main/10.1/index.html#//00150000000v000000

    
# Set Parameters #1nad 2 default values
if not self.params[0].altered:
      self.params[0].value = r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW"
    if not self.params[1].altered:
      self.params[1].datatype = "SQL Expression"
      self.params[1].value = ""

    # if the user changes Parameter #1, then update the datatype and values
    if self.params[0].value == r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW":
      self.params[1].datatype = "SQL Expression"
      self.params[1].value = "SITE_NUMBER = "
    elif self.params[0].value == r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW":
      self.params[1].datatype = "SQL Expression"
      self.params[1].filter.list = "SITE_NUMBER = "
    elif self.params[0].value not in [r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW", r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW"]:
      self.params[1].datatype = "String"
      descFields = arcpy.Describe(self.params[0].value).fields
      self.params[1].filter.list = [field.name for field in descFields]
      self.params[1].filter.values = self.params[1].filter.list


There seems to be a way to set the parameter datatype on the Parameter object, but I'm not sure how to pass that from getParameterInfo function into the updateParameter function:

http://resources.arcgis.com/en/help/main/10.1/index.html#//001500000035000000

I've been playing around with a bunch of approaches, but none seem to work. So, for what I tried to outline as my outcome above....is what I am trying to do possible?
Tags (2)
0 Kudos
3 Replies
JasonScheirer
Occasional Contributor III
You can't change a parameter's datatype, period. What you want to accomplish is best done by having two parameters, one a SQL Expression and one a String, and enabling/disabling one or the other in the validation code based on the first parameter's value.
0 Kudos
MikeMacRae
Occasional Contributor III
Thanks again Jason and I was afraid I had to do that, although it's not a terrible work around. I'll take what I can get!

Now, I attempted to make this happen and I am banging my head against the wall.

I can set the default values in the updateParameter with no issue. It's when I set the updates after the user has changed the parameter value. In my if/else statements, I am setting a condition on the value sitting in the first parameter (params[0]). The script isn't picking up the value at all, even though I set it in as a default value. It goes something like this:


  1. I set the script below, save and close the validator script.

  2. Open the tool and the parameter is set to "DatabaseConnections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW" as I expected

  3. The second parameter is empty as expected

  4. The third parameter is populated with the fields from the default parameter, even though I set an "not in list" condition in the final if/else statement which means the third condition "not in list" is being satisfied, even though the default value I set at the beginning is sitting inside the list....(I've also tried a <> and a simple else  with the same outcome)




  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."""
    
    # Set Parameters #1 and 2 default values
    if not self.params[0].altered:
      self.params[0].value = r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW"
    if not self.params[1].altered:
      self.params[1].value = ""
    if not self.params[2].altered:
      self.params[2].value = ""

    # if the user changes Parameter #1, then update the datatype and values
    if self.params[0].value == r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW":
      self.params[1].value = "SITE_NUMBER = "
      self.params[2].enabled = False
    elif self.params[0].value == r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW":
      self.params[1].filter.list = "SITE_NUMBER = "
      self.params[2].enabled = False
    elif self.params[0].value not in [r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW", r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW"]:
      self.params[1].datatype = "String"
      descFields = arcpy.Describe(self.params[0].value).fields
      self.params[1].filter.list = [field.name for field in descFields]
      self.params[1].filter.values = self.params[1].filter.list


This is weird. I created an updateMessage to spit out the value to see if I am missing something and it returns exactly what I set the parameter to NOT look for:

    if self.params[0].value not in ["Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW", ""Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW""]:
      self.params[0].setErrorMessage(self.params[0].value)


This line returns
Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW
which is exactly what I told it NOT to do in
if self.params[0].value not in ["Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW", ""Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW""]


Oh and get this, I even did:

    if self.params[0].value <> self.params[0].value:
      self.params[0].setErrorMessage(self.params[0].value)


And I get an error message. I told it that if it didn't equal itself, then set a message. See, I'm stumped.....

I tried setting the string path to raw string and then a none raw string. Same thing. The "value" property is supposed to be of type "Value Object". I don't know what that means, but maybe it means it will pick up the datatype of the parameter as a "Value Object"? The parameter datatype for the first param (param[0]) is of type "Feature Class", so is it not picking up the string as a feature class? Am I explaing my problem properly?? ha! Ughhh....
0 Kudos
MikeMacRae
Occasional Contributor III
Problem solved. I needed to set the conditional sentence to equal the string format of the input parameter. I think this is because I set the datatype to "Feature Class". I tested this by changing the datatype to string and my original validator worked. So, when setting conditions on non string datatype parameters, do something like this:

    # if the user changes Parameter #1, then update the datatype and values
    if str(self.params[0].value) == r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW":
      self.params[1].value = "SITE_NUMBER = "
      self.params[2].enabled = False
    elif str(self.params[0].value) == r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW":
      self.params[1].filter.list = "SITE_NUMBER = "
      self.params[2].enabled = False
    elif str(self.params[0].value) not in [r"Database Connections\CONNECT1.sde\AREA1_SPATIAL.ER_SVW", r"Database Connections\CONNECT1.sde\AREA2_SPATIAL.ER_SVW"]:
      self.params[1].datatype = "String"
      descFields = arcpy.Describe(self.params[0].value).fields
      self.params[1].filter.list = [str(field.name) for field in descFields]
      self.params[1].filter.values = self.params[1].filter.list
0 Kudos