I have this script on a button to copy data to a PLC from a database. Since this script takes 10 - 20 seconds to run, i would like to have the progress bar show progress. Issue I have is the progress bar updates only when the script has completed. Is there anyway to get value outside of the script to update as the script is running?
Event scripts run in the foreground. Painting the UI runs in the foreground, so it can't happen until your script finishes. You need to wrap up your long-running operation into a function, and then run that with invokeAsynchronous. That function can send updates back to the foreground with invokeLater.
You must not ever assign to component properties or call component methods from an asynchronous (background) function. You can really scramble Swing and/or deadlock it. Take a look at this thread for a discussion of possibilities:
Naturally, I recommend using the callAsync()
and assignLater()
functions from my later.py
script module.
A long-standing general rule of thumb, applicable to every GUI programming language I’ve ever used, is that foreground event scripts should never run longer that 1/10 of a second. Anything longer should be asynchronous. That would include any system.* call that might need to request something from the gateway. If you let events run longer, you will have user-notice-able UI freezes.
Resurrecting a bit of an old thread here, but am trying to use Async and InvokeLater functions and had a question on using them.
-
- For InvokeLater, how does that work if the user navigates away from the display that the InvokeLater is trying to do an update to (ie a progress bar or status label.text property). Does it just drop the commands since the referenced objects are no longer on the active display? Our navigation is via a tab strip header with Swap window option.
Here’s my code snippet. For this application, if the user navigates away and the script doesn’t finish, it’s fine, this is an on-demand batch report generation display, if they don’t wait for the report to finish, they just would need to come back and try again and be more patient! But ideally if they navigate away, I don’t want any errors popping up on the GUI when the async finishes in the background.
lblGenerationStatus = event.source.parent.getComponent('lblGenerationStatus')
lblGenerationStatus.text = "Generating Report"
lblHiddenFilename = event.source.parent.getComponent('lblHiddenFilename')
def batchGen():
(strFilename, strStatus) = WM_Reports.GenerateBatchReport(batchParams)
def UIFunc():
lblGenerationStatus.text = strStatus
lblHiddenFilename.text = strFilename
system.util.invokeLater(UIFunc)
system.util.invokeAsynchronous(batchGen)
When a Swing object is held in a variable in a background or other task, and the Swing object closes or is otherwise deleted from the display, it continues to exist “disconnected” from the UI until all references are gone. In your case the component’s text properties will be updated, invisibly, just before the components themselves are discarded. If enough time has passed by the time they execute, those component’s parent properties would be null and other bindings/event routines discarded.
You can edit your posts. And you need to mark your code sections so they format properly (with indents). Use triple back-quotes above and below your code. It should look like so in the post editor:
```
Pasted code or other plain text (logs...)
```
Phil,
Thanks for the feedback, on the code (and on posting).
So it sounds like I shouldn’t get any errors after navigating away, at least any due to the Async/InvokeLater functions. While testing my display, I was pretty sure I was getting some errors when I navigated away but not when I stayed on the display, but I haven’t been able to reproduce them, so might have been unrelated to the Async and a typo in a reference.
Well, any components passed to the async script will be present, but if you try to look up other components in the later script, they probably won't be there. That can generate errors. try...except...
is your friend. (: