Easy question on Parameters

948
7
10-06-2010 07:49 AM
JoshV
by
Occasional Contributor
Hello All-

I have a python script that I have imported into my toolbox.  I need three Parameters for this script and I found it easiest to use ArcToolbox.  by right clicking the script, I go to properties and then to Parameters.  I added a Parameter called Input_Data of Type "Feature Layer".  I added a second Parameter of Type "Field" and set it to be Obtained from Input_Data so that it will show all the Attributes of that Layer.  My third Parameter is the question I have.. I want it to show all the Values of whichever Attribute is Selected but when I add the Parameter of Type "Field" and select Obtained from, it only shows Input_Data as an option and not the second Parameter.  How can I do this?  I created a picture but unfortunately the ESRI site isn't allowing any attachments at the moment.
0 Kudos
7 Replies
ChrisSnyder
Regular Contributor III
You could do this in a rough way using the "SQL Expression" parameter type (make sure it is set to "obtained from" the Input_Data parameter).

Another fancier way is to build up some "validation" code. Here's an examples of what a clever coworker of mine came up with to included a toolbox pick list that gets populated with the folder names in a certain directory. I haven't messed with it yet (that's for next week, so I can't really answer many questions about it), but seems like it could be easily altered to give you the unique field values of your selected features using a searchcursor applied to the input featurelayer.


import os

class ToolValidator:
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  basePath = r"\\snarf\am\div_lm\ds\for_inv\data"

  def __init__(self):
    """Setup the Geoprocessor and the list of tool parameters."""
    import arcgisscripting as ARC
    self.GP            = ARC.create(9.3)
    self.params        = self.GP.getparameterinfo()
    self.fripnameParam = self.params[0]
    self.fristypeParam = self.params[1]
    return

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""
    self.fristypeParam.Value = self.fristypeParam.Filter.List[0]
    self.updateFripnamePickList()
    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."""
    self.updateFripnamePickList()
    return

  def updateMessages(self):
    """Modify the messages created by internal validation for each tool
    parameter.  This method is called after internal validation."""
    fripname = self.fripnameParam.Value
    fristype = self.fristypeParam.Value
    
    if fripname and fristype:
      if not os.path.isdir(os.path.join(self.basePath, fristype, fripname)):
        self.fripnameParam.SetErrorMessage(fripname + " does not exist")
    return

  def updateFripnamePickList(self):
    pickList = []
    for d in self.listSubdirectories(os.path.join(self.basePath, self.fristypeParam.Value)):
      pickList.append(d)
    self.fripnameParam.Filter.List = pickList
    return

  def listSubdirectories(self, directory):
    subdirectories = []
    for f in os.listdir(directory):
      if os.path.isdir(os.path.join(directory, f)):
        subdirectories.append(f)
    return subdirectories  
    
    # The thing in square brackets (next line) is a "list comprehension".
    #return [f for f in os.listdir(directory) if os.path.isdir(os.path.join(directory, f))]
0 Kudos
AndyAnderson
Occasional Contributor II
Thanks for your note, it helped me set a value for an output field based on an input field. One thing that I think might not work as written, though, is this expression:

os.path.join(self.basePath, fristype, fripname)

which equates to this statement:

os.path.join(self.basePath, self.fristypeParam.Value, self.fripnameParam.Value)

I was using something similar:

os.path.split(self.params[0].Value)

to extract an input directory and kept getting errors; turns out that Value is an object with a property also named Value, so the correct code is

os.path.split(self.params[0].Value.Value)

This problem in your code might not have been triggered or gone unnoticed because it's updating an error message.

A similar statement follows:

os.path.join(self.basePath, self.fristypeParam.Value)

But this code worked as written it was because self.fristypeParam.Value was retyped in an earlier statement:

self.fristypeParam.Value = self.fristypeParam.Filter.List[0]

I've written up a complete description of this issue here:

http://forums.arcgis.com/threads/15616-Value.Value?p=48310

-- Andy
0 Kudos
ChrisSnyder
Regular Contributor III
Good catch... This was stolen code, so I wasn't completely knowledgeable of it, but I see what you are pointing out. Thanks for the issue description http://forums.arcgis.com/threads/156....Value?p=48310. Is it just me, or wasn't it easier when everything was a string and you could always assume that was the case? I guess it's cooler and more flexible that you can pass objects, but...
0 Kudos
AndyAnderson
Occasional Contributor II
Is it just me, or wasn't it easier when everything was a string and you could always assume that was the case? I guess it's cooler and more flexible that you can pass objects, but...


More specifically, what's the point of having an object with no methods and exactly one property, a string?

-- Andy
0 Kudos
AndyAnderson
Occasional Contributor II
Just something else to be aware of�?� this Value object is only created by the GeoProcessor when a string is added to an input field. Otherwise it's undefined, and if you're setting it yourself at that point, you can assign it as a simple string and it still knows how to use it, e.g.

if self.params[0].Value :
[INDENT]�?�do something with Value.Value�?�[/INDENT]
else:
[INDENT]self.params[0].Value = �?�some string expression�?�[/INDENT]
0 Kudos
AndyAnderson
Occasional Contributor II
I wrote:
Value is an object with a property also named Value, so the correct code is

[INDENT]os.path.split(self.params[0].Value.Value)[/INDENT]


Turns out that another option would be:

[INDENT]os.path.split(str(self.params[0].Value))[/INDENT]

as this object knows how to convert itself to a string �?? if it knows a string is expected, as in

[INDENT]self.params[0].Value + "sometext"[/INDENT]
0 Kudos
MaryM
by
Occasional Contributor
I wanted to do this same thing for having a list of MXDs show in a drop down list on a tool.  I understand the easy way to add a List of Values manually.  However, I want one to be auto-generated in case an MXD is added in a certain folder.  I tried copying this a bit but I do not understand it so much.  Do you need to add anything in the main code of the tool to signify this information in the Validation code?  Where is it that it assigns the list generated to the filter list?
import arcpy
import os

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()
    self.mapTemplateParam = self.params[0]
    self.type1Param = self.params[1]
    return

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""
    self.mapTemplateParam.Value = self.mapTemplateParam.filter.list[0]
    self.getMapTemlateList();
    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."""
    self.getMapTemplateList()
    return

  def getMapTemplateList(self):
    folderPath=r"folderPathHere"#Folder of Map Templates
    mapTemplates = []
    for filename in os.listdir(folderPath):
        fullpath = os.path.join(folderPath, filename)
        if os.path.isfile(fullpath):
            basename, extension = os.path.splitext(fullpath)
            if extension.lower() == ".mxd":
                mapTemplates.append(fullpath)
    self.mapTemplateParam.filter.list = mapTemplates
    return


I get error line 4, line 18 and a IndexError: list index out of range?
0 Kudos