Passing multiple values into a window as parameter

Hi. I don’t think this currently exists, but part of my application requires going through a series of popup windows and sending data back and forth. Ideally, I would love to be able to create a python class and pass the object into the window and modify it along the way.

I know I could make a UDT, but I don’t want the data being shared across clients. Strictly local variables are what I would like, and I would like to do this without having to add a dynamic property to every window for every single value I want to use.

Open Screen1(and pass in myObj)

myObj.field1 = ‘abc’
myObj.field2 = 123

Open Screen2(and pass in myObj)

myObj.field3 = 1

Open Screen3(and pass in myObj)

(access field1, field2, and field3 and do something)

Any ideas?

Thanks!

why not a dataset?

Datasets are immutable, so that doesn't tie anything together.[quote="karnick"]Strictly local variables are what I would like, and I would like to do this without having to add a dynamic property to every window for every single value I want to use.[/quote]Well, you may need to add at least one . . .

It is possible to add custom properties to root containers and other components that don't fit the custom property editor's suggestions. In particular, you can add a property of type 'java.io.Serializable' and it will happily accept an assignment of a python dictionary. Assign that dictionary to properties in multiple windows and voila, you have a namespace shared across those windows (or components).
Start by manually adding such a property to your windows using the Script Console:from com.inductiveautomation.factorypmi.application.binding import DynamicPropertyDescriptor import java.io.Serializable dprop = DynamicPropertyDescriptor('MyAnything','Anything Serializable',java.io.Serializable,dict()) w = system.gui.getWindow('Main Window') w.rootContainer.dynamicProps.put(dprop.name, dprop)Make sure it's a new 'dprop' for each window! Then in event scripts you can assign the dictionary in one window to another.
I haven't tested all of this (you read the warning above, right?), but be aware that propertyChange events won't fire when you add or remove dictionary items.

Hello,

Phil’s suggestion will work in clients, but not in designers. The reason is because objects created with Python classes are unserialized differently than objects created with Java classes. So opening a window that has serialized Python objects will throw exceptions.

Using an object that is created with a Java class will work in designers and clients. Here’s an example using Java’s HashMap:

from com.inductiveautomation.factorypmi.application.binding import DynamicPropertyDescriptor import java.io.Serializable from java.util import HashMap dprop = DynamicPropertyDescriptor('MyAnything','Anything Serializable',java.io.Serializable,HashMap()) w = system.gui.getParentWindow(event) w.rootContainer.dynamicProps.put(dprop.name, dprop)
I think it would be very good if Ignition supported Python dictionaries and Python lists as custom properties directly. If you think so then vote for this feature request: ideas.inductiveautomation.com/fo … ists-as-cu

Best,

This is rather old, but…
Are python functions serialisable? I want to pass a function to a confirmation popup Window (the standard confirmation dialogue buttons are too small for operators to press on a touch screen) which I can then call if the operator presses “Yes” for example.

How would you actually use this code to pass something to another window?

Try using .putClientProperty() to attach the callback function to the popup window. The buttons would extract them and call them. You’d probably need to construct the function as if for use with invokeLater–arguments captured in closures.

1 Like

Awesome! I got a simple print "Hello Ignition" working. Thanks Phil, as always :slightly_smiling_face:

On button to call confirmation popup:

def fn():
	print 'Hello Ignition!'

window = system.nav.openWindow('Popups/ppuConfirmAction')
system.nav.centerWindow(window)

window.putClientProperty('functionOnYes', fn)

On ‘Yes’ button in confirmation popup window:

window = system.gui.getParentWindow(event)
fn = window.getClientProperty("functionOnYes")
fn()

To pass a parameter to use in the function however, I do this (same thing as required when you want to pass arguments into functions called from system.gui.createPopupMenu):

deviceTagPath = system.gui.getParentWindow(event).rootContainer.DeviceTagPath
	
def fnOnYes_create(deviceTagPath=deviceTagPath):
	def fnOnYes(deviceTagPath=deviceTagPath):
		system.tag.write(deviceTagPath  + '/Run Time Reset CMD', 1)
	return fnOnYes


message = 'This will reset the runtime hours. Are you sure?'
shared.util.gui.confirmAction(event, message, fnOnYes_create())
2 Likes

If you spend some time learning about python closures, you’ll find that the machinations in the latter example aren’t really necessary… (:

2 Likes

Hmm, so I could use something like this instead? Could I go one step further and remove the create function’s argument as well? (can’t test at the moment, on phone) from what I read closures only work within nested functions, so I presume not

deviceTagPath = system.gui.getParentWindow(event).rootContainer.DeviceTagPath
	
def fnOnYes_create(deviceTagPath=deviceTagPath) :
	def fnOnYes():
		system.tag.write(deviceTagPath  + '/Run Time Reset CMD', 1)
	return fnOnYes


message = 'This will reset the runtime hours. Are you sure?'
shared.util.gui.confirmAction(event, message, fnOnYes_create())

I’m guessing Phil means something more like this:

from functools import partial

deviceTagPath = system.gui.getParentWindow(event).rootContainer.DeviceTagPath

message = 'This will reset the runtime hours. Are you sure?'
shared.util.gui.confirmAction(event, message, partial(system.tag.write, deviceTagPath + '/Run Time Reset CMD', 1))
1 Like

No, not at all. A function definition captures variables in outer local scopes so long as they are explicitly used in the function. Captured at the point of definition. Put this in a button’s actionPerformed event:

logger = system.util.getLogger('SomeLogger')
def showEvent():
	logger.infof("Delayed event display: %s", event)

system.util.invokeLater(showEvent, 1000)

Put it in preview and click. You will see the event object printed on the console a second later. The event variable supplied as a local variable is included in showEvent’s closure. Along with logger. Simpler than explicitly defining function arguments and supplying them via partial. I generally don’t like using def in dynamic locations, as there appears to be a time penalty. But for passing arguments to spawned tasks, it is really readable–too convenient to not do.

(I converted later.py to this technique once I fully grokked it.)

2 Likes

Ohh, so I dont need the whole function-creating-function mechanic at all:

deviceTagPath = system.gui.getParentWindow(event).rootContainer.DeviceTagPath
	
def fnOnYes():
	system.tag.write(deviceTagPath  + '/Run Time Reset CMD', 1)


message = 'This will reset the runtime hours. Are you sure?'
shared.util.gui.confirmAction(event, message, fnOnYes)
1 Like