Button refreshing text as script runs

running 7.6.4-rc3

If I bind button with an action to update its text or even another label during execution, it only updates at the end of execution not during execution.

Eg if I have a button and bind the following to the actionperfromed event it does not behave as expected.

import time
event.source.text=“Waiting 2 seconds”
time.sleep(2)
event.source.text=“Waiting 5 seconds”
time.sleep(5)
event.source.text=“Finished”

Button press is delayed 7 seconds and then displays “finished”.

Is there a way to get the object to update immediately?

I have tried

event.source.repaint()
event.source.parent.repaint()

I have partial success if I do a

event.source.update(True) it refreshes immediately but crashes due to me not knowing what java.awt.Graphics object to pass it.

Regards

Tarek

Because your script is running in the EDT. The EDT controls painting as well. To elevate into a background thread, you will need to do the following:


def doLater():

	import time
	event.source.text  = "Waiting 2 seconds"
	time.sleep(2)
	event.source.text  = "Waiting 5 seconds"
	time.sleep(5)
	event.source.text  = event,"Finished"
system.util.invokeAsynchronous(doLater)

Technically, we should be moving the setting of text back into the EDT to prevent thread locks. It gets messier, but should look something like this.

In app.gui, place the following method

def setText():
	evt.source.text = text

In you button actionPerformed script, place the following

def doLater(event=event):
	#Initialize global variables to pass into back to edt
	global evt
	evt = event
	global text
	text = ""
	
	#import stuff
	import time,system
	from app.gui import setText

	text="Waiting 2 seconds"
	system.util.invokeLater(setText)
	
	time.sleep(2)
	text="Waiting 5 seconds"

	system.util.invokeLater(setText)
	time.sleep(5)

	text="Finished"
	system.util.invokeLater(setText)
system.util.invokeAsynchronous(doLater)

I like Kyle’s approach but I implemented it in a different style below.

Kyle’s script is written like he’s using Python 2.1, where nested scopes are disabled by default. Heh, most people that have been scripting in Ignition for awhile are probably writing Python 2.1 code.

In Ignition 7.4.0 Python was upgraded to Python 2.5 which allows nested scoping by default. This means that Python functions, classes and methods can access variables in outer scopes without having to do anything special. This can simplify and make code nicer.

Here’s a rewrite of Kyle’s script using nested scoping. Put this script in the actionPerformed event script of the button. In this style there is no need for global variables or a function in app.gui.

def doLater(event=event):
	def setText():
		event.source.text = text

	import time,system
	
	text="Waiting 2 seconds"
	system.util.invokeLater(setText)
	time.sleep(2)
	
	text="Waiting 5 seconds"
	system.util.invokeLater(setText)
	time.sleep(5)

	text="Finished"
	system.util.invokeLater(setText)
system.util.invokeAsynchronous(doLater)

Caveat: In Ignition nested scoping doesn’t work in top level Python functions in the Script Module Editor and in event scripts but the IA developers plan to fix this in Ignition 7.7. So for example the doLater function above could not access variables outside its scope because it is a top level function.

Thanks for the solution…now that I have learnt that the scripts run in the EDT it all makes sense.

I like nmudge’s nested solution to keep all the code together.

Will be nice to see the top level scoping caveat going away.