# Dropdown isOpen

**URL:** <https://forum.inductiveautomation.com/t/dropdown-isopen/94950>\
**Category:** Ignition\
**Tags:** ignition81, vision\
**Created:** [August 21, 2024, 3:19pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950 "2024-08-21T15:19:38Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 3:19pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/1 "2024-08-21T15:19:38Z")

</div>

I have a dropdown that partially overlaps another component that can also be clicked on. I would like to disable the component below the dropdown box if the dropdown box is open.

Just off-hand I think I could achieve this with the `onClick` and ~~`focusLost`~~ extension functions on the dropdown, but is there a way to just check if the dropdown is "open"?

edit: focus is not lost when the clicking off of the component. I was thinking that would work for the case where the user clicks any space outside of the dropdown box to close the dropdown.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 3:23pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/2 "2024-08-21T15:23:47Z")

</div>

`PMIComboBox` extends `JComboBox`, which defines an `isPopupVisible()` method:

> **[PMIComboBox](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.43/com/inductiveautomation/factorypmi/application/components/PMIComboBox.html)**
>
> declaration: package: com.inductiveautomation.factorypmi.application.components, class: PMIComboBox

So, yes - either `reference.isPopupVisible()` or `reference.popupVisible`.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 3:26pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/3 "2024-08-21T15:26:35Z")

</div>

Tried this on `propertyChange`, but that didn't work (didn't really expect it to, since `popupVisible` is not bindable). Where would I implement this?

```python
if event.propertyName == 'popupVisible':
	if event.newValue:
		en = False
	else:
		en = True

```

I could do it on `propertyChange` without checking the property name, but that is not really good practice.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 3:39pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/4 "2024-08-21T15:39:12Z")

</div>

I'll just us a timer's `propertyChange` script on the value to check it every 500 ms.

```python
if event.propertyName == 'value':
	dropdown = event.source.parent.getComponent('Dropdown')
	if dropdown.popupVisible:
		en = False
	else:
		en = True
		
	...

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 3:50pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/5 "2024-08-21T15:50:53Z")

</div>

Better would be to create and add a custom `PopupMenuListener`:

> **[JComboBox (Java SE 17 & JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/javax/swing/JComboBox.html#addPopupMenuListener(javax.swing.event.PopupMenuListener))**
>
> declaration: module: java.desktop, package: javax.swing, class: JComboBox

For example:

> [@Dropdown List Events](https://forum.inductiveautomation.com/t/dropdown-list-events/90537/4):
>
> I recommend either setting up a custom method or a library script to avoid redundancy. Example: [image] [image] Then on your propertyChange event handler, call the method anytime a selection change closes the dropdown [mouse selection] # handle slection changes that are triggered by the mouse if event.propertyName == 'selectedStringValue' and not event.source.popupVisible: event.source.filterNextDropdown() For the key released event handler the code would look something like this: if ev…

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 4:02pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/6 "2024-08-21T16:02:55Z")

</div>

Listeners are new to me, I tried this:

```python
if event.propertyName == 'componentRunning':
	from javax.swing.event import PopupMenuListener
	class PopupCloseListener(PopupMenuListener):
		en = True
		def popupMenuCanceled(self, subEvent, en):
			en = True
		
		def popupMenuWillBecomeVisible(self, subEvent, en):
			en = False
		
		def popupMenuWillBecomeInvisible(self, subEvent, en):
			en = True
			
		event.source.parent.getComponent('Switch 2POS Buttons').enabled = en
	
	event.source.addPopupMenuListener(PopupCloseListener())

```

Did not produce the desired results

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 4:16pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/7 "2024-08-21T16:16:47Z")

</div>

Try this:

```python
if event.propertyName == 'componentRunning':
   from javax.swing.event import PopupMenuListener
   class PopupCloseListener(PopupMenuListener):
      def __init__ (self, target):
         self.target = target
      def popupMenuCanceled(self, event):
         self.target.enabled = True
      
      def popupMenuWillBecomeVisible(self, event):
         self.target.enabled = False
      
      def popupMenuWillBecomeInvisible(self, event):
         self.target.enabled = True
   
   listener = PopupCloseListener(event.source.parent.getComponent('Switch 2POS Buttons'))
   event.source.addPopupMenuListener(listener)

