Ejemplo n.º 1
0
 def add_layer_tree_action(self,
                           title: str,
                           relation: QgsRelation,
                           slot,
                           data=None) -> QAction:
     # add legend context menu entry
     layer_tree_action = QAction(title, self.iface.mainWindow())
     self.iface.addCustomActionForLayerType(layer_tree_action, None,
                                            QgsMapLayer.VectorLayer, False)
     self.iface.addCustomActionForLayer(layer_tree_action,
                                        relation.referencedLayer())
     layer_tree_action.setData(relation.id())
     layer_tree_action.triggered.connect(
         lambda: slot(relation,
                      relation.referencedLayer().selectedFeatures(), data))
     self.layer_tree_actions.append(layer_tree_action)
    def run_aggregate(self,
                      relation: QgsRelation,
                      features: [QgsFeature],
                      data=None):
        if len(features) == 0:
            return

        conditions = []
        for referencing, referenced in relation.fieldPairs().items():
            break

        field = relation.referencedLayer().fields().field(referenced)
        quote = "" if field.isNumeric() else "'"

        for feature in features:
            base_condition = '{fk} = {quote}{parent_id}{quote}'.format(
                fk=referencing,
                parent_id=feature.attribute(referenced),
                quote=quote,
            )
            condition = "( {filter} AND {field} = aggregate(layer:='{lyr}', aggregate:='{agg}', expression:={field}, filter:={filter}) )".format(
                filter=base_condition,
                field=data[1],
                lyr=relation.referencingLayer().id(),
                agg=data[0])
            conditions.append(condition)
        expression = ' OR '.join(conditions)
        self.iface.showAttributeTable(relation.referencingLayer(), expression)
    def show_children(self,
                      relation: QgsRelation,
                      features: [QgsFeature],
                      data=None):
        """
        :param relation: the relation
        :param features: the list of feature on the referenced layer
        :return:
        """
        if len(features) == 0:
            return

        # works only for single key relation
        for referencing, referenced in relation.fieldPairs().items():
            break
        expression = '{fk} IN ({parent_ids})'.format(
            fk=referencing,
            parent_ids=', '.join([
                "{escape_referenced}{referenced}{escape_referenced}".format(
                    referenced=str(f.attribute(referenced)),
                    escape_referenced="'" if not relation.referencedLayer(
                    ).fields().field(referenced).isNumeric() else '')
                for f in features
            ]))
        self.iface.showAttributeTable(relation.referencingLayer(), expression)
 def create_menu_action(parent_menu: QMenu,
                        title: str,
                        relation: QgsRelation,
                        slot,
                        data=None):
     # add legend context menu entry
     action = QAction(title, parent_menu)
     action.setData(relation.id())
     action.triggered.connect(
         lambda: slot(relation,
                      relation.referencedLayer().selectedFeatures(), data))
     parent_menu.addAction(action)
    def add_map_layer_action(self,
                             title: str,
                             relation: QgsRelation,
                             slot,
                             data=None) -> QgsMapLayerAction:
        def layer_action_triggered(layer: QgsVectorLayer,
                                   features: [QgsFeature]):
            assert layer == relation.referencedLayer()
            slot(relation, features, data)

        action = QgsMapLayerAction(title, self.iface.mainWindow(),
                                   relation.referencedLayer(),
                                   QgsMapLayerAction.MultipleFeatures)
        if DEBUG:
            print(
                'Adding insert action for relation "{}" in layer "{}"'.format(
                    relation.name(),
                    relation.referencedLayer().name()))
        QgsGui.instance().mapLayerActionRegistry().addMapLayerAction(action)
        action.triggeredForFeatures.connect(layer_action_triggered)
        self.map_layer_actions.append(action)
