Problem with visionWindowOpened event

First a bit of history :slight_smile: We had a problem with running code when a window was opened. At first we put the code in internalFrameOpened, but this event only fires once when the window is first loaded - if the window is loaded from the cache this event doesn’t fire. (Disabling caching is not an option due to the number of users and slow speed of parts of the network.)

We then used the internalFrameActivated event. This worked but had an additional problem - if you click on another window (such as a docked header) and then back on the first window, the event fires again. We got round this by opening the window with a custom property called runOnce set to 1. The code in internalFrameActivated was set to only run if runOnce was set to 1. The last thing this code did was set runOnce to 0, so it would not run again until the window was closed and reopened.

This worked but always felt a bit messy, so we were pleased to hear about the new visionWindowOpened event in this post. It looked like this would solve the problem, but it seems to have a serious issue.

With both internalFrameOpened and internalFrameActivated, if you run an invokeLater command (to change something on-screen using the result of a binding for example), the invokeLater runs after the bindings are read (it must be being put on the stack after the request to read the bindings). With visionWindowOpened however, the invokeLater runs before the bindings are read. (It is actually possible to get it to work by nesting 2 invokeLater calls, but this doesn’t seem very practical!)

Is this intentional? It would seem sensible that using invokeLater with visionWindowOpened should work as ‘normal’ i.e. after any bindings have been read.

I have attached a demo window to show this in operation.
visionWindowOpened.proj (7.07 KB)

Ok, I see what you’re saying. However, your initial premise is wrong.

There is no such thing as “the bindings being read.” There is no “after” the bindings have been read. Bindings get started up, and then generate values asynchronously.

What you’re doing (using [tt]invokeLater[/tt] to run code that expects to be run after queries have completed) is a race condition and unsafe. You can convince yourself of this by changing your query to one that runs a bit slower, for example SELECT SLEEP(3) if you’re using MySQL. Then you’ll notice that all of your scripts run before the binding comes back with a value.

It is true that the [tt]internalFrameActivated[/tt] event comes after the [tt]visionWindowOpened[/tt] event, which is why in the scenario you created, the code in [/tt]visionWindowOpened[/tt] and the code in it’s [/tt]invokeLater[/tt] run before the query returns, but only because your query happens to run quickly. If the database connection pool was a bit backed up, or the database was under load, you’d see that code going back and forth between running before and running after the query finished, because it’s a race condition.

We intentionally put [tt]visionWindowOpened[/tt] before the bindings start up and after the passed parameters have been set, as this will be the least racy spot: you know that no bindings have evaluated yet. We thought this was better than putting it later, when some bindings may have completed.

Runnning code in an [tt]invokeLater[/tt] from a [tt]visionWindowOpened[/tt] will run after non-asynch bindings have executed (expression, property), but for async bingings, the only way to be sure is to put a property change event on the component with the binding itself to see when the first value comes in.

In case that was confusing, I can sum it up as: Yes you’re right about [tt]visionWindowOpened[/tt], but you were wrong about [tt]internalFrameActivated[/tt] in the first place. They both might run before bindings produce a value. Maybe we should step back a bit: what are you trying to accomplish?

Hi Carl,

Thanks for explaining things, that has really helped us understand how things work.

Our main use case is getting code to reliably run once every time a window is opened - it seems we can achieve this by putting our code into visionWindowOpened. If we have any code that relies on a binding we will put this into the propertyChange event of the object with the binding.

The last thing we’re trying to get working is setting the focus to a particular control when the window opens. We’ve tried putting the following code into both internalFrameActivated and visionWindowOpened but it doesn’t seem to work:def requestFocus(event=event): import system system.gui.getParentWindow(event).getComponentForPath('Root Container.txtMessage').requestFocusInWindow() system.util.invokeLater(requestFocus)We also tried running this code from a button but it didn’t seem to work there either. Any ideas?

Do you get an error when running it from a button?

No, nothing at all seems to happen :scratch:

That is weird. That means the component exists and allows focus. Did something else on the window get focus? Is there another script somewhere that does the same thing but for a different component?

This doesn’t have anything to do with where the script is called from. We discovered that although requestFocusInWindow() works for simple controls like a Text field, you cannot use it with more complicated controls such as the Text Area or the Popup Calendar, since these controls are themselves containers. If you use requestFocusInWindow() on one of these controls it sets the focus on the container, meaning you cannot see the focus on the screen. (Pressing then moves the focus to the contents of the container.)

We have written a quick function that allows you to set focus on either simple or more complicated controls - you can find it here.