Dropdown list separator

I need to create a dropdown list with (unselectable) separator lines. For example, in the Designer, if you expand the Project list in the menu bar, there are 3 sections in the list, separated by 2 lines: the top section has 3 choices at the top of the list (Comm Off, Comm Read-Only, and Comm Read/Write), then there is a separator line before the next section which has 3 choices (Properties, Gateway Event Scripts, and Client Event Scripts). This section is followed by yet another separator line before the last section with 2 choices (Preview Mode and Preview Language).

I don’t need to have expandable selections, radio buttons, or check boxes in my dropdown - just separator lines.

Is there a way to do this?

You can add a propetyChanged event script to the dropdown which, if a user selects your separator line, you revert the selected index back to its old value. Like this:

Dropdown Contents:



propertyChange script:

SEPARATOR_STR = "-----------------------------------"

if event.propertyName == "selectedIndex":
	if event.source.selectedStringValue == SEPARATOR_STR:
		event.source.selectedIndex = event.oldValue

Yes I thought of that too, but I’m looking for a “cleaner”, more standard look.

The standard look of a dropdown component is different than standard look of a menu component.

You might be able to write a module that grants access to a java.swing JMenu as a configurable Ignition component.
Or you could hack together something out of a template canvas. You could wrap your ‘menu item’ into a template with mouse hover/click event scripts that gets painted into layout cells of the canvas, with different text parameters for each item.

Interesting. Consider constructing a fake dropdown from a Label and a small Button. Have the button’s actionPerformed event construct and display a popup menu, but override its placement and size so it shows up below the Label. Have the menu items write to the Label .text property when selected (and whatever other functionality you need).

1 Like

I implemented the hacky template canvas approach, but with more of a “menu” style in mind. You could easily change the top label to look more like a dropdown.

I like that solution. I’m not quite sure how you implemented it though. Could you elaborate?

Never mind … I figured it out. Thanks for your suggestions.

EDIT: DON’T DO THIS. Use system.gui.createPopupMenu(). Unless you need to construct a menu with arbitrarily complex items.

Sure. Don’t say I didn’t warn you though: it’s hacky.

There are 2 templates needed (you could optionally add more to achieve the radio button/checkbox functionality)

  1. menubar_item

    A 200x18 template with a custom string parameter. The template contains only a label that fills the entire space, and has its text property bound to the templates string parameter. I implemented event scripts for mouseEntered and mouseExited, which merely set the event.source.background color to reflect if the option is highlighted or not.

  2. menubar_separator

    A 200x4 template with a grey line, that’s all.


To implement the above templates in a usable menu, you need a label and a template canvas. The label will act as the initial button (similar to the 'Project' menu item in the Designer), while the template canvas instantiates and formats the templates outlined above as an actual menu. I implemented both of these in another template, **menubar**, just for demonstration.*

The menubar template has a boolean custom parameter: menuOpened, which gets set when the user clicks the ‘Menu’ label and unset when they click off of it. menuOpened gets bound to the template canvases ‘visible’ property so it won’t show the menu unless they click the label. In the template canvas customizer you add the menu items along with seperators, and configure the parameters. Their position is determined by setting the Positioning to ‘Layout Positioning’: ‘cell 0 rowIndex’. Here’s a screenshot:


Parametrizing the mouseClicked events of the menuItems is an exercise left for the masochistic reader. I'd be interested to see how you implement them.
_*You'd probably just want to put the label and template canvas on the actual page and configure your menu as needed, otherwise you'd need to abstract the menuitem text parameters and generate the template canvas 'templates' dataset, which is a little more involved._
1 Like

Whoops, just saw your response. For future users you should report back with any improvements/further developments you come up with. :slight_smile::thumbsup:

Well I should say, “I kind of figured it out…”
I tried using system.gui.createPopupMenu which only seems to work with a right click. Is there a way to use/trigger that function with a left click?

I assigned the following script to the mouseReleased event of a label. (I would prefer that it worked with the actionPerformed event of a button).

from javax.swing import JMenuItem
def item1(event):
system.gui.messageBox(“You chose item 1”)
def item2(event):
system.gui.messageBox(“You chose item 2”)
def item3(event):
system.gui.messageBox(“You chose item 3”)

