Passing Field Names from a MultiValue Parameter

1482
8
05-03-2021 12:28 PM
EvanMyers1
Occasional Contributor

I am creating a custom tool that involves 3 steps.  First, allow the user to pick one or more feature classes from a geodatabase.  Next, from those chosen features, generate a list of fields that are from those selected feature classes.  Lastly, export each selected feature class to another database, and if the field was selected, do not include that field (if it exists of course).  It all kinda works like this code:

import arcpy
import os

fc = arcpy.GetParameterAsText(0).split(";") if arcpy.GetParameterAsText(0).find(";") > -1 else [arcpy.GetParameterAsText(0)]
fields = arcpy.GetParameterAsText(1).split(";") if arcpy.GetParameterAsText(1).find(";") > -1 else [arcpy.GetParameterAsText(1)]
output_database = arcpy.GetParameterAsText(2)


arcpy.AddMessage(fc)
# From the selected features, create a list
feature_list = []
for each in fc:
    # arcpy.CopyFeatures_management(each, "{}_Layer".format(each))
    arcpy.AddMessage(each)
    arcpy.MakeFeatureLayer_management(each, "{0}_lyr".format(os.path.splitext(os.path.basename(each))[0]))
    feature_list.append("{0}_lyr".format(os.path.splitext(os.path.basename(each))[0]))

# From the selected features list, create a list with their field names
fieldList = []
for each in feature_list:
    fieldnames = [f.name for f in arcpy.ListFields(each)]
    for fields in fieldnames:
        if fields not in fieldList:
            fieldList.append(fields)

print(fieldList)

# Using fieldList, exclude these fields from Field_mapping
# ........... 

So far, I have lines of code that accomplish the first two steps, but I am unsure how to incorporate this into a custom script.  I am assuming I need to use the tool validation?

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:
        lyr = self.params[0].valueAsText.split(";")
        fieldList = [f.name for f in arcpy.ListFields(lyr)]
        self.params[1].value=fieldList
    return

When I run this with the tool I get an IOError: *file path* does not exist.  I get this error a lot and I have no idea what causes it.

I don't know how I did it before but I managed to pass the fields from multiple feature classes, but the tool wouldn't work if I only selected one feature class.  I have since attempted to fix that issue but I got the above error message.  In my efforts to fix this error I have basically had to start over.

Anyway, my question is, how can I pass field names from the MultiValue Feature Class parameter to my next MultiValue Field parameter (which will also be used for another step)?

0 Kudos
8 Replies
BlakeTerhune
MVP Regular Contributor

Is your multivalue input for fields a boolean value list (checkboxes) or something else?

0 Kudos
EvanMyers1
Occasional Contributor

I managed to get the tool to stop the file path error but now it only lists the fields from first feature class (wControlValve).  When I remove that one, it lists the fields from the wHydrants.

Right now my tool looks like this:

EvanMyers1_0-1620072805747.png

0 Kudos
BlakeTerhune
MVP Regular Contributor

All the Python Toolboxes I've seen have updateParameters defined like

def updateParameters(self, parameters):

 So you should have a parameters argument that you can use instead of self.params, but that's just more of a curiosity.

self.params[0].values should return a list of values instead of splitting the delimited string. And I don't see where you're actually iterating over the list of feature classes. Try something like

if self.params[0].values:
    lyrs = self.params[0].values
    fieldList = []
    for lyr in self.params[0].values:
        fieldList.append([f.name for f in arcpy.ListFields(lyr)])
    self.params[1].values = sorted(fieldList)

 However, I've never worked with multivalue inputs so I could be way off.

0 Kudos
EvanMyers1
Occasional Contributor

I tried your suggestion and it did not work unfortunately.  I did a hybrid but now I get a message saying "ListFields(*gp_fixargs(args, True))) IOError: "C" does not exist"

if self.params[0].values:
    lyrs = self.params[0].valueAsText.split(";")[0]
    fieldList = []
    for lyr in lyrs:
        fieldList.append([f.name for f in arcpy.ListFields(lyr)])
    self.params[1].values = sorted(fieldList)

 

0 Kudos
BlakeTerhune
MVP Regular Contributor

For debugging, try only having this in updateParameters() just to see what you're working with

self.params[0].setWarningMessage(self.params[0].values)

 

0 Kudos
EvanMyers1
Occasional Contributor

Sorry for the late reply, but I tried your suggestion and nothing seemed to happen when I added this.  When I open the tool and add Feature Classes for my first parameter, nothing shows up in the fields list, but no errors either.

0 Kudos
BlakeTerhune
MVP Regular Contributor

It would show up in the yellow warning icon next to the input. You could also print it out with arcpy.AddMessage() but you have to comment out enough code to get it to run so you can see the message in the geoprocessing results.

0 Kudos
EvanMyers1
Occasional Contributor

I got my code working so far with no errors, and my field list updates properly when I add or remove feature classes, but now I have a new problem.

When I press the "Select All" or "Unselect All" buttons, they don't to do anything, and if I click anywhere (even in another application), all of the boxes are re-check themselves.

I suspect it is because of the updateParameters section of the tool Validator, but I can't wrap my head around the logic the tool is using.  No matter what my order of indentation is in the code it always wants to refresh the list.

Here is my code so far:

  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:
      fclist = self.params[0].valueAsText.split(";") if self.params[0].valueAsText.find(";") > -1 else [
        self.params[0].valueAsText]
      if ";" in fclist:
        fclist.split(";")
      elif "'" in fclist:
        [f.strip("'") for f in fclist]
      else:
        [f.strip("'") for f in fclist]
      if self.params[0].altered:
        fieldlist = []
        for each in fclist:
          fieldnames = [f.name for f in arcpy.ListFields(each)]
          for fields in fieldnames:
            if fields not in fieldlist:
              fieldlist.append(fields)
        self.params[1].value = fieldlist
    return

 

0 Kudos