Hi-
I'm trying to set the values of one control (Multi-value, data type: string <via checkboxes>) to another parameter control (Multi-value, data type: "any value" which produces a drop down with grid for items entered or selected with the add, delete, and re-ordering (up/down) buttons).
The goal is to use the reorder buttons on the 2nd control (of the auto-populated items) to reorganize the list by moving rows up and down, then taking the values out (as text type is fine) and processing them further.
The issue is that I can get the items to populate into the 2nd control (via the ToolValidator class, updateParameters method) but I can't change their order. If I use the text entry box to populate the grid, it will allow me to change the order of the items.
Here's the updated code snip:
Here's what the parameter page looks like:
Even though the Up/Down arrows are enabled, they don't actually re-order the items. Am I using the wrong Data Type or is there some other storage location I should manually be moving the items from the "Code(s) to move" to get them into the SequenceTheMoveCodes parameter control?
Please help!
Thanks!
Tad Hammer
Solved! Go to Solution.
You have a puzzler here. I've always had problems using global variables in the ToolValidator class. In addition, the .altered property doesn't always work as expected (at least for me). The trick seems to be: don't refresh a parameter if a proceeding one hasn't really changed. I think that is the basic problem you're having, and your globals weren't remembering any previous values correctly. Thus, every updateParameters check found a change, and the tool interface seemed as if it was locked up. It was actually being constantly refreshed to its previous state.
For my testing, I created a simple script tool with two input variables. The first used a multi-value string input from a value list filter "A;B;C;D" which gave me the check boxes. I used the multi-value "Any value" input to match your "SequenceTheMoveCodes" section. I did not use any globals in the ToolValidator. The operation seemed a bit quirky, but I could alter the order of the items, and delete some. This section would immediately refresh to new values when I deleted all values. Here's the code section from the ToolValidator:
def updateParameters(self):
if self.params[0].altered: # a checkbox has changed
if self.params[0].values: # something is checked
if self.params[1].values: # the select section has values
for v in self.params[0].values: # add new values
if v not in self.params[1].values:
self.params[1].values.append(v)
else: # populate select section with checkbox items
self.params[1].values = self.params[0].values
return
As I mentioned, the operation seemed a bit quirky. I am also wondering if there is any value in adding the list of checkboxes when items can be deleted and reordered in the "sequence" section if it is working correctly. So I tested the following code. The first parameter was set to "workspace" so I could select a geodatabase. The second parameter was a "string" which could be populated with a dropdown list of domain names. The final parameter was the multi-value "any value" option to match your "sequence" section. These parameters seem to be the most important of what you are working with. Again, I did not use any global variables.
def updateParameters(self):
if self.params[0].altered: # geodatabase parameter has changed
if self.params[0].value: # it has a value
# do checks on params[0] if required - confirm it is geodatabase
# get domain information
domains = arcpy.da.ListDomains(self.params[0].value)
# populate parameter with coded value domain names
self.params[1].filter.list = sorted([d.name for d in domains if d.domainType == "CodedValue"])
if self.params[1].value not in self.params[1].filter.list: # set domain selection if needed
self.params[1].value = self.params[1].filter.list[0]
if self.params[1].altered: # selected domain has changed
if self.params[1].value: # it has a value
domains = arcpy.da.ListDomains(self.params[0].value)
for d in domains: # get the coded values
if d.name == self.params[1].value:
coded_values = d.codedValues
dVals = [val for val, desc in coded_values.items()]
if not self.params[2].values: # the select list has no values in it
self.params[2].values = dVals # insert new values
else: # check the current values
for v in self.params[2].values:
if v not in dVals: # if not in dVals, then domain selection has changed
self.params[2].values = dVals # reset choices
break
return
Basically, if a geodatabase and a domain name have been selected, the sequence section of the tool is populated with the codes from the first coded-value domain found. When a domain code is highlighted, the "x" button will remove it from the list. The up and down arrows should move the code in the list. If the domain name is changed, the script checks to see that all values in the sequence area are in the codes used by the newly selected domain. If a value exists that is not in the domain codes, it is assumed the domain name was changed, and the sequence section is refreshed. As noted in the code, I do not check that the workspace is an actual database. Also there is no error processing if the selected database does not have coded value domains.
Here's what my test tool interface looks like
Hope this helps.
Hi Luke-
Thanks for getting back with me. I've updated the code snippet to include almost all the of ToolValidator (except the Messages part that I haven't added anything to). I am selecting a row before I try to use the buttons (the row turns blue). Then when I hit an arrow button, say I hit "Up" the item doesn't move up, but a grey bar (like a selected row) moves upward. It's like it doesn't take the data value along with it.
I just noticed something else that I think might be connected. I can either make manual entries to the SeqTheMoveCodes (once all have been cleared) or have them populated by the UpdateParameters code, but not both. For instance, I can't add values manually through the + button to the list once they are have pre-populated values in them.
Thanks!
BTW: I'm using ArcMap 10.6 against MS SQL Server
Could you describe the properties/settings you are using for the "SequenceTheMoveCodes" control?
Hi Randy-
SequenceTheMoveCodes is Required, Input, Multi-Value, "Any Value" data type-- that data type allowed me to have the re-order grid as well as a data entry box-- even though I don't need the drop down.
Thanks,
Tad
You have a puzzler here. I've always had problems using global variables in the ToolValidator class. In addition, the .altered property doesn't always work as expected (at least for me). The trick seems to be: don't refresh a parameter if a proceeding one hasn't really changed. I think that is the basic problem you're having, and your globals weren't remembering any previous values correctly. Thus, every updateParameters check found a change, and the tool interface seemed as if it was locked up. It was actually being constantly refreshed to its previous state.
For my testing, I created a simple script tool with two input variables. The first used a multi-value string input from a value list filter "A;B;C;D" which gave me the check boxes. I used the multi-value "Any value" input to match your "SequenceTheMoveCodes" section. I did not use any globals in the ToolValidator. The operation seemed a bit quirky, but I could alter the order of the items, and delete some. This section would immediately refresh to new values when I deleted all values. Here's the code section from the ToolValidator:
def updateParameters(self):
if self.params[0].altered: # a checkbox has changed
if self.params[0].values: # something is checked
if self.params[1].values: # the select section has values
for v in self.params[0].values: # add new values
if v not in self.params[1].values:
self.params[1].values.append(v)
else: # populate select section with checkbox items
self.params[1].values = self.params[0].values
return
As I mentioned, the operation seemed a bit quirky. I am also wondering if there is any value in adding the list of checkboxes when items can be deleted and reordered in the "sequence" section if it is working correctly. So I tested the following code. The first parameter was set to "workspace" so I could select a geodatabase. The second parameter was a "string" which could be populated with a dropdown list of domain names. The final parameter was the multi-value "any value" option to match your "sequence" section. These parameters seem to be the most important of what you are working with. Again, I did not use any global variables.
def updateParameters(self):
if self.params[0].altered: # geodatabase parameter has changed
if self.params[0].value: # it has a value
# do checks on params[0] if required - confirm it is geodatabase
# get domain information
domains = arcpy.da.ListDomains(self.params[0].value)
# populate parameter with coded value domain names
self.params[1].filter.list = sorted([d.name for d in domains if d.domainType == "CodedValue"])
if self.params[1].value not in self.params[1].filter.list: # set domain selection if needed
self.params[1].value = self.params[1].filter.list[0]
if self.params[1].altered: # selected domain has changed
if self.params[1].value: # it has a value
domains = arcpy.da.ListDomains(self.params[0].value)
for d in domains: # get the coded values
if d.name == self.params[1].value:
coded_values = d.codedValues
dVals = [val for val, desc in coded_values.items()]
if not self.params[2].values: # the select list has no values in it
self.params[2].values = dVals # insert new values
else: # check the current values
for v in self.params[2].values:
if v not in dVals: # if not in dVals, then domain selection has changed
self.params[2].values = dVals # reset choices
break
return
Basically, if a geodatabase and a domain name have been selected, the sequence section of the tool is populated with the codes from the first coded-value domain found. When a domain code is highlighted, the "x" button will remove it from the list. The up and down arrows should move the code in the list. If the domain name is changed, the script checks to see that all values in the sequence area are in the codes used by the newly selected domain. If a value exists that is not in the domain codes, it is assumed the domain name was changed, and the sequence section is refreshed. As noted in the code, I do not check that the workspace is an actual database. Also there is no error processing if the selected database does not have coded value domains.
Here's what my test tool interface looks like
Hope this helps.
Thanks Randy, I appreciate it!!!
Randy-
Do you know if I were to write a Python toolbox, could I take closer "control" of the checkbox control? Meaning, could I run validation code every time a checkbox was checked or unchecked? Right now, it's only validated after another control gains "focus". I didn't see where that was possible, but was wondering if you knew.
Thanks,
Tad
Although I haven't worked with check boxes in a Python toolbox, I would suspect that you would have a little bit more control. I do know that global variables work a little better in the Python toolbox provided they are created in getParameterInfo. See: Python toolbox tool objects do not remember instance variables between function calls? and Using Global Variables in Python Toolbox? From the discussion, I noticed the use of the pythonaddins.MessageBox that can be very useful in debugging a script tool or Python toolbox.
import arcpy
import pythonaddins
class ToolValidator(object):
....
def updateParameters(self):
# MessageBox( message, boxtitle)
pythonaddins.MessageBox('Altered:\n{}'.format(self.params[0].altered),'self.params[0]')
pythonaddins.MessageBox('Value:\n{}'.format(self.params[0].value),'self.params[0]')
By using the pythonaddins MessageBox I discovered that the .altered and .value property are very similar. The .altered appears to be a Boolean indicating if the parameter has a value (the parameter has been altered from being None to having some value). Once a parameter has a value, it will always test as being .altered = True. If you delete the parameter value, the it will test as false. I have not found a way to set .altered to false after a parameter has been checked/validated.
Once you are done with debug/testing, you would want to remove the pythonaddins as the MessageBox is extremely annoying for normal operation of a tool.