11

I am using ArcMap 10.2.

I'm trying to adapt a model and script combination found on ESRI's blog site titled 'Generating a multivalue choice list'.

However, I've concluded that part of the validation used in the embedded script is reliant upon the 'Frequency' Tool in order to function properly, but this is only available with and Advanced license (lame). The blog post explains the workflow and where to download the models and scripts (but I'll happily post them up here upon request). As far as I can tell, the core of the functionality I'm after, generating a multivalue choice list:

enter image description here

..is predicated upon the validation script working properly. Without the validation, I'm unable to get the values from the field to appear as a list. Is there anything I can remove out of this validation script to get the functionality I'm after, or is there a workaround? I'm unfamiliar with the validation process. Here is the code for the validation (I was going to post as a Code Sample, but this looks like it might be easier to follow): enter image description here

[Editor note: here is the actual validation code, the image is not correct]

import arcpy
class ToolValidator(object):
 """Class for validating a tool's parameter values and controlling
 the behavior of the tool's dialog."""
 def __init__(self):
 """Setup arcpy and the list of tool parameters."""
 self.params = arcpy.GetParameterInfo()
 def initializeParameters(self):
 """Refine the properties of a tool's parameters. This method is
 called when the tool is opened."""
 return
 def updateParameters(self):
 """Modify the values and properties of parameters before internal
 validation is performed. This method is called whenever a parmater
 has been changed."""
 if self.params[1].altered: #Set condition - if the input field value changes
 if self.params[1].value: #if the field parameter has a value
 for field in arcpy.Describe(self.params[0].value).fields: #iterate through fields in the input dataset
 if field.name.lower() == self.params[1].value.value.lower(): #find the field object with the same name as field parameter
 try:
 if self.params[2].values: #if this parameter has seleted values
 oldValues = self.params[2].values #set old values to the selected values
 except Exception:
 pass
 values = set() #create an empty set
 fieldname = self.params[1].value.value #set the value of variable fieldname equal to the input field value
 FrequencyTable = arcpy.Frequency_analysis (self.params[0].value, "in_memory\Frequency", self.params[1].value.value, "") #for large tables create a frequency table
 cursor = arcpy.SearchCursor(FrequencyTable, "", "", self.params[1].value.value, "{0} A".format(self.params[1].value.value)) #open a search cursor on the frequency table
 for row in cursor: #loop through each value
 values.add(row.getValue(fieldname)) #add the value to the set
 self.params[2].filter.list = sorted(values) #set the filter list equal to the sorted values
 newValues = self.params[2].filter.list
 try:
 if len(oldValues): # if some values are selected
 self.params[2].values = [v for v in oldValues if v in newValues] # check if seleted values in new list,
 # if yes, retain the seletion.
 except Exception:
 pass
 def updateMessages(self):
 """Modify the messages created by internal validation for each tool
 parameter. This method is called after internal validation."""
 return

Is it possible that my assumption (via testing) that the validation is the key piece is false, and that something else isn't allowing the values to be exposed as a selectable list? Many thanks in advance. Having this type of functionality will really jump start the adoption of several key workflows I'm trying to distribute in our company!

PolyGeo
65.5k29 gold badges115 silver badges349 bronze badges
asked Jan 9, 2014 at 17:11
8
  • 1
    What version of ArcGIS are you using? I ask because at 10.1 the arcpy.da.SearchCursor is much faster and more suitable for this task than the older arcpy.SearchCursor. Commented Jan 9, 2014 at 18:32
  • 1
    The validation code for the toolbox you linked is different from the validation code in the image you linked. The former requires an advanced license because it uses the Frequency tool. The latter, detailed in an earlier blog post, should not because it just uses standard arcpy functions like SearchCursor. I do not have an answer for you but if you piece the two together maybe you can figure it out. Commented Jan 9, 2014 at 18:36
  • @blah268 Its 10.2, sorry for missing that. Hmm, now that's a very interesting observation. I'll look at that, but I am curious: do I understand correctly that the validation is what passes the values as a choice list? the multi-choice being the functionality i'm after. I'll get back to you, and many thanks for the response! Commented Jan 9, 2014 at 18:45
  • 1
    The script tool parameter properties is where you set up the list of parameters and their properties (which includes a MultiValue property). The script tool validation is where this particular tool populates the multivalue parameter values based on other parameter values (feature class and field name). Playing around with it for larger feature classes, I wouldn't put this into production. Too slow, and also errors out if you don't have "Overwrite the outputs of geoprocessing operations" checked in the Geoprocessing options. Commented Jan 9, 2014 at 19:13
  • 1
    I am not able to chat but what I would suggest is editing your question to detail your requirements, what you have tried and what's not working. Commented Jan 9, 2014 at 19:43

