Need a Dropdown Component Trigger off of completed selection

I am looking for a bindable event for the dropdown component on a scripting function.

Current issue is that the GUI method of entry that I am pleased with is having a dropdown component trigger a script that will populate other components from a SQL query.

While I can bind it off a propertyChange script bound to a selectedID/selectedValue, these bindings evaluate immediately and trigger a SQL query with every change. A user that has a dropdown selected and holds down on the keyboard will trigger a great amount of requests.

My current musings on how to tackle the problem are to bind a timer component to the dropdown and only count when the mouse cursor is over the component and then reset back to 0 when the mouse moves off the dropdown, delaying any writes until the timer resets/goes to 0.

…but currently it seems a bit convoluted.

I’ve tried using invokeLater and invokeAsynchronous to better control the process from activation to finish, but the problem always goes back to ā€œHow do I define when a user is done with this component?ā€

the dropdown component is my least favorite input component, however if I were to use one I think I would Place the scripting on the ā€˜Focus Gained’ and ā€˜Focus Lost’ actions.

When do the queries actually need to execute?

Why not add a script that executes the queries once time when the window is opened? This can be done in the visionWindowOpened event on the window.

Sometimes the cleanest solution is a separate ā€œContinueā€ button that takes a page full of selections and moves the user to the next page, suitably filled in.

Aye, and that’s my backup plan. The cleanliness of a dropdown component and it’s space saving properties are what keep me trying to make it work.:smiley:

They should only execute when the operator has ā€œfinishedā€ with the dropdown component. Normally this would be off of focusLost, but that trigger depends on another component stealing focus.

I’ve achieved a level of functionality I am happy with looking for the ā€œEnterā€ key press but ideally if the focusLost triggered a bit differently it would suffice.

You can create a custom property that is updated after a selection is made.
Use the following:

from javax.swing.event import PopupMenuListener

class myPopUpMenuListener(PopupMenuListener):
	def __init__(self, myDropDown):
		self.dropDown = myDropDown
	def popupMenuWillBecomeInvisible(self, e):
		print self.dropDown.selectedValue, self.dropDown.selectedStringValue
		self.dropDown.myCustomProperty = self.dropDown.selectedValue

myDropDown = event.source.parent.getComponent('Dropdown')
dd.addPopupMenuListener(myPopUpMenuListener(myDropDown))
2 Likes

Thanks! Works great!

Thank you for your copious contributions. Pls go easy on me, I'm a novice programmer. I've tried adding your code as a "Custom Method" of the dropdown component. I was unable to figure out how to incorporate scripting with a "Custom Property". I added pseudo code comments to help me think through what is happening as follows (Am I even close?):

def DropdownSelectInvis(self):
	'''
	Some words about arguments, and not including self 
as a redundant argument
	'''
	
	#Import the PopupMenuListener java class in order to subclass it's features
	#for the dropdown list/box.
	from javax.swing.event import PopupMenuListener
	
	#Create python class with functions for handling initiation and to get 
	#invisibility property and set it to the invisible state.
	class myPopUpMenuListener(PopupMenuListener):
		def __init__(self, myDropDown):
			self.dropDown = myDropDown
		def popupMenuWillBecomeInvisible(self, e):
			print self.dropDown.selectedValue, self.dropDown.selectedStringValue
			self.dropDown.myCustomProperty = self.dropDown.selectedValue
	
	#pass the dropdown component to the method for adding it to the subclassed 
	#listener.
	myDropDown = event.source.parent.getComponent('Dropdown')
	dd.addPopupMenuListener(myPopUpMenuListener(myDropDown))

This works in the designer but not in my remotely deployed client. Can you help me understand how to understand/resolve this behavior? I feel like I still need to pipe/connect some things up to my actual components/properties...

Again, much appreciated.

Dan

Hi Sera,
I’ve added a custom method called ā€œdropFocusā€ on the dropdown in question, and placed the above code in it…
Did you put the code from jpark somewhere else?

I’m not seeing the values printed to the console indicating that the second function within the class is not being called.

I’ve created a custom property for the dropdown in question called "myCustomProperty " to see if it gets populated with
the selectedValue string value, but it doesn’t when I change the selected value.

Did you bind the ā€œmyCustomPropertyā€ on the dropdown component somehow?

What is the ā€œeā€ argument that is passed to the popupMenuWillBecomeInvisible function?

Wait…I think you guys are leveraging off the code already in the focusLost method…updating the custom property forces focus to be lost then??? Giving it a shot. Sorry for the detour.

Your core issue is probably that you’re adding this as a custom method.
Custom methods are just functions available from the component; they’re not necessarily being called unless you specifically use them in the script. You could add Jae’s code to the propertyChange event handler, and have it run once (note: will only work in a client) using this:

