I would like to catch a signal when a rule within a rule based layer toggles visibility. How to permanently check this


the same way we can receive a signal when a node in the layer tree toggles visibility:

layer= iface.layerTreeView().currentNode()

The problem is I cannot catch the visibility change on a rule.

To add more context:

The layer "SM_GIS pds_electricity < 10000" is a copy of "PDS Electricité <2000" but with other scale ranges.

(I will hide it for the user but that's not the question here).

What I want: if I set the rule "Prosumer" to visible in one layer , I want it to be set to visible in the same rule of the other layer.

enter image description here

A workaround is to make copies of the same layer and use single symbol and applying each rule to a new copy of the layer.

  • Welcome to GSE ! Can you give more context, the layer visibility rule for example, when it's append, etc. ? – J. Monticolo May 29 '19 at 12:13
  • Thanks! I have a layer with some rule based rendering. I did multiple copies of that layer to adapt symbology with scale. At the end I want to hide those copies and keep only one. So that when the user set visible layer(A).rule(1) the script detects it and toggle to visible the same rule in the other versions of the same layer. – Nathan Leenaerts May 29 '19 at 12:50

The signal you are searching is QgsMapLayer.styleChanged.

And here a code below. I use partial to give as parameter the layer who has the rule checked/unchecked, this is for the case you connect multiple layers to the updateRules function.

  • activeRules function create a dictionnary with label rules as keys and checkstate as values.
  • updateRules function change rule visibility for all other mapLayers if the rule name is found.
from functools import partial

def activeRules(vectorLayer):
    vlar = dict()
    rend = vectorLayer.renderer()
    for lsi in rend.legendSymbolItems():
        chked = rend.legendSymbolItemChecked(lsi.ruleKey())
        vlar[lsi.label()] = (Qt.Unchecked, Qt.Checked)[chked]
    return vlar

def updateRules(vectorLayer):
    vlrules = activeRules(vectorLayer)
    # from https://gis.stackexchange.com/a/261240/93097
    for ml in QgsProject.instance().mapLayers().values():
        mlid = ml.id()
        renderer  = ml.renderer()
        if renderer.type() == 'RuleRenderer':
            if mlid != vectorLayer.id():
                ltl = QgsProject.instance().layerTreeRoot().findLayer(mlid)
                ltm = iface.layerTreeView().model()
                legendNodes = ltm.layerLegendNodes(ltl)
                for ln in legendNodes:
                    nodname = ln.data(0)
                    if nodname in vlrules:
                        ln.setData(vlrules[nodname], Qt.CheckStateRole)

main_vl = QgsProject.instance().mapLayersByName("PDS Electricité")[0]
main_vl.styleChanged.connect(partial(updateRules, main_vl))
  • It does work. I did the workaround with copies of the same layer and scale rules... it works also but far less elegant. Thank you! – Nathan Leenaerts May 30 '19 at 8:10
  • Note that for faster code, if you have per layer unique rule names, you can add a break after the ln.setData(. The code I posted works with a layer that has multiple rules with the same name. – J. Monticolo May 30 '19 at 9:13

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.