예제 #1
0
    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)
예제 #3
0
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)
예제 #4
0
    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)))
예제 #5
0
    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))
예제 #6
0
    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())
예제 #7
0
    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(), [])
예제 #8
0
    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
예제 #9
0
    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)))
예제 #10
0
    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]))
예제 #11
0
    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))
예제 #12
0
    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
예제 #13
0
    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)
예제 #14
0
    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())
예제 #15
0
    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
예제 #16
0
    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')
예제 #17
0
    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)
예제 #18
0
    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
예제 #19
0
    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
예제 #20
0
    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)
예제 #21
0
    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(),
        ])
예제 #23
0
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(),
            ])
예제 #24
0
    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
예제 #25
0
    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')
예제 #26
0
    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
예제 #27
0
    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)