Listbox patterns or how do you string dependent inpu parameters in updateParametes()?

398
1
02-28-2013 05:07 AM
bradeckrose
New Contributor
GPers,

Having given up on ModelBuilder and Script Tools due to repeated Arc Desktop crashes, I have arrived down at the bare-metal:   the promising Python Toolbox.

I'm trying to write a tool that allows the user choose the workspace, then select from a specific set of feature classes, then automatically fill a dropdown of filtered subtypes for the selected feature.

This immediately presents the following problems:
What datatype to set for the parameters?  Should I make them all string instead of Workspace, feature class, etc
Is there an enumeration for the arcpy parameter datatypes so I don't have to type the bloody things and spell them correctly?
How to detect the appropriate change-events?
Should I set parameterDependencies() for any of them?
What is the actual datatype of arcpy.Parameter.value?  a describe object?  a string? type (arcpy.Parameter.value) merely returns "property"
Is there an arcpy.mapping dialog box that can display a table control?
For coded picklists, like subtypes, what is the strategy for picking the value, yet returning the code so it can be used in a SQL expression?   The best I've seen is a list expansion with a value matching criteria.   Can this be handled if the argument were defined as a Field Info instead?

Thus far, this is all I've been able to muster with no success:

import arcpy


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

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


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

    def getParameterInfo(self):
        """Define parameter definitions"""
        params = None
        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."""
        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."""
        return

class FeaturesBySubtype(AbstractTool):
    def __init__(self):
        self.label = "Query Features By Subtype"
        self.description = "Return all the features of the specified subtype for the specified feature class"
        self.canRunInBackground = True

    def getParameterInfo(self):
        p1 = arcpy.Parameter(
            displayName="Workspace",
            name="in_workspace",
            datatype="Workspace",
            parameterType="Required",
            direction="Input")

        p2 = arcpy.Parameter(
            displayName="Feature Class",
            name="in_featureclass",
            datatype="Feature Class",
            parameterType="Required",
            direction="Input")

        p3 = arcpy.Parameter(
            displayName="Subtype",
            name="in_subtype",
            datatype="string",
            parameterType="Required",
            direction="Input")

        p4 = arcpy.Parameter(
            displayName="Output Table",
            name="out_resulttable",
            datatype="Table View",
            parameterType="Required",
            direction="Output")
        
        params = [p1,p2,p3,p4]
        return params
    

    def updateParameters(self, parameters):
        if parameters[0].value:
            arcpy.env.workspace=parameters[0].value
            
        for a_parm in parameters:
            if a_parm.altered and not(a_parm.hasBeenValidated):
               a_parm.setWarningMessage(str.format('Parameter modified {0}', a_parm.name)  )
        parameters[1].filter.list=['Fuse','Switch']
        st = arcpy.da.ListSubtypes('Fuse')
        parameters[2].filter.type='ValueList'
        parameters[2].filter.list = st.values()
        return
    
    
    def execute(self, parameters, messages):
        arcpy.AddMessage(parameters)
        
        tableList = [parameters[1].value]
        
        fc_descr = arcpy.Describe ( parameters[1].value)
        subtype_field = fc_descr.subtypeFieldName
        
        whereClause = str.format("{0}={1}",subtype_field,parameters[2].value)
        lyrName = "QueryOutput"
        
        arcpy.AddMessage(whereClause)
        # Make Query Table...
        arcpy.MakeQueryTable_management(tableList, lyrName, None, None, None, whereClause)
     
        # Print the total rows
        arcpy.AddMessage( arcpy.GetCount_management(lyrName))
     

        return    
0 Kudos
1 Reply
curtvprice
MVP Esteemed Contributor
Having given up on ModelBuilder and Script Tools due to repeated Arc Desktop crashes, I have arrived down at the bare-metal: the promising Python Toolbox.


Hate to tell you Brad, but I've found .pyt to be pretty unstable compared to script toolboxes until you have enough experience to get the syntax exactly right. Also, if you were doing things with script tool validation etc that was crashing ArcMap, the same validation code will crash ArcMap just as effectively.  As far as I can tell, the tbx is simply a slightly different implementation of the same framework used in the pyt file. You do have a little more control with value table parameters than the tbx property sheets support.  (Maybe an Esri person can chime in, as I haven't gotten into that yet.) 

What datatype to set for the parameters? Should I make them all string instead of Workspace, feature class, etc
Is there an enumeration for the arcpy parameter datatypes so I don't have to type the bloody things and spell them correctly?
How to detect the appropriate change-events?


This is the same as it is with the validation code for tbx script tools. For datatypes that have string representation like Field, you can generate picklists as strings and then apply them to a filter. ".value" is the arcpy data type you have set it to be (that may or may not have an easy string representation you can use. Depends on the data type).
Arc 10.1 Help:

'>Understanding validation in script tools

Customizing tool behavior in a Python toolbox

So far I haven't been convinced to abandon the .tbx format in my work because 1) the tbx can store tool documentation inside the file and a .pyt doc hangs out in parallel .xml files, 2) I have 10.0 users to support, and 3) I am one of those lazy people  that likes it when someone does my work for me with property sheet interfaces so I don't have to write the code myself. (OK, I'll come clean, the folks that implemented the tbx properties are far better Python programmers than me.)

Honestly if you're getting started with Python toolboxes, I feel the best approach to start with is to make a good old fashioned tbx script tool with validation set up for you from the script tool's property sheets (and maybe a little of your own in the Validation tab), then convert to a pyt using this conversion tool.

Once you've done this you'll have auto-generated code to start with so you can get familiar with how it works with your particular application.
0 Kudos