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?
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.
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)
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