qgis / QGIS

QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)
https://qgis.org
GNU General Public License v2.0
10.09k stars 2.93k forks source link

"Reload layer" in context menu for each layer in Layers list #31507

Open andrewharvey opened 4 years ago

andrewharvey commented 4 years ago

Feature description.

In the Layers list, for each layers context menu I'd like a Reload layer next to remove layer.

Additional context This is useful when the file has changed on the disk and you just need QGIS to reload the layer instead of removing the layer and reading it (which would loose all the properties you may have set, which is a pain).

I'm not sure if QGIS is meant to auto-reload but I don't think I've ever seen this happen, at least on Linux.

DelazJ commented 4 years ago

By file changed, you mean new features (I think that already works) or new fields? There's a "Change data source" entry in the context menu (3.9 I think). Selecting the original file as replacement in this case would do the trick here.

andrewharvey commented 4 years ago

I just mean if the file changed in any way.

I just tested GeoJSON and it does correctly auto-reload as the file changes, but not for raster, if it auto-reloaded then I guess there is no need for a manual reload layer, but currently it doesn't autoreload rasters (at least my testing on linux).

I don't have 3.9 so can't test that at the moment, it sounds like it would help, but still half the pain is needed to select the new file, a reload feature would avoid that.

AlisterH commented 4 years ago

currently it doesn't autoreload rasters (at least my testing on linux)

It is not specific to linux. A simple example where you need to reload it is after using the gdal_rasterize processing algorithm.

AlisterH commented 3 years ago

I think this would be fairly easy to implement in a plugin if it wasn't desired in core qgis. To reload a raster layer (e.g. after using gdal_rasterize) I can select it and run this in the python console:


for layer in iface.layerTreeView().selectedLayers():
    if layer.type() == QgsMapLayer.RasterLayer:
        rdp = layer.dataProvider()
        rdp.setEditable(True)
        rdp.setEditable(False)
        layer.triggerRepaint()
ATL-Flaneur commented 3 years ago

This would be a very helpful feature. I'm in the midst of GDAL-based coding to generate rasters and using QGIS to view the results. It would save a lot of time if there was a hot key or menu command to reload the raster currently being viewed.

jreniel commented 1 year ago

I definitely could use this feature too!

bhjolly commented 1 year ago

I've been wanting this for years, especially when I spend a lot of time setting up the display. Easiest way is to save/reload the project but that's a bit convoluted

bierdosenhalter commented 1 year ago

please implement this

nicogodet commented 1 year ago

@bierdosenhalter

I will quote someone here :

"Things don't happen "magically". It always needs someone to do the work or contract with someone else to do the work. Once done, it is shared with everybody.

In this case it seems it never seemed important enough for someone to take the initiative.

Note that by opening this feature request, it won't be implemented automagically either, unless someone does it or contracts someone to do the work. It is different with bug reports. Bug reports, esp. the ones that trigger a crash or data corruption are usually addressed if they can be reproduced and fixed.

See also http://nyalldawson.net/2016/08/how-to-effectively-get-things-changed-in-qgis/ and http://nyalldawson.net/2016/08/how-to-effectively-get-things-changed-in-qgis-a-follow-up/

Feature requests are still welcome though, as they might trigger someone's interest and it can also be discussed with others - and if you contract with someone to do the implementation work for you, you can reference such a feature request."

abyrd commented 1 year ago

Starting with a few workarounds: on QGIS 3.16.16 I am able to get the desired behavior on the current raster layer by opening the Python console and using: iface.activeLayer().dataProvider().reloadData()

To apply this to all layers you can do:

for layer in QgsProject.instance().mapLayers().values():
    if layer.type() == QgsMapLayerType.RasterLayer:
        layer.dataProvider().reloadData()

Drawing on a suggestion at https://gis.stackexchange.com/a/58885/66716 you can use the QT filesystem watcher to reload automatically any time the source file changes:

from PyQt5.QtCore import QFileSystemWatcher
rasterLayer = iface.activeLayer()
watcher = QFileSystemWatcher()
watcher.addPath(rasterLayer.dataProvider().dataSourceUri())
watcher.fileChanged.connect(rasterLayer.dataProvider().reloadData)

I have not previously worked on the QGIS source code, but having seen that work I imagine it would be possible to add a context menu item that performs the same action as the above Python method. Below are my notes from looking into this possibility in case they help make this a reality.

Here is an issue about redrawing raster layers after file change: https://issues.qgis.org/issues/20536 That links to the solution commit: https://github.com/qgis/QGIS/commit/e174577a6adacb920bb668da9d24281b308cd17c This adds a function QgsGdalProvider::reloadData() in src/providers/gdal/qgsgdalprovider.cpp.

These days, it looks like the definition is in src/core/providers: https://github.com/qgis/QGIS/blob/master/src/core/providers/qgsdataprovider.h#L427

Comments state that this is "only available for providers which implement the reloadProviderData()", in line with the general implemetation here (void QgsDataProvider::reloadData()): https://github.com/qgis/QGIS/blob/master/src/core/providers/qgsdataprovider.cpp#L52

I believe this is the actual reload implementation that would be called on most raster layers (void QgsGdalProvider::reloadProviderData()): https://github.com/qgis/QGIS/blob/master/src/core/providers/gdal/qgsgdalprovider.cpp#L480

Here we can see some context menu items being added conditionally on the type of the layer: https://github.com/qgis/QGIS/blob/master/src/app/qgsapplayertreeviewmenuprovider.cpp#L384 And just above that some code that performs actions conditionally on the layer being a raster with a defined data provider.

Based on that, the code to add a menu item might look something like this (as yet untested as my IDE is still churning away indexing the QGIS source):

// NOTE, treat below as pseudocode just to get the general idea
if ( rlayer && rlayer->dataProvider() ) {
    QAction *reloadAction = new QAction( tr( "Reload data" ), menu );
    connect( reloadAction, &QAction::triggered, this, 
        /* lambda or reference to call rlayer->dataProvider()->reloadData() */ 
    );
    menu->addAction( reloadAction );
}
evetion commented 1 year ago

I would love this feature as well, as I often change/update underlying data in my workflows. This is especially problematic once the extent changes (tile added to a .vrt for example).

The change data source has been working well, but becomes cumbersome when you have many layers open in different folders, or just folders with many files. I think the behavior could be easily improved if one could just click ok, accepting the current data source. Now the button is greyed out until you scroll to and click the a layer source.

In the meantime, I've made a plugin that allows for reloading with the click of a button over at https://github.com/evetion/Reloader/, it's currently in review. @abyrd inspired me to add a filewatcher option with all the helpful links above. edit: It's now public 🎉

nk9 commented 10 months ago

Not sure if this is the same thing, but I'm having trouble getting QGIS to recognize changes in the XYZ basemap layer hosted on Mapbox. None of these tricks has worked for me so far. Would be great to have a "force refresh" context menu item for situations like this.