Skip to main content
Version: 8.1

Adding a Delay to a Script

Overview

In some cases, having a script execute after a delay is preferable. A common use case is waiting for some event elsewhere in the system to finish: a Tag change script executes that needs to wait for a new value from a separate Tag. One approach to this is to trigger our script, and then hold or wait until the other event occurs. On this page, we'll take a look at a couple of different approaches to this problem.

It is important to note that pausing a script can cause your client to lock. It is often preferred to look for another event to trigger the script you need. For the example above where we are waiting on a Tag to change, you might be able to use the Tag change to fire your script instead of waiting in the original script. It is up to you to determine the best trigger based on what exactly your script does.

Using the system.util.invokeLater Function

The system.util.invokeLater function is a great way to add a delay mid-way through the script. Simply create a function that represents all of the work that should occur after the delay, and pass the function to invokeLater, along with a delay period.

The example below calls two Message Boxes: once initially when pressed, and the other after a three second delay.

Python - Two Message Boxes
message = "All Done!"

# Create a function that will be called by invokeLater.
def runThisLater():
system.gui.messageBox(message)

# Call invokeLater with a 3000ms delay. Note that our function will not run immediately
# because invokeLater always executes once the rest of this script is complete.
system.util.invokeLater(runThisLater, 3000)

# Bring up another Message Box. This will appear before the "All Done!" message, because of invokeLater.
system.gui.messageBox("Waiting...")

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.

Using a Timer from Python's Threading Library

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.

Python - Python Threading 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.

Calling time.sleep from Python's Time Library

The simplest approach to pausing a script is to use the sleep() function in Python's Time Library:

Python - Sleeping the Code
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!"

Using a While Loop

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.

Python - While Loop Safeguard
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

Approaches to Avoid - Locking the Client

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.

danger

Using either of these methods to pause or delay a script can lock up the entire client, which may be very dangerous.

Reasons to Avoid time.sleep and While Loops

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.