Hello,
I have a python toolbox, that has the following tools:
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
Solved! Go to Solution.
You could possibly put those functions into a base class, and then use class inheritance for the classes that would use them.
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.
Ya, that's kind of what I was thinking. Thanks. Still searching around a bit for a proper way of doing this 🙂
You could possibly put those functions into a base class, and then use class inheritance for the classes that would use them.
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.
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')
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