Button Events Within Templates

I’m looking for a little help launching different popups from a button click within a multiple template instances. I can do this for the master template fine, but obviously it will launch the same popup from every template instance and I need to be able to launch different popups from each instance. When I try to do the mouse click scripting on the instance, it works, but only if you click on the template area around the button. I’m sure this is easy, but I’m just getting started with Ignition. Can someone point me in the right direction?

Could you have the popup window name as a template parameter. This will allow you to set a unique value for each template instance, thereby opening a different popup.

1 Like

I also was trying to do some sort of template button but couldn’t get it working as I’d like. Had the same problem with the mouse click scripting. Would be interested to see what others have come up with. I was hoping to pass in script parameters through the template.

So I think I got this working now if anyone is interested. I created a string template parameter that allowed me to pass my popup reference through. In my case, I enter Popups/mypopup in the template instance. Then in the master template, I created a mouseclicked script for the button and used the pass parameter to launch the popup in the script editor.

2 Likes

So, I’m basically trying to do something similar with having a button as a template.
I was able to customize the button for aesthetics and add some scripting to it in the template, but I want all the different instances of the template to be able to point to different pages as these are navigation buttons. The action performed event handler disappeared from the options for scripting for the button objects. I tried to script the ‘BACK’ and ‘HOME’ buttons using the mouse clicked scripting but it is not working for me. Any thoughts or ideas how to do this?

I’ve seen similar behavior. Scripting a mouse-click event to an instance of a template seems inconsistent in its operation. It often doesn’t work, but sometimes will. Using a template parameter for a pop-up name is certainly a workaround for many applications, but it seems that this is a bug. Either you should not be able to apply user interactive scripting to an instance of a template (gray it out, disable ctrl+J) or it should be able to handle the event consistently. It should either override any scripting in the template itself or should execute all the scripts in a predictable order (outermost to innermost or vice-versa)… Any comment, Inductive folks?

Thanks,
Max

Events are always processed innermost to outermost in Java Swing, whether it is a Template instance, a Container, a Group, or a layered component (JScrollPane holding a Table, etc.). If you want the template holder to run all event scripts, define none within the template. If you want to delegate to an outer component, have the inner event script search its own parents to find a suitable custom method to call.

Thanks for the clarification of the processing order. However, in my experience, with v7.9.6, mouse events configured on an instance of a template often don’t fire even if there are no mouse events configured within the template itself.

So how can I try to fire the action performed Event handler? It does not even show up in the object of the button template…
I tried it with the mouse clicked and it does not work. Basically trying to get the button object to open and swap a window…

That's why my recommendation is for the template button's event (actionPerformed or whatever) needs to call a custom method on the template holder. You cannot make the template holder's own events fire.

After some struggle with this exact problem, this is the solution I normally go with. The example is in a buttons actionPerformed event inside a template. The code depends on a custom property on the template string ActionPerformed.

template = event.source.parent # Or whatever path to the template itself.
parentContainer = template.parent.parent

if template.ActionPerformed != '':

	exec("def OnActionPerformed():" +
		template.ActionPerformed)

	OnActionPerformed()

This way the action performed string can execute any code passed in, e.g.

# Calls function 'onButtonAction' on the parent container containing the template with
# the buttons event object and the template the button resides in as arguments.
parentContainer.onButtonAction(event, template)

Or something as simple as this will also work

system.gui.closeParentWindow(event)
2 Likes

I’ll try this out! Thanks! So basically you are using the template object to to a ‘screen’ and this parameter is a custom property for each window I need to switch to?

The custom property ActionPerformed is on the template and of type string.

This string can then take whatever python code of your choice, a single line of code is easiest as you would have to include \n and \t in the string to write some valid multiline code.

The example parentContainer.onButtonAction(event, template) will just call a custom method named onButtonAction on the parent container of the template. This custom method can be named anything and is not restricted to be on the parent container as something like the examples below also is valid.

# Calls 'someCustomMethod' on the parent container of the parent container of the
# template :-)
parentContainer.parent.someCustomMethod(event)

# Calls 'whateverCustomMethod' on the parent window.
system.gui.getParentWindow(event).whateverCustomMethod()

# Sets 'CustomStringProperty' on the parent container to 'Hello World'
parentContainer.CustomStringProperty = 'Hello World'

# Increments 'ClickedCount' custom property on the template.
template.ClickedCount += 1

I basically did something similar that worked for me. I added a script under action performed within the button on the template. I set up a custom template property - String ActionPerformed.
What this does is that any new button object of the template Button, would have this property in which the script for the action performed could be stored and that gets executed.

This was my code:
template = event.source.parent # Or whatever path to the template itself.
parentContainer = template.parent.parent

if template.ActionPerformed != ‘’:

exec("def OnActionPerformed():" +
	template.ActionPerformed)

OnActionPerformed()

Using exec is quite dangerous though:

  • You’re losing all debug information (on what line did it execute)
  • It’s very hard to figure out the scope of the code (to what objects does the code have access)
  • The construction is very a-typical, so in a few months, you’ll probably don’t remember what you’ve done there.

There are enough ways to select between different functionality, but perhaps you should wonder if it all belongs under the same template.

exec is also unoptimizable within jython. The string has to be compiled every time it runs. It can never be ‘just-in-time’ compiled to faster java code, or within the JVM to native code. Which means it is always going to be slow. Not that important in some cases, but slow event handlers are the number one way to freeze a UI.
Even worse, if any part of the string passed to the template property to be exec’d comes from user input, you have an enormous security hole. Exactly the same kind of hole as SQL injection on a website.

Friends don’t let friends use exec.

1 Like

Here is how I do it.

from java.awt.event import MouseEvent
evt = MouseEvent(event.source.parent.parent,event.getID(),event.getWhen(),event.getModifiers(),event.x,event.y,event.clickCount,event.popupTrigger,event.button)
event.source.parent.parent.dispatchEvent(evt)

We take the originating event, from a label inside the template, and create a new instance of the mouse event, only changing the source. If we do not do this, then event.source will be equal to the triggering component inside the template, not the template itself. We then move up the component tree, where event.source.parent is the template itself, and event.source.parent.parent is the TemplateHolder, which is the template when it is placed on a screen. This will then fire the mouseEvent properties on the template instance itself, so you can have a template for your styling, but have the actions defined per instance.

Enclosed is an example button as well.

demo_nav_button_2018-04-27_1217.proj (6.5 KB)

12 Likes

This might be tempting, just don’t allow that.

That’s not true, the stack trace works perfectly, wouldn’t however if using eval.

I agree, but think this is easier to maintain/understand than anything else i’ve tried. IMO, the best solution would be if custom functions could be created on a the template component and reference that in a string and call it using getattr

My solution is easier to maintain. It works the same way as any other mouse event, but you can bind it to the template itself, and the instance of the template is where you set the action.

1 Like

@Kyle_Chase
awesome it works.
can you please elaborate little bit more.

I hope it helps others to understand better.