Progress bar not being updated

Hello,

I have a property change script that takes about 9 to 13 sec to execute. I want to display the execution progress with a progress bar. But my progress bar is being updated just twice: at the beginning with 0% and at the end of script with 100%. It doesn't update inbetween.

if event.propertyName=="selected" and event.source.selected==True:
	def actualiseTable():
		...

		event.source.parent.getComponent('Progress Bar').visible=True
		total_rows = tagPaths.rowCount
		processed_rows = 0
		def actualiseProgress():
			progress_percent = int(processed_rows / total_rows * 100)
			event.source.parent.getComponent('Progress Bar').value=progress_percent
		for x in range(tagPaths.rowCount):
			processed_rows += 1
			system.util.invokeLater(actualiseProgress)

			...

	system.util.invokeAsynchronous(actualiseTable)
	
	

What is your propertyChange script doing? For this to work as intended, the whole script that the event calls should be ran in the same asynchronous thread, touching the EDT as needed with invoke later. It looks like you are running the propertyChange event script in the EDT, and then running the actualizer asynchronously. That means that every invokeLater will be scheduled for after the gui operation completes giving the illusion that the status bar is updating to 100% instantly.

Just out of curiosity, does this mean that you are running a separate read or writeblocking operation per tag instead of batching them in one call?

That is the whole script the event calls. What is an EDT?
the script just calculates duration on of Tags (Alarm)

Event dispatch [gui] thread

I'm not seeing this in your code snippet. All I see is the progress bar control. Am I overlooking it?

No you are not overlooking something. I just posted the part regarding the progress bar for a better overview and since I have already posted the rest of the code in another discussion.

Go ahead and repost the whole thing as it's currently written so we can decipher why the progress bar part isn't working. You can highlight the whole thing and use the hide details option if you want to save space in your post.

if event.propertyName=="selected" and event.source.selected==True:
	def actualiseTable():
		start=event.source.parent.getComponent('CalStart').date
		ende=event.source.parent.getComponent('CalEnd').date
		folderPath = event.source.parent.dataPath 
		folderArchiv = folderPath + "/AlarmData/alarmArchiv"    
		modifiedHistoryDataset = system.dataset.toDataSet(["ID","Description", "Start ", "End", "Duration"], [])
		tagPaths=system.tag.readBlocking(folderPath +"/AlarmData/alarmList")[0].value	
		startTimestamp=None
		event.source.parent.getComponent('Progress Bar').visible=True
		total_rows = tagPaths.rowCount
		processed_rows = 0
		def actualiseProgress():
			progress_percent = int(processed_rows / total_rows * 100)
			event.source.parent.getComponent('Progress Bar').value=progress_percent
		for x in range(tagPaths.rowCount):
			processed_rows += 1
			system.util.invokeLater(actualiseProgress)
			identification = tagPaths.getValueAt(x,0) 
			description =tagPaths.getValueAt(x,1)
			tagPath = tagPaths.getValueAt(x,2) 
			history = system.tag.queryTagHistory([tagPath],start,ende,  aggregationMode="LastValue")
			for i in range(history.rowCount):
				if history.getValueAt(i,1) == 1:  # Check if the tag value is 1
					startTimestamp = history.getValueAt(i,0)
					endTimestamp=history.getValueAt(i + 1, 0) if i<history.rowCount-1 else system.date.now()
					if startTimestamp is not None and endTimestamp is not None:
						formatted_duration = shared.Func_Dataset.calculate_duration(startTimestamp, endTimestamp)
			        	startFormatted = system.date.format(startTimestamp, "dd.MM.yyyy HH:mm:ss")
			        	endFormatted = system.date.format(endTimestamp, "dd.MM.yyyy HH:mm:ss")
			        	modifiedHistoryDataset = system.dataset.addRow(modifiedHistoryDataset, [identification, description, startFormatted, endFormatted, formatted_duration])
		modifiedHistoryDataset=system.dataset.sort(modifiedHistoryDataset, 2, False)	        
		system.tag.writeBlocking(folderArchiv, modifiedHistoryDataset)

	system.util.invokeAsynchronous(actualiseTable)

This older topic still applies:

I read this post already and I'm doing exactly what it says, isn't it?

I made this more explicit:

I see some gui stuff being touched from the asynchronous thread. Another thing is that most of this will run really fast, the slow stuff will be the read and write blocks, so the incrementations are best placed directly after those. Here is a quick and dirty untested refactor you can try:

