When rendering layout legends, respect exclude-by-default flag

This commit is contained in:
Nyall Dawson 2025-08-18 10:36:39 +10:00
parent 88c261259c
commit c549f43bf9
6 changed files with 135 additions and 5 deletions

View File

@ -59,7 +59,6 @@ Emitted to refresh the legend.
}; };
class QgsLayoutItemLegend : QgsLayoutItem class QgsLayoutItemLegend : QgsLayoutItem
{ {
%Docstring(signature="appended") %Docstring(signature="appended")

View File

@ -59,7 +59,6 @@ Emitted to refresh the legend.
}; };
class QgsLayoutItemLegend : QgsLayoutItem class QgsLayoutItemLegend : QgsLayoutItem
{ {
%Docstring(signature="appended") %Docstring(signature="appended")

View File

@ -40,12 +40,50 @@
#include "qgslayoutreportcontext.h" #include "qgslayoutreportcontext.h"
#include "qgslayertreefiltersettings.h" #include "qgslayertreefiltersettings.h"
#include "qgsreferencedgeometry.h" #include "qgsreferencedgeometry.h"
#include "qgsmaplayerlegend.h"
#include <QDomDocument> #include <QDomDocument>
#include <QDomElement> #include <QDomElement>
#include <QPainter> #include <QPainter>
#include "qgsexpressioncontext.h" #include "qgsexpressioncontext.h"
//
// QgsLegendFilterProxyModel
//
QgsLegendFilterProxyModel::QgsLegendFilterProxyModel( QObject *parent )
: QgsLayerTreeFilterProxyModel( parent )
{
setIsDefaultLegend( true );
}
void QgsLegendFilterProxyModel::setIsDefaultLegend( bool isDefault )
{
mIsDefaultLegend = isDefault;
// only show private layers when not in default mode
setShowPrivateLayers( !mIsDefaultLegend );
invalidateFilter();
}
bool QgsLegendFilterProxyModel::layerShown( QgsMapLayer *layer ) const
{
if ( !layer )
return true;
if ( QgsMapLayerLegend *layerLegend = layer->legend(); mIsDefaultLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
{
return false;
}
return true;
}
//
// QgsLayoutItemLegend
//
QgsLayoutItemLegend::QgsLayoutItemLegend( QgsLayout *layout ) QgsLayoutItemLegend::QgsLayoutItemLegend( QgsLayout *layout )
: QgsLayoutItem( layout ) : QgsLayoutItem( layout )
, mLegendModel( new QgsLegendModel( nullptr, this ) ) , mLegendModel( new QgsLegendModel( nullptr, this ) )
@ -330,9 +368,8 @@ QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const
{ {
QgsLegendRenderer res( mLegendModel.get(), mSettings ); QgsLegendRenderer res( mLegendModel.get(), mSettings );
// only show private layers when not in auto update mode QgsLegendFilterProxyModel *proxy = new QgsLegendFilterProxyModel();
QgsLayerTreeFilterProxyModel *proxy = new QgsLayerTreeFilterProxyModel(); proxy->setIsDefaultLegend( !static_cast< bool >( mCustomLayerTree ) );
proxy->setShowPrivateLayers( static_cast< bool >( mCustomLayerTree ) );
res.setProxyModel( proxy ); res.setProxyModel( proxy );
return res; return res;

View File

@ -22,6 +22,7 @@
#include "qgis_sip.h" #include "qgis_sip.h"
#include "qgslayoutitem.h" #include "qgslayoutitem.h"
#include "qgslayertreemodel.h" #include "qgslayertreemodel.h"
#include "qgslayertreefilterproxymodel.h"
#include "qgslegendsettings.h" #include "qgslegendsettings.h"
#include "qgslayertree.h" #include "qgslayertree.h"
#include "qgsexpressioncontext.h" #include "qgsexpressioncontext.h"
@ -102,7 +103,37 @@ class CORE_EXPORT QgsLegendModel : public QgsLayerTreeModel
}; };
#ifndef SIP_RUN
/**
* \ingroup core
* \brief A layout item subclass for map legend filtering.
* \note Not available in Python bindings
* \since QGIS 4.0
*/
class CORE_EXPORT QgsLegendFilterProxyModel : public QgsLayerTreeFilterProxyModel
{
Q_OBJECT
public:
/**
* Constructor for QgsLegendFilterProxyModel, with the specified \a parent object.
*/
QgsLegendFilterProxyModel( QObject *parent SIP_TRANSFERTHIS = nullptr );
/**
* Sets whether the legend is showing the default legend for a project (as opposed
* to a customised legend).
*/
void setIsDefaultLegend( bool isDefault );
private:
bool layerShown( QgsMapLayer *layer ) const override;
bool mIsDefaultLegend = true;
};
#endif
/** /**
* \ingroup core * \ingroup core

View File

@ -2046,6 +2046,70 @@ class TestQgsLayoutItemLegend(QgisTestCase, LayoutItemTestCase):
self.assertTrue(self.render_layout_check("composer_legend_auto_wrap", layout)) self.assertTrue(self.render_layout_check("composer_legend_auto_wrap", layout))
def test_exclude_from_default_legend(self):
"""
Test layer legend setting for excluding from default legends
"""
point_path = os.path.join(TEST_DATA_DIR, "points.shp")
point_layer1 = QgsVectorLayer(point_path, "layer 1", "ogr")
point_layer2 = QgsVectorLayer(point_path, "layer 2", "ogr")
point_layer3 = QgsVectorLayer(point_path, "layer 3", "ogr")
p = QgsProject()
p.addMapLayers([point_layer1, point_layer2, point_layer3])
point_layer2.legend().setFlag(Qgis.MapLayerLegendFlag.ExcludeByDefault, True)
marker_symbol = QgsMarkerSymbol.createSimple(
{
"color": "#ff0000",
"outline_style": "no",
"size": "5",
"size_unit": "MapUnit",
}
)
point_layer1.setRenderer(QgsSingleSymbolRenderer(marker_symbol.clone()))
point_layer2.setRenderer(QgsSingleSymbolRenderer(marker_symbol.clone()))
point_layer3.setRenderer(QgsSingleSymbolRenderer(marker_symbol.clone()))
layout = QgsLayout(p)
layout.initializeDefaults()
legend = QgsLayoutItemLegend(layout)
legend.setTitle("Legend")
legend.attemptSetSceneRect(QRectF(120, 20, 80, 80))
legend.setFrameEnabled(True)
legend.setFrameStrokeWidth(QgsLayoutMeasurement(2))
legend.setBackgroundColor(QColor(200, 200, 200))
legend.setTitle("")
layout.addLayoutItem(legend)
legend.setResizeToContents(True)
legend.setStyleFont(
QgsLegendStyle.Style.Title, QgsFontUtils.getStandardTestFont("Bold", 16)
)
legend.setStyleFont(
QgsLegendStyle.Style.Group, QgsFontUtils.getStandardTestFont("Bold", 16)
)
legend.setStyleFont(
QgsLegendStyle.Style.Subgroup, QgsFontUtils.getStandardTestFont("Bold", 16)
)
legend.setStyleFont(
QgsLegendStyle.Style.Symbol, QgsFontUtils.getStandardTestFont("Bold", 16)
)
legend.setStyleFont(
QgsLegendStyle.Style.SymbolLabel,
QgsFontUtils.getStandardTestFont("Bold", 16),
)
legend.refresh()
legend.setResizeToContents(True)
legend.updateLegend()
self.assertTrue(
self.render_layout_check("composer_legend_exclude_by_default", layout)
)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()