Help on Timing on Initial Window Populate

I am trying to populate a form with SQL data when a from opens.

  • On the internalFrameActivated Event I run a call to a Root Container Script called Populate

event.source.getComponentForPath('Root Container').Populate(system.gui.getParentWindow(event).getComponentForPath('Root Container').Vessel)

  • In the Populate Script, I run a SQL Query that is based on a Custom Property for the window that is tied to a PLC Tag.
  • I have tried to reference the Custom Property directly and by reference the Property when the data is linked to a label.
  • The issue that I am having is that SQL Script is running before the Custom Property is updated, so I am getting data from the previous instance of the form and not the current instance.

So my question is how do I force the SQL Script to run when the Custom Property is updated on Window open?

I think you’re overcomplicating things a bit, but then I don’t have a good understanding of what you’re trying to accomplish.

What is the result of this SQL Script? Why does it need to be called from the internalFrameActivated event?

I would have a custom property on the root container In_FormData or similar naming wise that is selecting all your form data.

Then under each component, a custom property that I usually call initialValue, and it’s an expression of the form try({Root Container.In_FormData}[0, 'someColumn'], 'defaultValue'). Then I bind the appropriate component property to initial value.

You could try writing a script to populate each component but you will probably want a naming convention on your components to know 1) which components need to be populated and 2) what property of said component needs the populating.

I see you mentioned you are getting the old form data. What is the windows caching policy? Try setting it to Never.

I have a Window that I want to populate Fields and Labels with values from the database based on the value of a Tag from a PLC that is set a Custom Property for the Window.

In this case, I have an Order Number in the PLC that is in a Vessel Structure, so every Vessel Detail should produce unique values from the Database.

I am using the internalFrameActivated Event because that is the only way that I have found to trigger something on Window Open. My Python/Ignition Scripting is still weak and heavily influenced by years of only having VBA for my scripting.

You should be able to bind each Field to a query which uses the custom property, the bindings will update once when the window opens. No need for using the internalFrameActivated event in this case.

:face_vomiting: I feel your pain

1 Like

Thanks for pointing me in a new direction!!!

I will miss my hammer but this screwdriver does seem useful.

I’ll chime in here and say that it’s far more efficient the original way you were doing this, than to be calling the “same” query 50 times (or however many fields you have). Travis Cox did a presentation on improving performance, and this was one thing he specifically mentioned. Personally, I would continue down the first route, but you don’t want to use that Window event, you would put your script inside the propertyChange event and look for a change in your passed in property. And as @bkarabinchak.psi already mentioned, make sure to name your components usefully, for example with the sql field name within it, so that you can easily write to these components within a loop.
E.g.

ds = system.db.runPrepQuery('SELECT ....')
pds = system.dataset.toPyDataSet(ds)
fieldNames = list(ds.getColumnNames())
for field in fieldNames:
    event.source.getComponent("UserInput_{}".format(field)).text = pds[0][field]

** '{}'.format(field) will only work for Jython 2.7 (Ignition v8 and above). Use '%s' % field for v7
*** Not tested

2 Likes

My understanding was that the fields where different but all used the same information in the where clause.

I’m wondering if this is the best approach or if populating a dataset and then using expression bindings to lookup the values wouldn’t be better?

The values are just meant to be initial values from my understanding, so a binding isn’t appropriate in this case (imo). A bit like setting the “enter text here” value of an input field

I would use a single dataset property in the root container with a non-polling named query binding to obtain the relevant row from the DB. Then each field would use an expression binding to pick out the correct column value from the dataset. Wrap each of those in a try() so any initial nulls are ignored.

No scripting required. Bindings will all fire shortly after window load when the dataset is populated from the query binding (which itself fires with the passed-in tag parameter). The bindings won’t fire again, leaving the fields alone for user editing.

3 Likes

Ok Phil, you win this time (and every other time, let’s be honest)

2 Likes

Hey @pturmel copied my answer! (should I feel honored? or perhaps I learned it from him previously?)

1 Like

Heh. Missed it. Didn't re-read the whole topic. Your more complete recommendation has the advantage that you can compare each entry field's current value property to the corresponding initial value property to derive an "edited" status, which can then drive styling to show what has changed. I've been doing that where appropriate for ages, so you might have learned it from me.

1 Like