Select to view content in your preferred language

Struggling with python toolbox tools and parameter validation. updateParameters and updateMessages don't "work"

153
2
Friday
Glasnoct
New Contributor III

I'm currently acquainting myself with the parameter and parameter validation methods of the tool class and I can't quite figure out what's required to get error/warning messages to properly populate and how updateParameters and updateMessages differ for setErrorMessage/setWarningMessage. Either I get no message, the message doesn't update on value change, or its the wrong one. What is the correct code to make sure that a parameter is properly validated after it changes and then displays the proper message (if any)?

For a simple example, I just want to validate that a given parameter is in a list of strings:

 

project_ids = ['123', '456', '789']

class tool:
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = 'test tool'
        self.description = "test"

    def getParameterInfo(self) -> list:
        """Define the tool parameters."""
        project_id = arcpy.Parameter(name="project ID",
                                     displayName="project ID",
                                     datatype="GPString",
                                     parameterType="Required",  # Required|Optional|Derived
                                     direction="Input",  # Input|Output
                                     )
        return [project_id]


    def isLicensed(self):
        """Set whether the 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."""
        if parameters[0].altered:
            if parameters[0].value is None:
                parameters[0].setErrorMessage("No Project Specified")
            elif parameters[0].valueAsText not in project_ids:
                parameters[0].setErrorMessage('Unrecognized project ID')
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        # The only time I got messages to show up was when I put the code block from updateParameters here
        return

    @staticmethod
    def execute(self, parameters, messages=None):
        """The source code of the tool."""

        project_id = parameters[0]
        test_function(project_id)
        return

    def postExecute(self, parameters):
        """This method takes place after outputs are processed and
        added to the display."""
        return

 

0 Kudos
2 Replies
BlakeTerhune
MVP Regular Contributor

This is what I do and it seems to work. Maybe you're just missing clearMessage()

 

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."""
    project_id = parameters[0].valueAsText
    valid_project_ids = ['123', '456', '789']
    error_msg = f'Unrecognized project ID; please enter one of the following: {valid_project_ids}'
    if project_id:
        if project_id not in valid_project_ids:
            project_id.setErrorMessage(error_msg)
        else:
            project_id.clearMessage()
    else:
        project_id.setErrorMessage(error_msg)
    return

 

0 Kudos
AlfredBaldenweck
MVP Regular Contributor

TLDR:

  1. Use updateMessages()
  2. Change your parameter in getParameterInfo() from Required to Optional if you want to set an error message by default.
    1. You can either just set the error message OR
    2. You can also set the Optional parameter to be conditionally required if you're worried about it running without that parameter.

 

 

Longer message with how I got to that conclusion:

 

Spoiler

So, I think your biggest problem in this specific case is that you're using updateParameters() for messaging instead of updateMessages().

Spoiler
project_ids = ['123', '456', '789']
    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        if parameters[0].value:
            if parameters[0].valueAsText not in project_ids:
                parameters[0].setErrorMessage('Unrecognized project ID')
            else:
                parameters[0].setWarningMessage(parameters[0].valueAsText)
        # I can't get a default message to show up
        #else: 
        #    parameters[0].setErrorMessage("No Project Specified")
        return

The other issue, and one I couldn't initially solve to my satisfaction, is that you're trying to set a default message to show before anything happens.

I played around a bit, and it seemed to only be a problem if you use setErrorMessage(); setWarningMessage() works just fine as a default. However, setWarningMessage() will override the required bit of the parameter, allowing you to run the tool without giving it a value.

Spoiler
project_ids = ['123', '456', '789']
    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        if parameters[0].value:
            if parameters[0].valueAsText not in project_ids:
                parameters[0].setErrorMessage('Unrecognized project ID')
            # else:
                # parameters[0].setWarningMessage(parameters[0].valueAsText)
        else: 
            parameters[0].setWarningMessage("No Project Specified")
        return

So, I got to thinking and experimenting, and I think the true issue is actually not in updateParameters() or updateMessages(), but in getParameterInfo().

If you set your parameter to "Optional", rather than "Required", the issue of setting an error message disappears. 

Spoiler
project_ids = ['123', '456', '789']
    def getParameterInfo(self) -> list:
        """Define the tool parameters."""
        project_id = arcpy.Parameter(name="projectID",
                                     displayName="project ID",
                                     datatype="GPString",
                                     parameterType="Optional",  
                                     direction="Input",  
                                     )
                                     
        params = [project_id]
        return params    
    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        if parameters[0].value:
            if parameters[0].valueAsText not in project_ids:
                parameters[0].setErrorMessage('Unrecognized project ID')
            # else:
                # parameters[0].setWarningMessage(parameters[0].valueAsText)
        else: 
            # Both of these worked for me. 
            # The former is to conditionally set an optional parameter 
            # to "required", the latter just sets an error message. 
            # One or both is fine.
            ##parameters[0].setIDMessage('ERROR', 735)
            parameters[0].setErrorMessage("No Project Specified")
        return

 

 

0 Kudos