Power Table with three dropdowns

In the Power table is it possible to have ONLY, for example, THREE dropdowns in the column?
Like in this example from somewhere else, not Ignition…


I can create dropdowns in the configureEditor, but only for the entire column.
What I need is the dropdowns only in few rows in that column…?

On occasions when I have needed functionality outside of that available in the table component, I have used a template repeater, you can take the drop-down options from either the driving dataset or a binding within the row template, to help keep the headers in alignment with the table columns I find it useful to create just one row template which displays as a header or table row depending on its index.

You can do this using the Extension Function: configureCell.
Evaluate the conditions required to display the dropdown, then create the dropdown component and return it in the dictionary.

something like this:

That is exactly what I have in mind, but it doesn’t work in configureCell, only in configureEditor.
But in configureEditor you don’t have rowIndex only cellIndex and cellName. So you can’t ‘evaluate’ in which row I want dropdown…

Oh yes, I see the problem.

You could create a custom editor.
Paste this into the configureEditor and adjust as needed:

	from javax.swing import AbstractCellEditor,JComboBox, JTextField
	from javax.swing.table import TableCellEditor

	class myTableCellEditor(TableCellEditor, AbstractCellEditor):	
	
		def __init__(self, tableComponent):
			self.table= tableComponent
			self.editor = None
			
		
		def getTableCellEditorComponent(self, table, value, isSelected, rowIndex, vColIndex):	
			
			if rowIndex in [23, 25, 26]:
				self.editor = JComboBox(["Line 1", "Line 2", "Line 3"])
			else:
				self.editor = JTextField()
	
			return self.editor
		
		def getCellEditorValue(self):
			if type(self.editor) is JComboBox:
				newValue = self.editor.getSelectedItem() 
			else:
				newValue = self.editor.getText()
		
			return newValue
			
	if colName == "Vrednost":
		m = myTableCellEditor(self)
		return {"editor":m}

I got the idea from here:

Thank you. This now works… somehow…
This last column (vrednost) is integer type. And in the db field there can be values 0, 1, or 2.
What I need is in the dropdown to be text like OFF for 0, Hand for 1, AUTO for 2.
And when the user selects for example ‘HAND’, the number one (1) must be writen to the field.

Ok, here I’ve added the options and a few comments for you:

	from javax.swing import AbstractCellEditor,JComboBox, JTextField
	from javax.swing.table import TableCellEditor

	class myTableCellEditor(TableCellEditor, AbstractCellEditor):	
	
		def __init__(self, tableComponent):
			self.table= tableComponent
			self.editor = None
			# defind the options for the drop down
			self.options = {"OFF":0, "Hand":1, "Auto":2}
		
		def getTableCellEditorComponent(self, table, value, isSelected, rowIndex, vColIndex):	
			
			# work out if the row should show a combobox or text box.
			if rowIndex in range(10):
				# create a combo box with the options
				self.editor = JComboBox(self.options.keys())
			else:
				# create a text field for default editor
				self.editor = JTextField()
	
			return self.editor
		
		def getCellEditorValue(self):
			# is the editor a combobox?
			if type(self.editor) is JComboBox:
				# get the combobox value
				selectedValue = self.editor.getSelectedItem()
				# convert back to the options value
				newValue = self.options[selectedValue]
			else:
				# get the text field value
				newValue = self.editor.getText()
		
			return newValue
			
	if colName == "Vrednost":
		m = myTableCellEditor(self)
		return {"editor":m}
2 Likes

Great. Thank you. :bowing_man::+1:
That is exactly what I need.
I see you’re good with this. Can you please help me with another thing:
The dropdown is now showing only when you click on the cell.
Is it possible to show the dropdown all the time?

Sure, but it’s a bit of a hack.

First you need to update the getTableCellEditorComponent so the options are written explicitly. This ensures they are in the correct order. And set the selected index in the combobox.

def getTableCellEditorComponent(self, table, value, isSelected, rowIndex, vColIndex):	
			
			# work out if the row should show a combobox or text box.
			if rowIndex in [0,1,4,5]:
				# create a combo box with the options
				self.editor = JComboBox(["OFF", "Hand", "Auto"])
				self.editor.setSelectedIndex(value)
			else:
				# create a text field for default editor
				self.editor = JTextField()
	
			return self.editor

Then you need to add something like this to configureCell:

	from javax.swing import JComboBox

	data = {}

	if colName == 'Vrednost':
		if rowIndex in [0,1,4,5]:
			options = {0:"OFF", 1:"Hand", 2:"Auto"}
		
			if value in options:
				cb = JComboBox(["OFF", "Hand", "Auto"])
				cb.setSelectedIndex(value)
				data['renderer'] = cb 
	
	return data

This will create a combobox to display in the table, and a second combobox for editing. You need to make sure they are configured identically and then they will just look like one to the user.

6 Likes

Kudos for some excellent Swing hacking, David!

That’s wonderful. Thank you very much. :+1:
Now I have a ‘tiny’ problem: when selecting the value in the dropdown and the dropdown closes, the table ‘onCellEdited’ event won’t fire, unless you click on another field in the table.
If you click outside of the table or on another component, it wouldn’t fire either.
Is that solvable?

Try self.putClientProperty("terminateEditOnFocusLost", True) in initialize

I tried… nothing.
Unless you click to another field in the table, it won’t fire

Try self.table.putClientProperty, it might need to be on the inner JideTable.

Yes, now it works when you click on another component.
But not when the dropdown closes.
And I would really need it after the dropdown close… Because I have the db update in the ‘onCellEdited’ event and I would like to update it immediately, like when you press enter on another field in the table…
Is it even possible?

Bump…:door:

OK I’ve added an ItemListener to the Combobox to get the item changed event as soon as it’s changed:

In configureEditor use this:

	from javax.swing import AbstractCellEditor,JComboBox, JTextField
	from javax.swing.table import TableCellEditor
	from java.awt.event import ItemListener, ItemEvent
	
	# used to listen to item change events in the combobox
	class MyItemListener(ItemListener):
		
		# Constructor
		def __init__(self, table, value, rowIndex, colIndex):
			self.table = table
			self.rowIndex = rowIndex
			self.colIndex = colIndex
			self.value = value
			self.options = {"OFF":0, "Hand":1, "Auto":2}
		
		# this is fired when the combobox changes
		def itemStateChanged(self, event):
			if (event.getStateChange() == ItemEvent.SELECTED):
				item = event.getItem()

				# convert the new value to the index. E.g. OFF -> 0
				if item in self.options:
					newValue = self.options[item]
					
					# create a new dataset with the updated value					
					newDS = system.dataset.setValue(self.table.data, self.rowIndex, self.colIndex, newValue)
					
					# write back to the source property
					# update this to whereever your master data is
					self.table.parent.DS = newDS
			

	class myTableCellEditor(TableCellEditor, AbstractCellEditor):	
	
		def __init__(self, tableComponent):
			self.table= tableComponent
			self.editor = None
			# defind the options for the drop down
			self.options = {"OFF":0, "Hand":1, "Auto":2}
		
		def getTableCellEditorComponent(self, table, value, isSelected, rowIndex, colIndex):

			# work out if the row should show a combobox or text box.
			if rowIndex in [0,1,2,3]:
				# create a combo box with the options
				self.editor = JComboBox(self.options.keys())
				self.editor.setSelectedIndex(value)
				# add an Item changed listener
				self.editor.addItemListener(MyItemListener(self.table, value, rowIndex, colIndex))
			else:
				# create a text field for default editor
				self.editor = JTextField()
	
			return self.editor
		
		def getCellEditorValue(self):

			# is the editor a combobox?
			if type(self.editor) is JComboBox:
				# get the combobox value
				selectedValue = self.editor.getSelectedItem()
				# convert back to the options value
				newValue = self.options[selectedValue]
			else:
				# get the text field value
				newValue = self.editor.getText()
		
			return newValue
		

			
	if colName == "Vrednost":
		m = myTableCellEditor(self)
		return {"editor":m}

Keep this the same in configureCell:

	from javax.swing import JComboBox
	
	data = {}

	if colName == 'Vrednost':
		if rowIndex in [0,1,2,3]:
			options = {0:"OFF", 1:"Hand", 2:"Auto"}
		
			if value in options:
				cb = JComboBox(["OFF", "Hand", "Auto"])
				cb.setSelectedIndex(value)
				data['renderer'] = cb 
	
	return data

Now that the item is being updated in the configureEditor function, you need to prevent it from trying to update twice, so use this in the onCellEdited:

	if colName == 'Vrednost':
		if rowIndex not in [0,1,2,3]:
			self.parent.DS = system.dataset.setValue(self.data, rowIndex, colIndex, newValue)

I’ve tested it and it works pretty well for me.
Good luck.

1 Like
					# write back to the source property
					# update this to whereever your master data is
					self.table.parent.DS = newDS

I’m appreciating your effort to help and I’m very, very grateful.
I put your code in mine but I’m getting an error
AttributeError: 'com.inductiveautomation.factorypmi.application.com' object has no attribute 'DS'
at this quoted line…
I don’t understand what you mean by ‘update this to wherever your master data is’…

if you change it to:

self.data = newDS

it will update the dataset on the table itself.

But if you are wanting to update a database, you will need additional code. Basically you need to copy the code that you use in the onCellEdited.
The ideas is that this updates the data source early, and then the onCellEdited code is not needed.

2 Likes

Yes, I figured out that over time, playing with various bits of code…

Thank you again for your help. Greatly appreciated.

There is a barrel of beer waiting if you ever set foot in Slovenia… :beers: :slovenia: