8

I'm developing an addon for QGIS that needs to assign a UUID to any feature as soon as it is added to the layer. I'm using the featureAdded signal to write the UUID in the corresponding field of the feature.

This is the code that I am using (as simplified as possible):

def run(self):
    self.iface.mapCanvas().currentLayer().featureAdded.connect(self.onFeatureAdded)

def onFeatureAdded(self, fid):
    layer = self.iface.mapCanvas().currentLayer()
    layer.beginEditCommand("Set UUID")
    print layer.changeAttributeValue(fid, layer.fieldNameIndex('guid_pol'), 'some_random_uuid') # prints True
    layer.endEditCommand()

(I created a new plugin with the 'Plugin Builder' add-on and that above is the only code I have added to it.)

Unless I'm missing something, this follows the guidelines from the documentation in the QGIS developer cookbook: http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/vector.html#modifying-vector-layers-with-an-editing-buffer

When I edit the layer, if I add a feature the field 'guid_pol' is set to 'some_random_uuid' as expected. I can commit the changes without a problem, however, if instead of commiting the changes I try cancelling them QGIS crashes with error 0xC0000005 (Access Violation).

Interestingly, the command 'Set UUID' doesn't get added to the undo/redo stack (at least, not to the Undo/redo Panel) and if I try to undo it crashes as well.

I'm using QGIS 2.14 but I observed similar behaviour with QGIS 2.12.

I'm having a hard time getting this to work. Is there something I am doing wrong?

5

I answer myself :-) I have found an explanation here http://qgis-developer.osgeo.narkive.com/5wnziigA/wrapping-changeattributevalue-between-begin-and-end-editcommand#post2

Currently it is not safe to do calls that modify vector layer data in slots connected to signals notifying about data change (such as featureAdded). The issue is that at the point when those signals are emitted, their underlying undo commands were not yet pushed onto the stack, so doing further editing calls causes corruption of undo stack (undo command for follow up operation is placed before the first operation).

My workaround consists in delaying the handling of the feature addition using the editCommandEnded slot. This is the relevant code:

def onFeatureAdded(self, fid):
    if fid < 0:
        self._addedFeatures.append(fid)

def onEditCommandEnded(self):
    while self._addedFeatures:
        fid = self._addedFeatures.pop()
        self._handleAdded(fid)

def _handleAdded(self, fid):
    guid_pol = str(uuid4()) # RFC 4122 UUID v4
    try:
        self.layer.beginEditCommand(u"Assign UUID")
        self.layer.changeAttributeValue(fid, self.layer.fieldNameIndex('guid_pol'), guid_pol)
        self.layer.endEditCommand()
    except:
        self.layer.destroyEditCommand()
        raise

I hope this helps somebody else.

3

mhm,

Your answer was really great and solved our problem here. But to fully understand why this happened I how to solve it, I studied QGIS source code and my co-worker and I made an article explaining the problem in detail. Please, feel free to check it!

https://gis4programmers.wordpress.com/2017/02/26/working-properly-with-pyqgqis-edit-buffer-to-enable-undo-commands/

  • 1
    Even today with QGIS v3.4 I faced the issue and your post gave me a way to solve it, thanks for posting this! BTW, I had already upvoted your answer, like a year ago or so :D – Germán Carrillo Nov 14 '18 at 22:13

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.