Example #1
0
 def test_setName(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertEqual(poly_rel.name(),
                      'Polymorphic relations for "<NO LAYER>"')
     poly_rel.setReferencingLayer(self.referencingLayer.id())
     self.assertEqual(poly_rel.name(),
                      'Polymorphic relations for "referencinglayer"')
     poly_rel.setName('Polymorphic Relation 1')
     self.assertEqual(poly_rel.name(), 'Polymorphic Relation 1')
Example #2
0
 def test_setReferencedLayerIds(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertListEqual(poly_rel.referencedLayerIds(), [])
     poly_rel.setReferencedLayerIds(
         [self.referencedLayer1.id(),
          self.referencedLayer2.id()])
     self.assertListEqual(
         poly_rel.referencedLayerIds(),
         [self.referencedLayer1.id(),
          self.referencedLayer2.id()])
Example #3
0
 def createPolymorphicRelation(self):
     polyRel = QgsPolymorphicRelation()
     polyRel.setReferencingLayer(self.referencingLayer.id())
     polyRel.setReferencedLayerIds([self.referencedLayer.id()])
     polyRel.setReferencedLayerField('referenced_layer')
     polyRel.setReferencedLayerExpression('@layer_name')
     polyRel.addFieldPair('referenced_fid', 'x')
     return polyRel
Example #4
0
    def test_isValid(self):
        poly_rel = QgsPolymorphicRelation()
        self.assertFalse(poly_rel.isValid())

        poly_rel.setId('poly_rel1')
        self.assertFalse(poly_rel.isValid())

        poly_rel.setName('Polymorphic Relation Number One')
        self.assertFalse(poly_rel.isValid())

        poly_rel.setReferencingLayer(self.referencingLayer.id())
        self.assertFalse(poly_rel.isValid())

        poly_rel.setReferencedLayerIds(
            [self.referencedLayer1.id(),
             self.referencedLayer2.id()])
        self.assertFalse(poly_rel.isValid())

        poly_rel.setReferencedLayerField('referenced_layer')
        self.assertFalse(poly_rel.isValid())

        poly_rel.setReferencedLayerExpression('@layer_name')
        self.assertFalse(poly_rel.isValid())

        poly_rel.addFieldPair('referenced_fid', 'fid')
        self.assertTrue(poly_rel.isValid())
Example #5
0
    def test_generateRelations(self):
        poly_rel = QgsPolymorphicRelation()
        poly_rel.setId('poly_rel1')
        poly_rel.setName('Polymorphic Relation Number One')
        poly_rel.setReferencingLayer(self.referencingLayer.id())
        poly_rel.setReferencedLayerIds(
            [self.referencedLayer1.id(),
             self.referencedLayer2.id()])
        poly_rel.setReferencedLayerField('referenced_layer')
        poly_rel.setReferencedLayerExpression('@layer_name')
        poly_rel.addFieldPair('referenced_fid', 'fid')

        QgsProject.instance().relationManager().addPolymorphicRelation(
            poly_rel)

        self.assertTrue(poly_rel.isValid())

        rels = poly_rel.generateRelations()

        self.assertEqual(len(rels), 2)

        rel1, rel2 = rels

        self.assertTrue(rel1.isValid())
        self.assertEqual(rel1.polymorphicRelationId(), poly_rel.id())
        self.assertEqual(rel1.referencingLayer(), poly_rel.referencingLayer())
        self.assertEqual(rel1.referencedLayer(), self.referencedLayer1)
        self.assertEqual(rel1.fieldPairs(), {'referenced_fid': 'fid'})

        features = list(self.referencedLayer1.getFeatures())
        self.assertEqual(len(features), 3)
        self.assertEqual(
            rel1.getRelatedFeaturesFilter(features[0]),
            '"referenced_layer" = \'referencedlayer1\' AND "referenced_fid" = \'foo\''
        )
        it = rel1.getRelatedFeatures(features[0])
        self.assertListEqual([f.attributes() for f in it], [
            [1, 'referencedlayer1', 'foo', './file1.jpg'],
            [2, 'referencedlayer1', 'foo', './file2.jpg'],
        ])

        self.assertEqual(
            rel1.getRelatedFeaturesFilter(features[1]),
            '"referenced_layer" = \'referencedlayer1\' AND "referenced_fid" = \'bar\''
        )
        it = rel1.getRelatedFeatures(features[1])
        self.assertListEqual([f.attributes() for f in it], [
            [3, 'referencedlayer1', 'bar', './file3.jpg'],
        ])

        self.assertEqual(
            rel1.getRelatedFeaturesFilter(features[2]),
            '"referenced_layer" = \'referencedlayer1\' AND "referenced_fid" = \'foobar\'\'bar\''
        )
        it = rel1.getRelatedFeatures(features[2])
        self.assertListEqual([f.attributes() for f in it], [])

        self.assertTrue(rel2.isValid())
        self.assertEqual(rel2.polymorphicRelationId(), poly_rel.id())
        self.assertEqual(rel2.polymorphicRelationId(), poly_rel.id())
        self.assertEqual(rel2.referencingLayer(), poly_rel.referencingLayer())
        self.assertEqual(rel2.referencedLayer(), self.referencedLayer2)
        self.assertEqual(rel2.fieldPairs(), {'referenced_fid': 'fid'})

        features = list(self.referencedLayer2.getFeatures())
        self.assertEqual(len(features), 3)
        self.assertEqual(
            rel2.getRelatedFeaturesFilter(features[0]),
            '"referenced_layer" = \'referencedlayer2\' AND "referenced_fid" = \'foo\''
        )
        it = rel2.getRelatedFeatures(features[0])
        self.assertListEqual([f.attributes() for f in it], [])

        self.assertEqual(
            rel2.getRelatedFeaturesFilter(features[1]),
            '"referenced_layer" = \'referencedlayer2\' AND "referenced_fid" = \'bar\''
        )
        it = rel2.getRelatedFeatures(features[1])
        self.assertListEqual([f.attributes() for f in it], [])

        self.assertEqual(
            rel2.getRelatedFeaturesFilter(features[2]),
            '"referenced_layer" = \'referencedlayer2\' AND "referenced_fid" = \'foobar\'\'bar\''
        )
        it = rel2.getRelatedFeatures(features[2])
        self.assertListEqual(
            [f.attributes() for f in it],
            [[4, 'referencedlayer2', "foobar'bar", './file4.jpg']])
Example #6
0
    def test_layerRepresentation(self):
        poly_rel = QgsPolymorphicRelation()
        poly_rel.setId('poly_rel1')
        poly_rel.setName('Polymorphic Relation Number One')
        poly_rel.setReferencingLayer(self.referencingLayer.id())
        poly_rel.setReferencedLayerIds(
            [self.referencedLayer1.id(),
             self.referencedLayer2.id()])
        poly_rel.setReferencedLayerField('referenced_layer')
        poly_rel.setReferencedLayerExpression('@layer_name')
        poly_rel.addFieldPair('referenced_fid', 'fid')

        self.assertEqual(poly_rel.layerRepresentation(self.referencedLayer1),
                         'referencedlayer1')
Example #7
0
 def test_addFieldPair(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertEqual(poly_rel.fieldPairs(), {})
     poly_rel.addFieldPair('referenced_fid', 'fid')
     self.assertEqual(poly_rel.fieldPairs(), {'referenced_fid': 'fid'})
Example #8
0
 def test_setReferencedLayerExpression(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertEqual(poly_rel.referencedLayerExpression(), '')
     poly_rel.setReferencedLayerExpression('@layer_name')
     self.assertEqual(poly_rel.referencedLayerExpression(), '@layer_name')
Example #9
0
 def test_setReferencedLayerField(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertEqual(poly_rel.referencedLayerField(), '')
     poly_rel.setReferencedLayerField(self.referencingLayer.id())
     self.assertEqual(poly_rel.referencedLayerField(),
                      self.referencingLayer.id())
Example #10
0
 def test_setId(self):
     poly_rel = QgsPolymorphicRelation()
     self.assertEqual(poly_rel.id(), '')
     poly_rel.setId('poly_rel_1')
     self.assertEqual(poly_rel.id(), 'poly_rel_1')
Example #11
0
    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)
Example #12
0
class RelationEditorDocumentSideWidget(QgsAbstractRelationEditorWidget,
                                       WidgetUi):
    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 nmRelation(self):
        return self._nmRelation

    def config(self):
        return {}

    def setConfig(self, config):
        self.polymorphicRelationEnabled = config[
            'polymorphic_relation_enabled']
        self.polymorphicRelationId = config['polymorphic_relation_id']

        if self.polymorphicRelationEnabled is False:
            return

        self._polymorphicRelation = QgsProject.instance().relationManager(
        ).polymorphicRelation(self.polymorphicRelationId)

        self._setCardinality()

    def updateUi(self):
        self._updateUiTimer.start(200)

    def updateUiTimeout(self):
        print('DocumentRelationEditorDocumentSideWidget.updateUiTimeout')

        self.mFeaturesTreeWidget.clear()

        if self.relation().isValid() is False or self.feature().isValid(
        ) is False:
            return

        if self.cardinality == Cardinality.ManyToOne:
            self.updateUiManyToOne()

        if self.cardinality == Cardinality.ManyToMany:
            self.updateUiManyToMany()

        if self.cardinality == Cardinality.ManyToOnePolymorphic:
            self.updateUiManyToOnePolymorphic()

        if self.cardinality == Cardinality.ManyToManyPolymorphic:
            self.updateUiManyToManyPolymorphic()

    def updateUiManyToOne(self):
        layer = self.relation().referencingLayer()
        request = self.relation().getRelatedFeaturesRequest(self.feature())
        for feature in layer.getFeatures(request):
            treeWidgetItem = QTreeWidgetItem(
                self.mFeaturesTreeWidget,
                [QgsVectorLayerUtils.getFeatureDisplayString(layer, feature)])
            treeWidgetItem.setData(0, TreeWidgetItemRole.Type,
                                   TreeWidgetItemType.Feature)
            treeWidgetItem.setData(0, TreeWidgetItemRole.Layer, layer)
            treeWidgetItem.setData(0, TreeWidgetItemRole.Feature, feature)

    def updateUiManyToMany(self):
        layer = self.relation().referencingLayer()
        request = self.relation().getRelatedFeaturesRequest(self.feature())
        filters = []
        for feature in layer.getFeatures(request):
            referencedFeatureRequest = self.nmRelation(
            ).getReferencedFeatureRequest(feature)
            filterExpression = referencedFeatureRequest.filterExpression()
            filters.append("(" + filterExpression.expression() + ")")

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

            finalLayer = self.nmRelation().referencedLayer()
            for finalFeature in finalLayer.getFeatures(nmRequest):
                treeWidgetItem = QTreeWidgetItem(self.mFeaturesTreeWidget, [
                    QgsVectorLayerUtils.getFeatureDisplayString(
                        finalLayer, finalFeature)
                ])
                treeWidgetItem.setData(0, TreeWidgetItemRole.Type,
                                       TreeWidgetItemType.Feature)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Layer, finalLayer)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Feature, feature)

    def updateUiManyToOnePolymorphic(self):
        layerFeature = dict()
        for relation in self._polymorphicRelation.generateRelations():
            layer = relation.referencingLayer()
            request = relation.getRelatedFeaturesRequest(self.feature())
            finalLayer = relation.referencedLayer()
            for feature in layer.getFeatures(request):
                if finalLayer in layerFeature:
                    layerFeature[finalLayer].append(feature)
                else:
                    layerFeature[finalLayer] = [feature]

        for layer in layerFeature:
            treeWidgetItemLayer = QTreeWidgetItem(self.mFeaturesTreeWidget,
                                                  [layer.name()])
            treeWidgetItemLayer.setData(0, TreeWidgetItemRole.Type,
                                        TreeWidgetItemType.Layer)
            treeWidgetItemLayer.setData(0, TreeWidgetItemRole.Layer, layer)
            treeWidgetItemLayer.setIcon(0, QgsIconUtils.iconForLayer(layer))
            for feature in layerFeature[layer]:
                treeWidgetItem = QTreeWidgetItem(treeWidgetItemLayer, [
                    QgsVectorLayerUtils.getFeatureDisplayString(
                        layerFeature, feature)
                ])
                treeWidgetItem.setData(0, TreeWidgetItemRole.Type,
                                       TreeWidgetItemType.Feature)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Layer, layer)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Feature, feature)
            treeWidgetItemLayer.setExpanded(True)

    def updateUiManyToManyPolymorphic(self):
        layer = self.relation().referencingLayer()
        request = self.relation().getRelatedFeaturesRequest(self.feature())
        layerFeature = dict()
        for linkFeature in layer.getFeatures(request):
            for relation in self._polymorphicRelation.generateRelations():
                referencedFeatureRequest = relation.getReferencedFeatureRequest(
                    linkFeature)
                filterExpression = referencedFeatureRequest.filterExpression()
                nmRequest = QgsFeatureRequest()
                nmRequest.setFilterExpression(filterExpression.expression())

                finalLayer = relation.referencedLayer()
                for finalFeature in finalLayer.getFeatures(nmRequest):
                    features = finalFeature, linkFeature
                    if finalLayer in layerFeature:
                        layerFeature[finalLayer].append(features)
                    else:
                        layerFeature[finalLayer] = [features]

        for layer in layerFeature:
            treeWidgetItemLayer = QTreeWidgetItem(self.mFeaturesTreeWidget,
                                                  [layer.name()])
            treeWidgetItemLayer.setData(0, TreeWidgetItemRole.Type,
                                        TreeWidgetItemType.Layer)
            treeWidgetItemLayer.setData(0, TreeWidgetItemRole.Layer, layer)
            treeWidgetItemLayer.setIcon(0, QgsIconUtils.iconForLayer(layer))
            for feature, linkFeature in layerFeature[layer]:
                treeWidgetItem = QTreeWidgetItem(treeWidgetItemLayer, [
                    QgsVectorLayerUtils.getFeatureDisplayString(
                        layer, feature)
                ])
                treeWidgetItem.setData(0, TreeWidgetItemRole.Type,
                                       TreeWidgetItemType.Feature)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Layer, layer)
                treeWidgetItem.setData(0, TreeWidgetItemRole.Feature, feature)
                treeWidgetItem.setData(0, TreeWidgetItemRole.LinkFeature,
                                       linkFeature)
            treeWidgetItemLayer.setExpanded(True)

    def afterSetRelations(self):
        self._nmRelation = QgsProject.instance().relationManager().relation(
            str(self.nmRelationId()))

        self._setCardinality()

        self._checkTransactionGroup()

        if self.relation().isValid():
            self.relation().referencingLayer().editingStopped.connect(
                self.updateButtons)
            self.relation().referencingLayer().editingStarted.connect(
                self.updateButtons)

        if self.nmRelation().isValid():
            self.nmRelation().referencedLayer().editingStarted.connect(
                self.updateButtons)
            self.nmRelation().referencedLayer().editingStopped.connect(
                self.updateButtons)

        self.updateButtons()

    def parentFormValueChanged(self, attribute, newValue):
        self.updateUi()

    def checkLayerEditingMode(self):

        if self.relation().referencingLayer().isEditable() is False:
            QMessageBox.critical(
                self, self.tr("Layer not editable"),
                self.tr("Layer '{0}' is not in editing mode.").format(
                    self.relation().referencingLayer().name()))
            return False

        if self.nmRelation().isValid():
            if self.nmRelation().referencedLayer().isEditable() is False:
                QMessageBox.critical(
                    self, self.tr("Layer not editable"),
                    self.tr("Layer '{0}' is not in editing mode.").format(
                        self.nmRelation().referencedLayer().name()))
                return False

        return True

    def _setCardinality(self):

        if (self.nmRelation().isValid() is False
                and self.polymorphicRelationEnabled is False):
            self.cardinality = Cardinality.ManyToOne
            return

        elif (self.nmRelation().isValid()
              and self.polymorphicRelationEnabled is False):
            self.cardinality = Cardinality.ManyToMany
            return

        elif (self.polymorphicRelationEnabled
              and self._polymorphicRelation.referencingLayer().id()
              == self.relation().referencedLayer().id()):
            self.cardinality = Cardinality.ManyToOnePolymorphic
            return

        elif (self.polymorphicRelationEnabled
              and self._polymorphicRelation.referencingLayer().id()
              == self.relation().referencingLayer().id()):
            self.cardinality = Cardinality.ManyToManyPolymorphic
            return

        else:
            print("WARNING invalid cardinality set")

    @pyqtSlot(bool)
    def toggleEditing(self, state):

        super().toggleEditing(state)
        self.updateButtons()

    @pyqtSlot()
    def saveChildLayerEdits(self):

        super().saveEdits()

    def actionShowFormTriggered(self):

        if self.mFeaturesTreeWidget.currentItem() is None:
            QMessageBox.critical(self, self.tr("No feature selected"),
                                 self.tr("Please select a feature."))
            return

        if self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Type) != TreeWidgetItemType.Feature:
            QMessageBox.critical(self,
                                 self.tr("Selected item is not a feature"),
                                 self.tr("Please select a feature."))
            return

        showDocumentFormDialog = QgsAttributeDialog(
            self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Layer),
            self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Feature), False, self, True)
        showDocumentFormDialog.exec()
        self.updateUi()

    def actionLinkFeatureTriggered(self):

        if self.checkLayerEditingMode() is False:
            return

        if self.cardinality == Cardinality.ManyToOne:
            self.linkFeature()

        if self.cardinality == Cardinality.ManyToMany:
            self.linkFeature()

        if self.cardinality == Cardinality.ManyToOnePolymorphic:
            self.linkFeatureManyToOnePolymorphic()
            self.updateUi()

        if self.cardinality == Cardinality.ManyToManyPolymorphic:
            self.linkFeatureManyToManyPolymorphic()
            self.updateUi()

    def linkFeatureManyToOnePolymorphic(self):
        print("link ManyToOnePolymorphic")

    def linkFeatureManyToManyPolymorphic(self):

        nmRelations = dict()
        for relation in self._polymorphicRelation.generateRelations():
            nmRelations[relation.referencedLayer().name()] = relation

        layerName, ok = QInputDialog.getItem(self,
                                             self.tr("Please selct a layer"),
                                             self.tr("Layer:"),
                                             nmRelations.keys())

        if not ok:
            return

        nmRelation = nmRelations[layerName]
        selectionDlg = QgsFeatureSelectionDlg(nmRelation.referencedLayer(),
                                              self.editorContext(), self)
        selectionDlg.setWindowTitle(
            self.tr("Please select the features to link. Layer: {0}").format(
                layerName))
        if not selectionDlg.exec():
            return

        # Fields of the linking table
        fields = self.relation().referencingLayer().fields()

        linkAttributes = dict()
        linkAttributes[fields.indexFromName(
            self._polymorphicRelation.referencedLayerField(
            ))] = self._polymorphicRelation.layerRepresentation(
                nmRelation.referencedLayer())
        for key in self.relation().fieldPairs():
            linkAttributes[fields.indexOf(key)] = self.feature().attribute(
                self.relation().fieldPairs()[key])

        # Expression context for the linking table
        context = self.relation().referencingLayer().createExpressionContext()

        featureIterator = nmRelation.referencedLayer().getFeatures(
            QgsFeatureRequest().setFilterFids(
                selectionDlg.selectedFeatures()).setSubsetOfAttributes(
                    nmRelation.referencedFields()))
        relatedFeature = QgsFeature()
        newFeatures = []
        while featureIterator.nextFeature(relatedFeature):
            for key in nmRelation.fieldPairs():
                linkAttributes[fields.indexOf(key)] = relatedFeature.attribute(
                    nmRelation.fieldPairs()[key])

            linkFeature = QgsVectorLayerUtils.createFeature(
                self.relation().referencingLayer(), QgsGeometry(),
                linkAttributes, context)
            newFeatures.append(linkFeature)

        self.relation().referencingLayer().addFeatures(newFeatures)
        ids = []
        for feature in newFeatures:
            ids.append(feature.id())
        self.relation().referencingLayer().selectByIds(ids)

    def actionUnlinkFeatureTriggered(self):

        if self.checkLayerEditingMode() is False:
            return

        if self.mFeaturesTreeWidget.currentItem() is None:
            QMessageBox.critical(self, self.tr("No feature selected"),
                                 self.tr("Please select a feature to unlink."))
            return

        if self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Type) != TreeWidgetItemType.Feature:
            QMessageBox.critical(self,
                                 self.tr("Selected item is not a feature"),
                                 self.tr("Please select a feature."))
            return

        if self.cardinality == Cardinality.ManyToOne:
            self.unlinkFeature(self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Feature).id())

        if self.cardinality == Cardinality.ManyToMany:
            self.unlinkFeature(self.mFeaturesTreeWidget.currentItem().data(
                0, TreeWidgetItemRole.Feature).id())

        if self.cardinality == Cardinality.ManyToOnePolymorphic:
            print("unlink ManyToOnePolymorphic")

        if self.cardinality == Cardinality.ManyToManyPolymorphic:
            self.relation().referencingLayer().deleteFeature(
                self.mFeaturesTreeWidget.currentItem().data(
                    0, TreeWidgetItemRole.LinkFeature).id())
            self.updateUi()

    def updateButtons(self):

        toggleEditingButtonEnabled = False
        editable = False
        linkable = False
        spatial = False

        selectionNotEmpty = True
        if (self.mFeaturesTreeWidget.currentItem() is None
                or self.mFeaturesTreeWidget.currentItem().data(
                    0, TreeWidgetItemRole.Type) != TreeWidgetItemType.Feature):
            selectionNotEmpty = False

        if self.relation().isValid():
            toggleEditingButtonEnabled = self.relation().referencingLayer(
            ).supportsEditing()
            editable = self.relation().referencingLayer().isEditable()
            linkable = self.relation().referencingLayer().isEditable()
            spatial = self.relation().referencingLayer().isSpatial()

        if self.nmRelation().isValid():
            toggleEditingButtonEnabled |= self.nmRelation().referencedLayer(
            ).supportsEditing()
            editable = self.nmRelation().referencedLayer().isEditable()
            spatial = self.nmRelation().referencedLayer().isSpatial()

        self.actionToggleEditing.setEnabled(toggleEditingButtonEnabled)
        self.actionLinkFeature.setEnabled(linkable)
        self.actionUnlinkFeature.setEnabled(linkable and selectionNotEmpty)
        self.actionToggleEditing.setChecked(editable)
        self.actionSaveEdits.setEnabled(editable or linkable)

        self.actionShowForm.setEnabled(selectionNotEmpty)

        self.mToggleEditingToolButton.setVisible(
            self._layerInSameTransactionGroup is False)
        self.mSaveEditsToolButton.setVisible(
            self._layerInSameTransactionGroup is False)

    def _checkTransactionGroup(self):

        self._layerInSameTransactionGroup = False
        connectionString = PluginHelper.connectionString(
            self.relation().referencedLayer().source())
        transactionGroup = QgsProject.instance().transactionGroup(
            self.relation().referencedLayer().providerType(), connectionString)

        if transactionGroup is None:
            self.updateButtons()
            return

        if self.nmRelation().isValid():
            if (self.relation().referencedLayer() in transactionGroup.layers()
                    and self.relation().referencingLayer()
                    in transactionGroup.layers()
                    and self.nmRelation().referencedLayer()
                    in transactionGroup.layers()):
                self._layerInSameTransactionGroup = True
        else:
            if (self.relation().referencedLayer() in transactionGroup.layers()
                    and self.relation().referencingLayer()
                    in transactionGroup.layers()):
                self._layerInSameTransactionGroup = True