Ejemplo n.º 6
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)
Ejemplo n.º 7
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 batch_insert(self,
                     relation: QgsRelation,
                     features: [QgsFeature],
                     data=None):
        """
        :param relation: the relation
        :param features: the list of feature on the referenced layer
        :return:
        """
        layer = relation.referencingLayer()
        if not layer.isEditable():
            self.iface.messageBar().pushMessage(
                'Relation Batch Insert',
                self.tr('layer "{layer}" is not editable').format(
                    layer=layer.name()), Qgis.Warning)
            return

        if len(features) < 1:
            self.iface.messageBar().pushMessage(
                'Relation Batch Insert',
                self.
                tr('There is no features to batch insert for. Select some in layer "{layer}" first.'
                   ).format(layer=relation.referencedLayer().name()),
                Qgis.Warning)
            return

        default_values = {}
        orignal_cfg = {}
        first_feature_created = False
        referencing_feature = QgsFeature()
        features_written = 0
        ok = True

        for referenced_feature in features:
            # define values for the referencing field over the possible several field pairs
            for referencing_field, referenced_field in relation.fieldPairs(
            ).items():
                referencing_field_index = relation.referencingLayer().fields(
                ).indexFromName(referencing_field)

                # disabled the widgets for the referenced feature in the form (since it will be replaced)
                if not first_feature_created:
                    default_values[
                        referencing_field_index] = referenced_feature[
                            referenced_field]
                    orignal_cfg[
                        referencing_field_index] = layer.editorWidgetSetup(
                            referencing_field_index)
                    layer.setEditorWidgetSetup(
                        referencing_field_index,
                        QgsEditorWidgetSetup('Hidden', {}))

                else:
                    # it has been created at previous iteration
                    referencing_feature[
                        referencing_field_index] = referenced_feature[
                            referenced_field]

            if not first_feature_created:
                # show form for the feature with disabled widgets for the referencing fields
                ok, referencing_feature = self.iface.vectorLayerTools(
                ).addFeature(layer, default_values, QgsGeometry())
                if not ok:
                    break
                # restore widget config of the layer
                for index, cfg in orignal_cfg.items():
                    layer.setEditorWidgetSetup(index, cfg)
                first_feature_created = True
            else:
                ok = layer.addFeature(referencing_feature)
                if not ok:
                    break
            features_written += 1

        if ok:
            self.iface.messageBar().pushMessage(
                'Relation Batch Insert',
                self.tr('{count} features were written to "{layer}"').format(
                    count=features_written, layer=layer.name()))
        else:
            self.iface.messageBar().pushMessage(
                'Relation Batch Insert',
                self.tr('There was an error while inserting features, '
                        '{count} features were written to "{layer}", '
                        '{expected_count} were expected.').format(
                            count=features_written,
                            layer=layer.name(),
                            expected_count=len(features)), Qgis.Critical)
