5

I'm trying to implement a function that would warn the user when he changes active layer and has just made changes on it that his changes have not been saved.

I found this forum: Prevent QGIS user to change layers without saving? Which inspired me. I tried to translate it to QGIS 3 (see the code below).

def check_edit():
    layers = iface.editableLayers()
    if layers:
        layers = [layer for layer in QgsProject.instance().mapLayers().values()]
        iface.setActiveLayer(layers[0])
        iface.activeLayer()
        iface.actionToggleEditing().trigger() 

iface.currentLayerChanged.connect(check_edit)

But the result of it is not as expected because the changes are not saved. The changes appear, but the layer is not saved.

So I kept a little different code to which I added a pushMessage which warns the user which layer is concerned.

def check_edit():
    layers = iface.editableLayers()
    if layers:
        iface.messageBar().pushMessage("Attention : ", "Couche "+ iface.activeLayer().name() + " en édition et non sauvegardée !", level=Qgis.Warning, duration=7)
        iface.activeLayer() 
        iface.actionToggleEditing().trigger() 

iface.currentLayerChanged.connect(check_edit)

I therefore do not find my code very efficient in the sense that the user is warned but these modifications are not saved.

So I would like to know if it is possible to display this pop-up (screenshot) which would leave the choice to the user to save or not his modifications?

enter image description here

1 Answer 1

5

Yes, it's possible. The issue with the other answer you mentioned is that it doesn't keep track of the previous active layer, which you are interested in.

So, we need to tackle the problem differently. We do need to keep track of the layer you're interested in, which is the one that is losing the 'active' state.

Result

enter image description here

Code snippet to be run in the QGIS Python console

previous_active_layer = None

def show_dialog(layer_name):
    return QMessageBox.question( iface.mainWindow(),
            "Stop Editing",
            "Do you want to save the changes to layer {}?".format(layer_name),
            QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

def save_edits(layer):
    global previous_active_layer  # Because we'll modify it
    
    if previous_active_layer and previous_active_layer.isModified():
        res = show_dialog(previous_active_layer.name())
            
        if res == QMessageBox.Save:
            previous_active_layer.commitChanges()
        elif res == QMessageBox.Discard:
            previous_active_layer.rollBack()
                
    previous_active_layer = layer

iface.currentLayerChanged.connect(save_edits)

That's it!


Note 1: If you're working on a startup.py script, Make sure you add from qgis.PyQt.QtWidgets import QMessageBox and from qgis.utils import iface at the beginning of the script.

Note 2: Ideally, one would just call the QGIS function to do that, but it's not exposed yet in the API, that's why we need to deal with it by ourselves.

Note 3: If you're using transaction groups, the logic above could be insufficient. In that case, I'd rather suggest the QGIS project to expose the native way of showing such confirmation dialog.

4
  • 1
    Thank you very much for your answer ! It also works in startup.py, that's perfect.
    – hug117
    May 28, 2021 at 22:53
  • thank you for the update but I don't manage to make it works when I drop it in C:\Users\username\AppData\Roaming\QGIS\QGIS3, I get an error : name 'iface' is not defined. how did you do? although it works in pyhton's console but it is not convenient as I'd like to be launched from start. I'm using qgis3.16
    – FranckT
    Sep 23, 2021 at 21:40
  • 1
    @FranckT, in the startup.py file you should add the following imports (in the QGIS Python Console you don't need them): from qgis.PyQt.QtWidgets import QMessageBox and from qgis.utils import iface. Let me know if now it works for you. Sep 24, 2021 at 1:13
  • 1
    many thanks @GermánCarrillo
    – FranckT
    Sep 25, 2021 at 21:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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