From 452d597cbc7a88901f480ac25b96fa1436a8d2b0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 19 Aug 2024 10:40:11 +1000 Subject: [PATCH] Don't show private layers in print layout legends by default When a print layout legend is in auto mode, private layers should not be included Fixes #51427 --- src/core/layout/qgslayoutitemlegend.cpp | 17 +++++-- src/core/layout/qgslayoutitemlegend.h | 5 +- src/core/qgslegendrenderer.cpp | 9 ++++ src/core/qgslegendrenderer.h | 1 + tests/src/python/test_qgslayoutlegend.py | 44 ++++++++++++++++++ ...xpected_composer_legend_private_layers.png | Bin 0 -> 6536 bytes 6 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 tests/testdata/control_images/composer_legend/expected_composer_legend_private_layers/expected_composer_legend_private_layers.png diff --git a/src/core/layout/qgslayoutitemlegend.cpp b/src/core/layout/qgslayoutitemlegend.cpp index b14e9c7665d..9dc03f358e4 100644 --- a/src/core/layout/qgslayoutitemlegend.cpp +++ b/src/core/layout/qgslayoutitemlegend.cpp @@ -22,6 +22,7 @@ #include "qgslayoutmodel.h" #include "qgslayertree.h" #include "qgslayertreemodel.h" +#include "qgslayertreefilterproxymodel.h" #include "qgslegendrenderer.h" #include "qgslegendstyle.h" #include "qgslogger.h" @@ -153,7 +154,7 @@ void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsIt } mInitialMapScaleCalculated = true; - QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings ); + QgsLegendRenderer legendRenderer = createRenderer(); legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() ); const QPointF oldPos = pos(); @@ -265,7 +266,7 @@ void QgsLayoutItemLegend::draw( QgsLayoutItemRenderContext &context ) Q_NOWARN_DEPRECATED_POP } - QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings ); + QgsLegendRenderer legendRenderer = createRenderer(); legendRenderer.setLegendSize( rect().size() ); legendRenderer.drawLegend( rc ); @@ -288,7 +289,7 @@ void QgsLayoutItemLegend::adjustBoxSize() QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) : QgsLayoutUtils::createRenderContextForLayout( mLayout, nullptr ); - QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings ); + QgsLegendRenderer legendRenderer = createRenderer(); const QSizeF size = legendRenderer.minimumSize( &context ); QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ), 2 ); if ( size.isValid() ) @@ -329,6 +330,16 @@ void QgsLayoutItemLegend::ensureModelIsInitialized() const } } +QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const +{ + QgsLegendRenderer res( mLegendModel.get(), mSettings ); + + // only show private layers when not in auto update mode + res.proxyModel()->setShowPrivateLayers( static_cast< bool >( mCustomLayerTree ) ); + + return res; +} + QgsLegendModel *QgsLayoutItemLegend::model() { ensureModelIsInitialized(); diff --git a/src/core/layout/qgslayoutitemlegend.h b/src/core/layout/qgslayoutitemlegend.h index 8c4b8e31fbe..5516d684f04 100644 --- a/src/core/layout/qgslayoutitemlegend.h +++ b/src/core/layout/qgslayoutitemlegend.h @@ -351,7 +351,7 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem * A symbol size of 0.0 indicates no maximum is set. * * \see maximumSymbolSize() - * \since QGIS 3.16 + * \since QGIS 3.16: */ void setMaximumSymbolSize( double size ); @@ -642,6 +642,9 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem void setModelStyleOverrides( const QMap &overrides ); void ensureModelIsInitialized() const; + + QgsLegendRenderer createRenderer() const; + std::unique_ptr< QgsLegendModel > mLegendModel; std::unique_ptr< QgsLayerTree > mCustomLayerTree; bool mDeferLegendModelInitialization = true; diff --git a/src/core/qgslegendrenderer.cpp b/src/core/qgslegendrenderer.cpp index 510ed045cf2..be4f7d93063 100644 --- a/src/core/qgslegendrenderer.cpp +++ b/src/core/qgslegendrenderer.cpp @@ -37,6 +37,15 @@ QgsLegendRenderer::QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsL mProxyModel->setLayerTreeModel( mLegendModel ); } +QgsLegendRenderer::QgsLegendRenderer( QgsLegendRenderer &&other ) + : mLegendModel( other.mLegendModel ) + , mProxyModel( std::move( other.mProxyModel ) ) + , mSettings( std::move( other.mSettings ) ) + , mLegendSize( other.mLegendSize ) +{ + mProxyModel->setLayerTreeModel( mLegendModel ); +} + QgsLegendRenderer::~QgsLegendRenderer() = default; QSizeF QgsLegendRenderer::minimumSize( QgsRenderContext *renderContext ) diff --git a/src/core/qgslegendrenderer.h b/src/core/qgslegendrenderer.h index 8c317b27f83..8ba7fd6aeef 100644 --- a/src/core/qgslegendrenderer.h +++ b/src/core/qgslegendrenderer.h @@ -58,6 +58,7 @@ class CORE_EXPORT QgsLegendRenderer #ifndef SIP_RUN QgsLegendRenderer( const QgsLegendRenderer &other ) = delete; QgsLegendRenderer &operator=( const QgsLegendRenderer &other ) = delete; + QgsLegendRenderer( QgsLegendRenderer &&other ); #endif /** diff --git a/tests/src/python/test_qgslayoutlegend.py b/tests/src/python/test_qgslayoutlegend.py index d7297c05014..1ab07f27c2a 100644 --- a/tests/src/python/test_qgslayoutlegend.py +++ b/tests/src/python/test_qgslayoutlegend.py @@ -24,6 +24,7 @@ from qgis.PyQt.QtGui import ( from qgis.PyQt.QtXml import QDomDocument from qgis.core import ( + QgsMapLayer, QgsCategorizedSymbolRenderer, QgsCoordinateReferenceSystem, QgsExpression, @@ -441,6 +442,49 @@ class TestQgsLayoutItemLegend(QgisTestCase, LayoutItemTestCase): ) ) + def test_private_layers(self): + """ + Test legend does not contain private layers by default + """ + point_path = os.path.join(TEST_DATA_DIR, 'points.shp') + point_layer1 = QgsVectorLayer(point_path, 'points 1', 'ogr') + point_layer2 = QgsVectorLayer(point_path, 'points 2', 'ogr') + point_layer2.setFlags(QgsMapLayer.LayerFlag.Private) + p = QgsProject() + p.addMapLayers([point_layer1, point_layer2]) + + 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())) + + 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.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)) + + self.assertTrue( + self.render_layout_check( + 'composer_legend_private_layers', layout + ) + ) + def testResizeDisabled(self): """ Test that test legend does not resize if auto size is disabled diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_private_layers/expected_composer_legend_private_layers.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_private_layers/expected_composer_legend_private_layers.png new file mode 100644 index 0000000000000000000000000000000000000000..555788b28ae7fe8f56264e4f356aedd8624cfdcf GIT binary patch literal 6536 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 CkhIAF literal 0 HcmV?d00001