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 testExportFeatureRelations(self): """ Test exporting a feature with relations """ #parent layer parent = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer&field=foreignkey:integer", "parent", "memory") pr = parent.dataProvider() pf1 = QgsFeature() pf1.setFields(parent.fields()) pf1.setAttributes(["test1", 67, 123]) pf2 = QgsFeature() pf2.setFields(parent.fields()) pf2.setAttributes(["test2", 68, 124]) assert pr.addFeatures([pf1, pf2]) #child layer child = QgsVectorLayer( "Point?field=x:string&field=y:integer&field=z:integer", "referencedlayer", "memory") pr = child.dataProvider() f1 = QgsFeature() f1.setFields(child.fields()) f1.setAttributes(["foo", 123, 321]) f2 = QgsFeature() f2.setFields(child.fields()) f2.setAttributes(["bar", 123, 654]) f3 = QgsFeature() f3.setFields(child.fields()) f3.setAttributes(["foobar", 124, 554]) assert pr.addFeatures([f1, f2, f3]) QgsProject.instance().addMapLayers([child, parent]) rel = QgsRelation() rel.setId('rel1') rel.setName('relation one') rel.setReferencingLayer(child.id()) rel.setReferencedLayer(parent.id()) rel.addFieldPair('y', 'foreignkey') QgsProject.instance().relationManager().addRelation(rel) exporter = QgsJsonExporter() exporter.setVectorLayer(parent) self.assertEqual(exporter.vectorLayer(), parent) exporter.setIncludeRelated(True) self.assertEqual(exporter.includeRelated(), True) expected = """{ "type":"Feature", "id":0, "geometry":null, "properties":{ "fldtxt":"test1", "fldint":67, "foreignkey":123, "relation one":[{"x":"foo", "y":123, "z":321}, {"x":"bar", "y":123, "z":654}] } }""" self.assertEqual(exporter.exportFeature(pf1), expected) expected = """{ "type":"Feature", "id":0, "geometry":null, "properties":{ "fldtxt":"test2", "fldint":68, "foreignkey":124, "relation one":[{"x":"foobar", "y":124, "z":554}] } }""" self.assertEqual(exporter.exportFeature(pf2), expected) # with field formatter setup = QgsEditorWidgetSetup('ValueMap', {"map": { "apples": 123, "bananas": 124 }}) child.setEditorWidgetSetup(1, setup) expected = """{ "type":"Feature", "id":0, "geometry":null, "properties":{ "fldtxt":"test1", "fldint":67, "foreignkey":123, "relation one":[{"x":"foo", "y":"apples", "z":321}, {"x":"bar", "y":"apples", "z":654}] } }""" self.assertEqual(exporter.exportFeature(pf1), expected) # test excluding related attributes exporter.setIncludeRelated(False) self.assertEqual(exporter.includeRelated(), False) expected = """{ "type":"Feature", "id":0, "geometry":null, "properties":{ "fldtxt":"test2", "fldint":68, "foreignkey":124 } }""" self.assertEqual(exporter.exportFeature(pf2), expected) # test without vector layer set exporter.setIncludeRelated(True) exporter.setVectorLayer(None) expected = """{ "type":"Feature", "id":0, "geometry":null, "properties":{ "fldtxt":"test2", "fldint":68, "foreignkey":124 } }""" self.assertEqual(exporter.exportFeature(pf2), expected)
def test_add_feature_geometry(self): """ Test to add a feature with a geometry """ vl_pipes = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."pipes" (geom) sql=', 'pipes', 'postgres') vl_leaks = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."leaks" (geom) sql=', 'leaks', 'postgres') vl_leaks.startEditing() QgsProject.instance().addMapLayer(vl_pipes) QgsProject.instance().addMapLayer(vl_leaks) self.assertEqual(vl_pipes.featureCount(), 2) self.assertEqual(vl_leaks.featureCount(), 3) rel = QgsRelation() rel.setReferencingLayer(vl_leaks.id()) rel.setReferencedLayer(vl_pipes.id()) rel.addFieldPair('pipe', 'id') rel.setId('rel_pipe_leak') self.assertTrue(rel.isValid()) self.relMgr.addRelation(rel) # Mock vector layer tool to just set default value on created feature class DummyVlTools(QgsVectorLayerTools): def addFeature(self, layer, defaultValues, defaultGeometry): f = QgsFeature(layer.fields()) for idx, value in defaultValues.items(): f.setAttribute(idx, value) f.setGeometry(defaultGeometry) ok = layer.addFeature(f) return ok, f wrapper = QgsRelationWidgetWrapper(vl_leaks, rel) context = QgsAttributeEditorContext() vltool = DummyVlTools() context.setVectorLayerTools(vltool) context.setMapCanvas(self.mapCanvas) cadDockWidget = QgsAdvancedDigitizingDockWidget(self.mapCanvas) context.setCadDockWidget(cadDockWidget) wrapper.setContext(context) widget = wrapper.widget() widget.show() pipe = next(vl_pipes.getFeatures()) self.assertEqual(pipe.id(), 1) wrapper.setFeature(pipe) table_view = widget.findChild(QTableView) self.assertEqual(table_view.model().rowCount(), 1) btn = widget.findChild(QToolButton, 'mAddFeatureGeometryButton') self.assertTrue(btn.isVisible()) self.assertTrue(btn.isEnabled()) btn.click() self.assertTrue(self.mapCanvas.mapTool()) feature = QgsFeature(vl_leaks.fields()) feature.setGeometry(QgsGeometry.fromWkt('POINT(0 0.8)')) self.mapCanvas.mapTool().digitizingCompleted.emit(feature) self.assertEqual(table_view.model().rowCount(), 2) self.assertEqual(vl_leaks.featureCount(), 4) request = QgsFeatureRequest() request.addOrderBy("id", False) # get new created feature feat = next(vl_leaks.getFeatures('"id" is NULL')) self.assertTrue(feat.isValid()) self.assertTrue(feat.geometry().equals( QgsGeometry.fromWkt('POINT(0 0.8)'))) vl_leaks.rollBack()
def __init__(self, config, parent): super().__init__(config, parent) self._updateUiTimer = QTimer() self._updateUiTimer.setSingleShot(True) self._updateUiTimer.timeout.connect(self.updateUiTimeout) self.setupUi(self) print('DocumentRelationEditorFeatureSideWidget.__init__') self.documents_path = str() self.document_filename = str() self.model = DocumentModel() self._nmRelation = QgsRelation() self._layerInSameTransactionGroup = False self._currentDocumentId = None # Actions self.actionToggleEditing = QAction( QIcon(":/images/themes/default/mActionToggleEditing.svg"), self.tr("Toggle editing mode for child layers")) self.actionToggleEditing.setCheckable(True) self.actionSaveEdits = QAction( QIcon(":/images/themes/default/mActionSaveEdits.svg"), self.tr("Save child layer edits")) self.actionShowForm = QAction( QIcon(":/images/themes/default/mActionMultiEdit.svg"), self.tr("Show form")) self.actionAddFeature = QAction( QIcon(":/images/themes/default/symbologyAdd.svg"), self.tr("Add document")) self.actionDeleteFeature = QAction( QIcon(":/images/themes/default/mActionDeleteSelected.svg"), self.tr("Drop document")) self.actionLinkFeature = QAction( QIcon(":/images/themes/default/mActionLink.svg"), self.tr("Link document")) self.actionUnlinkFeature = QAction( QIcon(":/images/themes/default/mActionUnlink.svg"), self.tr("Unlink document")) # Tool buttons self.mToggleEditingToolButton.setDefaultAction( self.actionToggleEditing) self.mSaveEditsToolButton.setDefaultAction(self.actionSaveEdits) self.mShowFormToolButton.setDefaultAction(self.actionShowForm) self.mAddFeatureToolButton.setDefaultAction(self.actionAddFeature) self.mDeleteFeatureToolButton.setDefaultAction( self.actionDeleteFeature) self.mLinkFeatureToolButton.setDefaultAction(self.actionLinkFeature) self.mUnlinkFeatureToolButton.setDefaultAction( self.actionUnlinkFeature) self.mListViewToolButton.setIcon( QIcon(":/images/themes/default/mIconListView.svg")) self.mIconViewToolButton.setIcon( QIcon(":/images/themes/default/mActionIconView.svg")) self.mListViewToolButton.setChecked(self.currentView == str( RelationEditorFeatureSideWidget.LastView.ListView)) self.mIconViewToolButton.setChecked(self.currentView == str( RelationEditorFeatureSideWidget.LastView.IconView)) # Quick image providers self._previewImageProvider = PreviewImageProvider() self._fileTypeSmallIconProvider = FileTypeIconImageProvider(32) self._fileTypeBigIconProvider = FileTypeIconImageProvider(100) # Setup QML part self.view = QQuickWidget() self.view.rootContext().setContextProperty("documentModel", self.model) self.view.rootContext().setContextProperty("parentWidget", self) self.view.rootContext().setContextProperty( "CONST_LIST_VIEW", str(RelationEditorFeatureSideWidget.LastView.ListView)) self.view.rootContext().setContextProperty( "CONST_ICON_VIEW", str(RelationEditorFeatureSideWidget.LastView.IconView)) self.view.engine().addImageProvider("previewImageProvider", self._previewImageProvider) self.view.engine().addImageProvider("fileTypeSmallIconProvider", self._fileTypeSmallIconProvider) self.view.engine().addImageProvider("fileTypeBigIconProvider", self._fileTypeBigIconProvider) self.view.setSource( QUrl.fromLocalFile( os.path.join(os.path.dirname(__file__), '../qml/DocumentList.qml'))) self.view.setResizeMode(QQuickWidget.SizeRootObjectToView) self.layout().addWidget(self.view) # Set initial state for add / remove etc.buttons self.updateButtons() # Signal slots self.actionToggleEditing.triggered.connect(self.toggleEditing) self.actionSaveEdits.triggered.connect(self.saveChildLayerEdits) self.actionShowForm.triggered.connect(self.showDocumentForm) self.actionAddFeature.triggered.connect(self.addDocument) self.actionDeleteFeature.triggered.connect(self.dropDocument) self.actionLinkFeature.triggered.connect(self.linkDocument) self.actionUnlinkFeature.triggered.connect(self.unlinkDocument) self.mListViewToolButton.toggled.connect( self.listViewToolButtonToggled) self.mIconViewToolButton.toggled.connect( self.iconViewToolButtonToggled)
def test_returns_none_when_unknown_widget_id(self): self.assertIsNone(self.registry.create('babinatatitrunkina', {})) self.assertIsNone( self.registry.createConfigWidget('babinatatitrunkina', QgsRelation()))
def test_creates_new_widget(self): # define the widget class QgsExampleRelationEditorWidget(QgsAbstractRelationEditorWidget): def __init__(self, config, parent): super().__init__(config, parent) self.setLayout(QGridLayout()) self.label = QLabel() self.label.setText( f'According to the configuration, the checkboxin state was {str(config.get("checkboxin", "Unknown"))}' ) self.layout().addWidget(self.label) def config(self): return {} def setConfig(self, config): self.label.setText( f'According to the configuration, the checkboxin state was {str(config.get("checkboxin", "Unknown"))}' ) # define the config widget class QgsExampleRelationEditorConfigWidget( QgsAbstractRelationEditorConfigWidget): def __init__(self, relation, parent): super().__init__(relation, parent) self.setLayout(QGridLayout()) self.checkbox = QCheckBox('Is this checkbox checkboxin?') self.layout().addWidget(self.checkbox) def config(self): return {"checkboxin": self.checkbox.isChecked()} def setConfig(self, config): self.checkbox.setChecked(config.get('checkboxin', False)) # define the widget factory class QgsExampleRelationEditorWidgetFactory( QgsAbstractRelationEditorWidgetFactory): def type(self): return "example" def name(self): return "Example Relation Widget" def create(self, config, parent): return QgsExampleRelationEditorWidget(config, parent) def configWidget(self, relation, parent): return QgsExampleRelationEditorConfigWidget(relation, parent) self.assertIsNone(self.registry.create('example', {})) self.assertIsNone( self.registry.createConfigWidget('example', QgsRelation())) self.registry.addRelationWidget( QgsExampleRelationEditorWidgetFactory()) self.assertIsInstance(self.registry.create('example', {}), QgsExampleRelationEditorWidget) self.assertIsInstance( self.registry.createConfigWidget('example', QgsRelation()), QgsExampleRelationEditorConfigWidget)
def set_relations(): v2_pipe_view = QgsProject.instance().mapLayersByName("v2_pipe_view")[0] v2_pipe_id = v2_pipe_view.id() pipe_idx = v2_pipe_view.fields().indexFromName( "pipe_cross_section_definition_id") v2_orifice_view = QgsProject.instance().mapLayersByName( "v2_orifice_view")[0] v2_orifice_id = v2_orifice_view.id() orf_idx = v2_orifice_view.fields().indexFromName( "orf_cross_section_definition_id") v2_weir_view = QgsProject.instance().mapLayersByName("v2_weir_view")[0] v2_weir_id = v2_weir_view.id() weir_idx = v2_weir_view.fields().indexFromName( "weir_cross_section_definition_id") v2_culvert_view = QgsProject.instance().mapLayersByName( "v2_culvert_view")[0] v2_culvert_id = v2_culvert_view.id() cul_idx = v2_culvert_view.fields().indexFromName( "cul_cross_section_definition_id") v2_cross_section_location_view = QgsProject.instance().mapLayersByName( "v2_cross_section_location_view")[0] v2_cross_section_location_id = v2_cross_section_location_view.id() xsec_loc_idx = v2_cross_section_location_view.fields().indexFromName( "loc_definition_id") v2_global_settings = QgsProject.instance().mapLayersByName( "v2_global_settings")[0] v2_global_settings_id = v2_global_settings.id() glob_num_idx = v2_global_settings.fields().indexFromName( "numerical_settings_id") glob_inf_idx = v2_global_settings.fields().indexFromName( "simple_infiltration_settings_id") glob_ground_idx = v2_global_settings.fields().indexFromName( "groundwater_settings_id") glob_int_idx = v2_global_settings.fields().indexFromName( "interflow_settings_id") v2_numerical_settings = QgsProject.instance().mapLayersByName( "v2_numerical_settings")[0] v2_numerical_settings_id = v2_numerical_settings.id() v2_simple_infiltration = QgsProject.instance().mapLayersByName( "v2_simple_infiltration")[0] v2_simple_infiltration_id = v2_simple_infiltration.id() v2_groundwater = QgsProject.instance().mapLayersByName("v2_groundwater")[0] v2_groundwater_id = v2_groundwater.id() v2_interflow = QgsProject.instance().mapLayersByName("v2_interflow")[0] v2_interflow_id = v2_interflow.id() v2_cross_section_definition = QgsProject.instance().mapLayersByName( "v2_cross_section_definition")[0] v2_cross_section_definition_id = v2_cross_section_definition.id() global_setting_relations = [[ "6", "glob_num", 0, v2_global_settings_id, v2_numerical_settings_id, "numerical_settings_id" ], [ "7", "glob_inf", 0, v2_global_settings_id, v2_simple_infiltration_id, "simple_infiltration_settings_id" ], [ "8", "glob_ground", 0, v2_global_settings_id, v2_groundwater_id, "groundwater_settings_id" ], [ "9", "glob_int", 0, v2_global_settings_id, v2_interflow_id, "interflow_settings_id" ]] for id, name, strength, referencing_layer, referenced_layer, referencing_field in global_setting_relations: rel = QgsRelation() rel.setReferencingLayer(referencing_layer) rel.setReferencedLayer(referenced_layer) rel.addFieldPair(referencing_field, "id") rel.setId(id) rel.setName(name) rel.setStrength(strength) QgsProject.instance().relationManager().addRelation(rel) cross_section_relations = [[ "1", "pipe_xsec", 0, v2_pipe_id, v2_cross_section_definition_id, "pipe_cross_section_definition_id" ], [ "2", "weir_xsec", 0, v2_weir_id, v2_cross_section_definition_id, "weir_cross_section_definition_id" ], [ "3", "orf_xsec", 0, v2_orifice_id, v2_cross_section_definition_id, "orf_cross_section_definition_id" ], [ "4", "cul_xsec", 0, v2_culvert_id, v2_cross_section_definition_id, "cul_cross_section_definition_id" ], [ "5", "loc_view_xsec", 0, v2_cross_section_location_id, v2_cross_section_definition_id, "loc_definition_id" ]] for id, name, strength, referencing_layer, referenced_layer, referencing_field in cross_section_relations: rel = QgsRelation() rel.setReferencingLayer(referencing_layer) rel.setReferencedLayer(referenced_layer) rel.addFieldPair(referencing_field, "id") rel.setId(id) rel.setName(name) rel.setStrength(strength) QgsProject.instance().relationManager().addRelation(rel) cfg = dict() cfg["OrderByValue"] = True cfg["AllowNULL"] = False cfg["ShowOpenFormButton"] = True cfg["AllowAddFeatures"] = True cfg["ShowForm"] = False cfg["FilterFields"] = [] cfg["ChainFilters"] = False settingssetup = QgsEditorWidgetSetup("RelationReference", cfg) cfg = dict() cfg["OrderByValue"] = True cfg["AllowNULL"] = False cfg["ShowOpenFormButton"] = False cfg["AllowAddFeatures"] = True cfg["ShowForm"] = False cfg["FilterFields"] = ["shape", "width", "height"] cfg["ChainFilters"] = False xsecsetup = QgsEditorWidgetSetup("RelationReference", cfg) settings_list = [[v2_global_settings, glob_num_idx], [v2_global_settings, glob_inf_idx], [v2_global_settings, glob_ground_idx], [v2_global_settings, glob_int_idx]] xsec_list = [[v2_weir_view, weir_idx], [v2_pipe_view, pipe_idx], [v2_culvert_view, cul_idx], [v2_orifice_view, orf_idx], [v2_cross_section_location_view, xsec_loc_idx]] for view, idx in xsec_list: view.setEditorWidgetSetup(idx, xsecsetup) for view, idx in settings_list: view.setEditorWidgetSetup(idx, settingssetup) expression = " id || ' code: ' || code " v2_cross_section_definition.setDisplayExpression(expression)
def setUpClass(cls): """Run before all tests""" QCoreApplication.setOrganizationName("QGIS_Test") QCoreApplication.setOrganizationDomain( "QGIS_TestPyQgsPackageLayers.com") QCoreApplication.setApplicationName("QGIS_TestPyQgsPackageLayers") QgsSettings().clear() Processing.initialize() QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms()) cls.registry = QgsApplication.instance().processingRegistry() cls.tmp_dir = QTemporaryDir() cls.temp_path = os.path.join(cls.tmp_dir.path(), 'package_layers.gpkg') cls.temp_export_path = os.path.join(cls.tmp_dir.path(), 'package_layers_export.gpkg') # Create test DB """ Test data: Region 1 Province 1 City 1 City 2 Province 2 City 3 Region 2 Province 3 Province 4 City 4 """ ds = ogr.GetDriverByName('GPKG').CreateDataSource(cls.temp_path) lyr = ds.CreateLayer('region', geom_type=ogr.wkbNone) lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'region one' lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'region two' lyr.CreateFeature(f) lyr = ds.CreateLayer('province', geom_type=ogr.wkbNone) lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('region', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'province one' f['region'] = 1 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'province two' f['region'] = 1 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'province three' f['region'] = 2 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'province four' f['region'] = 2 lyr.CreateFeature(f) lyr = ds.CreateLayer('city', geom_type=ogr.wkbNone) lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('province', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'city one' f['province'] = 1 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'city two' f['province'] = 1 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'city three' f['province'] = 2 lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f['name'] = 'city four' f['province'] = 4 lyr.CreateFeature(f) f = None ds = None region = QgsVectorLayer(cls.temp_path + '|layername=region', 'region') province = QgsVectorLayer(cls.temp_path + '|layername=province', 'province') city = QgsVectorLayer(cls.temp_path + '|layername=city', 'city') QgsProject.instance().addMapLayers([region, province, city]) relMgr = QgsProject.instance().relationManager() rel = QgsRelation() rel.setId('rel1') rel.setName('province -> region') rel.setReferencingLayer(province.id()) rel.setReferencedLayer(region.id()) rel.addFieldPair('region', 'fid') assert rel.isValid() relMgr.addRelation(rel) rel = QgsRelation() rel.setId('rel2') rel.setName('city -> province') rel.setReferencingLayer(city.id()) rel.setReferencedLayer(province.id()) rel.addFieldPair('province', 'fid') assert rel.isValid() relMgr.addRelation(rel)
def __init__(self, config, parent): super().__init__(config, parent) self._updateUiTimer = QTimer() self._updateUiTimer.setSingleShot(True) self._updateUiTimer.timeout.connect(self.updateUiTimeout) self.setupUi(self) self.polymorphicRelationEnabled = False self.polymorphicRelationId = str() self.generatedRelationList = [] self._nmRelation = QgsRelation() self._polymorphicRelation = QgsPolymorphicRelation() self._layerInSameTransactionGroup = False self.cardinality = Cardinality.ManyToOne # Actions self.actionToggleEditing = QAction( QIcon(":/images/themes/default/mActionToggleEditing.svg"), self.tr("Toggle editing mode for child layers")) self.actionToggleEditing.setCheckable(True) self.actionSaveEdits = QAction( QIcon(":/images/themes/default/mActionSaveEdits.svg"), self.tr("Save child layer edits")) self.actionShowForm = QAction( QIcon(":/images/themes/default/mActionMultiEdit.svg"), self.tr("Show form")) self.actionLinkFeature = QAction( QIcon(":/images/themes/default/mActionLink.svg"), self.tr("Link feature")) self.actionUnlinkFeature = QAction( QIcon(":/images/themes/default/mActionUnlink.svg"), self.tr("Unlink feature")) # Tool buttons self.mToggleEditingToolButton.setDefaultAction( self.actionToggleEditing) self.mSaveEditsToolButton.setDefaultAction(self.actionSaveEdits) self.mShowFormToolButton.setDefaultAction(self.actionShowForm) self.mShowFormToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.mLinkFeaturesToolButton.setDefaultAction(self.actionLinkFeature) self.mLinkFeaturesToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.mUnlinkFeaturesToolButton.setDefaultAction( self.actionUnlinkFeature) self.mUnlinkFeaturesToolButton.setToolButtonStyle( Qt.ToolButtonIconOnly) # TreeWidgetItem menu self.mFeaturesTreeWidget.setContextMenuPolicy(Qt.ActionsContextMenu) self.mFeaturesTreeWidget.addAction(self.actionShowForm) self.mFeaturesTreeWidget.addAction(self.actionUnlinkFeature) # Set initial state for add / remove etc.buttons self.updateButtons() # Signal slots self.actionToggleEditing.triggered.connect(self.toggleEditing) self.actionSaveEdits.triggered.connect(self.saveChildLayerEdits) self.actionShowForm.triggered.connect(self.actionShowFormTriggered) self.actionLinkFeature.triggered.connect( self.actionLinkFeatureTriggered) self.actionUnlinkFeature.triggered.connect( self.actionUnlinkFeatureTriggered) self.mFeaturesTreeWidget.currentItemChanged.connect(self.updateButtons)
def on_resolve_href_(dialog, layer, feature, field): """ @param dialog the dialog where the feature form is opened @param layer the layer on which the href link stands @param feature the current feature @param field the field name storing the href URL @param linked_layer_id the QGIS layer id of the already resolved layer, for update """ from .import_gmlas_panel import ImportGmlasPanel path = feature[field] if not path: return # if parent is a Dialog, we are in a feature form # else in a attribute table is_feature_form = isinstance(dialog.parent, QDialog) # The href is resolved thanks to the OGR GMLAS driver. # We need to determine what is the "root" layer of the imported # href, so that we can connect the xlink:href link to the # newly loaded set of layers. # There seems to be no way to determine what is the "root" layer # of a GMLAS database. # So, we rely on XML parsing to determine the root element # and on layer xpath found in metadata # Download the file so that it is used for XML parsing # and for GMLAS loading import tempfile from ..core.gml_utils import extract_features_from_file from ..core.qgis_urlopener import remote_open_from_qgis from ..core.xml_utils import no_ns, no_prefix with remote_open_from_qgis(path) as fi: with tempfile.NamedTemporaryFile(delete=False) as fo: fo.write(fi.read()) tmp_file = fo.name _, _, nodes = extract_features_from_file(tmp_file) if not nodes: raise RuntimeError("No feature found in linked document") root_tag = nodes[0].tag # reuse the GMLAS import panel widget dlg = QDialog() import_widget = ImportGmlasPanel(dlg, gml_path=tmp_file) path_edit = QLineEdit(path, dlg) path_edit.setEnabled(False) btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dlg) layout = QVBoxLayout() layout.addWidget(path_edit) layout.addWidget(import_widget) layout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) layout.addWidget(btn) dlg.setLayout(layout) btn.accepted.connect(dlg.accept) btn.rejected.connect(dlg.reject) dlg.resize(400, 300) dlg.setWindowTitle("Options for xlink:href loading") if not dlg.exec_(): return # close the current form w = dialog while not isinstance(w, QDialog): w = w.parent() w.close() import_widget.do_load() # Add a link between the current layer # and the root layer of the newly loaded (complex) features # 1. determine the root layer and pkid of all its features root_layer = None for l in QgsProject.instance().mapLayers().values(): if no_ns(l.customProperty("xpath", "")) == no_prefix(root_tag): root_layer = l break if root_layer is None: raise RuntimeError("Cannot determine the root layer") pkid = layer.customProperty("pkid") pkid_value = feature[pkid] root_layer.startEditing() # 2. add a href_origin_pkid field in the root layer if "parent_href_pkid" not in [f.name() for f in root_layer.fields()]: new_field = QgsField(layer.fields().field(pkid)) new_field.setName("parent_href_pkid") root_layer.addAttribute(new_field) # 3. set its value to the id of current feature ids_to_change = [] for f in root_layer.getFeatures(): if f["parent_href_pkid"] is None: ids_to_change.append(f.id()) idx = root_layer.fields().indexFromName("parent_href_pkid") for fid in ids_to_change: # sets the pkid_value root_layer.changeAttributeValue(fid, idx, pkid_value) root_layer.commitChanges() # 4. declare a new QgsRelation rel_name = "1_n_" + layer.name() + "_" + field rel = QgsProject.instance().relationManager().relations().get(rel_name) if rel is None: rel = QgsRelation() rel.setId(rel_name) rel.setName(field) rel.setReferencedLayer(layer.id()) rel.setReferencingLayer(root_layer.id()) rel.addFieldPair("parent_href_pkid", pkid) QgsProject.instance().relationManager().addRelation(rel) # 5. declare the new relation in the form widgets # new 1:N in the current layer fc = layer.editFormConfig() rel_tab = fc.tabs()[1] rel_tab.addChildElement( QgsAttributeEditorRelation(rel.name(), rel, rel_tab)) # new field in the root layer fc = root_layer.editFormConfig() main_tab = fc.tabs()[0] main_tab.addChildElement( QgsAttributeEditorField("parent_href_pkid", idx, main_tab)) # declare as reference relation widget s = QgsEditorWidgetSetup( "RelationReference", { "AllowNULL": False, "ReadOnly": True, "Relation": rel.id(), "OrderByValue": False, "MapIdentification": False, "AllowAddFeatures": False, "ShowForm": True, }, ) root_layer.setEditorWidgetSetup(idx, s) # write metadata in layers href_resolved = layer.customProperty("href_resolved", []) if path not in href_resolved: layer.setCustomProperty("href_resolved", href_resolved + [path]) href_linked_layers = layer.customProperty("href_linked_layers", {}) href_linked_layers[field] = root_layer.id() layer.setCustomProperty("href_linked_layers", href_linked_layers) # 6. reload the current form from ..main import get_iface if is_feature_form: get_iface().openFeatureForm(layer, feature) else: get_iface().showAttributeTable(layer)
def open_db(self): edb_filename = self.open_db_lineedit.text() edb_name, ext = os.path.splitext(os.path.basename(str(edb_filename))) QgsMessageLog.logMessage("Loading edb %s" % edb_filename, 'AirviroOfflineEdb', QgsMessageLog.INFO) self.db_uri.setDatabase(edb_filename) self.con, self.cur = connect(str(self.db_uri.database())) self.epsg = get_epsg(self.con) root = QgsProject.instance().layerTreeRoot() edb_increment = 1 while root.findGroup(edb_name) is not None: edb_name = edb_name + unicode(edb_increment) edb_increment += 1 QgsMessageLog.logMessage("Adding edb layers in %s" % edb_name, 'AirviroOfflineEdb', QgsMessageLog.INFO) edb_group = root.addGroup(edb_name) point_group = edb_group.addGroup('Point sources') area_group = edb_group.addGroup('Area sources') grid_group = edb_group.addGroup('Grid sources') road_group = edb_group.addGroup('Road sources') subtable_group = edb_group.addGroup('Subtables') company_group = edb_group.addGroup('Companies') facility_group = edb_group.addGroup('Facilities') emis_group = edb_group.addGroup('Emissions') point_support_group = point_group.addGroup('Support tables') area_support_group = area_group.addGroup('Support tables') grid_support_group = grid_group.addGroup('Support tables') road_support_group = road_group.addGroup('Support tables') facility_support_group = facility_group.addGroup('Support tables') company_support_group = company_group.addGroup('Support tables') unit_group = subtable_group.addGroup('Units') road_vehicle_group = subtable_group.addGroup('Road vehicles') road_vehicle_support_group = road_vehicle_group.addGroup( 'Support tables') roadtype_group = subtable_group.addGroup('Roadtypes') emis_func_group = subtable_group.addGroup('Emission functions') searchkey_group = subtable_group.addGroup('Searchkeys') timevar_group = subtable_group.addGroup('Time variations') subgrp_group = subtable_group.addGroup('Substance groups') self.layers = {} schema = '' geom_table_column_dict = dict(GEOMETRY_TABLES_COLUMNS) for table in TABLES: if not table_in_db(self.cur, table): iface.messageBar().pushMessage("Warning", "Table %s not found in edb" % table, level=QgsMessageBar.WARNING, duration=3) continue geom_col = geom_table_column_dict.get(table, None) self.db_uri.setDataSource(schema, table, geom_col or '') layer_uri = self.db_uri.uri() # + "&crs=EPSG:4326" layer = QgsVectorLayer(layer_uri, table, 'spatialite') layer.setCrs( QgsCoordinateReferenceSystem( self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId)) if not layer.isValid(): raise ValueError(edb_filename) map_layer = QgsMapLayerRegistry.instance().addMapLayer( layer, False) if 'timevar' in table: group = timevar_group elif 'emission_function' in table: group = emis_func_group elif 'searchkey' in table: group = searchkey_group elif 'unit' in table: group = unit_group elif 'subgrp' in table: group = subgrp_group elif table == 'substances': group = subtable_group elif table.endswith('_emis'): group = emis_group elif table == 'points': group = point_group elif 'point_' in table: group = point_support_group elif table == 'areas': group = area_group elif 'area_' in table: group = area_support_group elif table == 'roads': group = road_group elif table in ('road_vehicle_link', 'road_alobs'): group = road_support_group elif 'road_' in table: group = road_vehicle_group elif 'roadtype' in table: group = roadtype_group elif table == 'facilties': group = facility_group elif 'facility' in table: group = facility_support_group elif 'companies' == table: group = company_group elif 'company' in table: group = company_support_group elif 'traffic_situation' in table: group = road_vehicle_support_group group.setVisible(False) group.setExpanded(False) group.addLayer(map_layer) self.layers[table] = map_layer.id() for table in TABLES: foreign_keys = get_foreign_keys(self.con, table) referencing_layer = self.layers[table] for row in foreign_keys: referenced_layer = self.layers[row['table']] from_column = row['from'] to_column = row['to'] rel = QgsRelation() rel.setReferencingLayer(referencing_layer) rel.setReferencedLayer(referenced_layer) rel.addFieldPair(from_column, to_column) rel_name = 'fk_%s_%s-%s_%s' % (table, from_column, row['table'], to_column) rel.setRelationId(rel_name) rel.setRelationName( 'fk_%s_%s-%s_%s' % (table, from_column, row['table'], to_column)) if not rel.isValid(): raise ValueError('Reference %s is invalid' % rel_name) QgsProject.instance().relationManager().addRelation(rel)
def test_generateId_empty_relation(self): rel = QgsRelation() # Check that it does not crash rel.generateId()
def import_in_qgis(gmlas_uri, provider, schema=None): """Imports layers from a GMLAS file in QGIS with relations and editor widgets @param gmlas_uri connection parameters @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite) @param schema name of the PostgreSQL schema where tables and metadata tables are """ if schema is not None: schema_s = schema + "." else: schema_s = "" ogr.UseExceptions() drv = ogr.GetDriverByName(provider) ds = drv.Open(gmlas_uri) if ds is None: raise RuntimeError("Problem opening {}".format(gmlas_uri)) # get list of layers sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format( schema_s) l = ds.ExecuteSQL(sql) layers = {} for f in l: ln = f.GetField("layer_name") if ln not in layers: layers[ln] = { 'uid': f.GetField("layer_pkid_name"), 'category': f.GetField("layer_category"), 'xpath': f.GetField("layer_xpath"), 'parent_pkid': f.GetField("layer_parent_pkid_name"), 'srid': f.GetField("srid"), 'geometry_column': f.GetField("f_geometry_column"), '1_n': [], # 1:N relations 'layer_id': None, 'layer_name': ln, 'layer': None, 'fields': [] } else: # additional geometry columns g = f.GetField("f_geometry_column") k = "{} ({})".format(ln, g) layers[k] = dict(layers[ln]) layers[k]["geometry_column"] = g crs = QgsCoordinateReferenceSystem("EPSG:4326") for ln in sorted(layers.keys()): lyr = layers[ln] g_column = lyr["geometry_column"] or None l = _qgis_layer(gmlas_uri, schema, lyr["layer_name"], g_column, provider, qgis_layer_name=ln) if not l.isValid(): raise RuntimeError("Problem loading layer {} with {}".format( ln, l.source())) if lyr["srid"]: crs = QgsCoordinateReferenceSystem("EPSG:{}".format(lyr["srid"])) l.setCrs(crs) QgsProject.instance().addMapLayer(l) layers[ln]['layer_id'] = l.id() layers[ln]['layer'] = l # add 1:1 relations relations_1_1 = [] sql = """ select layer_name, field_name, field_related_layer, r.child_pkid from {0}_ogr_fields_metadata f join {0}_ogr_layer_relationships r on r.parent_layer = f.layer_name and r.parent_element_name = f.field_name where field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK') and field_max_occurs=1 """.format(schema_s) l = ds.ExecuteSQL(sql) if l is not None: for f in l: rel = QgsRelation() rel.setId('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name')) rel.setName('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name')) # parent layer rel.setReferencingLayer( layers[f.GetField('layer_name')]['layer_id']) # child layer rel.setReferencedLayer( layers[f.GetField('field_related_layer')]['layer_id']) # parent, child rel.addFieldPair(f.GetField('field_name'), f.GetField('child_pkid')) #rel.generateId() if rel.isValid(): relations_1_1.append(rel) # add 1:N relations relations_1_n = [] sql = """ select layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid from {0}_ogr_fields_metadata f join {0}_ogr_layer_relationships r on r.parent_layer = f.layer_name and r.child_layer = f.field_related_layer where field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK') and field_max_occurs>1 -- junctions - 1st way union all select layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid from {0}_ogr_fields_metadata f join {0}_ogr_layer_relationships r on r.parent_layer = f.layer_name and r.child_layer = f.field_related_layer where field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE' -- junctions - 2nd way union all select field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid from {0}_ogr_fields_metadata f join {0}_ogr_layer_relationships r on r.parent_layer = f.layer_name and r.child_layer = f.field_related_layer where field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE' """.format(schema_s) l = ds.ExecuteSQL(sql) if l is not None: for f in l: rel = QgsRelation() rel.setId('1_n_' + f.GetField('layer_name') + '_' + f.GetField('child_layer') + '_' + f.GetField('parent_pkid') + '_' + f.GetField('child_pkid')) rel.setName(f.GetField('child_layer')) # parent layer rel.setReferencedLayer( layers[f.GetField('layer_name')]['layer_id']) # child layer rel.setReferencingLayer( layers[f.GetField('child_layer')]['layer_id']) # parent, child rel.addFieldPair(f.GetField('child_pkid'), f.GetField('parent_pkid')) #rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid') if rel.isValid(): relations_1_n.append(rel) # add relation to layer layers[f.GetField('layer_name')]['1_n'].append(rel) QgsProject.instance().relationManager().setRelations(relations_1_1 + relations_1_n) # add "show form" option to 1:1 relations for rel in relations_1_1: l = rel.referencingLayer() idx = rel.referencingFields()[0] s = QgsEditorWidgetSetup( "RelationReference", { 'AllowNULL': False, 'ReadOnly': True, 'Relation': rel.id(), 'OrderByValue': False, 'MapIdentification': False, 'AllowAddFeatures': False, 'ShowForm': True }) l.setEditorWidgetSetup(idx, s) # setup form for layers for layer, lyr in layers.items(): l = lyr['layer'] fc = l.editFormConfig() fc.clearTabs() fc.setLayout(QgsEditFormConfig.TabLayout) # Add fields c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer()) c.setIsGroupBox(False) # a tab for idx, f in enumerate(l.fields()): c.addChildElement(QgsAttributeEditorField(f.name(), idx, c)) fc.addTab(c) # Add 1:N relations c_1_n = QgsAttributeEditorContainer("1:N links", fc.invisibleRootContainer()) c_1_n.setIsGroupBox(False) # a tab fc.addTab(c_1_n) for rel in lyr['1_n']: c_1_n.addChildElement( QgsAttributeEditorRelation(rel.name(), rel, c_1_n)) l.setEditFormConfig(fc)
def createRelation(self): rel = QgsRelation() rel.setReferencingLayer(self.referencingLayer.id()) rel.setReferencedLayer(self.referencedLayer.id()) rel.addFieldPair('foreignkey', 'y') return rel
def testCreateExpression(self): """ Test creating an expression using the widget""" layer = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer", "test", "memory") # setup value relation parent_layer = QgsVectorLayer( "Point?field=stringkey:string&field=intkey:integer&field=display:string", "parent", "memory") f1 = QgsFeature(parent_layer.fields(), 1) f1.setAttributes(['a', 1, 'value a']) f2 = QgsFeature(parent_layer.fields(), 2) f2.setAttributes(['b', 2, 'value b']) f3 = QgsFeature(parent_layer.fields(), 3) f3.setAttributes(['c', 3, 'value c']) parent_layer.dataProvider().addFeatures([f1, f2, f3]) QgsProject.instance().addMapLayers([layer, parent_layer]) relationManager = QgsProject.instance().relationManager() relation = QgsRelation() relation.setId('relation') relation.setReferencingLayer(layer.id()) relation.setReferencedLayer(parent_layer.id()) relation.addFieldPair('fldtxt', 'stringkey') self.assertTrue(relation.isValid()) relationManager.addRelation(relation) # Everything valid config = {'Relation': relation.id(), 'AllowNULL': True} w = QgsRelationReferenceSearchWidgetWrapper(layer, 0, None) w.setConfig(config) w.widget().setForeignKey('a') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'a\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'a\'') w.widget().setForeignKey('b') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'b\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'b\'') w.widget().setForeignKey('c') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'c\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'c\'') w.widget().setForeignKey(None) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt" IS NOT NULL')
def test_representValue(self): first_layer = QgsVectorLayer("none?field=foreign_key:integer", "first_layer", "memory") self.assertTrue(first_layer.isValid()) second_layer = QgsVectorLayer( "none?field=pkid:integer&field=decoded:string", "second_layer", "memory") self.assertTrue(second_layer.isValid()) QgsProject.instance().addMapLayers([first_layer, second_layer]) f = QgsFeature() f.setAttributes([123]) first_layer.dataProvider().addFeatures([f]) f = QgsFeature() f.setAttributes([123, 'decoded_val']) second_layer.dataProvider().addFeatures([f]) relMgr = QgsProject.instance().relationManager() fieldFormatter = QgsRelationReferenceFieldFormatter() rel = QgsRelation() rel.setId('rel1') rel.setName('Relation Number One') rel.setReferencingLayer(first_layer.id()) rel.setReferencedLayer(second_layer.id()) rel.addFieldPair('foreign_key', 'pkid') self.assertTrue(rel.isValid()) relMgr.addRelation(rel) # Everything valid config = {'Relation': rel.id()} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), 'decoded_val') # Code not find match in foreign layer config = {'Relation': rel.id()} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '456'), '456') # Invalid relation id config = {'Relation': 'invalid'} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123') # No display expression config = {'Relation': rel.id()} second_layer.setDisplayExpression(None) self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123') # Invalid display expression config = {'Relation': rel.id()} second_layer.setDisplayExpression('invalid +') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123') # Missing relation config = {} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123') # Inconsistent layer provided to representValue() config = {'Relation': rel.id()} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(second_layer, 0, config, None, '123'), '123') # Inconsistent idx provided to representValue() config = {'Relation': rel.id()} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 1, config, None, '123'), '123') # Invalid relation rel = QgsRelation() rel.setId('rel2') rel.setName('Relation Number Two') rel.setReferencingLayer(first_layer.id()) rel.addFieldPair('foreign_key', 'pkid') self.assertFalse(rel.isValid()) relMgr.addRelation(rel) config = {'Relation': rel.id()} second_layer.setDisplayExpression('decoded') self.assertEqual( fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123') QgsProject.instance().removeAllMapLayers()
def applyJoin(self): self.dlg.show() selectedFields = self.getSelFieldList() or [] if self.dlg.targetLayerCombo.currentText( )[:6] != 'Select' and self.dlg.joinLayerCombo.currentText( )[:6] != 'Select' and self.dlg.spatialTypeCombo.currentText( )[:6] != 'Select': targetLayer = self.layerSet[ self.dlg.targetLayerCombo.currentText()] joinLayer = self.layerSet[self.dlg.joinLayerCombo.currentText()] joinLayerFields = [ field.name() for field in joinLayer.pendingFields() ] #targetLayerFields = [field.name() for field in targetLayer.pendingFields()] targetLayerFields = [] for field in targetLayer.pendingFields(): if field.name()[0:7] == 'spjoin_': idx = targetLayer.pendingFields().fieldNameIndex( field.name()) self.tra.ce("removing:" + field.name() + str(idx)) targetLayer.dataProvider().deleteAttributes([idx]) targetLayer.removeExpressionField(idx) else: targetLayerFields.append(field.name()) self.tra.ce(targetLayerFields) targetLayer.updateFields() joinField = 'spjoin_rif' exp = "geom" + self.dlg.spatialTypeCombo.currentText( ) + "('" + joinLayer.name() + "','$id')" self.tra.ce(exp) #add a rif field if build relation requested if self.dlg.checkBuildRelation.checkState() == Qt.Checked: #joinLayer.addExpressionField('$id', QgsField('spjoin_rif', QVariant.Int)) joinLayer.dataProvider().addAttributes( [QgsField(joinField, QVariant.Int)]) #joinLayer.updateFields() idx = joinLayer.dataProvider().fields().fieldNameIndex( joinField) expObj = QgsExpression('$id') changes = {} for feature in joinLayer.getFeatures(): value = expObj.evaluate(feature) #joinLayer.dataProvider().changeAttributeValues({feature.id():{idx:value}}) changes[feature.id()] = {idx: value} joinLayer.dataProvider().changeAttributeValues(changes) if self.dlg.checkDynamicJoin.checkState() == Qt.Checked: targetLayer.addExpressionField( exp, QgsField(joinField, QVariant.Int)) else: #Create static rif field targetLayer.dataProvider().addAttributes( [QgsField(joinField, QVariant.Int)]) #targetLayer.updateFields() F = [ field.name() for field in targetLayer.dataProvider().fields() ] self.tra.ce(F) #Compile spatial expression to get feature rifs expObj = QgsExpression(exp) expObj.prepare(targetLayer.pendingFields()) idx = targetLayer.dataProvider().fields().fieldNameIndex( joinField) self.tra.ce("new " + joinField + str(idx)) changes = {} #init progress bar self.dlg.progressBar.setMinimum(0) self.dlg.progressBar.setMaximum(targetLayer.featureCount()) #cicle into feature to build mod vector for count, feature in enumerate(targetLayer.getFeatures()): self.dlg.progressBar.setValue(count) value = expObj.evaluate(feature) changes[feature.id()] = {idx: value} self.tra.ce(changes) #apply mod vector targetLayer.dataProvider().changeAttributeValues(changes) targetLayer.updateFields() #build expression fields to connect to join field rif for f in selectedFields: fieldType = joinLayer.pendingFields().field(f).type() exp = """dbvaluebyid('%s','%s',"%s")""" % (joinLayer.name(), f, joinField) self.tra.ce(exp) targetLayer.addExpressionField( exp, QgsField('spjoin_' + f, fieldType)) targetLayer.updateFields() if self.dlg.checkBuildRelation.checkState() == Qt.Checked: jRel = QgsRelation() jRel.setRelationId(targetLayer.name() + "_" + str(uuid.uuid1())) jRel.setRelationName("%s_%s-rif_%s" % (targetLayer.name(), self.dlg.spatialTypeCombo.currentText(), joinLayer.name())) jRel.setReferencedLayer(joinLayer.id()) jRel.setReferencingLayer(targetLayer.id()) jRel.addFieldPair('spjoin_rif', 'spjoin_rif') QgsProject.instance().relationManager().addRelation(jRel) self.dlg.hide()