Python toolbox output parameter

6419
10
04-22-2014 09:19 AM
WayneBoras
New Contributor III
Hello,

I'm using ArcGIS 10.1 and having trouble trying to set and get an output parameter in the execute() method of a Python toolbox. To illustrate, I've created a simple Python Toolbox which takes two input parameters (a and b) and adds them together to get a single output parameter (total). The code is below.

Using arcpy.SetParameterAsText(2, total) to set the output parameter works fine. Logically, after setting the value, I should be able to fetch it using arcpy.GetParameterAsText(2). However, this call always returns None. I instead have to use parameters[2].valueAsText to return the value. There is an inconsistency in the usage of SetParameterAsText and GetParameterAsText.

What is the "proper" way to set and get an output parameter in a Python toolbox? The link below explains to use valueAsText to get the values but says nothing about setting parameters.

http://resources.arcgis.com/en/help/main/10.1/index.html#//001500000037000000

Thanks,
Wayne


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 = [Tool]


class Tool(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"""
        a = arcpy.Parameter(
            displayName = 'a',
            name = 'a',
            datatype = 'GPLong',
            parameterType = 'Required',
            direction = 'Input'            
        )

        b = arcpy.Parameter(
            displayName = 'b',
            name = 'b',
            datatype = 'GPLong',
            parameterType = 'Required',
            direction = 'Input'            
        )        

        total = arcpy.Parameter(
            displayName = 'Total',
            name = 'total',
            datatype = 'GPLong',
            parameterType = 'Derived',
            direction = 'Output'            
        )        

        # Set defaults
        a.value = 100
        b.value = 200
        
        params = [a, b, total]
        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."""
        # Print all parameters before any changes are made
        ndx = 0
        for p in parameters:
            arcpy.AddMessage('parameters[%d].valueAsText: %s'% (ndx, p.valueAsText))
            arcpy.AddMessage('arcpy.GetParameterAsText(%d): %s' % (ndx, arcpy.GetParameterAsText(ndx)))
            ndx += 1

        arcpy.AddMessage('=' * 50)
        
        # Set the output parameter
        total = int(parameters[0].valueAsText) + int(parameters[1].valueAsText)
        arcpy.SetParameterAsText(2, total)
        
        # Print all parameters again
        ndx = 0
        for p in parameters:
            arcpy.AddMessage('parameters[%d].valueAsText: %s'% (ndx, p.valueAsText))
            arcpy.AddMessage('arcpy.GetParameterAsText(%d): %s' % (ndx, arcpy.GetParameterAsText(ndx)))
            ndx += 1
            
        return    
Tags (2)
0 Kudos
10 Replies
benberman
Occasional Contributor
Is there a particular reason that you are using the python toolbox vs. using the esri template interface?
0 Kudos
WayneBoras
New Contributor III
Is there a particular reason that you are using the python toolbox vs. using the esri template interface?


No reason other than the fact that I was a Python developer before I was a GIS developer. The Python toolbox seemed more "Pythonic" to me, with all of the functionality encapsulated in a Python class (including parameter definitions, isLicensed(), etc.).

I have since rewritten it as a script tool and it works fine. The Python script tool should also work.

Wayne
KevinBell
Occasional Contributor III

I'd love to see how your fixed code looks! 

WayneBoras
New Contributor III

Hi Kevin,

It's as simple as this:

import arcpy

# Fetch input values
a = arcpy.GetParameter(0)
b = arcpy.GetParameter(1)

# Perform calculation
total = a + b

# Return the result
arcpy.SetParameter(2, total)

And the parameters for the accompanying script tool look like this:

ScriptToolA.JPGScriptToolB.JPGScriptToolC.JPG

KevinBell
Occasional Contributor III

When I do that I see my output correctly in the results window in arcmap, but after publishing as a gp service I'm not sure how to get the results in from the rest directory.  I get an error FAILED from the submit job screen on the rest directory.

WayneBoras
New Contributor III

Try turning on debug messages in the GP service and running again:

  1. In ArcMap, add a connection to your ArcGIS server if you don't already have one.
  2. Right-click the service and select Service Properties.
  3. On the Parameters tab, under Properties, set Message Level to Error.
  4. Click OK.
  5. Reload the REST endpoint in your web browser and submit it again. You should see more details about the error on the web page.
KevinBell
Occasional Contributor III

FAILED...  I just want to let my web developer get back 'Hello world' when calling this

Job Messages:

  • esriJobMessageTypeInformative: Submitted.
  • esriJobMessageTypeInformative: Executing...
  • esriJobMessageTypeError: ERROR 000816: The tool is not valid.
  • esriJobMessageTypeError: Failed.

Here's the code.  Not much happening here.

import arcpy
import os

class Toolbox(object):
    def __init__(self):
        self.label = "Get Nearby Addresses"
        self.alias = ""

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

class GetNearbyAddresses(object):
    def __init__(self):
        self.label = "Get Nearby Addresses"
        self.description = "Get Nearby Addresses within a buffer."
        self.canRunInBackground = False

    def getParameterInfo(self):
       
        params = []
       
        param0 = arcpy.Parameter(
            displayName = "Latitude",
            name = "lat",
            datatype = "Double",
            parameterType = "Required",
            direction = "Input")
        params.append(param0)
       
        param1 = arcpy.Parameter(
                    displayName = "Longitude",
                    name = "lon",
                    datatype = "Double",
                    parameterType = "Required",
                    direction = "Input")
        params.append(param1)
       
        param2 = arcpy.Parameter(
            displayName = "Buffer Distance",
            name = "distance",
            datatype = "Long",
            parameterType = "Required",
            direction = "Input")
        params.append(param2)
       
        param3 = arcpy.Parameter(
                    displayName = "Address List",
                    name = "addresses",
                    datatype = "GPString",
                    parameterType = "Derived",
                    direction = "Output")
        params.append(param3)       
        return params


    def execute(self, parameters, messages):
        try:
            #arcpy.env.workspace = r'E:\gis\ims\20141028_getAddressesWithinBuffer\default.gdb'
            #arcpy.env.overwriteOutput = True
           
            lat = parameters[0].valueAsText
            lon = parameters[1].valueAsText
            distance = parameters[2].valueAsText
           
            #inpoint = arcpy.Point(X=lon, Y=lat)
            #ptGeometry = arcpy.PointGeometry(inpoint, arcpy.SpatialReference(4326))

            #arcpy.Buffer_analysis(ptGeometry, 'buffer', distance + ' FEET', \
                                  #"FULL", "ROUND", "NONE", "")
            #print 'made the buffer.'
           
            #get intersecting addresses
           
      
            #parameters[3].value = 'laksdjflaskdjflksajdf'
            arcpy.SetParameter(3,'Hello World!')
            return
       

        except Exception, ErrorDesc:
            sErr = "ERROR:\n" + str(ErrorDesc)
            messages.addErrorMessage(sErr)
        return
0 Kudos
WayneBoras
New Contributor III

Your code works when I publish it to my server. You should be seeing more detail in the error message. There is probably a Python exception being thrown somewhere (not just esriJobMessageTypeError) . Can you confirm that you set Message Level to Error?

KevinBell
Occasional Contributor III

Thanks for testing. I'm failing on a 10.1 server, but my 10.2 server succeeds!

0 Kudos