2

In my python toolbox, I am trying to populate a table with the fields of two different feature classes.

Here the GUI.

enter image description here

I am trying to list the fields of Baseline Feature Class to the 1st column... 1 field per row.

In the 2nd column, I am trying to populate a dropdown list with the all the field values of the Updated Feature Class.

I've marked up the image below by adding the appearance of a dropdown list in the 2nd column.

The purpose here of the table is to allow the user to associate the fields from the updated feature class to the fields of the baseline feature class since the fields names might differ.

Is it possible to put selection lists in tables and if so, how?

My code so far:

class Toolbox(object):
    def __init__(self):
        """lorem etc"""
        self.label = "Toolbox"
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [SecuredAreas]


class SecuredAreas(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Secured Areas"
        self.description = """lorem etc"""
        self.canRunInBackground = True

    def getParameterInfo(self):
        """In the getParameterInfo method, tool parameters are defined by
        creating Parameter objects and setting their properties."""

        default_data = {}
        try:
            with open(parameter_values_filename) as data_file:
                default_data = json.load(data_file)
        except:
            pass

        # First parameter
        baseline_name = "sa_baseline_feature_class"
        sa_baseline_feature_class = arcpy.Parameter(
            displayName="SA Baseline Feature Class",
            name=baseline_name,
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Input"
        )
        attr_name = "sa_baseline_feature_class_default"
        if attr_name in default_data:
            sa_baseline_feature_class.value = default_data[attr_name]

        # Second parameter
        new_feature_class_name = "new_feature_class"
        new_feature_class = arcpy.Parameter(
            displayName="Updated Feature Class",
            name=new_feature_class_name,
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Input"
        )
        attr_name = "new_feature_class_default"
        if attr_name in default_data:
            new_feature_class.value = default_data[attr_name]

        # Third parameter
        output_workspace = arcpy.Parameter(
            displayName="Set Output Workspace",
            name="output_workspace",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input"
        )
        attr_name = "output_workspace_default"
        if attr_name in default_data:
            output_workspace.value = default_data[attr_name]

        # Fourth parameter
        temporary_workspace = arcpy.Parameter(
            displayName="Temporary Workspace",
            name="temporary_workspace",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input"
        )
        attr_name = "temporary_workspace_default"
        if attr_name in default_data:
            temporary_workspace.value = default_data[attr_name]

        # Fifth parameter
        # "Set up entirely new additions feature class"
        name_for_new_features = arcpy.Parameter(
            displayName="Name for New Addition Features",
            name="name_for_new_features",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Output"
        )
        attr_name = "name_for_new_features_default"
        if attr_name in default_data:
            name_for_new_features.value = default_data[attr_name]

        # Sixth parameter
        name_for_overlap_features = arcpy.Parameter(
            displayName="Name for Overlap Features",
            name="name_for_overlap_features",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Output"
        )
        attr_name = "name_for_overlap_features_default"
        if attr_name in default_data:
            name_for_overlap_features.value = default_data[attr_name]

        # Secenth parameter
        name_for_no_change_features = arcpy.Parameter(
            displayName="Name for No Change Features",
            name="name_for_no_change_features",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Output"
        )
        attr_name = "name_for_no_change_features_default"
        if attr_name in default_data:
            name_for_no_change_features.value = default_data[attr_name]

        # Eigth parameter
        name_for_attr_change_features = arcpy.Parameter(
            displayName="Name for Attribute Changes Features",
            name="name_for_attr_change_features",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Output"
        )
        attr_name = "name_for_attr_change_features_default"
        if attr_name in default_data:
            name_for_attr_change_features.value = default_data[attr_name]

        # Ninth paramter
        search_fields = arcpy.Parameter(
            displayName="Search Fields",
            name="search_fields",
            datatype="Value Table",
            parameterType="Required",
            multiValue=True,
            direction="Input"
        )

        search_fields.parameterDependencies = [sa_baseline_feature_class.name]
        search_fields.columns = [
                ['Field', 'Baseline Field'],
                ['Field', 'Updated Feature Set - Available Fields']
            ]

        params = [sa_baseline_feature_class, new_feature_class,
                  output_workspace, temporary_workspace,
                  name_for_new_features, name_for_overlap_features,
                  name_for_no_change_features, name_for_attr_change_features,
                  search_fields]

        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""

        sa_baseline_feature_class = parameters[0]
        new_feature_class = parameters[1]
        output_workspace = parameters[2]
        temporary_workspace = parameters[3]
        name_for_new_features = parameters[4]
        name_for_overlap_features = parameters[5]
        name_for_no_change_features = parameters[6]
        name_for_attr_change_features = parameters[7]
        search_fields = parameters[8]

        defaults = {
            'sa_baseline_feature_class_default': sa_baseline_feature_class.valueAsText,
            'new_feature_class_default': new_feature_class.valueAsText,
            'output_workspace_default': output_workspace.valueAsText,
            'temporary_workspace_default': temporary_workspace.valueAsText,
            'name_for_new_features_default': name_for_new_features.valueAsText,
            'name_for_overlap_features_default': name_for_overlap_features.valueAsText,
            'name_for_no_change_features_default': name_for_no_change_features.valueAsText,
            'name_for_attr_change_features_default': name_for_attr_change_features.valueAsText,
        }
        with open(parameter_values_filename, 'w') as outfile:
            json.dump(
                    defaults,
                    outfile,
                    indent=4,
                    sort_keys=True,
                    separators=(',', ':')
                    )

        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""

        sa_baseline_feature_class = parameters[0].valueAsText
        new_feature_class = parameters[1].valueAsText
        output_workspace = parameters[2].valueAsText
        temporary_workspace = parameters[3].valueAsText
        name_for_new_features = parameters[4].valueAsText
        name_for_overlap_features = parameters[5].valueAsText
        name_for_no_change_features = parameters[6].valueAsText
        name_for_attr_change_features = parameters[7].valueAsText
        search_fields = parameters[8]

        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        return
2
  • 2
    Try creating a new tool (for testing) that has only your 2 fields and the table so you're only dealing with what's required in this question, and can simplify the code snippet.
    – Midavalo
    Sep 29, 2016 at 19:42
  • @Midavalo, yes, should have done that. Thanks for bring it up.
    – Eric C
    Sep 30, 2016 at 17:12

1 Answer 1

1

Doing what you want to do is relatively straightforward but there are a few tricky bits. Firstly let me summarise how this process should be formed:

  1. Once you load the tool, it initiates itself and calls getParameterInfo. Since we do not know yet which feature classes to be defined for the first and second parameters, the Search Fields should be just defined and empty (I made this Disabled for the first instance as well to avoid confusing the user). So the code below is just sufficient (beware a few changes!).

    # Ninth paramter
    search_fields = arcpy.Parameter(
        displayName = "Search Fields",
        name = "search_fields",
        datatype = 'GPValueTable',
        parameterType = "Required",
        enabled = False,
        direction ="Input"
    )        
    
    search_fields.parameterDependencies = [sa_baseline_feature_class.name]
    search_fields.columns = [
            ['Field', 'Baseline Field'],
            ['String', 'Updated Feature Set - Available Fields']
        ]
    search_fields.filters[1].type = 'ValueList'
    
  2. After the user enters two feature classes, we need to populate Search Fields which should be done via updateParameters. However there is an issue here, we want to populate the fields of the first feature class (updating the values) only once and let user to find corresponding fields in the second parameter. Therefore, we need a control variable, which I created a class variable, initiated. This variable should be placed just after class declaration:

    class SecuredAreas(object):
        initiated = False
    
  3. After this, the code below should go into updateParameters:

    if parameters[0].altered:
        parameters[8].enabled = True
    
    if not SecuredAreas.initiated and parameters[1].altered:
        vt = [f.name for f in arcpy.ListFields(parameters[0].value)]
        vt2 = [f.name for f in arcpy.ListFields(parameters[1].value)]
        parameters[8].values = [[a,None] for a in iter(vt)]            
        parameters[8].filters[1].list = vt2
        SecuredAreas.initiated = True
    
  4. However, there is still an issue, even though we update the values, value-list, etc. it will not take effect until another parameter get focus, altered and lost focus. This is a persistent issue as mentioned in various places as how to force toolbox to refresh, for example, without any solution yet.

3
  • Thank you for the comment fatih_dur. There is an issue though. Where you write search_fields.filters[1].type = 'ValueList'. When tries to execute, I get an exception: 'Parameter' object has no attribute 'filters' . Do you know why this might be? Also, what ArcMap version are you using?
    – Eric C
    Sep 30, 2016 at 14:58
  • what is your version? This one works in 10.3.1
    – fatih_dur
    Oct 1, 2016 at 12:15
  • I have 10.2.2. Ok, gonna try to download 10.3.1. Thx.
    – Eric C
    Oct 4, 2016 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.