Refactored Code Sample
# Run only when the selected property changes to true
if event.propertyName=="selected" and event.newValue:
	
	# Asynchronous function to assemble table data
	def actualiseTable():
		
		# Function for touching the gui thread when updating the progress bar
		def actualiseProgress():
			
			# Total = total tagpath iterations plus two (Initial write and final write)
			totalIterations = tagPaths.rowCount + 2
			
			progress_percent = int((processed_rows / total_rows) * 100)
			event.source.parent.getComponent('Progress Bar').value = progress_percent
		
		# Complete the folder path
		folderArchiv = folderPath + "/AlarmData/alarmArchiv"    
		
		# Initialize a modified history dataset with headers and an empty body list
		modifiedHistoryDataset = system.dataset.toDataSet(["ID","Description", "Start ", "End", "Duration"], [])
		
		# Read the tag value
		tagPaths=system.tag.readBlocking(folderPath +"/AlarmData/alarmList")[0].value	
		
		# Trigger progress bar update
		system.util.invokeLater(actualiseProgress)
		
		# Create an empty timestamp variable
		startTimestamp=None
		
		# Create variables for various row counts
		total_rows = tagPaths.rowCount # Alarm list
		processed_rows = 0 # independant variable for enumerating the iteration
		
		# Iterate through the alarm list
		for x in xrange(tagPaths.rowCount):
			processed_rows += 1 # increment enumeration
			
			# Get various variables variables from the tagpaths dataset
			identification = tagPaths.getValueAt(x,0) 
			description =tagPaths.getValueAt(x,1)
			tagPath = tagPaths.getValueAt(x,2) 
			
			# Query the tag history
			history = system.tag.queryTagHistory([tagPath],start,ende,  aggregationMode="LastValue")
			
			# Trigger progress bar update
			system.util.invokeLater(actualiseProgress)
			
			# Iterate through the history and modify the history dataset as needed
			for i in range(history.rowCount):
				if history.getValueAt(i,1) == 1:  # Check if the tag value is 1
					
					# Get the timestamps from each row of the history
					startTimestamp = history.getValueAt(i,0)
					endTimestamp=history.getValueAt(i + 1, 0) if i<history.rowCount-1 else system.date.now()
					
					# If both timestamps exist for an entry...
					if startTimestamp is not None and endTimestamp is not None:
						
						# Format the timestamps
						formatted_duration = shared.Func_Dataset.calculate_duration(startTimestamp, endTimestamp)
			        	startFormatted = system.date.format(startTimestamp, "dd.MM.yyyy HH:mm:ss")
			        	endFormatted = system.date.format(endTimestamp, "dd.MM.yyyy HH:mm:ss")
			        	
			        	# Add the row to the history dataset
			        	modifiedHistoryDataset = system.dataset.addRow(modifiedHistoryDataset, [identification, description, startFormatted, endFormatted, formatted_duration])
		
		# Sort the dataset and write it to a tag
		modifiedHistoryDataset=system.dataset.sort(modifiedHistoryDataset, 2, False)	        
		system.tag.writeBlocking(folderArchiv, modifiedHistoryDataset)
		
		# Trigger the final progress bar update
		system.util.invokeLater(actualiseProgress)
				
	# Make the progress bar visible
	event.source.parent.getComponent('Progress Bar').visible=True
		
	
	# Get start and end dates
	start = event.source.parent.getComponent('CalStart').date
	ende=event.source.parent.getComponent('CalEnd').date
	
	# Get the data path
	folderPath = event.source.parent.dataPath 
	
	# Call a long running funcion asynchronously
	system.util.invokeAsynchronous(actualiseTable)

Just tried it, it still updates only at the beginning with 0% and at the end with 100%

Thank you anyway :slight_smile:

It could be an integer division problem. Anything fractional in integer division evaluates to zero, so try rewriting the calculation like this:

progress_percent = int(float(processed_rows / total_rows) * 100.0)

My hypothesis is that the fractional integer division is always evaluating to 0 * 100 = 0 until, finally at the end, the integer division evaluates to 1 * 100

Yes this sounds plausible, but adding the "float" doesn't solve the problem

Uggh. The way I wrote it, inside the float conversion is still integer division. float(0) * 100.0

Try it like this:

progress_percent = int(float(processed_rows) / float(total_rows) * 100.0)

You probably only need to convert the numerator, but... better to have it and not need it at this point.

1 Like

It works, thank you so much :heart:
Have a great day

1 Like