Select to view content in your preferred language

Python Toolbox, selected value from dropdown box "disappears" after selection

2898
4
09-17-2020 01:57 PM
JCann
by
Emerging Contributor

I have not found anything on this issue, and it is not easy to search for. I have a Python Toolbox that has multiple, user-selected feature class inputs with each feature class input having 3 or 6 dropdowns for values. The tool is set up so the feature class inputs are optional and each value input is optional, basically, the user can choose to include a feature class and include 1 to 6 values from that feature class. This setup was as per the requester, so I am trying to maintain it as close as possible.  As the title says, I am having values "disappear" after selecting the value; I click on the value, it shows in the box, and then resets to zero or none, depending on the box. This is behavior is occurring on features with 6 values; with values 1 and 2 working properly and 3-6 showing then disappearing after selection. I am not seeing this behavior on 3 value features, all three dropdowns work as expected. I have the dropdown values hardcoded in the toolbox and have copied a portion of the script into a new toolbox and tested auto-populating dropdown values from within def updateParameters(self, parameters). Dropdowns populate correctly but exhibit the same behavior as the hardcoded ones. I have attached two short screen captures. Geocapture is from the complete toolbox showing a three value feature with no issues, and a 6 value feature with "disappearing" selections. Geocapture2 is from the test-toolbox and auto-populates the values based on the feature and exhibits the same behavior. Please note, the screen capture videos did not capture the dropdown boxes popping up. The block of code starting with "params13" is from the complete toolbox. I did not post the entire script as it is around a thousand lines long. The second block of code is from the test-toolbox using auto-populate. I think this is a recent occurrence, possibly after 2.6 came, out because this was written and tested back in May and June and was working and has cropped up in the final testing and editing. Any ideas on where to go from here or possible fixes would be most appreciated. I am guessing it is something simple and I am just missing it.

params13 = arcpy.Parameter(
            displayName="Vegetation Feature Class",
            name="inVegfeature",
            datatype="DEFeatureClass",
            parameterType="Optional",
            direction="Input",)
        params13.value = None

        # First Input
        params14 = arcpy.Parameter(
            displayName="First Vegeation Class",
            name="veg1",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input")
        params14.value = 0
        params14.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]
        # Second Input
        params15 = arcpy.Parameter(
            displayName="Second Vegetation Class",
            name="veg2",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input",)
        params15.value = 0
        params15.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]
        # Third Input
        params16 = arcpy.Parameter(
            displayName="Third Vegeation Class",
            name="veg3",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input",)
        params16.value = 0
        params16.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]
        # Fourth Input
        params17 = arcpy.Parameter(
            displayName="Fourth Vegeation Class",
            name="veg3",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input",)
        params17.value = 0
        params17.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]
        # Fifth Input
        params18 = arcpy.Parameter(
            displayName="Fifth Vegeation Class",
            name="veg3",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input",)
        params18.value = 0
        params18.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]
        # Sixth Input
        params19 = arcpy.Parameter(
            displayName="Sixth Vegeation Class",
            name="veg3",
            datatype="GPDouble",
            parameterType="Optional",
            direction="Input",)
        params19.value = 0
        params19.filter.list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                                14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
                                38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
                                62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                                74, 75, 76]                                                                        
# -*- coding: utf-8 -*-

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

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


