ArcGIS Pro Python toolbox

Dr. Huidae Cho
Institute for Environmental and Spatial Analysis...University of North Georgia

1   Custom toolbox vs. Python toolbox

Custom toolbox

  • Organized in three parts
  • Wizard for beginners
  • Source code in any editor
  • Parameters through the wizard
  • Validation code through the toolbox
  • Supports Python and model tools

Python toolbox

  • Single Python script with a .pyt extension
  • One class for the toolbox
  • One class for each tool
  • Source code in any editor
  • Additional capabilities
    • Value tables
    • Composite data types
    • Custom license checking

2   Hello World in custom toolbox

hello-world-in-custom-toolbox.png

param1 = arcpy.GetParameterAsText(0)
param2 = sys.argv[2]

arcpy.AddError(param1)
arcpy.AddWarning(param2)
arcpy.AddMessage("Hello World!")

2.1   Try Hello World!

From ArcGIS Pro

From the command line (cmd.exe) “after” closing ArcGIS Pro

"C:\Program Files\ArcGIS\Pro\bin\Python\Scripts\propy.bat" HelloWorld.py

3   Hello World in Python toolbox

HelloWorldPython.pyt

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 = 'HelloWorld Python Toolbox'
        self.alias = 'HelloWorldPythonToolbox'

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

class HelloWorldPython(object):
    def __init__(self):
        '''Define the tool (tool name is the name of the class).'''
        self.label = 'Hello World Python'
        self.description = 'This is Hello World Python!'
        self.canRunInBackground = False

    def getParameterInfo(self):
        '''Define parameter definitions'''
        param1 = arcpy.Parameter(
            displayName='Param 1',
            name='param1',
            datatype='GPString',
            parameterType='Required',
            direction='Input')

        param2 = arcpy.Parameter(
            displayName='Param 2',
            name='param2',
            datatype='GPString',
            parameterType='Required',
            direction='Input')

        params = [param1, param2]
        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.'''
        arcpy.AddError(parameters[0].valueAsText)
        arcpy.AddWarning(parameters[1].valueAsText)
        arcpy.AddMessage('Hello World Python!')
        print('Done!')
        return

3.1   Try Hello World Python!

From ArcGIS Pro

From the command line (cmd.exe) “after” closing ArcGIS Pro

doit.py

import arcpy

arcpy.ImportToolbox('HelloWorldPython.pyt')
arcpy.HelloWorldPython_HelloWorldPythonToolbox('Hello', 'World')

Run

"C:\Program Files\ArcGIS\Pro\bin\Python\Scripts\propy.bat" doit.py

3.2   Need two files for testing?

Let’s try this.

MyModule.py

class MyPrint:
    def __init__(self, name):
        self.name = name
    def print(self):
        print(self.name)

printme.py

from MyModule import MyPrint

myprint = MyPrint('print me')
myprint.print()

3.3   Combine MyModule.py and printme.py

Class and test code in one file

MyModule.py

class MyPrint:
    def __init__(self, name):
        self.name = name
    def print(self):
        print(self.name)
test = MyPrint('print myself')
test.print()

Good? Let’s use this module from printme.py.

from MyModule import MyPrint
myprint = MyPrint('print me')
myprint.print()

What happens?

3.4   Proper way of combining MyModule.py and printme.py

New MyModule.py

class MyPrint:
    def __init__(self, name):
        self.name = name
    def print(self):
        print(self.name)

if __name__ == '__main__':
    test = MyPrint('print myself')
    test.print()

Now, try the same printme.py

3.5   Can we use this pattern for Python toolboxes?

Try this HelloWorldPython.pyt.

# Python toolbox code above
if __name__ == '__main__':
    arcpy.ImportToolbox('HelloWorldPython.pyt')
    arcpy.HelloWorldPython_HelloWorldPythonToolbox('Hello', 'World')

Run it.

"C:\Program Files\ArcGIS\Pro\bin\Python\Scripts\propy.bat" HelloWorldPython.pyt

What happens?

3.6   Separation of concerns

Design principal in software development

Separate presentation and logic layers.

What can we use when __name__ is always __main__?

sys.executable

  • From ArcGIS Pro: C:\Program Files\ArcGIS\Pro\bin\ArcGISPro.exe
  • From the command line: C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe

Try this.

# Python toolbox code above
if not 'ArcGISPro.exe' in sys.executable:
    arcpy.ImportToolbox('HelloWorldPython.pyt')
    arcpy.HelloWorldPython_HelloWorldPythonToolbox('Hello', 'World')

What happens?

3.7   Proper way of testing Python toolboxes

Again, separation of concerns

We don’t want to emulate ArcGIS Pro just to pass the parameters array with the valueAsText attribute.

The execute method is ArcGIS Pro’s interface to your algorithm.

Separate out your algorithm from execute so you can invoke the algorithm without ArcGIS Pro.

Let’s try this.

# Python toolbox code here, but modify execute
    def execute(self, parameters, messages):
        '''The source code of the tool.'''
        self.main(parameters[0].valueAsText, parameters[1].valueAsText)
        return

    def main(self, param1, param2):
        arcpy.AddError(param1)
        arcpy.AddWarning(param2)
        arcpy.AddMessage('Hello World Python!..')
        print('Done!')
        return

if not 'ArcGISPro.exe' in sys.executable:
    hello = HelloWorldPython()
    hello.main('Hello', 'World')