```

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 4:18pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/8 "2024-08-21T16:18:45Z")

</div>

`Switch 2POS Buttons` is not the dropdown, it is the component that I want to disable when the dropdown is open and re-enable when it is closed

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 21, 2024, 4:21pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/9 "2024-08-21T16:21:12Z")

</div>

> [@paul-griffith](#):
>
> Try

Ewww! `import`, `class`, and `def` directly in an event script. ☹

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 4:22pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/10 "2024-08-21T16:22:50Z")

</div>

Ya, would probably be better to pass the listener the event and a list of properties to set to false and make this a more generic library script.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [August 21, 2024, 4:30pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/11 "2024-08-21T16:30:33Z")

</div>

Yeah, you're sending it to the custom listener as the target.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 4:59pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/12 "2024-08-21T16:59:07Z")

</div>

Yeah, definitely store the class definition in a project library script or something. Just trying to demonstrate the concept.

Here's the same example but commented to explain what's going on in more detail:

```python
if event.propertyName == 'componentRunning':

   from javax.swing.event import PopupMenuListener

   # define a class that extends from the Java interface we care about
   class PopupCloseListener(PopupMenuListener):
      # give this class a custom constructor that takes one argument, 'target'
      def __init__ (self, target):
         # store the target parameter we get on construction into a class-internal field, `self.target`
         self.target = target
      
      # to override a Java interface method, you must match the signature of the Java method exactly
      # (plus the `self` parameter, because Python)
      # Java has strict overloading, so adding parameters and stuff doesn't work - this is why your first
      # example didn't work
      def popupMenuCanceled(self, event):
         # this will be called automatically because we're providing a listener
         # so when the popup menu is canceled, we modify the enabled property of our self.target field
         self.target.enabled = True
      
      def popupMenuWillBecomeVisible(self, event):
         self.target.enabled = False
      
      def popupMenuWillBecomeInvisible(self, event):
         self.target.enabled = True
   
   # construct an instance of our listener class, passing in our 'target' component as a reference
   # this is what will get stored in the 'target' field inside the class
   listener = PopupCloseListener(event.source.parent.getComponent('Switch 2POS Buttons'))
   # then add our instance to the actual class we care about
   event.source.addPopupMenuListener(listener)

```

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 5:56pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/13 "2024-08-21T17:56:07Z")

</div>

It's making a little more sense.

If I wanted to make this generic and have a `dropdownDisableOtherComponent()` library script like

```python
def dropdownDisableOtherComponent(componentProp):
	'''
		Args
			componentProp	:	componentProperty to toggle True/False
	'''

```

How would I just pass the `componentProp` to the listener, instead of the entire component? That way I could even use this for visibility, etc.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [August 21, 2024, 6:05pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/14 "2024-08-21T18:05:36Z")

</div>

You would change the listener definition (it would really only work "well" with binary values).

```python
if event.propertyName == 'componentRunning':
   from javax.swing.event import PopupMenuListener
   class PopupCloseListener(PopupMenuListener):
      def __init__ (self, target,attrs):
         self.target = target
         self.attrs = attrs
      def popupMenuCanceled(self, event):
         for attr in attrs:
              setattr(self.target,attr,True)
      
      def popupMenuWillBecomeVisible(self, event):
         for attr in attrs:
              setattr(self.target,attr,False)
      
      def popupMenuWillBecomeInvisible(self, event):
         for attr in attrs:
              setattr(self.target,attr,True)
   
   listener = PopupCloseListener(event.source.parent.getComponent('Switch 2POS Buttons'),["Enabled","Visible"])
   event.source.addPopupMenuListener(listener)