Ejemplo n.º 9
0
class LinkerDock(QDockWidget, Ui_linker, SettingDialog):
    def __init__(self, iface):
        # QGIS
        self.iface = iface
        self.settings = MySettings()
        self.linkRubber = QgsRubberBand(self.iface.mapCanvas())
        self.featureHighlight = None
        # Relation management
        self.relationManager = QgsProject.instance().relationManager()
        self.relationManager.changed.connect(self.loadRelations)
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationWidgetWrapper = None
        self.editorContext = QgsAttributeEditorContext()
        self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools())

        # GUI
        QDockWidget.__init__(self)
        self.setupUi(self)
        SettingDialog.__init__(self, MySettings(), False, True)
        self.drawButton.setChecked(self.settings.value("drawEnabled"))

        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas())
        self.mapTool.setButton(self.identifyReferencingFeatureButton)

        # Connect signal/slot
        self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
        self.mapTool.featureIdentified.connect(self.setReferencingFeature)

        # load relations at start
        self.loadRelations()

    def showEvent(self, QShowEvent):
        self.drawLink()

    def closeEvent(self, e):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)
        self.linkRubber.reset()
        self.deleteHighlight()
        self.deleteWrapper()
        self.disconnectLayer()

    def disconnectLayer(self):
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside)

    def runForFeature(self, relationId, layer, feature):
        index = self.relationComboBox.findData(relationId)
        self.relationComboBox.setCurrentIndex(index)
        self.setReferencingFeature(feature)
        self.show()
        if not layer.isEditable():
            self.iface.messageBar().pushMessage("Link It", "Cannot set a new related feature since %s is not editable" % layer.name(), QgsMessageBar.WARNING, 4)
        else:
            self.relationReferenceWidget.mapIdentification()

    @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked")
    def activateMapTool(self):
        self.iface.mapCanvas().setMapTool(self.mapTool)

    def deactivateMapTool(self):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)

    def loadRelations(self):
        self.deleteWrapper()
        self.disconnectLayer()
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationComboBox.currentIndexChanged.disconnect(self.currentRelationChanged)
        self.relationComboBox.clear()
        for relation in self.relationManager.referencedRelations():
            if relation.referencingLayer().hasGeometryType():
                self.relationComboBox.addItem(relation.name(), relation.id())
        self.relationComboBox.setCurrentIndex(-1)
        self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
        self.currentRelationChanged(-1)

    def currentRelationChanged(self, index):
        # disconnect previous relation
        if self.relation.isValid():
            try:
                self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged)
                self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged)
                self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside)
            except TypeError:
                pass

        self.referencingFeatureLayout.setEnabled(index >= 0)
        relationId = self.relationComboBox.itemData(index)
        self.relation = self.relationManager.relation(relationId)
        self.mapTool.setLayer(self.relation.referencingLayer())
        self.setReferencingFeature()
        # connect
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.connect(self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.connect(self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.connect(self.layerValueChangedOutside)

    def setReferencingFeature(self, feature=QgsFeature()):
        self.deactivateMapTool()
        self.referencingFeature = QgsFeature(feature)
        self.deleteWrapper()

        # disable relation reference widget if no referencing feature
        self.referencedFeatureLayout.setEnabled(feature.isValid())

        # set line edit
        if not self.relation.isValid() or not feature.isValid():
            self.referencingFeatureLineEdit.clear()
            return
        self.referencingFeatureLineEdit.setText("%s" % feature.id())

        fieldIdx = self.referencingFieldIndex()
        widgetConfig = self.relation.referencingLayer().editorWidgetV2Config(fieldIdx)
        self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create("RelationReference",
                                                                               self.relation.referencingLayer(),
                                                                               fieldIdx,
                                                                               widgetConfig,
                                                                               self.relationReferenceWidget,
                                                                               self,
                                                                               self.editorContext)

        self.relationWidgetWrapper.setEnabled(self.relation.referencingLayer().isEditable())
        self.relationWidgetWrapper.setValue(feature[fieldIdx])
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)
        # override field definition to allow map identification
        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        # update drawn link
        self.highlightReferencingFeature()
        self.drawLink()

    def deleteWrapper(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged)
            self.relationWidgetWrapper.setValue(None)
            del self.relationWidgetWrapper
            self.relationWidgetWrapper = None

    def foreignKeyChanged(self, newKey):
        if not self.relation.isValid() or not self.relation.referencingLayer().isEditable() or not self.referencingFeature.isValid():
            self.drawLink()
            return
        if not self.relation.referencingLayer().editBuffer().changeAttributeValue(self.referencingFeature.id(), self.referencingFieldIndex(), newKey):
            self.iface.messageBar().pushMessage("Link It", "Cannot change attribute value.", QgsMessageBar.CRITICAL)
        self.drawLink()

    def relationEditableChanged(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.setEnabled(self.relation.isValid() and self.relation.referencingLayer().isEditable())

    def layerValueChangedOutside(self, fid, fieldIdx, value):
        if not self.relation.isValid() or not self.referencingFeature.isValid() or self.relationWidgetWrapper is None:
            return
        # not the correct feature
        if fid != self.referencingFeature.id():
            return
        # not the correct field
        if fieldIdx != self.referencingFieldIndex():
            return
        # widget already has this value
        if value == self.relationWidgetWrapper.value():
            return
        self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged)
        self.relationWidgetWrapper.setValue(value)
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)

    def referencingFieldIndex(self):
        if not self.relation.isValid():
            return -1
        fieldName = self.relation.fieldPairs().keys()[0]
        fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName)
        return fieldIdx

    @pyqtSlot(bool, name="on_drawButton_toggled")
    def drawLink(self):
        self.settings.setValue("drawEnabled", self.drawButton.isChecked())
        self.linkRubber.reset()
        if not self.drawButton.isChecked() or not self.referencingFeature.isValid() or not self.relation.isValid():
            return

        referencedFeature = self.relationReferenceWidget.referencedFeature()
        if not referencedFeature.isValid():
            return

        p1 = self.centroid(self.relation.referencedLayer(), referencedFeature)
        p2 = self.centroid(self.relation.referencingLayer(), self.referencingFeature)
        geom = arc(p1, p2)

        self.linkRubber.setToGeometry(geom, None)
        self.linkRubber.setWidth(self.settings.value("rubberWidth"))
        self.linkRubber.setColor(self.settings.value("rubberColor"))
        self.linkRubber.setLineStyle(Qt.DashLine)

    def centroid(self, layer, feature):
        geom = feature.geometry()
        if geom.type() == QGis.Line:
            geom = geom.interpolate(geom.length()/2)
        else:
            geom = geom.centroid()
        return self.iface.mapCanvas().mapSettings().layerToMapCoordinates(layer, geom.asPoint())

    @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked")
    def highlightReferencingFeature(self):
        self.deleteHighlight()
        if not self.relation.isValid() or not self.referencingFeature.isValid():
            return
        
        self.featureHighlight = QgsHighlight(self.iface.mapCanvas(), self.referencingFeature.geometry(), self.relation.referencingLayer())
        settings = QSettings()
        color = QColor( settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha()))
        bbuffer = float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM))
        minWidth = float(settings.value("/Map/highlight/minWidth", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM))
        
        self.featureHighlight.setColor(color)
        color.setAlpha(alpha)
        self.featureHighlight.setFillColor(color)
        self.featureHighlight.setBuffer(bbuffer)
        self.featureHighlight.setMinWidth(minWidth)
        self.featureHighlight.show()

        timer = QTimer(self)
        timer.setSingleShot(True)
        timer.timeout.connect(self.deleteHighlight)
        timer.start(3000)

    def deleteHighlight(self):
        if self.featureHighlight:
            del self.featureHighlight
        self.featureHighlight = None
