Version: 2.18, and I cannot upgrade to 3.
I am adapting a script I found elsewhere on stackexchange which is supposed to indefinitely refresh all layers in the TOC at one-second intervals. This is the code as I execute it:
from threading import Timer
import qgis.core
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
def repainit(_iface):
print("refreshing")
for layer in _iface.legendInterface().layers():
if layer.type() == QgsMapLayer.VectorLayer:
print(layer.name())
layer.triggerRepaint()
But when I try to get it going with:
rt = RepeatedTimer(1, repainit, iface)
The entire application crashes (qgis-bin.exe has stopped working) after printing the first layer name. Does this have something to do with something not being thread safe here? I don't see anything that would be.
I even tried this (more hacky) method, but it doesn't actually seem to update the layers at all, because the database driving them is updating but the changes are not being reflected.
import qgis.core
import time
reservoir = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_storage.geom"][0]
controller = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_controller.geom"][0]
roundpipe = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_distrib_elem_pipe_round.geom"][0]
i=0
while i < 30:
i+=1
reservoir.triggerRepaint()
controller.triggerRepaint()
roundpipe.triggerRepaint()
iface.mapCanvas().refresh()
time.sleep(1)
print(i)
UPDATE #1:
I have made some progress, the following script actually allows me to target specific layers. But it behaves very strangely. It runs indefinitely, but at some point spits out that a minidump file has been written to the C drive. If i just ignore this error, the layers continue refreshing as normal, but I am forced to quit QGIS if I click "OK" on this error.
from threading import Timer
import qgis.core
reservoir = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_storage.geom"][0]
controller = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_controller.geom"][0]
roundpipe = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_distrib_elem_pipe_round.geom"][0]
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
def repainit(_iface, _layer):
layer = _layer
if layer:
layer.triggerRepaint()
print "repaint {}".format(layer)
rt_r = RepeatedTimer(1, repainit, iface, reservoir)
rt_c = RepeatedTimer(1, repainit, iface, controller)
rt_rp = RepeatedTimer(1, repainit, iface, roundpipe)
#rt.stop()
UPDATE #2
I haven't solved the problem of iterating dynamically across all layers in a loop, nor have I been able to get the canvas itself to refresh, also refreshing all layers. What I have been able to do however is modify the original script to refresh select layers, which I have named
- utn9_ntw_feat_device_storage
- utn9_ntw_feat_device_controller
- utn9_ntw_feat_device_distrib_elem_pipe_round
Like so:
from threading import Timer
import qgis.core
reservoir = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_storage"][0]
controller = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_device_controller"][0]
roundpipe = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == "utn9_ntw_feat_distrib_elem_pipe_round"][0]
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
def repainit():
reservoir.triggerRepaint()
controller.triggerRepaint()
roundpipe.triggerRepaint()
rt = RepeatedTimer(1, repainit)
#rt.stop()
I have let it run for about 20 minutes now and no crash has occurred.
I am not marking this as solved because I was looking for a way to refresh all layers in table of contents, and looping through them seems to cause a crash. This is an OK solution for me in the meantime, because I know the layers I want to refresh ahead of time.