11

I created a Python Toolbox tool to reorder fields and create a new feature class with the reordered fields. The tool works nicely and I am able to use a value table to let the user arrange the fields in the order they choose or they can fill in a rank value for each field. However, the annoying part of this tool is that all fields need to be added to the value table one at a time before reordering.

I am trying to set this up to bring in all the fields to the value table by default and any unwanted fields can be removed before reordering them. Has anyone had success doing something like this before? I am trying to achieve this in the UpdateParameters method. Here is the code I am trying:

import arcpy
import os

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Reorder Fields"
        self.alias = "Reorder Fields"

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


class ReorderFields(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Reorder Fields"
        self.description = ""
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        fc = arcpy.Parameter(displayName='Features',
            name='features',
            datatype='Feature Layer',
            parameterType='Required',
            direction='Input')

        vt = arcpy.Parameter(
            displayName='Fields',
            name='Fields',
            datatype='Value Table',
            parameterType='Required',
            direction='Input')

        output = arcpy.Parameter(
            displayName='Output Features',
            name='output_features',
            datatype='Feature Class',
            parameterType='Required',
            direction='Output')

        vt.columns = [['Field', 'Fields'], ['Long', 'Ranks']]
        vt.parameterDependencies = [fc.name]    
        params = [fc, vt, output]
        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."""
        if parameters[0].value:
            if not parameters[1].altered:
                fields = [f for f in arcpy.Describe(str(parameters[0].value)).fields
                          if f.type not in ('OID', 'Geometry')]
                vtab = arcpy.ValueTable(2)
                for field in fields:
                    vtab.addRow("{0} {1}".format(field.name, ''))
                parameters[1].value = vtab
        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."""
        fc = parameters[0].valueAsText
        vt = parameters[1].valueAsText
        output = parameters[2].valueAsText

enter image description here

I want to bring all the fields as shown in the value table above by default. I also tried using the parameters[1].value to add rows the the specific value table from the GUI, but that gave me errors. I'm using ArcGIS 10.2.2.

2
  • 1
    I haven't done anything like that before, so just throwing an idea out there and it could be very unreasonable, but can you store all the fields from all the layers in your init and then when a user selects a layer, it uses that data structure to populate the fields? as I said I've never worked with this before and just trying to put in my 2 insignificant cents
    – dassouki
    Commented Dec 3, 2014 at 21:37
  • Thanks for the suggestion. I tried that in the getParameterInfo() method with both adding rows to the vt param object itself and creating a new value table, adding the rows and setting the vt.value to the new value table with still no luck. I don't think I can use this in the ReorderFields instantiation because the fields are dependent on an input feature class. Maybe I can make a value table object in the init and try setting the vt.value to the self.valueTable once the rows have been populated.
    – crmackey
    Commented Dec 3, 2014 at 22:00

1 Answer 1

8
+50

Change the updateParameters as follows:

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."""
    if parameters[0].value:
        if not parameters[1].altered:
            fields = [f for f in arcpy.Describe(str(parameters[0].value)).fields
                      if f.type not in ('OID', 'Geometry')]
            vtab = []
            for field in fields:
                vtab.append([field.name,None])
            parameters[1].values = vtab
    return

Your mistake here is to try changing an already initiated parameter instead of its values due to using wrong property. Please see 'values' property of Parameter (arcpy).

1
  • 1
    Beautiful! I can't believe I missed that...It seems so obvious. Thank you very much!
    – crmackey
    Commented Dec 5, 2014 at 15:17

Your Answer

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

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