2

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.

PolyGeo
65.5k29 gold badges115 silver badges350 bronze badges
asked Feb 9, 2024 at 22:39

1 Answer 1

3

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()

enter image description here

answered Feb 9, 2024 at 23:32
1
  • 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 worse Commented Feb 12, 2024 at 15:43

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.