Skip to content

Commit b256075

Browse files
committedDec 27, 2016
Add a base class implemention for QgsVectorDataProvider::changeFeatures
Previously this method would only succeed for providers which explicitly implement it. Now, providers which do not implement changeFeatures but do support both ChangeAttributeValues and ChangeGeometries capabilities will use a non-optimised version of changeFeatures which calls changeAttributeValues and changeGeometries in turn. This makes QgsVectorDataProvider::changeFeatures easier to use in scripts - instead of writing manual fallbacks for providers which do not implement it you can instead safely call this method regardless of the provider and it will succeed wherever both the attributes/geometries can be changed. Also add a provider unit test covering this.
1 parent 901cd29 commit b256075

File tree

3 files changed

+61
-5
lines changed

3 files changed

+61
-5
lines changed
 

‎src/core/qgsvectordataprovider.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,13 @@ bool QgsVectorDataProvider::changeGeometryValues( const QgsGeometryMap &geometry
128128
bool QgsVectorDataProvider::changeFeatures( const QgsChangedAttributesMap &attr_map,
129129
const QgsGeometryMap &geometry_map )
130130
{
131-
Q_UNUSED( attr_map );
132-
Q_UNUSED( geometry_map );
133-
return false;
131+
if ( !( capabilities() & ChangeAttributeValues ) || !( capabilities() & ChangeGeometries ) )
132+
return false;
133+
134+
bool result = true;
135+
result = result && changeAttributeValues( attr_map );
136+
result = result && changeGeometryValues( geometry_map );
137+
return result;
134138
}
135139

136140
bool QgsVectorDataProvider::createSpatialIndex()

‎src/core/qgsvectordataprovider.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,14 +274,18 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
274274
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes );
275275

276276
/**
277-
* Changes attribute values of existing features.
277+
* Changes attribute values of existing features. This should
278+
* succeed if the provider reports the ChangeAttributeValues capability.
278279
* @param attr_map a map containing changed attributes
279280
* @return true in case of success and false in case of failure
280281
*/
281282
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map );
282283

283284
/**
284-
* Changes attribute values and geometries of existing features.
285+
* Changes attribute values and geometries of existing features. This should
286+
* succeed if the provider reports both the ChangeAttributeValues and
287+
* ChangeGeometries capabilities. Providers which report the ChangeFeatures
288+
* capability implement an optimised version of this method.
285289
* @param attr_map a map containing changed attributes
286290
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
287291
* that will have their geometries changed.

‎tests/src/python/providertestbase.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,51 @@ def testChangeGeometries(self):
802802
# expect fail
803803
self.assertFalse(l.dataProvider().changeGeometryValues(changes),
804804
'Provider reported no ChangeGeometries capability, but returned true to changeGeometryValues')
805+
806+
def testChangeFeatures(self):
807+
if not getattr(self, 'getEditableLayer', None):
808+
return
809+
810+
l = self.getEditableLayer()
811+
self.assertTrue(l.isValid())
812+
813+
features = [f for f in l.dataProvider().getFeatures()]
814+
815+
# find 2 features to change attributes for
816+
features = [f for f in l.dataProvider().getFeatures()]
817+
# need to keep order here
818+
to_change = [f for f in features if f.attributes()[0] == 1]
819+
to_change.extend([f for f in features if f.attributes()[0] == 2])
820+
# changes by feature id, for changeAttributeValues call
821+
attribute_changes = {to_change[0].id(): {1: 501, 3: 'new string'}, to_change[1].id(): {1: 502, 4: 'NEW'}}
822+
# changes by pk, for testing after retrieving changed features
823+
new_attr_map = {1: {1: 501, 3: 'new string'}, 2: {1: 502, 4: 'NEW'}}
824+
825+
# find 2 features to change geometries for
826+
to_change = [f for f in features if f.attributes()[0] == 1]
827+
to_change.extend([f for f in features if f.attributes()[0] == 3])
828+
# changes by feature id, for changeGeometryValues call
829+
geometry_changes = {to_change[0].id(): QgsGeometry.fromWkt('Point (10 20)'), to_change[1].id(): QgsGeometry()}
830+
# changes by pk, for testing after retrieving changed features
831+
new_geom_map = {1: QgsGeometry.fromWkt('Point ( 10 20 )'), 3: QgsGeometry()}
832+
833+
if l.dataProvider().capabilities() & QgsVectorDataProvider.ChangeGeometries and l.dataProvider().capabilities() & QgsVectorDataProvider.ChangeAttributeValues:
834+
# expect success
835+
result = l.dataProvider().changeFeatures(attribute_changes, geometry_changes)
836+
self.assertTrue(result,
837+
'Provider reported ChangeGeometries and ChangeAttributeValues capability, but returned False to changeFeatures')
838+
839+
# check result
840+
self.testGetFeatures(l.dataProvider(), changed_attributes=new_attr_map, changed_geometries=new_geom_map)
841+
842+
# change empty list, should return true for consistency
843+
self.assertTrue(l.dataProvider().changeFeatures({}, {}))
844+
845+
elif not l.dataProvider().capabilities() & QgsVectorDataProvider.ChangeGeometries:
846+
# expect fail
847+
self.assertFalse(l.dataProvider().changeFeatures(attribute_changes, geometry_changes),
848+
'Provider reported no ChangeGeometries capability, but returned true to changeFeatures')
849+
elif not l.dataProvider().capabilities() & QgsVectorDataProvider.ChangeAttributeValues:
850+
# expect fail
851+
self.assertFalse(l.dataProvider().changeFeatures(attribute_changes, geometry_changes),
852+
'Provider reported no ChangeAttributeValues capability, but returned true to changeFeatures')

0 commit comments

Comments
 (0)
Please sign in to comment.