class Tool(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Tool"
        self.description = ""
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        # Geology Class Feature Class Parameters
        params0 = arcpy.Parameter(
            displayName="Geology Feature Class",
            name="inGeofeature",
            datatype="DEFeatureClass",
            parameterType="Optional",
            direction="Input",)
        params0.value = None
        # First Input
        params1 = arcpy.Parameter(
            displayName="First Geology Class",
            name="geo1",
            datatype="GPString",
            parameterType="Optional",
            direction="Input")
        params1.value = None
        
        # Second Input
        params2 = arcpy.Parameter(
            displayName="Second Geology Class",
            name="geo2",
            datatype="GPString",
            parameterType="Optional",
            direction="Input",)
        params2.value = None

        # Third Input
        params3 = arcpy.Parameter(
            displayName="Third Geology Class",
            name="geo3",
            datatype="GPString",
            parameterType="Optional",
            direction="Input",)
        params3.value = None
        
        # Fourth Input
        params4 = arcpy.Parameter(
            displayName="Fourth Geology Class",
            name="geo3",
            datatype="GPString",
            parameterType="Optional",
            direction="Input",)
        params4.value = None

        # Fifth Input
        params5 = arcpy.Parameter(
            displayName="Fifth Geology Class",
            name="geo3",
            datatype="GPString",
            parameterType="Optional",
            direction="Input",)
        params5.value = None
        
        # Sixth Input
        params6 = arcpy.Parameter(
            displayName="Sixth Geology Class",
            name="geo3",
            datatype="GPString",
            parameterType="Optional",
            direction="Input",)
        params6.value = None
        
        params = [params0, params1, params2, params3, params4, params5, params6]
        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."""
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[1].filter.list = sorted(list(set([row[0] for row in rows])))
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[2].filter.list = sorted(list(set([row[0] for row in rows])))
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[3].filter.list = sorted(list(set([row[0] for row in rows])))
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[4].filter.list = sorted(list(set([row[0] for row in rows])))
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[5].filter.list = sorted(list(set([row[0] for row in rows])))
        if parameters[0].value:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                parameters[6].filter.list = sorted(list(set([row[0] for row in rows])))
        
        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."""
        return
4 Replies
RandyBurton
MVP Alum

The first issue is lines 64, 73 and 82 of the second script which sets the various parameter's name to the same value, "geo3", which is the name for params3 in line 55.  This will cause parameters 3 through 6 to have the same value; when any one of these gets changed, the others in the group will also change.  You have a similar problem in your first script as the name "veg3" is also used multiple times.

Additional thoughts:

The updateParameters section is a bit awkward, but in a quick test it does work.  You could combine these six parameters into one that is "multiple values".  This may also apply to vegetation class as well.

Regarding the vegetation class, all the numbers appear to be integers, but you are using a type of float (double).  This could be an issue.  You may also wish to use a filter type of "Range":

param0.filter.type = "Range"
param0.filter.list = [0, 76]‍‍
JCann
by
Emerging Contributor

Not sure where the same values came from, I just flat missed them. This project has been moving between computers and drives and reworked multiple times, thank you for catching that. Everything is working now that the parameter names have been changed. I also changed the double to long where it applied, I had missed in the documentation that long worked for integer values. I agree with the awkward part, the number of optional parameters that also have to be dropdowns has made this a challenge, especially for this being my first, large python toolbox project (two tools with 38 parameters each). Thanks, again for the assistance.

0 Kudos
williamwinner
Occasional Contributor

I know this has been answered, but you have a lot of unnecessary duplication.  The Update Parameters routine could be re-written as:

    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 Not parameters[0].value is None:
            with arcpy.da.SearchCursor(parameters[0].valueAsText, "GLG_SYM") as rows:
                geoList = sorted(set([row[0] for row in rows]))

            for i in range(1,7):
                parameters[i].filter.list = geoList
        
        return‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

First, there's no need to run the if 6 different times.

Second, there's no need to open a search cursor 6 different times.

Third, the sorted function returns a list and accepts an iterable variable.  A set and a list are both iterable so sorted will return a sorted list given a set.

Finally, while if parameters[0].value: does work, it's not very readable code.  Changing it to what is above very quickly tells someone else what you're really testing for.

As an aside, the range(1,7) will cycle through 1-6 and is just one of many reasons why I hate python...

JCann
by
Emerging Contributor

William, thank you for that post, I am new to python and I am the only one where I work that uses python, or any language for that matter, so haven’t really been able to get feedback and suggestions beyond what I can find on the web and books. I did not know that I could use a range to populate parameters filters, this is incredibly helpful going forward. 

0 Kudos