13

I'm trying to get layers to update automatically when their data source changes. I'm using R to write a shapefile with an attribute, and colouring according to that attribute in QGIS.

I want to write a new shapefile with different attribute values, and have the Qgis map colours update. Step 1 is triggering that process, step 2 is making the layer reload from the modified shapefile. Its step 2 I am worrying about here.

Other questions/mailing list chatter mentions using triggerRepaint on the layer - that doesn't work. Other suggestions include setCacheImage(None) and again that doesn't work. The layer does update eventually, but I really can't see the logic, and sometimes it happens by surprise after I've done nothing. Or maybe I did something two minutes ago.

The one reproducible way of getting it to update is to duplicate the layer from the legend menu - the duplicate always gets its data from the current shapefile, and the original layer updates itself too! So there must be some way of doing it.

I think it was working better in 2.8, but this is 2.10 so maybe there's a new bug somewhere.

Related, but doesn't work for me in 2.10:

How to automatically reload raster layers if source is changed in QGIS?

Other things I've tried:

  • layer.dataProvider().dataChanged.emit() - worked once, then not again on the same layer

I think I've tracked down why duplicating the layer works - if I create a new throwaway layer based on the updated layer and then call .triggerRepaint() on the updated layer, it updates on the map canvas:

QgsVectorLayer( layer.source(), "layer copy", layer.providerType() )
layer.triggerRepaint()

If I use a different layer source it doesn't work, so it seems to be if you create a layer object based on the same layer source...

A quick test just now with a raster layer (from a GeoTIFF), and just calling rlayer.triggerRepaint() seems to reliably update the view of the raster in the map canvas.

  • You might need to post some sample code. – Nathan W Aug 29 '15 at 1:06
  • @NathanW most of what I'm doing is from the gui - load layer, style it - then its just getting the layer and those few lines in the Python console. I'm disinclined to stick this in the framework of a plugin until I know I can make the principle work! I was hoping there'd be a quick answer ("call layer.updateFromNewDataYouFool()") but I'll fill this out with more code (including R code to make the shapefiles) later. – Spacedman Aug 29 '15 at 6:38
  • To be sure, you tried using both commands subsequently: layer.setCacheImage(None) and layer.triggerRepaint()? – Matthias Kuhn Aug 29 '15 at 8:07
  • Yes @MatthiasKuhn - although sometimes that works, but not often. I just wrote a modified shapefile, did both those things in the Python console (on the right layer), no visual update. The simplest thing that has worked 100% so far is creating a new throwaway layer object based on the original layer source as mentioned above and then triggerRepaint() on the original layer. v 2.10.1-Pisa – Spacedman Aug 29 '15 at 8:36
  • I have a suspicion that this could be related to the introduction of the OGR connection pool. Can you perform some tests if there is a difference if you replace the file on disk or edit the existing file? – Matthias Kuhn Aug 29 '15 at 18:30
4

This is related to the introduction of the OGR connection pool. [1]

Before QGIS 2.10, a file was reopened on every single access (e.g. repaint).

Since QGIS 2.10 the file handle is kept open and this means if a file is replaced the handle still points to the old file on Unix based systems.

QGIS 2.10: workaround

Unfortunately there is no API to nicely force QGIS to reopen the file in QGIS 2.10. As a workaround you can use an ugly hack:

layer.dataProvider().changeAttributeValues( { -1: { 0: 0 } } )
layer.triggerRepaint()

QGIS 2.12: solution

I just introduced a new method which will be available starting from QGIS 2.12:

layer.dataProvider().forceReload()
layer.triggerRepaint()

General approach

If you have the possibility to control how the file is being overwritten you can open the existing files with write permissions and change the content instead of replacing the files completely (delete/recreate) on disk.

[1] The connection pool was introduced to significantly speed up access to certain data sources.

  • Looks like the best solution. The .changeAttributeValues brings up an "ERROR 1: Attempt to read shape with feature id (-1) out of available range." but that's okay. – Spacedman Aug 29 '15 at 21:27
2

If you pan or otherwise refresh the map it should update.

This article says that you can use the following in PyQGIS:

myLayer.triggerRepaint()

To refresh all layers following function can be used:

def refresh_layers(self):
    for layer in qgis.utils.iface.mapCanvas().layers():
         layer.triggerRepaint()
  • As I said in my question, and as mentioned in the link I gave, triggerRepaint() doesn't work. refresh() on the map canvas doesn't work. Setting the cache image to None (which is now deprecated in the API docs) doesn't work. I just tried all these things on a newly modified shapefile layer, panned the map about, toggled vis on and off, it didn't work. "Duplicate" the layer and it updates instantly though. Have you tried these things yourself (on 2.10)? – Spacedman Aug 28 '15 at 7:35
  • I think we need @nathan-w to answer this. I haven't tried it myself... – Alex Leith Aug 28 '15 at 10:30
  • I tried on #qgis on IRC but maybe I need to post to qgis-dev mailing list... – Spacedman Aug 28 '15 at 10:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.