Contents
Strategic Partner Links
Sepasoft - MES Modules
Cirrus Link - MQTT Modules
Resources
Knowledge Base Articles
Inductive University
Forum
IA Support
SDK Documentation
SDK Examples
One of the main limitations with invokeLater is that you can not pass parameters to the function that will be called. Parameters need to be initialized and determined elsewhere in your script, usually in the function definition.
The Threading Library has a Timer function that works in a very similar fashion to invokeLater. The main difference is that you can pass parameters to the function parameter when calling the Timer. Take a look at Python's official documentation for more details on the Timer object.
The example below will again call two Message Boxes with a three second delay between them. However, the text that is defined in the second Message Box is specified when starting the Timer.
from threading import Timer # Create a function that will be called by the Timer. def runThisLater(param): system.gui.messageBox(str(param)) # Constructs a Timer object that runs a function after a specified interval of time has passed. # Note the start() at the end: this is required to start the Timer. Don't forget this part! Timer(3.0, runThisLater, ["Stop Waiting, I'm done"]).start() # Bring up another Message Box. This will appear before the "All Done!" message, because of the Timer system.gui.messageBox("Waiting")
Another benefit to using the Timer, is you can cancel the execution of the Timer's action using the cancel() function, but it only works if the Timer is still in its waiting stage.
The simplest approach to pausing a script is to use the sleep() function in Python's Time Library:
from time import sleep # This will pause execution of the script for 3 seconds. After that time, the script will continue. sleep(3) print "I'm awake!"
The While Loop is another simple approach to adding a delay: simply keep looping until the other event occurs. As always, you will want to take steps to ensure that an infinite loop never occurs: easiest by initializing a counter variable before iterating in the loop, and breaking out if the counter reaches a certain value.
counter = 0 while not otherEvent: checkOtherEvent() counter += 1 # Use this to break out if the event takes too long. You'll need the rest of your script to account for this possibility. if counter >= 10000: print "Took too long. Leaving the While Loop" break
While the sleep() and while loop functions are simple to use, they can cause problems by locking up the client and because of this, we generally recommend avoiding them if possible. Typically, system.util.invokeLater on a delay can accomplish the same task.
Note, that there are use cases for both approaches outlined below especially so in regard to the While Loop. However, neither function should be used to force a delay in a Client.
Using either of these methods to pause or delay a script can lock up the entire client, which may be very dangerous.
If used on a component, these approaches could lock up the Client or Designer. Scripts called from a component run in the same thread that the Client and Designer use to refresh the screen. Whenever a script on a component triggers, the screen is unable to refresh until the script finishes. In most cases, this is so fast that no one notices. If your script is intentionally calling sleep() or using a long running While Loop, then the Client will lock up for the duration.
Additionally, these approaches run an extended period of time, and block other component based scripts from executing. They do not yield to other scripts while waiting.
Because of these two reasons, the sleep() and While Loop functions can cause your clients to appear unresponsive, when really they are just running a script that prevents the screen from refreshing.
As mentioned earlier, the system.util.invokeLater function (also mentioned on this page) can provide the same functionality. If a sleep() or While Loop function must be used, then they should be called from system.util.invokeAsynchronous to prevent blocking any other scripts.
In many cases, you may need to show your users a large number of entries on a Table or Template Repeater. As more entries are added to the system, adding a search field that can filter results becomes more appealing. Furthermore, being able to filter as the user types (as opposed to forcing them to hit enter every time) adds some polish to the window. However, if the entries are backed by the database, you will not want to run a query every time a user presses a key. Instead, it would be preferable to wait for a delay in input, and run one query to limit strain on the database.
The following is not a traditional example that you would find here in the User Manual, and assumes you are comfortable with many concepts in Ignition.
One approach to adding a delay involves two scripts:
Additionally, we will need some criteria that our script can use to determine if it is safe to run our query. You can have multiple criteria here, but for the sake of simplicity we will use the Text Property on the Text Field. This way we will know what the user typed before the delay, and then can compare it back to the Text property after the delay. If the Text on the component after the delay is different, then we don't want to run the query as the user may still be typing.
The keyReleased script is shown below. It is using a .05 second delay, but could of course can be modified. We're creating a new Timer object every time a key is released, so there will be many of these scripts firing as the user types. However, they are fairly lightweight, and don't directly interact with the database, so having many executions of this script isn't taxing on the system.
# We're using the Timer object for this because we want to pass parameters to the function that will run after the delay. from threading import Timer # Ignoring arrow keys. if (event.keyCode < 37 or event.keyCode > 40): # Grab the text in the component currently. currentText = event.source.text # Calls the function after 0.5 seconds, and pass the currentText. Timer (0.5, event.source.sendingQuery,[currentText]).start()
Below is the sendingQuery script. As mentioned, this script will determine if the user ceased keyboard activity before running a query against the database. This example is using a Custom Method on the Text Field, so it uses the self
argument to reference the source component.
def sendingQuery (self, oldText): # Read the value of the text property, since it may have changed during the delay. currentSearchText = self.text # If the text before the delay is the same as the after the delay, there has been a pause in keyboard activity, so we should run the query. if (currentSearchText == oldText): # If the text field isn't empty, then we need to filter the results with a WHERE clause and our criteria. if(currentSearchText != ""): newQuery = "SELECT * FROM employees WHERE CONCAT(firstname, ' ', lastname) like '%" + currentSearchText + "%'" # If the Text Field Is blank, then we should run the query again, but this time without a filter, so all results appear. else: newQuery = "SELECT * FROM employees"
We can now run our query in between user keyboard activity.