if event.propertyName == 'componentRunning':
	from javax.swing.event import PopupMenuListener

	class myPopUpMenuListener(PopupMenuListener):
		def __init__(self, myDropDown):
			self.dropDown = myDropDown
		def popupMenuWillBecomeInvisible(self, e):
			print self.dropDown.selectedValue, self.dropDown.selectedStringValue
			self.dropDown.myCustomProperty = self.dropDown.selectedValue

	myDropDown = event.source.parent.getComponent('Dropdown')
	dd.addPopupMenuListener(myPopUpMenuListener(myDropDown))

The ā€˜e’ argument of popupMenuWillBecomeInvisible is the ā€˜event’ object that’s automatically passed into the function by Swing when the event happens.

1 Like

Hi Paul. Thx for your reply.

I’ve tried placing jae’s ā€œnakedā€ class in the existing focusLost method (testing my theory that the focusLost event handler has something built-in that jae was leveraging), and commented out the code in the custom method
I created. (maybe i need to delete it altogether?) but received a name error pointing to ā€œddā€ (the last line of code of the class in question) I assumed this was ā€œbuilt-inā€ to the listener but I haven’t read the docs yet, despite my better judgement telling me I should.

The same name error is raised when I deploy the class in the propertyChange method from the client. Curiously, the designer client is behaving as desired (dropdown is retracting when an item is selected) in preview mode-just as it was when the class was placed in the custom method I added. (Thinking I need to check that behavior with all scripting removed now.)

However, if I open or close the window containing the dropdown box while in preview mode, the name error is thrown, indicating at least that the property change event filtered for ā€œcomponentRunningā€ is working…however, I’ve not seen the console receive any printed values nor has the custom property I created ā€œmyCustomPropertyā€ been written to with the selectedValue as the function suggests it might.

Thanks for coming along the ride Paul.

Name Error as follows:

  File "<event:propertyChange>", line 12, in <module>
NameError: name 'dd' is not defined

	at org.python.core.Py.NameError(Py.java:260)
	at org.python.core.PyFrame.getname(PyFrame.java:257)
	at org.python.pycode._pyx43.f$0(<event:propertyChange>:12)
	at org.python.pycode._pyx43.call_function(<event:propertyChange>)
	at org.python.core.PyTableCode.call(PyTableCode.java:165)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1275)
	at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:626)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:168)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.access$000(ActionAdapter.java:40)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter$ActionRunner.run(ActionAdapter.java:280)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$500(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

Ignition v7.8.4 (b2016082217)
Java: Oracle Corporation 1.8.0_131

Try replacing the DD with a reference to the DropDown Object.

Let me see if I can find an example.

#Pull a Reference to the DropDown Object you want to 
YourOwnDD = event.source.parent.getComponent('dd_Filter')


#Then branch from there
#If you call from an event generator on the dropdown
event.source.addPopupMenuListener(myPopUpMenuListener(YourOwnDD))

#If you call from anywhere else.
YourOwnDD.addPopupMenuListener(myPopUpMenuListener(YourOwnDD))

#If you call from a a custom method.
self.addPopupMenuListener(myPopUpMenuListener(YourOwnDD))
if event.propertyName == 'componentRunning':
	from javax.swing.event import PopupMenuListener

	class myPopUpMenuListener(PopupMenuListener):
		def __init__(self, myDropDown):
			self.dropDown = myDropDown
		def popupMenuWillBecomeInvisible(self, e):
			print self.dropDown.selectedValue, self.dropDown.selectedStringValue
			self.dropDown.myCustomProperty = self.dropDown.selectedValue

	myDropDown = event.source
	myDropDown.addPopupMenuListener(myPopUpMenuListener(myDropDown))

Minor change; I hadn’t paid enough attention to what Jae was doing.

Breaking it down: Basically, once the component is initialized it will fire this code once.
This code creates a custom ā€˜PopupMenuListener’ (a default Java object) that fires the custom popupMenuWillBecomeInvisible function that sets the custom property value. It holds a reference back to the original dropdown as the second argument to the __init__ function.

1 Like

I’m remiss in not mentioning that I have the dropdown in ā€œTableā€ display mode (vs. ā€œListā€). Since this seems like it might be a low level event or item listener issue, might this be a factor? (this is me flexing some new knowledge from reading some of the docs…I’m not usually this java lingo savy.)

The most recent code posted should work fine with the list or dropdown renderers.

Thanks Sera…your initial comment lead me to look at the ā€œpopupTriggerā€ listed as an event object property under the mouseReleased event handler…I’ll explore your suggestion shortly, but Paul is bring some code suggestions too. Let me see what he’s doing.

Hi Paul. Tried the updated code in the propertyChange event handler. Still no dice getting the table to disappear once an item is selected. However, I’ve changed the display mode to ā€œListā€ from ā€œTableā€ and the list now disappears when an item is selected, which means users will need to know which city is associated with a particular airport code…

Since the dropdown (JComboBox) is a combination of many components, perhaps the invisible property of the embedded table should be the target of our bit flipping adventure?

awesome. Thanks Sera. Appreciate your input.

I was able to accomplish what I believe you were wanting with this script:
if event.newValue != event.oldValue:
if event.propertyName == ā€œselectedStringValueā€:
… (execute your function here)

2 Likes