5

I have a set of layers contained in several groups ("First group", "Second group" and "Third group"). I created a function where when a layer from any of these groups has its visibility toggled, it is joined by a 'parent' layer which shares the same name as the group the layer is in:

Example

So in the example shown, if I click on Layer_1, it will be joined to the First Group layer. If I click on Layer_8, it will be joined to the Third group layer etc.


The problem is that when I click on a layer from the "First, Second or Third group", it is joined several times instead of just the once.

For example, if I click Layer_3 once and then check the Joins properties:

Problem


Here is the code:

from PyQt4.QtCore import *
root = QgsProject.instance().layerTreeRoot()
main_group = root.findGroup('Main group')
layerField = 'ID'

def update_joins():
    for group in main_group.children():
        layer = QgsMapLayerRegistry.instance().mapLayersByName( group.name() )[-1]
        for child in group.children():
            node = root.findLayer(child.layer().id()) 
            if node.isVisible() == Qt.Checked:  
                childField = 'ID'
                joinObject = QgsVectorJoinInfo()
                joinObject.joinLayerId = child.layer().id()
                joinObject.joinFieldName = childField
                joinObject.targetFieldName = layerField
                joinObject.memoryCache = True
                layer.addJoin(joinObject) 
            else:
                layer.removeJoin(child.layer().id())

root.visibilityChanged.connect(update_joins)

I'm not exactly sure why it is being joined several times, is there a way to make sure it is only joined once?

I checked QgsVectorJoinInfo and the QgsVectorLayer::vectorJoins to see if it is possible to do a check (i.e. if this layer has been joined then pass) but couldn't find much information.

  • If you have layer3 on with one join and then you turn on layer4 does one join get added to layer3 and one added layer4 or does it automatically add many joins to both? – artwork21 Jan 26 '17 at 12:47
  • @artwork21 - Yes you are correct, one does get added to layer_3 and layer_4. – Joseph Jan 26 '17 at 12:49
  • 1
    I think your logic right now is just looking what is on and not looking if the layer already has a join. Outside of the function maybe define a dictionary with the keys being the layer names and the value being the join state (True or False). Within the if statement before performing the join look into the dictionary and see if the join state value is True or False, if False perform the join and set the value in the dict to True. Same for the else statement, if the layer is off remove the join (as you have) and set the related dictionary layer value to False. – artwork21 Jan 26 '17 at 13:03
  • @artwork21 - Good logic, thanks for mentioning it. I found that if I add layer.removeJoin(child.layer().id()) directly after the if node.isVisible() == Qt.Checked: statement, the final product is that the layer is only joined once. However, the unnecessary looping is not really efficient so will try and see about creating your logic :) – Joseph Jan 26 '17 at 14:37
2

Update

Thinking more about this using a dictionary to hold the layer names and the related layer join field name and evaluating each checked layer to see if it has the reference joined field should be a better method. If join field is present, than skip, otherwise perform the join.

from PyQt4.QtCore import *
root = QgsProject.instance().layerTreeRoot()
main_group = root.findGroup('Main group')
layerField = 'ID'
layerFieldRef = {'First group':'FirstGroupLayerFieldName', 
               'Second group':'SecondGroupLayerFieldName', 
               'Second group':'SecondGroupLayerFieldName',                    
               'Second group':'SecondGroupLayerFieldName',   
               'Second group':'SecondGroupLayerFieldName', 
               'Third group':'ThirdGroupLayerFieldName', 
               'Third group':'ThirdGroupLayerFieldName',           
               'Third group':'ThirdGroupLayerFieldName'
                }

def update_joins():
    for group in main_group.children():
        layer = QgsMapLayerRegistry.instance().mapLayersByName( group.name() )[-1]
        for child in group.children():
            node = root.findLayer(child.layer().id()) 
            if node.isVisible() == Qt.Checked:
                # check if related layer join field is present, if not present than join
                fields = layer.pendingFields()
                for key in layerFieldRef:
                    if fields.indexFromName(key + '_' + layerFieldRef[key]) == -1:  
                        childField = 'ID'
                        joinObject = QgsVectorJoinInfo()
                        joinObject.joinLayerId = child.layer().id()
                        joinObject.joinFieldName = childField
                        joinObject.targetFieldName = layerField
                        joinObject.memoryCache = True
                        layer.addJoin(joinObject) 
            else:
                layer.removeJoin(child.layer().id())

root.visibilityChanged.connect(update_joins)

This should eliminate extra joins on a layer that already has a join.

  • Thanks for the update, seems to work fine so happy to accept this ;) – Joseph Jan 27 '17 at 15:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.