Select to view content in your preferred language

PYT: Prevent function call until tool is initialized, not when toolbox is initialized?

186
1
Jump to solution
09-04-2024 03:07 PM
AlfredBaldenweck
MVP Regular Contributor

So, the title doesn't make a lot of sense, but what I have going on is:

I have a PYT containing several tools.

I've added a class variable to one of the tools that calls a function to create a dictionary.

class Toolbox:
    def __init__(self):
        self.label = ""
        self.alias = ""
        self.tools = [tool1, tool2]
class tool1:
    def createDict():
        retDict = {}
        return retDict
    retDict = createDict()

    def __init__(self):
        self.label = ""
        self.description = ""

    def getParameterInfo(self):
        return params

     def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        pass
        return

    def postExecute(self, parameters):
        return

 

The issue that I'm having is that the function (Lines 7-9) to create that class variable (Line 10) is called when I initialize the toolbox, not when I initialize that particular tool. This is making opening the toolbox take longer than it should, particularly given that I might want to use the other several tools and don't care about that dictionary getting created.

 

How can I make it so the function doesn't get called until I open that tool? I'm cool with waiting however long I need to; I just don't want to wait until I need to.

I'd be cool with like, adding it as a derived parameter in getParameterInfo(), but there isn't an option in Parameter data types to just use a python dictionary.

Any ideas? Thanks!

1 Solution

Accepted Solutions
AlfredBaldenweck
MVP Regular Contributor

Okay, I figured out two solutions here.

Solution 1 (unpreferred): Create a parameter and load the dictionary into it as a string (not preferred). This is kind of clunky and you end up having to import json for it to work. Also, this is a specific solution for a specific datatype, so not great.

 

Spoiler
class Toolbox:
    def __init__(self):
        self.label = ""
        self.alias = ""
        self.tools = [tool1, tool2]
class tool1:
    def createDict():
        retDict = {}
        return retDict

    def __init__(self):
        self.label = ""
        self.description = ""

    def getParameterInfo(self):
        param0 = arcpy.Parameter(displayName="Dictionary",
                                 name="retDict",
                                 datatype="GPString",
                                 parameterType="Required",
                                 # multivalue= True,
                                 direction="Input")
        param0.value = str(tool1.createDict()).replace("'",'"')
        params = [param0]
        return params

     def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        import json
        retDict = loads(parameters[0].value)
        return

    def postExecute(self, parameters):
        return

Solution 2 (preferred): Create a class variable, but just don't run it until you initialize the tool. That is, populate it in getParameterInfo().

This seems a lot cleaner -- no changing between data types, no extra modules, etc.

Spoiler
class Toolbox:
    def __init__(self):
        self.label = ""
        self.alias = ""
        self.tools = [tool1, tool2]
class tool1:
    def createDict():
        retDict = {}
        return retDict
    # I set the variable to initialize empty.
    # This is in part so I don't forget that it's there.
    retDict = {}

    def __init__(self):
        self.label = ""
        self.description = ""

    def getParameterInfo(self):
        param0 = ...
        # I used setattr() because doing self.retDict 
        # didn't take the change.
        setattr(tool1, "retDict", tool1.createDict())
        return params

     def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        pass
        return

    def postExecute(self, parameters):
        return

Hopefully this helps someone down the line.

 

View solution in original post

1 Reply
AlfredBaldenweck
MVP Regular Contributor

Okay, I figured out two solutions here.

Solution 1 (unpreferred): Create a parameter and load the dictionary into it as a string (not preferred). This is kind of clunky and you end up having to import json for it to work. Also, this is a specific solution for a specific datatype, so not great.

 

Spoiler
class Toolbox:
    def __init__(self):
        self.label = ""
        self.alias = ""
        self.tools = [tool1, tool2]
class tool1:
    def createDict():
        retDict = {}
        return retDict

    def __init__(self):
        self.label = ""
        self.description = ""

    def getParameterInfo(self):
        param0 = arcpy.Parameter(displayName="Dictionary",
                                 name="retDict",
                                 datatype="GPString",
                                 parameterType="Required",
                                 # multivalue= True,
                                 direction="Input")
        param0.value = str(tool1.createDict()).replace("'",'"')
        params = [param0]
        return params

     def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        import json
        retDict = loads(parameters[0].value)
        return

    def postExecute(self, parameters):
        return

Solution 2 (preferred): Create a class variable, but just don't run it until you initialize the tool. That is, populate it in getParameterInfo().

This seems a lot cleaner -- no changing between data types, no extra modules, etc.

Spoiler
class Toolbox:
    def __init__(self):
        self.label = ""
        self.alias = ""
        self.tools = [tool1, tool2]
class tool1:
    def createDict():
        retDict = {}
        return retDict
    # I set the variable to initialize empty.
    # This is in part so I don't forget that it's there.
    retDict = {}

    def __init__(self):
        self.label = ""
        self.description = ""

    def getParameterInfo(self):
        param0 = ...
        # I used setattr() because doing self.retDict 
        # didn't take the change.
        setattr(tool1, "retDict", tool1.createDict())
        return params

     def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        pass
        return

    def postExecute(self, parameters):
        return

Hopefully this helps someone down the line.