3 Answers 3

10

I thought some people might find this valuable. ESRI was gracious enough to help work through this and find an alternate to the validation used in the blog post which does not require an Advanced license. While I certainly had to figure out some additional items, I cannot take credit for the validation code. But, the ends justify the means and this qualifies as the answer to my question. Here you go:

import arcpy
class ToolValidator(object):
 """Class for validating a tool's parameter values and controlling
 the behavior of the tool's dialog."""
 def __init__(self):
 """Setup arcpy and the list of tool parameters."""
 self.params = arcpy.GetParameterInfo()
 def initializeParameters(self):
 """Refine the properties of a tool's parameters. This method is
 called when the tool is opened."""
 return
 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].value and self.params[1].value:
 self.params[2].filter.list = sorted({row[0] for row in arcpy.da.SearchCursor(self.params[0].value, self.params[1].value.value) if row[0]})
 def updateMessages(self):
 """Modify the messages created by internal validation for each tool
 parameter. This method is called after internal validation."""
 return

Using the arcpy.da.SearchCursor returns values from the chosen field very fast considering the number of records its searching (at least in my data). I may start a new thread to see if anyone has any ideas on how to apply a filter to the validation based on a query. I hope this helps someone, but I'm glad we have an answer!

Midavalo
30k11 gold badges53 silver badges108 bronze badges
answered Jan 14, 2014 at 15:00
1

I did it in an other way: using database consist of fivel levels, without choosing the shapefile or fields just by selecting items from first level the validation script generates the values for the second level according to your choice in first level, her the script:

import arcpy
class ToolValidator(object):
 """Class for validating a tool's parameter values and controlling
 the behavior of the tool's dialog."""
 def __init__(self): 
 """Setup arcpy and the list of tool parameters.""" 
 self.params = arcpy.GetParameterInfo() 
 def initializeParameters(self): 
 """Refine the properties of a tool's parameters. This method is 
 called when the tool is opened.""" 
 return 
 def updateParameters(self):
 fc="C:/LUCS/System_shapes/sys.shp"
## fc = arcpy.MakeFeatureLayer_management(Lucssys) 
 """Modify the values and properties of parameters before internal 
 validation is performed. This method is called whenever a parmater 
 has been changed.""" 
## if self.params[0].value and self.params[0].value:
 fc="C:/LUCS/System_shapes/sys.shp" 
 col= ("L1_NAM") 
 self.params[0].filter.list = [str(val) for val in 
 sorted( 
 set( 
 row.getValue(col) 
 for row in arcpy.SearchCursor(fc, None, None,col)))] 
 if self.params[0].value not in self.params[0].filter.list: 
 self.params[0].value = self.params[0].filter.list[0]
 if self.params[0].value:
 fc="C:/LUCS/System_shapes/sys.shp" 
 col1= ("L1_NAM")
 col2= ("L2_NAM") 
 fields=(col1,col2)
##___________level2___________________________________________________________
 fc="C:/LUCS/System_shapes/sys.shp" 
 col1= ("L1_NAM")
 col2= ("L2_NAM") 
 fields=(col1,col2)
 Level0list=[]
 Level0list_uniq=[]
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col1)) ==(str(self.params[0].value)):
 Level0list.append (row.getValue(col2))
 for elem in Level0list:
 if elem not in Level0list_uniq:
 Level0list_uniq.append(elem)
 if self.params[1].value not in self.params[1].filter.list: 
 self.params[1].filter.list =Level0list_uniq
##________________level3______________________________________________________ 
 fc="C:/LUCS/System_shapes/sys.shp" 
 col2= ("L2_NAM")
 col3= ("L3_NAM") 
 fields=(col2,col3)
 Level2list=[]
 Level2list_uniq=[]
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col2)) ==(str(self.params[1].value)):
 Level2list.append (row.getValue(col3))
 for elem in Level2list:
 if elem not in Level2list_uniq:
 Level2list_uniq.append(elem)
 if self.params[2].value not in self.params[2].filter.list: 
 self.params[2].filter.list =Level2list_uniq
##________________level4______________________________________________________ 
 fc="C:/LUCS/System_shapes/sys.shp" 
 col3= ("L3_NAM")
 col4= ("L4_NAM") 
 fields=(col3,col4)
 Level3list=[]
 Level3list_uniq=[]
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col3)) ==(str(self.params[2].value)):
 Level3list.append (row.getValue(col4))
 for elem in Level3list:
 if elem not in Level3list_uniq:
 Level3list_uniq.append(elem)
 if self.params[3].value not in self.params[3].filter.list: 
 self.params[3].filter.list =Level3list_uniq
