Multiselect dropdown in Vision

FYI I found another little bug that prevented toggling the "table header" off while in table mode.
In the "setDropdown" custom method, line 178.

The existing code:

	# Check to see if table mode is selected, and if so, display the table grid
	if multiSelectDropdown.tableMode:
		table.showGrid = True
		
		# Check if we should display the header based on the multiSelectDropdown's tableModeShowHeader custom property
		# If we should show the header, set its height to match the height of the textFilter.
		if multiSelectDropdown.tableModeShowHeader: 
			table.tableHeader.setPreferredSize(Dimension(table.tableHeader.preferredSize.width, textFilter.height))	

	# If table mode is not selected, hide the table grid and the table header
	else:
		table.showGrid = False
		table.tableHeader.setPreferredSize(Dimension(table.tableHeader.preferredSize.width, 0))
	

Needs the following else statement added as below:

	# Check to see if table mode is selected, and if so, display the table grid
	if multiSelectDropdown.tableMode:
		table.showGrid = True
		
		# Check if we should display the header based on the multiSelectDropdown's tableModeShowHeader custom property
		# If we should show the header, set its height to match the height of the textFilter.
		if multiSelectDropdown.tableModeShowHeader: 
			table.tableHeader.setPreferredSize(Dimension(table.tableHeader.preferredSize.width, textFilter.height))	
		else:
			# 23/04/2025 - rlee - added else statement to ensure 'tableModeShowHeader' is respected in both cases...
			table.tableHeader.setPreferredSize(Dimension(table.tableHeader.preferredSize.width, 0))
	# If table mode is not selected, hide the table grid and the table header
	else:
		table.showGrid = False
		table.tableHeader.setPreferredSize(Dimension(table.tableHeader.preferredSize.width, 0))
	

ON ANOTHER NOTE- Filtering all columns?

I'm having a bit of trouble trying to figure out why the filtering is not working for my current use case.
I've got 3 columns in the Data property, INT, STRING, STRING.

I am able to filter based on the first two columns (I believe - maybe its just working on the second TaskUUID column).
I've tested this by starting typing CT- or PT- and it filters accordingly. Also filters purely on numbers ie: 455**

But I can't seem to filter on the final "Description" column, by typing something like "grind" or "chain".

Any ideas why this may be?

I believe this filtering is handled by the custom method "showDropdown", in the function "setInternalData" ?

And it seems to loop through all columns to filter so not sure why it wouldn't work?

1 Like

Thanks

The multiselect dropdown is an adaptation of the Searchable Dropdown, which is why it has that cool filtering ability. The original version of the searchable dropdown didn't have a table mode, and it would display the 1st column if only one column were present or the second column if two or more columns were present. The displayed column is referred to in the code as the label column, and my filtering algorithm works off the resultant list of labels from that column. I don't believe it looks at the hidden value column at all.

At some point, somebody in the forum asked me to add a table mode, so I did because it seemed quite useful, but it simply didn't occur to me to expand the filtering logic to the other displayed columns.

Filtering on all columns is certainly doable, and it's obviously a useful improvement, but at this point, I'm not sure what the timeline would be for me to get it done, published, and approved. That said, I will look into this, and let you know how to do it when I figure it out, assuming you don't beat me to it.

You were correct; that was where I put it. I was able to get the desired functionality by replacing the setInternalData function with this version:

	# This function sets the 'internalData' of the textfield based on the provided menu items.
	def setInternalData(menuItems):
		
		# If in table mode, filter on all visible columns
		# Use the textfield's text directly, 
		# ...and make the filter results case insensitive by casting all rest values to lower case strings
		if self.parent.tableMode:
			filteredData = [[searchableDropdown.Data.getValueAt(row, column) for column in range(searchableDropdown.Data.columnCount)]
				for row in xrange(searchableDropdown.Data.rowCount)
				if any(textfield.text.lower() in unicode(searchableDropdown.Data.getValueAt(row, column)).lower()
					for column in xrange(labelIndex, searchableDropdown.Data.columnCount))]
		else:
			# Iterate through each row of the 'Data' custom property of the searchable dropdown.
			# If the value in the label column matches one of the menu items, 
			# add the entire corresponding row to a filtered list of rows called 'filteredData'
			filteredData = [
				[searchableDropdown.Data.getValueAt(row, column) for column in xrange(searchableDropdown.Data.columnCount)]
				for row in xrange(searchableDropdown.Data.rowCount) 
				if unicode(searchableDropdown.Data.getValueAt(row, labelIndex)) in menuItems]
		
		# Convert the filtered list into a dataset and assign it to the 'internalData' custom property of the textfield.
		textfield.internalData = system.dataset.toDataSet(system.dataset.getColumnHeaders(searchableDropdown.Data), filteredData)

It checks the current text field value against every visible column when the dropdown is in table mode, and it forces the test strings to lower case, so the user doesn't have to worry about matching capitalization.

Thanks I'll look at updating my side. Just wondering what the menuItems is coming from being passed into setInternalData?

Historical context:
In the original searchable dropdown, they were literally JMenuItems. The dropdown was just a simple popup menu,. If I recall correctly, when I added the table mode, I modified the function to use a list of strings instead, but I imagine the variable name still made sense to me in the context of a dropdown, so I left it the way it was.

When I converted the searchable dropdown to a multiselect dropdown, I essentially made it always in table mode, albeit a table with only one column when the table mode property is false, and I didn't touch that function at all, so the menuItems variable name is still there.

The "memuItwms" come from the label column. Just like a traditional drop-down, the label column is column 1 when the data property has two or more columns, and it is column 0 when there is only one column.

Ah thanks for the context. I don't seem to have the variable menuItems at all though, could you please paste the whole custom method for showDropdown here so I can trace it out a bit?

Oops. I just realized what you are saying. When I wrote this code for you, I must have opened the searchable dropdown instead of the multiselect dropdown by mistake. My apologies, I guess I did do away with the menuItems variable when I did the conversion. Here is the function rewritten for the multiselect dropdown:

	def setInternalData():
	
		# Iterate through each row of the 'Data' custom property of the searchable dropdown.
		# If the value in the label column matches one of the menu items, 
		# add the entire corresponding row to a filtered list of rows called 'filteredData'
		filteredData = [[multiSelectDropdown.Data.getValueAt(row, column) for column in range(multiSelectDropdown.Data.columnCount)]
			for row in xrange(multiSelectDropdown.Data.rowCount)
			if any(textFilter.text.lower() in unicode(multiSelectDropdown.Data.getValueAt(row, column)).lower()
				for column in xrange(labelIndex, multiSelectDropdown.Data.columnCount))]
		
		# Convert the filtered list into a dataset and assign it to the 'internalData' custom property of the textFilter.
		textFilter.internalData = system.dataset.toDataSet(system.dataset.getColumnHeaders(multiSelectDropdown.Data), filteredData)
1 Like