Python Toolbox Question

937
6
Jump to solution
02-22-2021 07:34 AM
ChrisHolmes
Occasional Contributor III

Hello,

I have a python toolbox, that has the following tools:

ChrisHolmes_0-1614007521632.png

Some of these scripts use the same procedures, so I moved those procedures to the top of the file above all of the individual classes. Which seems to make sense, I don't have exact copies of the same procedure inside each class, fewer lines of code, smaller file...

The top of the file looks like this (this is not all the procedures that are at the top just the first few):

 

 

 

import arcpy
import os
import re
import shutil
import configparser
import subprocess
from time import gmtime, strftime


def OpenAprx(aprx):
    #open the passed in mxd file in a new arcmap instance
    arcpy.AddMessage('Opening: {}'.format(aprx))
    subprocess.Popen([r'C:\Program Files\ArcGIS\Pro\bin\ArcGISPro.exe',aprx])

def CreateImage(prj, mapName, lyrsOn, lyrsOff, lyrExtent, lytName, mapScale, pngFilePath):
    arcpy.AddMessage('CreateImage start {}'.format(strftime("%H:%M:%S", gmtime())))
    arcpy.AddMessage('prj: {}'.format(prj))
    SetLayers(prj, mapName, lyrsOn, 'on')
    arcpy.AddMessage('SetLayers ON completed {}'.format(strftime("%H:%M:%S", gmtime())))
    arcpy.AddMessage('len lyrsOff: {}'.format(len(lyrsOff)))
    if len(lyrsOff) > 0:
        SetLayers(prj, mapName, lyrsOff, 'off')
        arcpy.AddMessage('SetLayers OFF completed {}'.format(strftime("%H:%M:%S", gmtime())))
    ExportImage(prj, mapName, lyrExtent, lytName, mapScale, pngFilePath)
    arcpy.AddMessage('ExportImage completed {}'.format(strftime("%H:%M:%S", gmtime())))

def SetLayers(prj, mapName, lyrs, status):
    arcpy.AddMessage('SetLayers prj = {}'.format(prj))
    aprx = arcpy.mp.ArcGISProject(prj)
    m = aprx.listMaps(mapName)[0]
    
    for lyr in m.listLayers():
        if lyr.name.upper() in lyrs:
            if status == 'on':
                lyr.visible = True
            else:
                lyr.visible = False
    #aprx.save()
    del aprx

 

 

 

 

This all seems great to me but the problem is the code executes way slower than when the procedures are all copied in the individual classes. Why is this? 

Thanks

0 Kudos
1 Solution

Accepted Solutions
JeffK
by MVP Regular Contributor
MVP Regular Contributor

You could possibly put those functions into a base class, and then use class inheritance for the classes that would use them.

 

View solution in original post

6 Replies
JeffK
by MVP Regular Contributor
MVP Regular Contributor

I won't pretend that I know how everything gets complied to interpretation but I would think you are essentially creating global functions that have to be compiled/indexed separately than being contained to that a class as a method, so there is some overhead with having to reach out to that function to perform the process.

ChrisHolmes
Occasional Contributor III

Ya, that's kind of what I was thinking. Thanks. Still searching around a bit for a proper way of doing this :)

JeffK
by MVP Regular Contributor
MVP Regular Contributor

You could possibly put those functions into a base class, and then use class inheritance for the classes that would use them.

 

ChrisHolmes
Occasional Contributor III

I think you're on the right track. Here's what I came up with, which is working and is executing in an acceptable amount of time. The actual .pyt file is quite long so I set up an 'example.pyt' which shows how the code

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

        # List of tool classes associated with this toolbox
        self.tools = [SomeTool, AnotherTool,]
        
    def Procedure1(self, parameter1, parameter2):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        
    def Procedure2(self, parameter1):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        self.Procedure3('something')
        
    def Procedure3(self, parameter1):
        #This is a procedure used by Procedure2
        #code goes here...

class SomeTool(object):
    
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Some 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."""
        def Procedure4(inParameter):
            #This procedure is just for SomeTool
            
            #code goes here...
            
        aVariable = Procedure4('blah')
        
        sT = Toolbox()
        sT.Procedure1(aVariable,'something else')
        sT.Procedure2('something else')
            
        del sT
        
        return

class AnotherTool(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Another 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):
        aT = Toolbox()
        aT.Procedure2('something else')
            
        del aT
        
        return

flows.

 

0 Kudos
JohannesLindner
MVP Regular Contributor

I think Jeff meant something like this:

 

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 = ""
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [SomeTool, AnotherTool,]
        


class BaseTool(object):
    # This class contains all methods that are used by multiple tools in the toolbox.

    def Procedure1(self, parameter1, parameter2):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        
    def Procedure2(self, parameter1):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        self.Procedure3('something')
        
    def Procedure3(self, parameter1):
        #This is a procedure used by Procedure2
        #code goes here...


class SomeTool(BaseTool):  # this class inherits every method from BaseTool
    
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Some 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."""
        
        # BaseTool contains Procedure1 and Procedure2, and SomeTool inherits them, so you can just call them with self.Procedure1
        aVariable = self.Procedure4('blah')
        self.Procedure1(aVariable,'something else')
        self.Procedure2('something else')


    def Procedure4(inParameter):
        #This procedure is just for SomeTool
        #code goes here...



class AnotherTool(BaseTool):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Another 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):
        self.Procedure2('something else')

 


Have a great day!
Johannes
ChrisHolmes
Occasional Contributor III

Thanks Johannes. I've adjusted my example file. It's cleaner, executes about the same as before. But makes better sense. Cheers

# -*- coding: utf-8 -*-

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

        # List of tool classes associated with this toolbox
        self.tools = [SomeTool, AnotherTool]
        
class BaseTool(object):
    import arcpy
    
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = ""
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [SomeTool, AnotherTool]
        
    def Procedure1(self, parameter1, parameter2):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        arcpy.AddMessage('hi from Procedure1. parameter1: {}; parameter2: {}'.format(parameter1,parameter2))
        
    def Procedure2(self, parameter1):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        self.Procedure3(parameter1)
        
    def Procedure3(self, parameter1):
        #This is a procedure used by multiple tools in the pyt file.
        #code goes here...
        arcpy.AddMessage('message from Procedure2: {}'.format(parameter1))
    

class SomeTool(BaseTool):
    
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Some 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."""
        def Procedure4(inParameter):
            #This procedure is just for SomeTool
            return('phrase passed into Procedure4: {}'.format(inParameter))
            #code goes here...
            
        aVariable = Procedure4('blah')
        
        self.Procedure1(aVariable,'something else')
        self.Procedure2('hi there')
        
        return

class AnotherTool(BaseTool):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Another 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):
       
        self.Procedure2('whatever')
        
        return
0 Kudos