Ejemplo n.º 10
0
class LinkerDock(QDockWidget, Ui_linker, SettingDialog):
    def __init__(self, iface):
        # QGIS
        self.iface = iface
        self.settings = MySettings()
        self.linkRubber = QgsRubberBand(self.iface.mapCanvas())
        self.featureHighlight = None
        # Relation management
        self.relationManager = QgsProject.instance().relationManager()
        self.relationManager.changed.connect(self.loadRelations)
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationWidgetWrapper = None
        self.editorContext = QgsAttributeEditorContext()
        self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools())

        # GUI
        QDockWidget.__init__(self)
        self.setupUi(self)
        SettingDialog.__init__(self, MySettings(), False, True)
        self.drawButton.setChecked(self.settings.value("drawEnabled"))

        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas())
        self.mapTool.setButton(self.identifyReferencingFeatureButton)

        # Connect signal/slot
        self.relationComboBox.currentIndexChanged.connect(
            self.currentRelationChanged)
        self.mapTool.featureIdentified.connect(self.setReferencingFeature)

        # load relations at start
        self.loadRelations()

    def showEvent(self, QShowEvent):
        self.drawLink()

    def closeEvent(self, e):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)
        self.linkRubber.reset()
        self.deleteHighlight()
        self.deleteWrapper()
        self.disconnectLayer()

    def disconnectLayer(self):
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.disconnect(
                self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.disconnect(
                self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.disconnect(
                self.layerValueChangedOutside)

    def runForFeature(self, relationId, layer, feature):
        index = self.relationComboBox.findData(relationId)
        self.relationComboBox.setCurrentIndex(index)
        self.setReferencingFeature(feature)
        self.show()
        if not layer.isEditable():
            self.iface.messageBar().pushMessage(
                "Link It",
                "Cannot set a new related feature since %s is not editable" %
                layer.name(), QgsMessageBar.WARNING, 4)
        else:
            self.relationReferenceWidget.mapIdentification()

    @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked")
    def activateMapTool(self):
        self.iface.mapCanvas().setMapTool(self.mapTool)

    def deactivateMapTool(self):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)

    def loadRelations(self):
        self.deleteWrapper()
        self.disconnectLayer()
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationComboBox.currentIndexChanged.disconnect(
            self.currentRelationChanged)
        self.relationComboBox.clear()
        for relation in self.relationManager.referencedRelations():
            if relation.referencingLayer().hasGeometryType():
                self.relationComboBox.addItem(relation.name(), relation.id())
        self.relationComboBox.setCurrentIndex(-1)
        self.relationComboBox.currentIndexChanged.connect(
            self.currentRelationChanged)
        self.currentRelationChanged(-1)

    def currentRelationChanged(self, index):
        # disconnect previous relation
        if self.relation.isValid():
            try:
                self.relation.referencingLayer().editingStarted.disconnect(
                    self.relationEditableChanged)
                self.relation.referencingLayer().editingStopped.disconnect(
                    self.relationEditableChanged)
                self.relation.referencingLayer(
                ).attributeValueChanged.disconnect(
                    self.layerValueChangedOutside)
            except TypeError:
                pass

        self.referencingFeatureLayout.setEnabled(index >= 0)
        relationId = self.relationComboBox.itemData(index)
        self.relation = self.relationManager.relation(relationId)
        self.mapTool.setLayer(self.relation.referencingLayer())
        self.setReferencingFeature()
        # connect
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.connect(
                self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.connect(
                self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.connect(
                self.layerValueChangedOutside)

    def setReferencingFeature(self, feature=QgsFeature()):
        self.deactivateMapTool()
        self.referencingFeature = QgsFeature(feature)
        self.deleteWrapper()

        # disable relation reference widget if no referencing feature
        self.referencedFeatureLayout.setEnabled(feature.isValid())

        # set line edit
        if not self.relation.isValid() or not feature.isValid():
            self.referencingFeatureLineEdit.clear()
            return
        self.referencingFeatureLineEdit.setText("%s" % feature.id())

        fieldIdx = self.referencingFieldIndex()
        widgetConfig = self.relation.referencingLayer().editorWidgetV2Config(
            fieldIdx)
        self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create(
            "RelationReference", self.relation.referencingLayer(), fieldIdx,
            widgetConfig, self.relationReferenceWidget, self,
            self.editorContext)

        self.relationWidgetWrapper.setEnabled(
            self.relation.referencingLayer().isEditable())
        self.relationWidgetWrapper.setValue(feature[fieldIdx])
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)
        # override field definition to allow map identification
        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        # update drawn link
        self.highlightReferencingFeature()
        self.drawLink()

    def deleteWrapper(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.valueChanged.disconnect(
                self.foreignKeyChanged)
            self.relationWidgetWrapper.setValue(None)
            del self.relationWidgetWrapper
            self.relationWidgetWrapper = None

    def foreignKeyChanged(self, newKey):
        if not self.relation.isValid() or not self.relation.referencingLayer(
        ).isEditable() or not self.referencingFeature.isValid():
            self.drawLink()
            return
        if not self.relation.referencingLayer().editBuffer(
        ).changeAttributeValue(self.referencingFeature.id(),
                               self.referencingFieldIndex(), newKey):
            self.iface.messageBar().pushMessage(
                "Link It", "Cannot change attribute value.",
                QgsMessageBar.CRITICAL)
        self.drawLink()

    def relationEditableChanged(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.setEnabled(
                self.relation.isValid()
                and self.relation.referencingLayer().isEditable())

    def layerValueChangedOutside(self, fid, fieldIdx, value):
        if not self.relation.isValid() or not self.referencingFeature.isValid(
        ) or self.relationWidgetWrapper is None:
            return
        # not the correct feature
        if fid != self.referencingFeature.id():
            return
        # not the correct field
        if fieldIdx != self.referencingFieldIndex():
            return
        # widget already has this value
        if value == self.relationWidgetWrapper.value():
            return
        self.relationWidgetWrapper.valueChanged.disconnect(
            self.foreignKeyChanged)
        self.relationWidgetWrapper.setValue(value)
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)

    def referencingFieldIndex(self):
        if not self.relation.isValid():
            return -1
        fieldName = self.relation.fieldPairs().keys()[0]
        fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName)
        return fieldIdx

    @pyqtSlot(bool, name="on_drawButton_toggled")
    def drawLink(self):
        self.settings.setValue("drawEnabled", self.drawButton.isChecked())
        self.linkRubber.reset()
        if not self.drawButton.isChecked(
        ) or not self.referencingFeature.isValid(
        ) or not self.relation.isValid():
            return

        referencedFeature = self.relationReferenceWidget.referencedFeature()
        if not referencedFeature.isValid():
            return

        p1 = self.centroid(self.relation.referencedLayer(), referencedFeature)
        p2 = self.centroid(self.relation.referencingLayer(),
                           self.referencingFeature)
        geom = arc(p1, p2)

        self.linkRubber.setToGeometry(geom, None)
        self.linkRubber.setWidth(self.settings.value("rubberWidth"))
        self.linkRubber.setColor(self.settings.value("rubberColor"))
        self.linkRubber.setLineStyle(Qt.DashLine)

    def centroid(self, layer, feature):
        geom = feature.geometry()
        if geom.type() == QGis.Line:
            geom = geom.interpolate(geom.length() / 2)
        else:
            geom = geom.centroid()
        return self.iface.mapCanvas().mapSettings().layerToMapCoordinates(
            layer, geom.asPoint())

    @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked")
    def highlightReferencingFeature(self):
        self.deleteHighlight()
        if not self.relation.isValid() or not self.referencingFeature.isValid(
        ):
            return

        self.featureHighlight = QgsHighlight(
            self.iface.mapCanvas(), self.referencingFeature.geometry(),
            self.relation.referencingLayer())
        settings = QSettings()
        color = QColor(
            settings.value("/Map/highlight/color",
                           QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = int(
            settings.value("/Map/highlight/colorAlpha",
                           QGis.DEFAULT_HIGHLIGHT_COLOR.alpha()))
        bbuffer = float(
            settings.value("/Map/highlight/buffer",
                           QGis.DEFAULT_HIGHLIGHT_BUFFER_MM))
        minWidth = float(
            settings.value("/Map/highlight/minWidth",
                           QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM))

        self.featureHighlight.setColor(color)
        color.setAlpha(alpha)
        self.featureHighlight.setFillColor(color)
        self.featureHighlight.setBuffer(bbuffer)
        self.featureHighlight.setMinWidth(minWidth)
        self.featureHighlight.show()

        timer = QTimer(self)
        timer.setSingleShot(True)
        timer.timeout.connect(self.deleteHighlight)
        timer.start(3000)

    def deleteHighlight(self):
        if self.featureHighlight:
            del self.featureHighlight
        self.featureHighlight = None
Ejemplo n.º 11
0
class DocumentModel(QAbstractTableModel):

    DocumentIdRole = Qt.UserRole + 1
    DocumentPathRole = Qt.UserRole + 2
    DocumentNameRole = Qt.UserRole + 3
    DocumentExistsRole = Qt.UserRole + 4
    DocumentToolTipRole = Qt.UserRole + 5
    DocumentIsImageRole = Qt.UserRole + 6

    def __init__(self, parent: QObject = None):
        super(DocumentModel, self).__init__(parent)
        self._relation = QgsRelation()
        self._nmRelation = QgsRelation()
        self._documents_path = str()
        self._document_filename = str()
        self._feature = QgsFeature()
        self._document_list = []

    def init(self, relation: QgsRelation, nmRelation: QgsRelation,
             feature: QgsFeature, documents_path: str, document_filename: str):
        self._relation = relation
        self._nmRelation = nmRelation
        self._documents_path = documents_path
        self._document_filename = document_filename
        self._feature = feature
        self.reloadData()

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return len(self._document_list)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return 1

    def headerData(self,
                   section: int,
                   orientation: Qt.Orientation,
                   role: int = ...):
        return None

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
        return flags

    def data(self, index: QModelIndex, role: int = ...):
        if index.row() < 0 or index.row() >= self.rowCount(QModelIndex()):
            return None

        return self._document_list[index.row()][role]

    def setData(self,
                index: QModelIndex,
                value,
                role: int = Qt.EditRole) -> bool:
        if index.row() < 0 or index.row() >= self.rowCount(QModelIndex()):
            return False

        return False

    def roleNames(self):
        return {
            self.DocumentIdRole: b'DocumentId',
            self.DocumentPathRole: b'DocumentPath',
            self.DocumentNameRole: b'DocumentName',
            self.DocumentExistsRole: b'DocumentExists',
            self.DocumentToolTipRole: b'DocumentToolTip',
            self.DocumentIsImageRole: b'DocumentIsImage'
        }

    def reloadData(self):
        self.beginResetModel()
        self._document_list = []

        if self._relation.isValid() is False or self._feature.isValid(
        ) is False:
            self.endResetModel()
            return

        feature_list = []
        layer = self._relation.referencingLayer()
        request = self._relation.getRelatedFeaturesRequest(self._feature)
        for feature in layer.getFeatures(request):
            feature_list.append(feature)

        if self._nmRelation.isValid():
            filters = []
            for joinTableFeature in feature_list:
                referencedFeatureRequest = self._nmRelation.getReferencedFeatureRequest(
                    joinTableFeature)
                filterExpression = referencedFeatureRequest.filterExpression()
                filters.append("(" + filterExpression.expression() + ")")

            nmRequest = QgsFeatureRequest()
            nmRequest.setFilterExpression(" OR ".join(filters))

            feature_list = []
            layer = self._nmRelation.referencedLayer()
            for documentFeature in layer.getFeatures(nmRequest):
                feature_list.append(documentFeature)

        for documentFeature in feature_list:
            documents_path = str()
            if self._documents_path:
                exp = QgsExpression(self._documents_path)
                context = QgsExpressionContext()
                context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                context.setFeature(documentFeature)
                documents_path = exp.evaluate(context)

            document_filename = str()
            if self._document_filename:
                exp = QgsExpression(self._document_filename)
                context = QgsExpressionContext()
                context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                context.setFeature(documentFeature)
                document_filename = exp.evaluate(context)
            file_info = QFileInfo(QDir(str(documents_path)),
                                  str(document_filename))

            # ToolTip
            toolTipList = []
            toolTipList.append("<ul>")
            for field in documentFeature.fields():
                index = documentFeature.fields().indexFromName(field.name())
                toolTipList.append("<li><strong>{0}</strong>: {1}</li>".format(
                    field.displayName(), documentFeature[index]))
            toolTipList.append("</ul>")

            self._document_list.append({
                self.DocumentIdRole:
                documentFeature.id(),
                self.DocumentPathRole:
                file_info.filePath(),
                self.DocumentNameRole:
                file_info.fileName(),
                self.DocumentExistsRole:
                file_info.exists(),
                self.DocumentToolTipRole:
                "".join(toolTipList),
                self.DocumentIsImageRole:
                PreviewImageProvider.isMimeTypeSupported(file_info.filePath())
            })

        self.endResetModel()