menu = system.gui.createPopupMenu([],[])
menuitem1 = JMenuItem(“Item 1”,actionPerformed=item1)
menuitem2 = JMenuItem(“Item 2”,actionPerformed=item2)
menuitem3 = JMenuItem(“Item 3”,actionPerformed=item3)
menu.add(menuitem1)
menu.addSeparator()
menu.add(menuitem2)
menu.add(menuitem3)
menu.show(event)

Trying to find a way to trigger the mouseReleased event of a label through a script…

Then, I could get the actionPerformed event of a button to do the dirty work.

TIL createPopupMenu is a thing. Can’t believe I haven’t needed that before, much more elegant than template abuse. But it’s never a bad time to show off the Turing-completeness of the template canvas :wink:

I was able to implement your script with minor modifications on a buttons actionPerformed event:

def item1(event):
	system.gui.messageBox("You chose item 1")
def item2(event):
	system.gui.messageBox("You chose item 2")
def item3(event):
	system.gui.messageBox("You chose item 3")

from javax.swing import JMenuItem

menu = system.gui.createPopupMenu([],[])
menuitem1 = JMenuItem("Item 1",actionPerformed=item1)
menuitem2 = JMenuItem("Item 2",actionPerformed=item2)
menuitem3 = JMenuItem("Item 3",actionPerformed=item3)
menu.add(menuitem1)
menu.addSeparator()
menu.add(menuitem2)
menu.add(menuitem3)
menu.show(event.source, 0, event.source.height)
4 Likes

So, move the button to the right and restyle it to show just the "down arrow" of a dropdown. Place a label where the button was and style it to look like a text entry field. Then use the label as the relative location for showing the menu.

I just found the solution too … if you supply menu.show(event) with coordinates like this:

menu.show(event, x, y)

then the script will work with the mouseClicked event (hence, a left-click will run the script)
Thanks to both bfuson and pturmel for your help.

You don’t necessarily need to construct the menu like that. Using system.gui.createPopup, you can do this:

def item1(event):
	system.gui.messageBox("You chose item 1")
def item2(event):
	system.gui.messageBox("You chose item 2")
def item3(event):
	system.gui.messageBox("You chose item 3")
itemNames = ["item1","item2","","item3"]
itemFuncs = [item1,item2,None,item3]
menu = system.gui.createPopupMenu(itemNames,itemFuncs)
menu.show(event,event.x,event.y)

Placing None for the function works.

1 Like

Nice!
I was looking for a way to do that too.

Is there a way to pass arguments to these functions?

I poked around a bit and found you can wrap the given event in a class that JPopupMenu.show() expects (something that inherits java.awt.event.ComponentEvent) along with whatever your custom data is.

Something like:

from java.awt.event import ComponentEvent

class EventWrapper(ComponentEvent):
	def __init__(self, event):
		ComponentEvent.__init__(self, event.getComponent(), event.getID())
		self.custom_parameter = ""

def item1(event):
	system.gui.messageBox("You chose item 1: " + wrapper.custom_parameter)
def item2(event):
	system.gui.messageBox("You chose item 2")
def item3(event):
	system.gui.messageBox("You chose item 3")
	
wrapper = EventWrapper(event)
wrapper.custom_parameter = "I'm a custom event parameter!"
	
itemNames = ["item1","item2","","item3"]
itemFuncs = [item1,item2,None,item3]
menu = system.gui.createPopupMenu(itemNames,itemFuncs)
menu.show(wrapper,event.x,event.y)

I suspect there’s a better or more specific way.

2 Likes

I meant to ask about having each menu option call the same function, but with different parameters. Something like this nonfunctional example code:

	def setHOA(event, value):
		system.tag.write("deviceHOA", value)
	
	names = ["Auto", None, "Off", "Hand"]
	functions = [setHOA(1), None, setHOA(0), setHOA(2)]
	menu = system.gui.createPopupMenu(names, functions)
	menu.show(event, event.x, event.y)

If I want to keep the code approachable, I’m guessing that it would probably be best for me to stick to defining three separate functions.