PyQGIS Print Layout – Print Layout Legend and setLegendFilterByMapEnabled()

pyqgispyqgis-3qgis-3qgis-print-layouts

I'm creating a legend in my print composer and only want "visible", or active, layers shown in the legend. This means I want only layers with the box checked in my layers panel.

Here is how I create my legend.

legend = QgsLayoutItemLegend(layout)
legend.setTitle("Legend")
layout.addLayoutItem(legend)

Looking at the documentation for QgsLayoutItemLegend there is a method: setLegendFilterByMapEnabled() whose description is:
enter image description here

This leads me to believe if I do something like:

legend.setLegendFilterByMapEnabled(True)

My legend will only include "active" layers. But my legend still includes all layers, active and inactive.

What am I missing?

Best Answer

You need to link the legend item to a map item in your layout which is an instance of the QgsLayoutItemMap class. So if you have done something like:

map = QgsLayoutItemMap(layout)
layout.addItem(map)

link it with the legend before enabling the filtering:

legend.setLinkedMap(map) # pass a QgsLayoutItemMap object
legend.setLegendFilterByMapEnabled(True)
legend.refresh()

Alternatively, to collect checked layers from the project layer tree view and add those to your layout legend, you could do something like this:

lyrs_to_add = [l for l in QgsProject().instance().layerTreeRoot().children() if l.isVisible()]
legend = QgsLayoutItemLegend(layout)
legend.setTitle('Legend')
legend.setAutoUpdateModel(False)
group = legend.model().rootGroup()
group.clear()
for l in lyrs_to_add:
    if l.nodeType() == 0:
        subgroup = group.addGroup(l.name())
        checked = l.checkedLayers()
        for c in checked:
            subgroup.addLayer(c)
    elif l.nodeType() == 1:
        group.addLayer(l.layer())
layout.addItem(legend)
legend.adjustBoxSize()
legend.refresh()

This will maintain groups etc. but unfortunately it won't filter unchecked nodes for categorized or graduated layers (I still can't work out how to do that).