Working from the Python console in QGIS, I am trying to create an 'animation' of sorts by selecting one line after another in a layer with parallel lines, with a pause on each line. I've started from some code in PyQGIS cookbook. The problem is that the display doesn't update until the script is complete.
import time
from qgis.core import *
import qgis.utils
layer = iface.activeLayer()
features = layer.getFeatures()
for feature in features:
layer.select(feature.id())
print("Feature ID: ", feature.id())
iface.mapCanvas().refresh()
time.sleep(0.1)
layer.deselect(feature.id())
If I remove the deselect line from the above I'm left with all lines selected at the end, with it in there's no change at all.
In this post ( Can QGIS map canvas update while python script is running?) it is suggested to use threads but it is an old post and the context is different so I'm not sure it is relevant.
I am hoping to eventually build an interactive plugin, so although this seems like an artificial (ie frivolous) requirement, it is an important step in my learning.
I'm running QGIS 3.23.3.
1 Answer 1
This can be achieved by using a QTimer
instead of time.sleep
.
layer = iface.activeLayer()
# instantiate a Qtimer() object and set its interval
timer = QTimer()
timer.setInterval(300) # milliseconds
# create a dictionary with integers `0 -> number of features` as keys and feature ids as values.
# this is not strictly necessary but will allow for non-sequential FIDs
fids = [x.id() for x in layer.getFeatures()]
fid_dict = dict(zip(range(0, len(fids) - 1), fids))
# initialise a variable to store the current index
idx = 0
# the function that will be called on the `timeout` signal of the QTimer
def my_function():
# clear any currently selected features
layer.removeSelection()
# reference the idx variable defined earlier
global idx
# call the function as long as there are features left
if idx <= max(fid_dict.keys()):
# make a selection by getting the feature id from the dictionary using the index
layer.select(fid_dict[idx])
print("Feature ID: ", fid_dict[idx])
# refreshing the canvas is not necessary for this use case, but it might needed if you decide to zoom to the feature, or change the symbology of the layer within the function
# iface.mapCanvas().refresh()
# increment the index
idx += 1
else:
# stop the QTimer when there are no features left to select
timer.stop()
# connect the function to the timeout signal of the QTimer
timer.timeout.connect(my_function)
# start the QTimer
timer.start()
To stop the timer early, call:
timer.stop()
-
Thanks @Matt this works. I notice, though, that reducing the timer interval doesn't necessarily speed it up. At 100 msecs it's ok, at 50 it's jerky and below that I'm back to not getting any screen updates. I uncommented the refresh command as a test, and that made it even worsemarcp– marcp2024年02月12日 15:43:51 +00:00Commented Feb 12, 2024 at 15:43