def testTransactionGroupExpressionFields(self): """Test issue GH #39230, this is not really specific to GPKG""" project = QgsProject() project.setAutoTransaction(True) tmpfile = os.path.join( self.basetestpath, 'tempGeoPackageTransactionExpressionFields.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT (1 1)')) f.SetField('str_field', 'one') lyr.CreateFeature(f) del lyr del ds vl = QgsVectorLayer(tmpfile + '|layername=test', 'test', 'ogr') f = QgsField('expression_field', QVariant.Int) idx = vl.addExpressionField('123', f) self.assertEqual(vl.fields().fieldOrigin(idx), QgsFields.OriginExpression) project.addMapLayers([vl]) feature = next(vl.getFeatures()) feature.setAttributes([None, 'two', 123]) self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeature(feature)) self.assertFalse(vl.dataProvider().hasErrors())
def testTransactionDirtyName(self): # create a vector layer based on oracle vl = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="QGIS"."SOME_POLY_DATA" (GEOM) sql=', 'test', 'oracle') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # update the data within the transaction tr = vl.dataProvider().transaction() sql = """UPDATE "QGIS"."SOME_POLY_DATA" SET "pk"=1 where "pk"=1""" name = "My Awesome Transaction!" self.assertTrue(tr.executeSql(sql, True, name)[0]) # test name self.assertEqual(vl.undoStack().command(0).text(), name) # rollback vl.rollBack()
def create(self, path: str, qgis_project: QgsProject): qgis_project.setAutoTransaction(self.auto_transaction) qgis_project.setEvaluateDefaultValues(self.evaluate_default_values) qgis_layers = list() for layer in self.layers: qgis_layer = layer.create() self.layer_added.emit(qgis_layer.id()) if not self.crs and qgis_layer.isSpatial(): self.crs = qgis_layer.crs() qgis_layers.append(qgis_layer) qgis_project.addMapLayers(qgis_layers, not self.legend) if self.crs: if isinstance(self.crs, QgsCoordinateReferenceSystem): qgis_project.setCrs(self.crs) else: qgis_project.setCrs( QgsCoordinateReferenceSystem.fromEpsgId(self.crs)) qgis_relations = list( qgis_project.relationManager().relations().values()) dict_domains = { layer.layer.id(): layer.is_domain for layer in self.layers } for relation in self.relations: rel = relation.create(qgis_project, qgis_relations) assert rel.isValid() qgis_relations.append(rel) if rel.referencedLayerId() in dict_domains and dict_domains[ rel.referencedLayerId()]: editor_widget_setup = QgsEditorWidgetSetup( 'RelationReference', { 'Relation': rel.id(), 'ShowForm': False, 'OrderByValue': True, 'ShowOpenFormButton': False }) referencing_layer = rel.referencingLayer() referencing_layer.setEditorWidgetSetup( rel.referencingFields()[0], editor_widget_setup) qgis_project.relationManager().setRelations(qgis_relations) for layer in self.layers: layer.create_form(self) if self.legend: self.legend.create(qgis_project) if path: qgis_project.write(path)
def testTransactionDirty(self): # create a vector layer based on oracle vl = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="QGIS"."SOME_POLY_DATA" (GEOM) sql=', 'test', 'oracle') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # check that the feature used for testing is ok ft0 = vl.getFeatures('pk=1') f = QgsFeature() self.assertTrue(ft0.nextFeature(f)) # update the data within the transaction tr = vl.dataProvider().transaction() sql = """UPDATE "QGIS"."SOME_POLY_DATA" set "pk"=33 where "pk"=1""" self.assertTrue(tr.executeSql(sql, True)[0]) # check that the pk of the feature has been changed ft = vl.getFeatures('pk=1') self.assertFalse(ft.nextFeature(f)) ft = vl.getFeatures('pk=33') self.assertTrue(ft.nextFeature(f)) # underlying data has been modified but the layer is not tagged as # modified self.assertTrue(vl.isModified()) # undo sql query vl.undoStack().undo() # check that the original feature with pk is back ft0 = vl.getFeatures('pk=1') self.assertTrue(ft0.nextFeature(f)) # redo vl.undoStack().redo() # check that the pk of the feature has been changed ft1 = vl.getFeatures('pk=1') self.assertFalse(ft1.nextFeature(f)) # rollback vl.rollBack()
def testTransactionNotDirty(self): # create a vector ayer based on postgres vl = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # check that the feature used for testing is ok ft0 = vl.getFeatures('pk=1') f = QgsFeature() self.assertTrue(ft0.nextFeature(f)) # update the data within the transaction tr = vl.dataProvider().transaction() sql = "update qgis_test.some_poly_data set pk=33 where pk=1" self.assertTrue(tr.executeSql(sql, True)[0]) # check that the pk of the feature has been changed ft = vl.getFeatures('pk=1') self.assertFalse(ft.nextFeature(f)) ft = vl.getFeatures('pk=33') self.assertTrue(ft.nextFeature(f)) # underlying data has been modified but the layer is not tagged as # modified self.assertTrue(vl.isModified()) # undo sql query vl.undoStack().undo() # check that the original feature with pk is back ft0 = vl.getFeatures('pk=1') self.assertTrue(ft0.nextFeature(f)) # redo vl.undoStack().redo() # check that the pk of the feature has been changed ft1 = vl.getFeatures('pk=1') self.assertFalse(ft1.nextFeature(f)) p.setAutoTransaction(False)
def testTransactionDirty(self): # create a vector layer based on postgres vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # check that the feature used for testing is ok ft0 = vl.getFeatures('pk=1') f = QgsFeature() self.assertTrue(ft0.nextFeature(f)) # update the data within the transaction tr = vl.dataProvider().transaction() sql = "update qgis_test.some_poly_data set pk=33 where pk=1" self.assertTrue(tr.executeSql(sql, True)[0]) # check that the pk of the feature has been changed ft = vl.getFeatures('pk=1') self.assertFalse(ft.nextFeature(f)) ft = vl.getFeatures('pk=33') self.assertTrue(ft.nextFeature(f)) # underlying data has been modified but the layer is not tagged as # modified self.assertTrue(vl.isModified()) # undo sql query vl.undoStack().undo() # check that the original feature with pk is back ft0 = vl.getFeatures('pk=1') self.assertTrue(ft0.nextFeature(f)) # redo vl.undoStack().redo() # check that the pk of the feature has been changed ft1 = vl.getFeatures('pk=1') self.assertFalse(ft1.nextFeature(f))
def testTransactionTuple(self): # create a vector layer based on postgres vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # execute a query which returns a tuple tr = vl.dataProvider().transaction() sql = "select * from qgis_test.some_poly_data" self.assertTrue(tr.executeSql(sql, False)[0]) # underlying data has not been modified self.assertFalse(vl.isModified())
def testTransactionTuple(self): # create a vector layer based on postgres vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # execute a query which returns a tuple tr = vl.dataProvider().transaction() sql = "select * from qgis_test.some_poly_data" self.assertTrue(tr.executeSql(sql, False)[0]) # underlying data has not been modified self.assertFalse(vl.isModified())
def testTransactionTuple(self): # create a vector layer based on oracle vl = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="QGIS"."SOME_POLY_DATA" (GEOM) sql=', 'test', 'oracle') self.assertTrue(vl.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl]) vl.startEditing() # execute a query which returns a tuple tr = vl.dataProvider().transaction() sql = 'SELECT * from "QGIS"."SOME_POLY_DATA"' self.assertTrue(tr.executeSql(sql, False)[0]) # underlying data has not been modified self.assertFalse(vl.isModified())
def test_transactionGroupEditingStatus(self): """Not particularly related to PG but it fits here nicely: test GH #39282""" project = QgsProject() project.setAutoTransaction(True) vl_b = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."books" sql=', 'books', 'postgres') vl_a = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."authors" sql=', 'authors', 'postgres') project.addMapLayers([vl_a, vl_b]) vl_a.startEditing() self.assertTrue(vl_a.isEditable()) self.assertTrue(vl_b.isEditable()) self.assertTrue(vl_a.commitChanges(False)) self.assertTrue(vl_a.isEditable()) self.assertTrue(vl_b.isEditable())
def create(self, path: str, qgis_project: QgsProject, group: QgsLayerTreeGroup): qgis_project.setAutoTransaction(self.auto_transaction) qgis_project.setEvaluateDefaultValues(self.evaluate_default_values) qgis_layers = list() for layer in self.layers: qgis_layer = layer.create() self.layer_added.emit(qgis_layer.id()) if not self.crs and qgis_layer.isSpatial(): self.crs = qgis_layer.crs() qgis_layers.append(qgis_layer) qgis_project.addMapLayers(qgis_layers, not self.legend) if self.crs: if isinstance(self.crs, QgsCoordinateReferenceSystem): qgis_project.setCrs(self.crs) else: crs = QgsCoordinateReferenceSystem.fromEpsgId(self.crs) if not crs.isValid(): crs = QgsCoordinateReferenceSystem(self.crs) # Fallback qgis_project.setCrs(crs) qgis_relations = list( qgis_project.relationManager().relations().values()) dict_domains = { layer.layer.id(): layer.is_domain for layer in self.layers } for relation in self.relations: rel = relation.create(qgis_project, qgis_relations) assert rel.isValid() qgis_relations.append(rel) referencing_layer = rel.referencingLayer() referencing_field_constraints = referencing_layer.fieldConstraints( rel.referencingFields()[0]) allow_null = not bool(referencing_field_constraints & QgsFieldConstraints.ConstraintNotNull) # If we have an extended ili2db domain, we need to filter its values filter_expression = "\"{}\" = '{}'".format( ENUM_THIS_CLASS_COLUMN, relation.child_domain_name ) if relation.child_domain_name else '' if rel.referencedLayerId() in dict_domains and dict_domains[ rel.referencedLayerId()]: editor_widget_setup = QgsEditorWidgetSetup( 'RelationReference', { 'Relation': rel.id(), 'ShowForm': False, 'OrderByValue': True, 'ShowOpenFormButton': False, 'AllowNULL': allow_null, 'FilterExpression': filter_expression, 'FilterFields': list() }) else: editor_widget_setup = QgsEditorWidgetSetup( 'RelationReference', { 'Relation': rel.id(), 'ShowForm': False, 'OrderByValue': True, 'ShowOpenFormButton': False, 'AllowAddFeatures': True, 'AllowNULL': allow_null }) referencing_layer = rel.referencingLayer() referencing_layer.setEditorWidgetSetup(rel.referencingFields()[0], editor_widget_setup) qgis_project.relationManager().setRelations(qgis_relations) # Set Bag of Enum widget for layer_name, bag_of_enum in self.bags_of_enum.items(): for attribute, bag_of_enum_info in bag_of_enum.items(): layer_obj = bag_of_enum_info[0] cardinality = bag_of_enum_info[1] domain_table = bag_of_enum_info[2] key_field = bag_of_enum_info[3] value_field = bag_of_enum_info[4] minimal_selection = cardinality.startswith('1') current_layer = layer_obj.create() field_widget = 'ValueRelation' field_widget_config = { 'AllowMulti': True, 'UseCompleter': False, 'Value': value_field, 'OrderByValue': False, 'AllowNull': True, 'Layer': domain_table.create().id(), 'FilterExpression': '', 'Key': key_field, 'NofColumns': 1 } field_idx = current_layer.fields().indexOf(attribute) setup = QgsEditorWidgetSetup(field_widget, field_widget_config) current_layer.setEditorWidgetSetup(field_idx, setup) if minimal_selection: constraint_expression = 'array_length("{}")>0'.format( attribute) current_layer.setConstraintExpression( field_idx, constraint_expression, self.tr('The minimal selection is 1')) for layer in self.layers: layer.create_form(self) if self.legend: self.legend.create(qgis_project, group) if path: qgis_project.write(path)
def _test(autoTransaction): """Test buffer methods within and without transactions - create a feature - save - retrieve the feature - change geom and attrs - test changes are seen in the buffer """ def _check_feature(wkt): f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), wkt) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.geometry().asWkt().upper(), wkt) ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(ml.isValid()) d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_a = QgsVectorLayer(newFileName + '|layername=layer_a') self.assertTrue(layer_a.isValid()) project = QgsProject() project.setAutoTransaction(autoTransaction) project.addMapLayers([layer_a]) ########################################### # Tests with a new feature self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) _check_feature('POINT (7 45)') # Need to fetch the feature because its ID is NULL (-9223372036854775808) f = next(layer_a.getFeatures()) self.assertEqual(len(buffer.addedFeatures()), 1) layer_a.undoStack().undo() self.assertEqual(len(buffer.addedFeatures()), 0) layer_a.undoStack().redo() self.assertEqual(len(buffer.addedFeatures()), 1) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) # Now change attribute self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(len(buffer.addedFeatures()), 1) # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) # Change multiple attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 321, 2: 456}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(spy_attribute_changed[1], [f.id(), 2, 456]) buffer = layer_a.editBuffer() # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.attribute('int2'), None) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [f.id(), 2, None]) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [f.id(), 1, 123]) # Change geometry f = next(layer_a.getFeatures()) spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertTrue(len(spy_geometry_changed), 1) self.assertEqual(spy_geometry_changed[0][0], f.id()) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) _check_feature('POINT (9 43)') self.assertEqual(buffer.changedGeometries(), {}) layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _check_feature('POINT (9 43)') self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(10 44)'))) _check_feature('POINT (10 44)') # This is another surprise: geometry edits get collapsed into a single # one because they have the same hardcoded id layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertTrue(layer_a.commitChanges()) ########################################### # Tests with the existing feature # Get the feature f = next(layer_a.getFeatures()) self.assertTrue(f.isValid()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') # Change single attribute self.assertTrue(layer_a.startEditing()) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 123) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 111, 2: 654}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [1, 1, 111]) self.assertEqual(spy_attribute_changed[1], [1, 2, 654]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 111, 654]) self.assertEqual(buffer.changedAttributeValues(), {1: { 1: 111, 2: 654 }}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [1, 1, 123]) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [1, 2, None]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 123, None]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change geometry spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)') self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)') spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) layer_a.undoStack().undo() self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(7 45)').asWkt()) self.assertEqual(buffer.changedGeometries(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) # Delete an existing feature self.assertTrue(layer_a.deleteFeature(f.id())) with self.assertRaises(StopIteration): next(layer_a.getFeatures()) self.assertEqual(buffer.deletedFeatureIds(), [f.id()]) layer_a.undoStack().undo() self.assertTrue(layer_a.getFeature(f.id()).isValid()) self.assertEqual(buffer.deletedFeatureIds(), []) ########################################### # Test delete # Delete a new feature f = QgsFeature(layer_a.fields()) f.setAttribute('int', 555) f.setGeometry(QgsGeometry.fromWkt('point(8 46)')) self.assertTrue(layer_a.addFeatures([f])) f = [ f for f in layer_a.getFeatures() if f.attribute('int') == 555 ][0] self.assertTrue(f.id() in buffer.addedFeatures()) self.assertTrue(layer_a.deleteFeature(f.id())) self.assertFalse(f.id() in buffer.addedFeatures()) self.assertFalse(f.id() in buffer.deletedFeatureIds()) layer_a.undoStack().undo() self.assertTrue(f.id() in buffer.addedFeatures()) ########################################### # Add attribute field = QgsField('attr1', QVariant.String) self.assertTrue(layer_a.addAttribute(field)) self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), []) layer_a.undoStack().redo() self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) self.assertTrue(layer_a.commitChanges()) ########################################### # Remove attribute self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.deleteAttribute(attr_idx)) self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) layer_a.undoStack().undo() self.assertEqual(buffer.deletedAttributeIds(), []) self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) # This is totally broken at least on OGR/GPKG: the rollback # does not restore the original fields if False: layer_a.undoStack().redo() self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) # Rollback! self.assertTrue(layer_a.rollBack()) self.assertIn('attr1', layer_a.dataProvider().fields().names()) self.assertIn('attr1', layer_a.fields().names()) self.assertEqual(layer_a.fields().names(), layer_a.dataProvider().fields().names()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.startEditing()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) ########################################### # Rename attribute attr_idx = layer_a.fields().lookupField(field.name()) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) self.assertTrue(layer_a.renameAttribute(attr_idx, 'new_name')) self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) layer_a.undoStack().redo() self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) ############################################# # Try hard to make this fail for transactions if autoTransaction: self.assertTrue(layer_a.commitChanges()) self.assertTrue(layer_a.startEditing()) f = next(layer_a.getFeatures()) # Do for i in range(10): spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 2, i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: i }}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), i) # Undo/redo for i in range(9): # Undo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Redo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().redo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 9 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 9 - i]) # Undo again spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Last check f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 0 }}) layer_a.undoStack().undo() buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), None)
def testTransaction(self): tmpfile = os.path.join(self.basetestpath, 'testTransaction.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('lyr1', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) lyr = ds.CreateLayer('lyr2', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1.isValid()) vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl1, vl2]) self.assertTrue(vl1.startEditing()) self.assertIsNotNone(vl1.dataProvider().transaction()) self.assertTrue(vl1.deleteFeature(1)) # An iterator opened on the layer should see the feature deleted self.assertEqual( len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) # But not if opened from another connection vl1_external = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1_external.isValid()) self.assertEqual( len([f for f in vl1_external.getFeatures(QgsFeatureRequest())]), 1) del vl1_external self.assertTrue(vl1.commitChanges()) # Should still get zero features on vl1 self.assertEqual( len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) # Test undo/redo self.assertTrue(vl2.startEditing()) self.assertIsNotNone(vl2.dataProvider().transaction()) self.assertTrue(vl2.editBuffer().deleteFeature(1)) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.editBuffer().deleteFeature(2)) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 0) vl2.undoStack().undo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) vl2.undoStack().undo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) vl2.undoStack().redo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.commitChanges()) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) del vl1 del vl2 vl2_external = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2_external.isValid()) self.assertEqual( len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1) del vl2_external
def create(self, path: str, qgis_project: QgsProject): qgis_project.setAutoTransaction(self.auto_transaction) qgis_project.setEvaluateDefaultValues(self.evaluate_default_values) qgis_layers = list() for layer in self.layers: qgis_layer = layer.create() self.layer_added.emit(qgis_layer.id()) if not self.crs and qgis_layer.isSpatial(): self.crs = qgis_layer.crs() qgis_layers.append(qgis_layer) qgis_project.addMapLayers(qgis_layers, not self.legend) if self.crs: if isinstance(self.crs, QgsCoordinateReferenceSystem): qgis_project.setCrs(self.crs) else: qgis_project.setCrs( QgsCoordinateReferenceSystem.fromEpsgId(self.crs)) qgis_relations = list( qgis_project.relationManager().relations().values()) dict_domains = { layer.layer.id(): layer.is_domain for layer in self.layers } for relation in self.relations: rel = relation.create(qgis_project, qgis_relations) assert rel.isValid() qgis_relations.append(rel) if rel.referencedLayerId() in dict_domains and dict_domains[ rel.referencedLayerId()]: editor_widget_setup = QgsEditorWidgetSetup( 'RelationReference', { 'Relation': rel.id(), 'ShowForm': False, 'OrderByValue': True, 'ShowOpenFormButton': False }) else: editor_widget_setup = QgsEditorWidgetSetup( 'RelationReference', { 'Relation': rel.id(), 'ShowForm': False, 'OrderByValue': True, 'ShowOpenFormButton': False, 'AllowAddFeatures': True }) referencing_layer = rel.referencingLayer() referencing_layer.setEditorWidgetSetup(rel.referencingFields()[0], editor_widget_setup) qgis_project.relationManager().setRelations(qgis_relations) # Set Bag of Enum widget for layer_name, bag_of_enum in self.bags_of_enum.items(): for attribute, bag_of_enum_info in bag_of_enum.items(): layer_obj = bag_of_enum_info[0] cardinality = bag_of_enum_info[1] domain_table = bag_of_enum_info[2] key_field = bag_of_enum_info[3] value_field = bag_of_enum_info[4] allow_null = cardinality.startswith('0') allow_multi = cardinality.endswith('*') current_layer = layer_obj.create() field_widget = 'ValueRelation' field_widget_config = { 'AllowMulti': allow_multi, 'UseCompleter': False, 'Value': value_field, 'OrderByValue': False, 'AllowNull': allow_null, 'Layer': domain_table.create().id(), 'FilterExpression': '', 'Key': key_field, 'NofColumns': 1 } field_idx = current_layer.fields().indexOf(attribute) setup = QgsEditorWidgetSetup(field_widget, field_widget_config) current_layer.setEditorWidgetSetup(field_idx, setup) for layer in self.layers: layer.create_form(self) if self.legend: self.legend.create(qgis_project) if path: qgis_project.write(path)
def testTransaction(self): tmpfile = os.path.join(self.basetestpath, 'testTransaction.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('lyr1', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) lyr = ds.CreateLayer('lyr2', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1.isValid()) vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl1, vl2]) self.assertTrue(vl1.startEditing()) self.assertIsNotNone(vl1.dataProvider().transaction()) self.assertTrue(vl1.deleteFeature(1)) # An iterator opened on the layer should see the feature deleted self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) # But not if opened from another connection vl1_external = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1_external.isValid()) self.assertEqual(len([f for f in vl1_external.getFeatures(QgsFeatureRequest())]), 1) del vl1_external self.assertTrue(vl1.commitChanges()) # Should still get zero features on vl1 self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) # Test undo/redo self.assertTrue(vl2.startEditing()) self.assertIsNotNone(vl2.dataProvider().transaction()) self.assertTrue(vl2.editBuffer().deleteFeature(1)) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.editBuffer().deleteFeature(2)) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 0) vl2.undoStack().undo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) vl2.undoStack().undo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) vl2.undoStack().redo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.commitChanges()) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) del vl1 del vl2 vl2_external = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2_external.isValid()) self.assertEqual(len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1) del vl2_external
def create(self, path: str, qgis_project: QgsProject): qgis_project.setAutoTransaction(self.auto_transaction) qgis_project.setEvaluateDefaultValues(self.evaluate_default_values) qgis_layers = list() for layer in self.layers: qgis_layer = layer.create() self.layer_added.emit(qgis_layer.id()) if not self.crs and qgis_layer.isSpatial(): self.crs = qgis_layer.crs() qgis_layers.append(qgis_layer) qgis_project.addMapLayers(qgis_layers, not self.legend) if self.crs: if isinstance(self.crs, QgsCoordinateReferenceSystem): qgis_project.setCrs(self.crs) else: crs = QgsCoordinateReferenceSystem.fromEpsgId(self.crs) if not crs.isValid(): crs = QgsCoordinateReferenceSystem(self.crs) # Fallback qgis_project.setCrs(crs) qgis_relations = list( qgis_project.relationManager().relations().values()) dict_layers = {layer.layer.id(): layer for layer in self.layers} for relation in self.relations: rel = relation.create(qgis_project, qgis_relations) assert rel.isValid() qgis_relations.append(rel) referenced_layer = dict_layers.get(rel.referencedLayerId(), None) referencing_layer = dict_layers.get(rel.referencingLayerId(), None) if referenced_layer and referenced_layer.is_domain: editor_widget_setup = QgsEditorWidgetSetup( "RelationReference", { "Relation": rel.id(), "ShowForm": False, "OrderByValue": True, "ShowOpenFormButton": False, "AllowNULL": True, "FilterExpression": "\"{}\" = '{}'".format(ENUM_THIS_CLASS_COLUMN, relation.child_domain_name) if relation.child_domain_name else "", "FilterFields": list(), }, ) elif referenced_layer and referenced_layer.is_basket_table: editor_widget_setup = QgsEditorWidgetSetup( "RelationReference", { "Relation": rel.id(), "ShowForm": False, "OrderByValue": True, "ShowOpenFormButton": False, "AllowNULL": True, "AllowAddFeatures": False, "FilterExpression": "\"topic\" = '{}' and attribute(get_feature('{}', 't_id', \"dataset\"), 'datasetname') != '{}'" .format( referencing_layer.model_topic_name, "T_ILI2DB_DATASET" if referenced_layer.provider == "ogr" else "t_ili2db_dataset", CATALOGUE_DATASETNAME, ) if referencing_layer.model_topic_name else "", "FilterFields": list(), }, ) else: editor_widget_setup = QgsEditorWidgetSetup( "RelationReference", { "Relation": rel.id(), "ShowForm": False, "OrderByValue": True, "ShowOpenFormButton": False, "AllowAddFeatures": True, "AllowNULL": True, }, ) referencing_layer = rel.referencingLayer() referencing_layer.setEditorWidgetSetup(rel.referencingFields()[0], editor_widget_setup) qgis_project.relationManager().setRelations(qgis_relations) # Set Bag of Enum widget for layer_name, bag_of_enum in self.bags_of_enum.items(): for attribute, bag_of_enum_info in bag_of_enum.items(): layer_obj = bag_of_enum_info[0] cardinality = bag_of_enum_info[1] domain_table = bag_of_enum_info[2] key_field = bag_of_enum_info[3] value_field = bag_of_enum_info[4] minimal_selection = cardinality.startswith("1") current_layer = layer_obj.create() field_widget = "ValueRelation" field_widget_config = { "AllowMulti": True, "UseCompleter": False, "Value": value_field, "OrderByValue": False, "AllowNull": True, "Layer": domain_table.create().id(), "FilterExpression": "", "Key": key_field, "NofColumns": 1, } field_idx = current_layer.fields().indexOf(attribute) setup = QgsEditorWidgetSetup(field_widget, field_widget_config) current_layer.setEditorWidgetSetup(field_idx, setup) if minimal_selection: constraint_expression = 'array_length("{}")>0'.format( attribute) current_layer.setConstraintExpression( field_idx, constraint_expression, self.tr("The minimal selection is 1"), ) for layer in self.layers: layer.create_form(self) if self.legend: self.legend.create(qgis_project) custom_layer_order = list() for custom_layer_name in self.custom_layer_order_structure: custom_layer = qgis_project.mapLayersByName(custom_layer_name) if custom_layer: custom_layer_order.append(custom_layer[0]) if custom_layer_order: root = qgis_project.layerTreeRoot() order = root.customLayerOrder() if root.hasCustomLayerOrder( ) else [] order.extend(custom_layer_order) root.setCustomLayerOrder(custom_layer_order) root.setHasCustomLayerOrder(True) if path: qgis_project.write(path)
def _test(autoTransaction): """Test buffer methods within and without transactions - create a feature - save - retrieve the feature - change geom and attrs - test changes are seen in the buffer """ def _check_feature(wkt): f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), wkt) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.geometry().asWkt().upper(), wkt) ml = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertTrue(ml.isValid()) d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(os.path.join(d.path(), 'transaction_test.gpkg'))) options.layerName = 'layer_b' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) layer_a = QgsVectorLayer(os.path.join(d.path(), 'transaction_test.gpkg|layername=layer_a')) self.assertTrue(layer_a.isValid()) project = QgsProject() project.setAutoTransaction(autoTransaction) project.addMapLayers([layer_a]) ########################################### # Tests with a new feature self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) _check_feature('POINT (7 45)') # Need to fetch the feature because its ID is NULL (-9223372036854775808) f = next(layer_a.getFeatures()) self.assertEqual(len(buffer.addedFeatures()), 1) layer_a.undoStack().undo() self.assertEqual(len(buffer.addedFeatures()), 0) layer_a.undoStack().redo() self.assertEqual(len(buffer.addedFeatures()), 1) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) # Now change attribute self.assertEqual(buffer.changedAttributeValues(), {}) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(buffer.addedFeatures()), 1) # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 321) layer_a.undoStack().undo() self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) # Change geometry f = next(layer_a.getFeatures()) self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _check_feature('POINT (9 43)') self.assertEqual(buffer.changedGeometries(), {}) layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _check_feature('POINT (9 43)') self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(10 44)'))) _check_feature('POINT (10 44)') # This is anothr surprise: geometry edits get collapsed into a single # one because they have the same hardcoded id layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertTrue(layer_a.commitChanges()) ########################################### # Tests with the existing feature # Get the feature f = next(layer_a.getFeatures()) self.assertTrue(f.isValid()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') self.assertTrue(layer_a.startEditing()) layer_a.changeAttributeValue(f.id(), 1, 321) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}}) layer_a.undoStack().undo() self.assertEqual(buffer.changedAttributeValues(), {}) # Change geometry self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)') self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)') layer_a.undoStack().undo() self.assertEqual(buffer.changedGeometries(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) # Delete an existing feature self.assertTrue(layer_a.deleteFeature(f.id())) with self.assertRaises(StopIteration): next(layer_a.getFeatures()) self.assertEqual(buffer.deletedFeatureIds(), [f.id()]) layer_a.undoStack().undo() self.assertTrue(layer_a.getFeature(f.id()).isValid()) self.assertEqual(buffer.deletedFeatureIds(), []) ########################################### # Test delete # Delete a new feature f = QgsFeature(layer_a.fields()) f.setAttribute('int', 555) f.setGeometry(QgsGeometry.fromWkt('point(8 46)')) self.assertTrue(layer_a.addFeatures([f])) f = [f for f in layer_a.getFeatures() if f.attribute('int') == 555][0] self.assertTrue(f.id() in buffer.addedFeatures()) self.assertTrue(layer_a.deleteFeature(f.id())) self.assertFalse(f.id() in buffer.addedFeatures()) self.assertFalse(f.id() in buffer.deletedFeatureIds()) layer_a.undoStack().undo() self.assertTrue(f.id() in buffer.addedFeatures()) ########################################### # Add attribute field = QgsField('attr1', QVariant.String) self.assertTrue(layer_a.addAttribute(field)) self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), []) layer_a.undoStack().redo() self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) self.assertTrue(layer_a.commitChanges()) ########################################### # Remove attribute self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.deleteAttribute(attr_idx)) self.assertEqual(buffer.deletedAttributeIds(), [2]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) layer_a.undoStack().undo() self.assertEqual(buffer.deletedAttributeIds(), []) self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) layer_a.undoStack().redo() self.assertEqual(buffer.deletedAttributeIds(), [2]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertTrue(layer_a.rollBack()) ########################################### # Rename attribute self.assertTrue(layer_a.startEditing()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) self.assertTrue(layer_a.renameAttribute(attr_idx, 'new_name')) self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) layer_a.undoStack().redo() self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) self.assertEqual(layer_a.fields().lookupField(field.name()), -1)