I am working on a plugin for QGIS (2.12). It uses data stored in an Spatialite database. When the user creates a new feature, the plugin should:
- update the feature attribute table (the popup form is disabled)
- update another (non-geometry) table in the database
- if the polygon overlaps any existing polygons:
- update the polygon geometry using the difference method
The modifications do not need any user input, and can happen in 'real-time' or following a commitChanges(), but the user should remain in (or be returned to) an editing session.
What is the best strategy for this? Should I use the provider, the vector layer functions, the iface.vectorLayerTools?
So far, I have not had much success. I have a class that is created on the layer.editingStarted() signal that listens for changes to geometry. It includes the following:
def __init__(self, iface, vlayer, db):
self.vlayer = vlayer
self.edit_buffer = vlayer.editBuffer()
def connect_signals
self.edit_buffer.featureAdded.connect(self.feature_added)
self.edit_buffer.geometryChanged.connect(self.geometry_changed)
self.vlayer.beforeCommitChanges.connect(self.disconnect_eb_signals)
self.vlayer.committedFeaturesAdded.connect(self.committed_add)
I first tried to use the edit buffer's featureAdded()
signal e.g.
def feature_added(self, fid):
self.vlayer.beginEditCommand('Update attributes')
self.vlayer.changeAttributeValue(fid, 1, 'new_value')
self.vlayer.endEditCommand()
Using the temporary fids given to the pre-commit features prevented making the changes to the other tables, which needed to reference them. Also, this method caused QGIS to crash/segmentation fault if the Undo command was used (on Linux, and with Undo, Redo on Windows).
Is this a bug, or am I just approaching this in the wrong way?
I have also tried using the commitedFeaturesAdded()
signal. This solved the undo/redo issues and allowed me to update the attribute table directly in the database via SQL commands, after the feature was created with the final fid. However, now when I try to change the geometry and check for intersection/overlap again I get stuck in a loop of newly committed features.
Finally, I tried calculating the modified geometry, exporting as Wkt and using GeomFromText to update the vlayer
table directly. In this case, the SQL is run without error, but the geometry doesn't seem to be updated.
Best Answer
I have found a way to do this. Changes to the feature geometry (e.g. removing overlaps) and attributes are made to committed new features, only after the
committedFeaturesAdded
signal has fired. Signals from the EditBuffer can be used to trigger messages but not database changes.Where modifications fail, it is easier to delete the user feature and add a new one with the modified values.
I have posted code below that form a working example.