##________________level5______________________________________________________ 
 fc="C:/LUCS/System_shapes/sys.shp" 
 col4= ("L4_NAM")
 col5= ("L5_NAM") 
 fields=(col4,col5)
 Level4list=[]
 Level4list_uniq=[]
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col4)) ==(str(self.params[3].value)):
 Level4list.append (row.getValue(col5))
 for elem in Level4list:
 if elem not in Level4list_uniq:
 Level4list_uniq.append(elem)
 if self.params[4].value not in self.params[4].filter.list: 
 self.params[4].filter.list =Level4list_uniq
 def updateMessages(self): 
 """Modify the messages created by internal validation for each tool 
 parameter. This method is called after internal validation.""" 
0
Add new conditions to ensure a single option when the same term exists in more than one category. ِand to force arcpy to deal with arabic fonts
import arcpy
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
class ToolValidator(object):
 """Class for validating a tool's parameter values and controlling
 the behavior of the tool's dialog."""
 def __init__(self): 
 """Setup arcpy and the list of tool parameters.""" 
 self.params = arcpy.GetParameterInfo() 
 def updateParameters(self):
 fc="C:/LUCS/System_shapes/sys.shp"
 col= ("L1_NAM")
 ##________________level1_________________
 self.params[0].filter.list = [str(val) for val in 
 sorted( 
 set( 
 row.getValue(col) 
 for row in arcpy.SearchCursor(fc, None, None,col)))] 
 if self.params[0].value not in self.params[0].filter.list: 
 self.params[0].value = self.params[0].filter.list[0]
 if self.params[0].value:
 fc="C:/LUCS/System_shapes/sys.shp" 
 col1= ("L1_NAM")
 col2= ("L2_NAM")
 col3= ("L3_NAM") 
 col4= ("L4_NAM")
 col5= ("L5_NAM") 
 fields=(col1,col2,col3,col4,col5)
 Level1list=[]
 Level1list_uniq=[]
 Level2list=[]
 Level2list_uniq=[]
 Level3list=[]
 Level3list_uniq=[]
 Level4list=[]
 Level4list_uniq=[]
 Level5list=[]
 Level5list_uniq=[]
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col1)) ==(str(self.params[0].value)):
 Level1list.append (row.getValue(col2))
 for elem in Level1list:
 if elem not in Level1list_uniq:
 Level1list_uniq.append(elem)
 if self.params[1].value not in self.params[1].filter.list: 
 self.params[1].filter.list =Level1list_uniq
 ##________________level3_________________ 
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col1)) ==(str(self.params[0].value)):
 if (row.getValue(col2)) ==(str(self.params[1].value)):
 Level2list.append (row.getValue(col3))
 for elem in Level2list:
 if elem not in Level2list_uniq:
 Level2list_uniq.append(elem)
 if self.params[2].value not in self.params[2].filter.list: 
 self.params[2].filter.list =Level2list_uniq
 ##________________level4_______________ 
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col1)) ==(str(self.params[0].value)):
 if (row.getValue(col3)) ==(str(self.params[2].value)):
 Level3list.append (row.getValue(col4))
 for elem in Level3list:
 if elem not in Level3list_uniq:
 Level3list_uniq.append(elem)
 if self.params[3].value not in self.params[3].filter.list: 
 self.params[3].filter.list =Level3list_uniq
 ##________________level5_______________ 
 cursor = arcpy.SearchCursor(fc)
 for row in cursor:
 if (row.getValue(col1)) ==(str(self.params[0].value)):
 if (row.getValue(col4)) ==(str(self.params[3].value)):
 Level4list.append (row.getValue(col5))
 for elem in Level4list:
 if elem not in Level4list_uniq:
 Level4list_uniq.append(elem)
 if self.params[4].value not in self.params[4].filter.list: 
 self.params[4].filter.list =Level4list_uniq
 return
4
  • Please format you whole code properly. Commented Jul 22, 2019 at 19:28
  • it was done, its working in 10.5.0 Commented Jul 22, 2019 at 19:42
  • Part of you code is formated, but as you can see other lines are not (e.g. the import statements of your code). Use the { } button to properly format your code. Commented Jul 22, 2019 at 19:59
  • It also looks like you are missing the definition of a class. Commented Jul 22, 2019 at 20:10

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.