diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index 4888e6d1677..c3766624938 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -986,6 +986,8 @@ if the geometry type of the new data source matches the current geometry type of :param loadDefaultStyleFlag: set to true to reset the layer's style to the default for the data source +.. seealso:: :py:func:`dataSourceChanged` + .. versionadded:: 3.2 %End @@ -2295,6 +2297,15 @@ by the backend data provider). signals: + void dataSourceChanged(); +%Docstring +Emitted whenever the layer's data source has been changed. + +.. seealso:: :py:func:`setDataSource` + +.. versionadded:: 3.4 +%End + void selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect ); %Docstring This signal is emitted when selection was changed diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index ef2bf8a7116..111047cf4ba 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -1482,7 +1482,10 @@ void QgsVectorLayer::setDataSource( const QString &dataSource, const QString &ba setDataProvider( provider, options ); if ( !mValid ) + { + emit dataSourceChanged(); return; + } // Always set crs setCoordinateSystem(); @@ -1520,6 +1523,7 @@ void QgsVectorLayer::setDataSource( const QString &dataSource, const QString &ba setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) ); } + emit dataSourceChanged(); emit repaintRequested(); } diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 2a8853ba92c..6e262b9528e 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -964,6 +964,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * \param options provider options * \param loadDefaultStyleFlag set to true to reset the layer's style to the default for the * data source + * \see dataSourceChanged() * \since QGIS 3.2 */ void setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag = false ); @@ -2062,6 +2063,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte signals: + /** + * Emitted whenever the layer's data source has been changed. + * + * \see setDataSource() + * + * \since QGIS 3.4 + */ + void dataSourceChanged(); + /** * This signal is emitted when selection was changed * diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 5e35092c027..f35aca42a7d 100755 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -22,6 +22,7 @@ from qgis.PyQt.QtXml import QDomDocument from qgis.core import (QgsWkbTypes, QgsAction, + QgsDataProvider, QgsDefaultValue, QgsEditorWidgetSetup, QgsVectorLayer, @@ -60,6 +61,7 @@ from qgis.core import (QgsWkbTypes, from qgis.gui import (QgsAttributeTableModel, QgsGui ) +from qgis.PyQt.QtTest import QSignalSpy from qgis.testing import start_app, unittest from featuresourcetestbase import FeatureSourceTestCase from utilities import unitTestDataPath @@ -292,6 +294,48 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(layer.undoStack().index(), 2) self.assertEqual(layer.featureCount(), 4) + def testSetDataSource(self): + """ + Test changing a layer's data source + """ + layer = createLayerWithOnePoint() + layer.setCrs(QgsCoordinateReferenceSystem("epsg:3111")) + r = QgsSingleSymbolRenderer(QgsSymbol.defaultSymbol(QgsWkbTypes.PointGeometry)) + layer.setRenderer(r) + self.assertEqual(layer.renderer().symbol().type(), QgsSymbol.Marker) + + spy = QSignalSpy(layer.dataSourceChanged) + + options = QgsDataProvider.ProviderOptions() + # change with layer of same type + points_path = os.path.join(unitTestDataPath(), 'points.shp') + layer.setDataSource(points_path, 'new name', 'ogr', options) + + self.assertTrue(layer.isValid()) + self.assertEqual(layer.name(), 'new name') + self.assertEqual(layer.wkbType(), QgsWkbTypes.Point) + self.assertEqual(layer.crs().authid(), 'EPSG:4326') + self.assertIn(points_path, layer.dataProvider().dataSourceUri()) + self.assertEqual(len(spy), 1) + + # should have kept the same renderer! + self.assertEqual(layer.renderer(), r) + + # layer with different type + lines_path = os.path.join(unitTestDataPath(), 'lines.shp') + layer.setDataSource(lines_path, 'new name2', 'ogr', options) + + self.assertTrue(layer.isValid()) + self.assertEqual(layer.name(), 'new name2') + self.assertEqual(layer.wkbType(), QgsWkbTypes.MultiLineString) + self.assertEqual(layer.crs().authid(), 'EPSG:4326') + self.assertIn(lines_path, layer.dataProvider().dataSourceUri()) + self.assertEqual(len(spy), 2) + + # should have reset renderer! + self.assertNotEqual(layer.renderer(), r) + self.assertEqual(layer.renderer().symbol().type(), QgsSymbol.Line) + # ADD FEATURE def test_AddFeature(self):