2

Is there a way to count legend items displayed in the print composer ?

I would like to adjust by expression the number of columns of my legend based on the number of legend items.

Example : if there are 6 or less legend items I want my legend to be displayed on 3 columns. If the number of items exceeds 6 then I want my legend to be displayed on 4 columns.

You can find below a screenshot for illustration purposes which features 7 legend items with a column count of 4.

enter image description here

0

1 Answer 1

6

You can count the number of nodes in your legend using PyQGIS like below (standalone use)

project = QgsProject.instance()
manager = project.layoutManager()
layout = manager.layoutByName('myLayout')

# We consider only one legend
itemLegend = [item for item in layout.items() if isinstance(item, QgsLayoutItemLegend)][0]

# It's a tree, we should follow it recursively but did not
# want to bother: only took the first level for the example
layerTreeChildren = itemLegend.model().rootGroup().children()

# Count the number of elements in each legend block
[len(itemLegend.model().layerLegendNodes(i)) for i in layerTreeChildren]


particularLayer = next(iter(QgsProject.instance().mapLayersByName('my layer name')), None)

if particularLayer:
    # You could also use layerOriginalLegendNodes
    # instead of layerLegendNodes
    print(len(itemLegend.model().layerLegendNodes(itemLegend.model().rootGroup().findLayer(particularLayer))))

Then you will need to create a custom Python expression function to use it easily

@qgsfunction(args='auto', group='Custom', usesGeometry=False, referencedColumns=[])
def countLegendElements(layoutname, layername, feature, parent):
    project = QgsProject.instance()
    manager = project.layoutManager()
    layout = manager.layoutByName(layoutname)
    itemLegend = [item for item in layout.items() if isinstance(item, QgsLayoutItemLegend)][0]
    particularLayer = next(iter(QgsProject.instance().mapLayersByName(layername)), None)

    if particularLayer:
        # You could also use layerOriginalLegendNodes
        # instead of layerLegendNodes
        return len(itemLegend.model().layerLegendNodes(itemLegend.model().rootGroup().findLayer(particularLayer)))
    else:
        return None

Save the custom expression and use in the expression countLegendElements('layoutName', 'layerName') to get the count of legend nodes.

You can reuse this count to change conditions to set your columns number.

The limitations of my current custom function are that I suppose there is only one legend in the composer (you could change the code to search with an id for the legend instead) and I also suppose your layer name is unique (if not, the code will take the first one). I also do not care particularly about highly nested layers.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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