```

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 21, 2024, 9:04pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/15 "2024-08-21T21:04:28Z")

</div>

yep, binary is what I want. It can be transformed after the fact if needed, but I just need it for enable/disable for now.

I'll give this a try.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [August 21, 2024, 9:21pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/16 "2024-08-21T21:21:05Z")

</div>

My example was for multiple properties in a single component, you cans modify it appropriately for multiple components single attribute, or multiple of both.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 9:25pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/17 "2024-08-21T21:25:09Z")

</div>

You could go fully functional - accept a function during construction of your listener. Call that function inside the listener with true or false to represent the state of the boolean.  
Where you actually use the listener, also define the callback to modify the enabled, visible, other properties. Slightly more brain breaking, but a lot more flexible.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 21, 2024, 10:03pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/18 "2024-08-21T22:03:48Z")

</div>

Something like this, to get maximally silly with it:

```python
# in project library
class PopupMenuAdapter(PopupMenuListener):
    def __init__ (self, callback):
        self.callback = callback

    def popupMenuCanceled(self, event):
        self.callback(event.source.popupVisible)

    def popupMenuWillBecomeVisible(self, event):
        self.callback(event.source.popupVisible)

    def popupMenuWillBecomeInvisible(self, event):
        self.callback(event.source.popupVisible)

##############################

# in component event
if event.propertyName == 'componentRunning':
    # define the function to execute when the popup menu is opened or closed
    # the popupVisible argument will reflect the current state and can be used to drive other logic
    def callback(popupVisible):
        # we can access event.source here because the callback is defined in the same scope
        event.source.parent.getComponent('Switch 2POS Buttons').visible = not popupVisible
    
    listener = PopupMenuAdapter(callback)
    event.source.addPopupMenuListener(listener)

```

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [August 22, 2024, 4:04am UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/19 "2024-08-22T04:04:35Z")

</div>

> [@amarks](#):
>
> `if event.propertyName == 'componentRunning':`

I know I've written this a 100 times in the forum, but it's something I need to go back through and correct. The `componentRunning` property is a bool, and it's `propertyChange` event fires twice during the normal life cycle of the component. Once at initialization when the property becomes `True` and once at window close when the property becomes `False`.

For this reason, I now write it this way:

```python
# Only occurs once at initialization
if event.propertyName == 'componentRunning' and event.newValue:

```

If the initialization script schedules anything with invokeLater or makes any asynchronous calls, null pointer exceptions and other unexpected behaviors can occur. Overall, I'd say that 99% of the time it probably doesn't matter if an initialization script runs at window close, but it's still an unnecessary inefficiency that should be corrected.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 22, 2024, 1:33pm UTC](https://forum.inductiveautomation.com/t/dropdown-isopen/94950/20 "2024-08-22T13:33:19Z")

</div>

I would assume I need to somehow pass a list of components and a list of lists of properties (one list for each component), but I'm not quite sure how to make that change with the `self.target` and `self.attrs`

Something like this?

```python
if event.propertyName == 'componentRunning' and event.newValue:
   from javax.swing.event import PopupMenuListener
   class PopupCloseListener(PopupMenuListener):
      def __init__ (self, target,attrs):
         self.target = target
         self.attrs = attrs
      def popupMenuCanceled(self, event):
         for i in range(0,len(target)):
            for attr in attrs:
                setattr(self.target[i],attr,True)
      
      def popupMenuWillBecomeVisible(self, event):
         for i in range(0,len(target)):
            for attr in attrs:
                setattr(self.target[i],attr,False)
      
      def popupMenuWillBecomeInvisible(self, event):
         for i in range(0,len(target)):
            for attr in attrs:
                setattr(self.target[i],attr,True)
   
   listener = PopupCloseListener([event.source.parent.getComponent('Switch 2POS Buttons')],[["Enabled","Visible"]])
   event.source.addPopupMenuListener(listener)

```

_(still just doing this in the component script for now, until I get it working)_

[Next page](https://forum.inductiveautomation.com/t/dropdown-isopen/94950.md?page=2)
