def test_zip_unzip(self): tmpDir = QTemporaryDir() tmpFile = "{}/project.qgz".format(tmpDir.path()) project = QgsProject() l0 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr") l1 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "lines.shp"), "lines", "ogr") project.addMapLayers([l0, l1]) self.assertTrue(project.write(tmpFile)) project2 = QgsProject() self.assertFalse(project2.isZipped()) self.assertTrue(project2.fileName() == "") self.assertTrue(project2.read(tmpFile)) self.assertTrue(project2.isZipped()) self.assertTrue(project2.fileName() == tmpFile) layers = project2.mapLayers() self.assertEqual(len(layers.keys()), 2) self.assertTrue(layers[l0.id()].isValid(), True) self.assertTrue(layers[l1.id()].isValid(), True) project2.clear() self.assertFalse(project2.isZipped())
def testLayers(self): """ check that layers are shown in widget model""" p = QgsProject.instance() layer = QgsVectorLayer("Point", "layer1", "memory") layer2 = QgsVectorLayer("Point", "layer2", "memory") p.addMapLayers([layer, layer2]) w = QgsExpressionBuilderWidget() m = w.model() # check that layers are shown items = m.findItems('layer1', Qt.MatchRecursive) self.assertEqual(len(items), 1) items = m.findItems('layer2', Qt.MatchRecursive) self.assertEqual(len(items), 1) # change project p2 = QgsProject() layer3 = QgsVectorLayer("Point", "layer3", "memory") p2.addMapLayers([layer3]) w.setProject(p2) m = w.model() items = m.findItems('layer1', Qt.MatchRecursive) self.assertEqual(len(items), 0) items = m.findItems('layer2', Qt.MatchRecursive) self.assertEqual(len(items), 0) items = m.findItems('layer3', Qt.MatchRecursive) self.assertEqual(len(items), 1)
class TestQgsLayerTreeView(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # setup a dummy project self.project = QgsProject() self.layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") self.layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") self.layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3", "memory") self.project.addMapLayers([self.layer, self.layer2, self.layer3]) self.model = QgsLayerTreeModel(self.project.layerTreeRoot()) def testSetModel(self): view = QgsLayerTreeView() # should not work string_list_model = QStringListModel() view.setModel(string_list_model) self.assertFalse(view.model()) # should work view.setModel(self.model) self.assertEqual(view.model(), self.model) def testSetCurrentLayer(self): view = QgsLayerTreeView() view.setModel(self.model) current_layer_changed_spy = QSignalSpy(view.currentLayerChanged) self.assertFalse(view.currentLayer()) view.setCurrentLayer(self.layer3) self.assertEqual(view.currentLayer(), self.layer3) self.assertEqual(len(current_layer_changed_spy), 1) view.setCurrentLayer(self.layer) self.assertEqual(view.currentLayer(), self.layer) self.assertEqual(len(current_layer_changed_spy), 2) view.setCurrentLayer(None) self.assertFalse(view.currentLayer()) self.assertEqual(len(current_layer_changed_spy), 3) def testDefaultActions(self): view = QgsLayerTreeView() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) # show in overview action view.setCurrentLayer(self.layer) self.assertEqual(view.currentNode().customProperty('overview', 0), False) show_in_overview = actions.actionShowInOverview() show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), True) show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), False)
def testTransformContextInheritsFromProject(self): """Test that when a layer is added to a project it inherits its context""" rl = QgsRasterLayer(self.rpath, 'raster') self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p = QgsProject() self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p.setTransformContext(self.ctx) self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p.addMapLayers([rl]) self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
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 testCustomLayerOrder(self): """ test project layer order""" prj = QgsProject() layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3", "memory") prj.addMapLayers([layer, layer2, layer3]) layer_order_changed_spy = QSignalSpy(prj.layerTreeRoot().customLayerOrderChanged) prj.layerTreeRoot().setCustomLayerOrder([layer2, layer]) self.assertEqual(len(layer_order_changed_spy), 1) prj.layerTreeRoot().setCustomLayerOrder([layer2, layer]) self.assertEqual(len(layer_order_changed_spy), 1) # no signal, order not changed self.assertEqual(prj.layerTreeRoot().customLayerOrder(), [layer2, layer]) prj.layerTreeRoot().setCustomLayerOrder([layer]) self.assertEqual(prj.layerTreeRoot().customLayerOrder(), [layer]) self.assertEqual(len(layer_order_changed_spy), 2) # remove a layer prj.layerTreeRoot().setCustomLayerOrder([layer2, layer, layer3]) self.assertEqual(len(layer_order_changed_spy), 3) prj.removeMapLayer(layer) self.assertEqual(prj.layerTreeRoot().customLayerOrder(), [layer2, layer3]) self.assertEqual(len(layer_order_changed_spy), 4) # save and restore file_name = os.path.join(QDir.tempPath(), 'proj.qgs') prj.setFileName(file_name) prj.write() prj2 = QgsProject() prj2.setFileName(file_name) prj2.read() self.assertEqual([l.id() for l in prj2.layerTreeRoot().customLayerOrder()], [layer2.id(), layer3.id()]) # clear project prj.clear() self.assertEqual(prj.layerTreeRoot().customLayerOrder(), [])
def prepareIteratorLayout(self): layer_path = os.path.join(TEST_DATA_DIR, 'france_parts.shp') layer = QgsVectorLayer(layer_path, 'test', "ogr") project = QgsProject() project.addMapLayers([layer]) # select epsg:2154 crs = QgsCoordinateReferenceSystem() crs.createFromSrid(2154) project.setCrs(crs) layout = QgsPrintLayout(project) layout.initializeDefaults() # fix the renderer, fill with green props = {"color": "0,127,0", "outline_width": "4", "outline_color": '255,255,255'} fillSymbol = QgsFillSymbol.createSimple(props) renderer = QgsSingleSymbolRenderer(fillSymbol) layer.setRenderer(renderer) # the atlas map atlas_map = QgsLayoutItemMap(layout) atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130)) atlas_map.setFrameEnabled(True) atlas_map.setLayers([layer]) layout.addLayoutItem(atlas_map) # the atlas atlas = layout.atlas() atlas.setCoverageLayer(layer) atlas.setEnabled(True) atlas_map.setExtent( QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338)) atlas_map.setAtlasDriven(True) atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) atlas_map.setAtlasMargin(0.10) return project, layout
def testTransformContextIsSyncedFromProject(self): """Test that when a layer is synced when project context changes""" rl = QgsRasterLayer(self.rpath, 'raster') self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p = QgsProject() self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p.setTransformContext(self.ctx) self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p.addMapLayers([rl]) self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) # Now change the project context tc2 = QgsCoordinateTransformContext() p.setTransformContext(tc2) self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) p.setTransformContext(self.ctx) self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857))) self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
def test_mapLayersByName(self): """ test retrieving map layer by name """ p = QgsProject() # test no crash with empty registry self.assertEqual(p.mapLayersByName('bad'), []) self.assertEqual(p.mapLayersByName(None), []) l1 = createLayer('test') l2 = createLayer('test2') p.addMapLayers([l1, l2]) self.assertEqual(p.mapLayersByName('bad'), []) self.assertEqual(p.mapLayersByName(None), []) self.assertEqual(p.mapLayersByName('test'), [l1]) self.assertEqual(p.mapLayersByName('test2'), [l2]) #duplicate name l3 = createLayer('test') p.addMapLayer(l3) self.assertEqual(set(p.mapLayersByName('test')), set([l1, l3]))
def testQgdCreation(self): # New project p = QgsProject() self.assertTrue(p.auxiliaryStorage().isValid()) # Save the project path = tmpPath() qgs = path + '.qgs' self.assertTrue(p.write(qgs)) self.assertTrue(os.path.exists(qgs)) # Auxiliary storage is empty so .qgd file should not be saved qgd = path + '.qgd' self.assertFalse(os.path.exists(qgd)) # Add a vector layer and an auxiliary layer in the project vl = createLayer() self.assertTrue(vl.isValid()) p.addMapLayers([vl]) pkf = vl.fields().field(vl.fields().indexOf('pk')) al = p.auxiliaryStorage().createAuxiliaryLayer(pkf, vl) self.assertTrue(al.isValid()) vl.setAuxiliaryLayer(al) # Add an auxiliary field to have a non empty auxiliary storage pdef = QgsPropertyDefinition('propname', QgsPropertyDefinition.DataTypeNumeric, '', '', 'ut') self.assertTrue(al.addAuxiliaryField(pdef)) # Save the project newpath = tmpPath() qgs = newpath + '.qgs' self.assertTrue(p.write(qgs)) self.assertTrue(os.path.exists(qgs)) # Auxiliary storage is NOT empty so .qgd file should be saved now qgd = newpath + '.qgd' self.assertTrue(os.path.exists(qgd))
def testRelativePaths(self): """ Test whether paths to layer sources are stored as relative to the project path """ tmpDir = QTemporaryDir() tmpFile = "{}/project.qgs".format(tmpDir.path()) copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp")) copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp")) copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx")) copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif")) project = QgsProject() l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr") l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr") l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal") self.assertTrue(l0.isValid()) self.assertTrue(l1.isValid()) self.assertTrue(l2.isValid()) self.assertTrue(project.addMapLayers([l0, l1, l2])) self.assertTrue(project.write(tmpFile)) del project with open(tmpFile, 'r') as f: content = ''.join(f.readlines()) self.assertTrue('source="./lines.shp"' in content) self.assertTrue('source="./points.shp"' in content) self.assertTrue('source="./landsat_4326.tif"' in content) # Re-read the project and store absolute project = QgsProject() self.assertTrue(project.read(tmpFile)) store = project.layerStore() self.assertEquals(set([l.name() for l in store.mapLayers().values()]), set(['lines', 'landsat', 'points'])) project.writeEntryBool('Paths', '/Absolute', True) tmpFile2 = "{}/project2.qgs".format(tmpDir.path()) self.assertTrue(project.write(tmpFile2)) with open(tmpFile2, 'r') as f: content = ''.join(f.readlines()) self.assertTrue('source="{}/lines.shp"'.format(tmpDir.path()) in content) self.assertTrue('source="{}/points.shp"'.format(tmpDir.path()) in content) self.assertTrue('source="{}/landsat_4326.tif"'.format(tmpDir.path()) in content) del project
def testSymbolicLinkInProjectPath(self): """ Test whether paths to layer sources relative to the project are stored correctly when project'name contains a symbolic link. In other words, test if project's and layers' names are correctly resolved. """ tmpDir = QTemporaryDir() tmpFile = "{}/project.qgs".format(tmpDir.path()) copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp")) copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp")) copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx")) copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif")) project = QgsProject() l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr") l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr") l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal") self.assertTrue(l0.isValid()) self.assertTrue(l1.isValid()) self.assertTrue(l2.isValid()) self.assertTrue(project.addMapLayers([l0, l1, l2])) self.assertTrue(project.write(tmpFile)) del project # Create symbolic link to previous project tmpDir2 = QTemporaryDir() symlinkDir = os.path.join(tmpDir2.path(), "dir") os.symlink(tmpDir.path(), symlinkDir) tmpFile = "{}/project.qgs".format(symlinkDir) # Open project from symmlink and force re-save. project = QgsProject() self.assertTrue(project.read(tmpFile)) self.assertTrue(project.write(tmpFile)) del project with open(tmpFile, 'r') as f: content = ''.join(f.readlines()) self.assertTrue('source="./lines.shp"' in content) self.assertTrue('source="./points.shp"' in content) self.assertTrue('source="./landsat_4326.tif"' in content)
def testLayerChangeDirtiesProject(self): """ Test that making changes to certain layer properties results in dirty projects """ p = QgsProject() l = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr") self.assertTrue(l.isValid()) self.assertTrue(p.addMapLayers([l])) p.setDirty(False) l.setCrs(QgsCoordinateReferenceSystem('EPSG:3111')) self.assertTrue(p.isDirty()) p.setDirty(False) l.setName('test') self.assertTrue(p.isDirty()) p.setDirty(False) self.assertTrue(l.setSubsetString('class=\'a\'')) self.assertTrue(p.isDirty())
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 testProjectStorage(self): # New project without fileName p0 = QgsProject() self.assertTrue(p0.auxiliaryStorage().isValid()) # Create new layers with key otherwise auxiliary layers are not # automacially created when added in project vl0 = createLayer() vl0Shp = writeShape(vl0, 'vl0.shp') vl1 = createLayer() vl1Shp = writeShape(vl1, 'vl1.shp') vl0 = QgsVectorLayer(vl0Shp, 'points', 'ogr') self.assertTrue(vl0.isValid()) vl1 = QgsVectorLayer(vl1Shp, 'points', 'ogr') self.assertTrue(vl1.isValid()) # Add layers to project and check underlying auxiliary layers p0.addMapLayers([vl0, vl1]) self.assertTrue(vl0.loadAuxiliaryLayer(p0.auxiliaryStorage(), 'pk')) self.assertTrue(vl1.loadAuxiliaryLayer(p0.auxiliaryStorage(), 'num_char')) al0 = vl0.auxiliaryLayer() al1 = vl1.auxiliaryLayer() self.assertEqual(al0.joinInfo().targetFieldName(), 'pk') self.assertEqual(al1.joinInfo().targetFieldName(), 'num_char') # Add a field in auxiliary layers pdef0 = QgsPropertyDefinition('propname', QgsPropertyDefinition.DataTypeNumeric, '', '', 'ut') self.assertTrue(al0.addAuxiliaryField(pdef0)) pdef1 = QgsPropertyDefinition('propname1', QgsPropertyDefinition.DataTypeString, '', '', 'ut') self.assertTrue(al1.addAuxiliaryField(pdef1)) # Check auxiliary fields names af0Name = QgsAuxiliaryLayer.nameFromProperty(pdef0, False) self.assertEqual(af0Name, 'ut_propname') af1Name = QgsAuxiliaryLayer.nameFromProperty(pdef1, False) self.assertEqual(af1Name, 'ut_propname1') # Set value for auxiliary fields req = QgsFeatureRequest().setFilterExpression("name = 'Honey'") f = QgsFeature() vl0.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) af0Name = QgsAuxiliaryLayer.nameFromProperty(pdef0, True) index0 = vl0.fields().indexOf(af0Name) vl0.changeAttributeValue(f.id(), index0, 333) req = QgsFeatureRequest().setFilterExpression("name = 'Apple'") f = QgsFeature() vl1.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) af1Name = QgsAuxiliaryLayer.nameFromProperty(pdef1, True) index1 = vl1.fields().indexOf(af1Name) vl1.changeAttributeValue(f.id(), index0, 'myvalue') req = QgsFeatureRequest().setFilterExpression("name = 'Orange'") f = QgsFeature() vl1.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) vl1.changeAttributeValue(f.id(), index0, 'myvalue1') # Save the project in a zip file f = tmpPath() + '.qgz' p0.write(f) # Open the zip file with embedded auxiliary storage p1 = QgsProject() p1.read(f) # Check that auxiliary fields are well loaded in layers self.assertEqual(len(p1.mapLayers().values()), 2) for vl in p1.mapLayers().values(): al = vl.auxiliaryLayer() self.assertEqual(len(al.auxiliaryFields()), 1) af = al.auxiliaryFields()[0] afPropDef = QgsAuxiliaryLayer.propertyDefinitionFromField(af) self.assertEqual(afPropDef.origin(), 'ut') if vl.auxiliaryLayer().joinInfo().targetFieldName() == 'pk': self.assertEqual(afPropDef.name(), 'propname') self.assertEqual(al.featureCount(), 1) req = QgsFeatureRequest().setFilterExpression("name = 'Honey'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index0], 333.0) else: # num_char self.assertEqual(al.featureCount(), 2) self.assertEqual(afPropDef.name(), 'propname1') req = QgsFeatureRequest().setFilterExpression("name = 'Apple'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index1], 'myvalue') req = QgsFeatureRequest().setFilterExpression("name = 'Orange'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index1], 'myvalue1')
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 testThemeChanged(self): """ Test that the mapTheme(s)Changed signals are correctly emitted in all relevant situations """ project = QgsProject() collection = QgsMapThemeCollection(project) record = QgsMapThemeCollection.MapThemeRecord() theme_changed_spy = QSignalSpy(collection.mapThemeChanged) themes_changed_spy = QSignalSpy(collection.mapThemesChanged) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 1) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 1) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 2) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 2) # update collection.update('theme1', record) self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 3) # remove invalid collection.removeMapTheme('i wish i was a slave to an age old trade... like riding around on rail cars and working long days') self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(len(themes_changed_spy), 3) # remove valid collection.removeMapTheme('theme1') self.assertEqual(len(theme_changed_spy), 3) # not changed - removed! self.assertEqual(len(themes_changed_spy), 4) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 4) self.assertEqual(len(themes_changed_spy), 5) # clear collection.clear() self.assertEqual(len(theme_changed_spy), 4) # not changed - removed! self.assertEqual(len(themes_changed_spy), 6) # check that mapThemeChanged is emitted if layer is removed layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") project.addMapLayers([layer, layer2]) # record for layer1 record.addLayerRecord(QgsMapThemeCollection.MapThemeLayerRecord(layer)) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 5) self.assertEqual(len(themes_changed_spy), 7) # now kill layer 2 project.removeMapLayer(layer2) self.assertEqual(len(theme_changed_spy), 5) # signal should not be emitted - layer is not in record # now kill layer 1 project.removeMapLayer(layer) app.processEvents() self.assertEqual(len(theme_changed_spy), 6) # signal should be emitted - layer is in record
def testDuplicateFeature(self): """ test duplicating a feature """ project = QgsProject().instance() # LAYERS # - add first layer (parent) layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer", "parentlayer", "memory") # > check first layer (parent) self.assertTrue(layer1.isValid()) # - set the value for the copy layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)")) # > check first layer (parent) self.assertTrue(layer1.isValid()) # - add second layer (child) layer2 = QgsVectorLayer( "Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer", "childlayer", "memory") # > check second layer (child) self.assertTrue(layer2.isValid()) # - add layers project.addMapLayers([layer1, layer2]) # FEATURES # - add 2 features on layer1 (parent) l1f1orig = QgsFeature() l1f1orig.setFields(layer1.fields()) l1f1orig.setAttributes(["F_l1f1", 100]) l1f2orig = QgsFeature() l1f2orig.setFields(layer1.fields()) l1f2orig.setAttributes(["F_l1f2", 101]) # > check by adding features self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig, l1f2orig])) # add 4 features on layer2 (child) l2f1orig = QgsFeature() l2f1orig.setFields(layer2.fields()) l2f1orig.setAttributes(["F_l2f1", 201, 100]) l2f2orig = QgsFeature() l2f2orig.setFields(layer2.fields()) l2f2orig.setAttributes(["F_l2f2", 202, 100]) l2f3orig = QgsFeature() l2f3orig.setFields(layer2.fields()) l2f3orig.setAttributes(["F_l2f3", 203, 100]) l2f4orig = QgsFeature() l2f4orig.setFields(layer2.fields()) l2f4orig.setAttributes(["F_l2f4", 204, 101]) # > check by adding features self.assertTrue(layer2.dataProvider().addFeatures( [l2f1orig, l2f2orig, l2f3orig, l2f4orig])) # RELATION # - create the relationmanager relMgr = project.relationManager() # - create the relation rel = QgsRelation() rel.setId('rel1') rel.setName('childrel') rel.setReferencingLayer(layer2.id()) rel.setReferencedLayer(layer1.id()) rel.addFieldPair('foreign_key', 'pkid') rel.setStrength(QgsRelation.Composition) # > check relation self.assertTrue(rel.isValid()) # - add relation relMgr.addRelation(rel) # > check if referencedLayer is layer1 self.assertEqual(rel.referencedLayer(), layer1) # > check if referencingLayer is layer2 self.assertEqual(rel.referencingLayer(), layer2) # > check if the layers are correct in relation when loading from relationManager relations = project.relationManager().relations() relation = relations[list(relations.keys())[0]] # > check if referencedLayer is layer1 self.assertEqual(relation.referencedLayer(), layer1) # > check if referencingLayer is layer2 self.assertEqual(relation.referencingLayer(), layer2) # > check the relatedfeatures ''' # testoutput 1 print( "\nAll Features and relations") featit=layer1.getFeatures() f=QgsFeature() while featit.nextFeature(f): print( f.attributes()) childFeature = QgsFeature() relfeatit=rel.getRelatedFeatures(f) while relfeatit.nextFeature(childFeature): print( childFeature.attributes() ) print( "\n--------------------------") print( "\nFeatures on layer1") for f in layer1.getFeatures(): print( f.attributes() ) print( "\nFeatures on layer2") for f in layer2.getFeatures(): print( f.attributes() ) ''' # DUPLICATION # - duplicate feature l1f1orig with children layer1.startEditing() results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig, project, 0) # > check if name is name of duplicated (pk is different) result_feature = results[0] self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt')) # > check duplicated child layer result_layer = results[1].layers()[0] self.assertEqual(result_layer, layer2) # > check duplicated child features self.assertTrue(results[1].duplicatedFeatures(result_layer)) ''' # testoutput 2 print( "\nFeatures on layer1 (after duplication)") for f in layer1.getFeatures(): print( f.attributes() ) print( "\nFeatures on layer2 (after duplication)") for f in layer2.getFeatures(): print( f.attributes() ) print( "\nAll Features and relations") featit=layer1.getFeatures() f=QgsFeature() while featit.nextFeature(f): print( f.attributes()) childFeature = QgsFeature() relfeatit=rel.getRelatedFeatures(f) while relfeatit.nextFeature(childFeature): print( childFeature.attributes() ) ''' # > compare text of parent feature self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt')) # - create copyValueList childFeature = QgsFeature() relfeatit = rel.getRelatedFeatures(result_feature) copyValueList = [] while relfeatit.nextFeature(childFeature): copyValueList.append(childFeature.attribute('fldtxt')) # - create origValueList childFeature = QgsFeature() relfeatit = rel.getRelatedFeatures(l1f1orig) origValueList = [] while relfeatit.nextFeature(childFeature): origValueList.append(childFeature.attribute('fldtxt')) # - check if the ids are still the same self.assertEqual(copyValueList, origValueList)
def test_getFeatureFeatureEnvelopeCrs(self): """Test issue GH #48642""" project = QgsProject() layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldint:integer", "layer", "memory") project.addMapLayers([layer]) project.writeEntry("WFSLayers", "/", [layer.id()]) f = QgsFeature(layer.fields()) f.setGeometry(QgsGeometry.fromWkt('point(807305 5592878)')) f.setAttributes([123]) layer.dataProvider().addFeatures([f]) f = QgsFeature(layer.fields()) f.setGeometry(QgsGeometry.fromWkt('point(812191 5589555)')) f.setAttributes([123]) layer.dataProvider().addFeatures([f]) query_string = "?" + "&".join([ "%s=%s" % i for i in list({ "SERVICE": "WFS", "REQUEST": "GetFeature", "VERSION": "1.1.0", "TYPENAME": "layer", "SRSNAME": "EPSG:4326" }.items()) ]) header, body = self._execute_request_project(query_string, project) root = et.fromstring(body) e = root.findall('.//gml:Envelope', root.nsmap)[0] self.assertEqual(e.attrib, {'srsName': 'EPSG:4326'}) self.assertEqual([c[:4] for c in e.findall('.//')[0].text.split(' ')], ['7.25', '44.7']) self.assertEqual([c[:4] for c in e.findall('.//')[1].text.split(' ')], ['7.29', '44.8']) query_string = "?" + "&".join([ "%s=%s" % i for i in list({ "SERVICE": "WFS", "REQUEST": "GetFeature", "VERSION": "1.1.0", "TYPENAME": "layer", "SRSNAME": "EPSG:4326", "BBOX": "7.2,44.5,8.2,45.1,EPSG:4326" }.items()) ]) header, body = self._execute_request_project(query_string, project) root = et.fromstring(body) e = root.findall('.//gml:Envelope', root.nsmap)[0] self.assertEqual(e.attrib, {'srsName': 'EPSG:4326'}) self.assertEqual([c[:4] for c in e.findall('.//')[0].text.split(' ')], ['7.2', '44.5']) self.assertEqual([c[:4] for c in e.findall('.//')[1].text.split(' ')], ['8.2', '45.1']) query_string = "?" + "&".join([ "%s=%s" % i for i in list({ "SERVICE": "WFS", "REQUEST": "GetFeature", "VERSION": "1.1.0", "TYPENAME": "layer", "SRSNAME": "EPSG:4326", "BBOX": "807305,5589555,812191,5592878,EPSG:3857" }.items()) ]) header, body = self._execute_request_project(query_string, project) root = et.fromstring(body) e = root.findall('.//gml:Envelope', root.nsmap)[0] self.assertEqual(e.attrib, {'srsName': 'EPSG:4326'}) self.assertEqual([c[:4] for c in e.findall('.//')[0].text.split(' ')], ['7.25', '44.7']) self.assertEqual([c[:4] for c in e.findall('.//')[1].text.split(' ')], ['7.29', '44.8'])
class TestQgsLayerTreeView(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # setup a dummy project self.project = QgsProject() self.layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") self.layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") self.layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3", "memory") self.layer4 = QgsVectorLayer("Point?field=fldtxt:string", "layer4", "memory") self.layer5 = QgsVectorLayer("Point?field=fldtxt:string", "layer5", "memory") self.project.addMapLayers([self.layer, self.layer2, self.layer3]) self.model = QgsLayerTreeModel(self.project.layerTreeRoot()) def nodeOrder(self, group): nodeorder = [] layerTree = QgsLayerTree() for node in group: if QgsLayerTree.isGroup(node): groupname = node.name() nodeorder.append(groupname) for child in self.nodeOrder(node.children()): nodeorder.append(groupname + '-' + child) elif QgsLayerTree.isLayer(node): nodeorder.append(node.layer().name()) return nodeorder def testSetModel(self): view = QgsLayerTreeView() # should not work string_list_model = QStringListModel() view.setModel(string_list_model) self.assertFalse(view.model()) # should work view.setModel(self.model) self.assertEqual(view.model(), self.model) def testSetCurrentLayer(self): view = QgsLayerTreeView() view.setModel(self.model) current_layer_changed_spy = QSignalSpy(view.currentLayerChanged) self.assertFalse(view.currentLayer()) view.setCurrentLayer(self.layer3) self.assertEqual(view.currentLayer(), self.layer3) self.assertEqual(len(current_layer_changed_spy), 1) view.setCurrentLayer(self.layer) self.assertEqual(view.currentLayer(), self.layer) self.assertEqual(len(current_layer_changed_spy), 2) view.setCurrentLayer(None) self.assertFalse(view.currentLayer()) self.assertEqual(len(current_layer_changed_spy), 3) def testDefaultActions(self): view = QgsLayerTreeView() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) # show in overview action view.setCurrentLayer(self.layer) self.assertEqual(view.currentNode().customProperty('overview', 0), False) show_in_overview = actions.actionShowInOverview() show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), True) show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), False) def testMoveOutOfGroupActionLayer(self): """Test move out of group action on layer""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) view.setCurrentLayer(self.layer5) moveOutOfGroup = actions.actionMoveOutOfGroup() moveOutOfGroup.trigger() self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), self.layer5.name(), groupname, groupname + '-' + self.layer4.name(), ]) def testMoveToTopActionLayer(self): """Test move to top action on layer""" view = QgsLayerTreeView() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual(self.project.layerTreeRoot().layerOrder(), [self.layer, self.layer2, self.layer3]) view.setCurrentLayer(self.layer3) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual(self.project.layerTreeRoot().layerOrder(), [self.layer3, self.layer, self.layer2]) def testMoveToTopActionGroup(self): """Test move to top action on group""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) nodeLayerIndex = self.model.node2index(group) view.setCurrentIndex(nodeLayerIndex) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), self.layer.name(), self.layer2.name(), self.layer3.name(), ]) def testMoveToTopActionEmbeddedGroup(self): """Test move to top action on embeddedgroup layer""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) view.setCurrentLayer(self.layer5) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual(self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer5.name(), groupname + '-' + self.layer4.name(), ])
class TestQgsLayerTreeView(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # setup a dummy project self.project = QgsProject() self.layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") self.layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") self.layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3", "memory") self.layer4 = QgsVectorLayer("Point?field=fldtxt:string", "layer4", "memory") self.layer5 = QgsVectorLayer("Point?field=fldtxt:string", "layer5", "memory") self.project.addMapLayers([self.layer, self.layer2, self.layer3]) self.model = QgsLayerTreeModel(self.project.layerTreeRoot()) def nodeOrder(self, group): nodeorder = [] layerTree = QgsLayerTree() for node in group: if QgsLayerTree.isGroup(node): groupname = node.name() nodeorder.append(groupname) for child in self.nodeOrder(node.children()): nodeorder.append(groupname + '-' + child) elif QgsLayerTree.isLayer(node): nodeorder.append(node.layer().name()) return nodeorder def testSetModel(self): view = QgsLayerTreeView() # should not work string_list_model = QStringListModel() view.setModel(string_list_model) self.assertFalse(view.model()) # should work view.setModel(self.model) self.assertEqual(view.model(), self.model) def testSetCurrentLayer(self): view = QgsLayerTreeView() view.setModel(self.model) current_layer_changed_spy = QSignalSpy(view.currentLayerChanged) self.assertFalse(view.currentLayer()) view.setCurrentLayer(self.layer3) self.assertEqual(view.currentLayer(), self.layer3) self.assertEqual(len(current_layer_changed_spy), 1) view.setCurrentLayer(self.layer) self.assertEqual(view.currentLayer(), self.layer) self.assertEqual(len(current_layer_changed_spy), 2) view.setCurrentLayer(None) self.assertFalse(view.currentLayer()) self.assertEqual(len(current_layer_changed_spy), 3) def testDefaultActions(self): view = QgsLayerTreeView() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) # show in overview action view.setCurrentLayer(self.layer) self.assertEqual(view.currentNode().customProperty('overview', 0), False) show_in_overview = actions.actionShowInOverview() show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), True) show_in_overview.trigger() self.assertEqual(view.currentNode().customProperty('overview', 0), False) def testMoveOutOfGroupActionLayer(self): """Test move out of group action on layer""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) view.setCurrentLayer(self.layer5) moveOutOfGroup = actions.actionMoveOutOfGroup() moveOutOfGroup.trigger() self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), self.layer5.name(), groupname, groupname + '-' + self.layer4.name(), ]) def testMoveToTopActionLayer(self): """Test move to top action on layer""" view = QgsLayerTreeView() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual(self.project.layerTreeRoot().layerOrder(), [self.layer, self.layer2, self.layer3]) view.setCurrentLayer(self.layer3) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual(self.project.layerTreeRoot().layerOrder(), [self.layer3, self.layer, self.layer2]) def testMoveToTopActionGroup(self): """Test move to top action on group""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) nodeLayerIndex = self.model.node2index(group) view.setCurrentIndex(nodeLayerIndex) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), self.layer.name(), self.layer2.name(), self.layer3.name(), ]) def testMoveToTopActionEmbeddedGroup(self): """Test move to top action on embeddedgroup layer""" view = QgsLayerTreeView() group = self.project.layerTreeRoot().addGroup("embeddedgroup") group.addLayer(self.layer4) group.addLayer(self.layer5) groupname = group.name() view.setModel(self.model) actions = QgsLayerTreeViewDefaultActions(view) self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer4.name(), groupname + '-' + self.layer5.name(), ]) view.setCurrentLayer(self.layer5) movetotop = actions.actionMoveToTop() movetotop.trigger() self.assertEqual( self.nodeOrder(self.project.layerTreeRoot().children()), [ self.layer.name(), self.layer2.name(), self.layer3.name(), groupname, groupname + '-' + self.layer5.name(), groupname + '-' + self.layer4.name(), ])
def testRelativePaths(self): """ Test whether paths to layer sources are stored as relative to the project path """ tmpDir = QTemporaryDir() tmpFile = "{}/project.qgs".format(tmpDir.path()) copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp")) copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp")) copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf")) copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx")) copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif")) project = QgsProject() l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr") l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr") l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal") self.assertTrue(l0.isValid()) self.assertTrue(l1.isValid()) self.assertTrue(l2.isValid()) self.assertTrue(project.addMapLayers([l0, l1, l2])) self.assertTrue(project.write(tmpFile)) del project with open(tmpFile, 'r') as f: content = ''.join(f.readlines()) self.assertTrue('source="./lines.shp"' in content) self.assertTrue('source="./points.shp"' in content) self.assertTrue('source="./landsat_4326.tif"' in content) # Re-read the project and store absolute project = QgsProject() self.assertTrue(project.read(tmpFile)) store = project.layerStore() self.assertEquals(set([l.name() for l in store.mapLayers().values()]), set(['lines', 'landsat', 'points'])) project.writeEntryBool('Paths', '/Absolute', True) tmpFile2 = "{}/project2.qgs".format(tmpDir.path()) self.assertTrue(project.write(tmpFile2)) with open(tmpFile2, 'r') as f: content = ''.join(f.readlines()) self.assertTrue( 'source="{}/lines.shp"'.format(tmpDir.path()) in content) self.assertTrue( 'source="{}/points.shp"'.format(tmpDir.path()) in content) self.assertTrue('source="{}/landsat_4326.tif"'.format( tmpDir.path()) in content) del project
def testThemeChanged(self): """ Test that the mapTheme(s)Changed signals are correctly emitted in all relevant situations """ project = QgsProject() collection = QgsMapThemeCollection(project) record = QgsMapThemeCollection.MapThemeRecord() theme_changed_spy = QSignalSpy(collection.mapThemeChanged) themes_changed_spy = QSignalSpy(collection.mapThemesChanged) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 1) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 1) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 2) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 2) # update collection.update('theme1', record) self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 3) # remove invalid collection.removeMapTheme( 'i wish i was a slave to an age old trade... like riding around on rail cars and working long days' ) self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(len(themes_changed_spy), 3) # remove valid collection.removeMapTheme('theme1') self.assertEqual(len(theme_changed_spy), 3) # not changed - removed! self.assertEqual(len(themes_changed_spy), 4) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 4) self.assertEqual(len(themes_changed_spy), 5) # clear collection.clear() self.assertEqual(len(theme_changed_spy), 4) # not changed - removed! self.assertEqual(len(themes_changed_spy), 6) # check that mapThemeChanged is emitted if layer is removed layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") project.addMapLayers([layer, layer2]) # record for layer1 record.addLayerRecord(QgsMapThemeCollection.MapThemeLayerRecord(layer)) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 5) self.assertEqual(len(themes_changed_spy), 7) # now kill layer 2 project.removeMapLayer(layer2) self.assertEqual( len(theme_changed_spy), 5) # signal should not be emitted - layer is not in record # now kill layer 1 project.removeMapLayer(layer) app.processEvents() self.assertEqual(len(theme_changed_spy), 6) # signal should be emitted - layer is in record
def testDuplicateFeature(self): """ test duplicating a feature """ project = QgsProject().instance() # LAYERS # - add first layer (parent) layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer", "parentlayer", "memory") # > check first layer (parent) self.assertTrue(layer1.isValid()) # - set the value for the copy layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)")) # > check first layer (parent) self.assertTrue(layer1.isValid()) # - add second layer (child) layer2 = QgsVectorLayer("Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer", "childlayer", "memory") # > check second layer (child) self.assertTrue(layer2.isValid()) # - add layers project.addMapLayers([layer1, layer2]) # FEATURES # - add 2 features on layer1 (parent) l1f1orig = QgsFeature() l1f1orig.setFields(layer1.fields()) l1f1orig.setAttributes(["F_l1f1", 100]) l1f2orig = QgsFeature() l1f2orig.setFields(layer1.fields()) l1f2orig.setAttributes(["F_l1f2", 101]) # > check by adding features self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig, l1f2orig])) # add 4 features on layer2 (child) l2f1orig = QgsFeature() l2f1orig.setFields(layer2.fields()) l2f1orig.setAttributes(["F_l2f1", 201, 100]) l2f2orig = QgsFeature() l2f2orig.setFields(layer2.fields()) l2f2orig.setAttributes(["F_l2f2", 202, 100]) l2f3orig = QgsFeature() l2f3orig.setFields(layer2.fields()) l2f3orig.setAttributes(["F_l2f3", 203, 100]) l2f4orig = QgsFeature() l2f4orig.setFields(layer2.fields()) l2f4orig.setAttributes(["F_l2f4", 204, 101]) # > check by adding features self.assertTrue(layer2.dataProvider().addFeatures([l2f1orig, l2f2orig, l2f3orig, l2f4orig])) # RELATION # - create the relationmanager relMgr = project.relationManager() # - create the relation rel = QgsRelation() rel.setId('rel1') rel.setName('childrel') rel.setReferencingLayer(layer2.id()) rel.setReferencedLayer(layer1.id()) rel.addFieldPair('foreign_key', 'pkid') rel.setStrength(QgsRelation.Composition) # > check relation self.assertTrue(rel.isValid()) # - add relation relMgr.addRelation(rel) # > check if referencedLayer is layer1 self.assertEqual(rel.referencedLayer(), layer1) # > check if referencingLayer is layer2 self.assertEqual(rel.referencingLayer(), layer2) # > check if the layers are correct in relation when loading from relationManager relations = project.relationManager().relations() relation = relations[list(relations.keys())[0]] # > check if referencedLayer is layer1 self.assertEqual(relation.referencedLayer(), layer1) # > check if referencingLayer is layer2 self.assertEqual(relation.referencingLayer(), layer2) # > check the relatedfeatures ''' # testoutput 1 print( "\nAll Features and relations") featit=layer1.getFeatures() f=QgsFeature() while featit.nextFeature(f): print( f.attributes()) childFeature = QgsFeature() relfeatit=rel.getRelatedFeatures(f) while relfeatit.nextFeature(childFeature): print( childFeature.attributes() ) print( "\n--------------------------") print( "\nFeatures on layer1") for f in layer1.getFeatures(): print( f.attributes() ) print( "\nFeatures on layer2") for f in layer2.getFeatures(): print( f.attributes() ) ''' # DUPLICATION # - duplicate feature l1f1orig with children layer1.startEditing() results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig, project, 0) # > check if name is name of duplicated (pk is different) result_feature = results[0] self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt')) # > check duplicated child layer result_layer = results[1].layers()[0] self.assertEqual(result_layer, layer2) # > check duplicated child features self.assertTrue(results[1].duplicatedFeatures(result_layer)) ''' # testoutput 2 print( "\nFeatures on layer1 (after duplication)") for f in layer1.getFeatures(): print( f.attributes() ) print( "\nFeatures on layer2 (after duplication)") for f in layer2.getFeatures(): print( f.attributes() ) print( "\nAll Features and relations") featit=layer1.getFeatures() f=QgsFeature() while featit.nextFeature(f): print( f.attributes()) childFeature = QgsFeature() relfeatit=rel.getRelatedFeatures(f) while relfeatit.nextFeature(childFeature): print( childFeature.attributes() ) ''' # > compare text of parent feature self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt')) # - create copyValueList childFeature = QgsFeature() relfeatit = rel.getRelatedFeatures(result_feature) copyValueList = [] while relfeatit.nextFeature(childFeature): copyValueList.append(childFeature.attribute('fldtxt')) # - create origValueList childFeature = QgsFeature() relfeatit = rel.getRelatedFeatures(l1f1orig) origValueList = [] while relfeatit.nextFeature(childFeature): origValueList.append(childFeature.attribute('fldtxt')) # - check if the ids are still the same self.assertEqual(copyValueList, origValueList)
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)