diff --git a/src/core/layout/qgslayoutitemlegend.cpp b/src/core/layout/qgslayoutitemlegend.cpp index 20ba9cb41e16..001bf6f16cd9 100644 --- a/src/core/layout/qgslayoutitemlegend.cpp +++ b/src/core/layout/qgslayoutitemlegend.cpp @@ -31,6 +31,7 @@ #include "qgssymbollayerutils.h" #include "qgslayertreeutils.h" #include "qgslayoututils.h" +#include "qgsmapthemecollection.h" #include #include #include @@ -702,7 +703,7 @@ void QgsLayoutItemLegend::setLinkedMap( QgsLayoutItemMap *map ) if ( mMap ) { setupMapConnections( mMap, true ); - mThemeName = mMap->themeToRender( mMap->createExpressionContext() ); + mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) ); } updateFilterByMap(); @@ -754,6 +755,15 @@ void QgsLayoutItemLegend::updateFilterByMapAndRedraw() updateFilterByMap( true ); } +void QgsLayoutItemLegend::setModelStyleOverrides( const QMap &overrides ) +{ + mLegendModel->setLayerStyleOverrides( overrides ); + const QList< QgsLayerTreeLayer * > layers = mLegendModel->rootGroup()->findLayers(); + for ( QgsLayerTreeLayer *nodeLayer : layers ) + mLegendModel->refreshLayerLegend( nodeLayer ); + +} + void QgsLayoutItemLegend::mapLayerStyleOverridesChanged() { if ( !mMap ) @@ -768,10 +778,7 @@ void QgsLayoutItemLegend::mapLayerStyleOverridesChanged() } else { - mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() ); - const QList< QgsLayerTreeLayer * > layers = mLegendModel->rootGroup()->findLayers(); - for ( QgsLayerTreeLayer *nodeLayer : layers ) - mLegendModel->refreshLayerLegend( nodeLayer ); + setModelStyleOverrides( mMap->layerStyleOverrides() ); } adjustBoxSize(); @@ -781,7 +788,35 @@ void QgsLayoutItemLegend::mapLayerStyleOverridesChanged() void QgsLayoutItemLegend::mapThemeChanged( const QString &theme ) { + if ( mThemeName == theme ) + return; + mThemeName = theme; + + // map's theme has been changed, so make sure to update the legend here + if ( mLegendFilterByMap ) + { + // legend is being filtered by map, so we need to re run the hit test too + // as the style overrides may also have affected the visible symbols + updateFilterByMap( false ); + } + else + { + if ( mThemeName.isEmpty() ) + { + setModelStyleOverrides( QMap() ); + } + else + { + // get style overrides for theme + const QMap overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName ); + setModelStyleOverrides( overrides ); + } + } + + adjustBoxSize(); + + updateFilterByMap(); } void QgsLayoutItemLegend::updateFilterByMap( bool redraw ) @@ -798,7 +833,18 @@ void QgsLayoutItemLegend::updateFilterByMap( bool redraw ) void QgsLayoutItemLegend::doUpdateFilterByMap() { if ( mMap ) - mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() ); + { + if ( !mThemeName.isEmpty() ) + { + // get style overrides for theme + const QMap overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName ); + mLegendModel->setLayerStyleOverrides( overrides ); + } + else + { + mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() ); + } + } else mLegendModel->setLayerStyleOverrides( QMap() ); diff --git a/src/core/layout/qgslayoutitemlegend.h b/src/core/layout/qgslayoutitemlegend.h index 984b1e37b55e..291ee3aa9191 100644 --- a/src/core/layout/qgslayoutitemlegend.h +++ b/src/core/layout/qgslayoutitemlegend.h @@ -545,6 +545,8 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem void setupMapConnections( QgsLayoutItemMap *map, bool connect = true ); + void setModelStyleOverrides( const QMap &overrides ); + std::unique_ptr< QgsLegendModel > mLegendModel; std::unique_ptr< QgsLayerTreeGroup > mCustomLayerTree; diff --git a/tests/src/python/test_qgslayoutlegend.py b/tests/src/python/test_qgslayoutlegend.py index 42aac4995ae2..0f95626aa138 100644 --- a/tests/src/python/test_qgslayoutlegend.py +++ b/tests/src/python/test_qgslayoutlegend.py @@ -33,6 +33,10 @@ QgsMapLayerLegendUtils, QgsLegendStyle, QgsFontUtils, + QgsLineSymbol, + QgsMapThemeCollection, + QgsCategorizedSymbolRenderer, + QgsRendererCategory, QgsApplication) from qgis.testing import (start_app, unittest @@ -514,6 +518,111 @@ def testThemes(self): legend.setLinkedMap(map3) self.assertFalse(legend.themeName()) + def testLegendRenderWithMapTheme(self): + """Test rendering legends linked to map themes""" + QgsProject.instance().removeAllMapLayers() + + point_path = os.path.join(TEST_DATA_DIR, 'points.shp') + point_layer = QgsVectorLayer(point_path, 'points', 'ogr') + line_path = os.path.join(TEST_DATA_DIR, 'lines.shp') + line_layer = QgsVectorLayer(line_path, 'lines', 'ogr') + QgsProject.instance().clear() + QgsProject.instance().addMapLayers([point_layer, line_layer]) + + marker_symbol = QgsMarkerSymbol.createSimple({'color': '#ff0000', 'outline_style': 'no', 'size': '5'}) + point_layer.setRenderer(QgsSingleSymbolRenderer(marker_symbol)) + point_layer.styleManager().addStyleFromLayer("red") + + line_symbol = QgsLineSymbol.createSimple({'color': '#ff0000', 'line_width': '2'}) + line_layer.setRenderer(QgsSingleSymbolRenderer(line_symbol)) + line_layer.styleManager().addStyleFromLayer("red") + + red_record = QgsMapThemeCollection.MapThemeRecord() + point_red_record = QgsMapThemeCollection.MapThemeLayerRecord(point_layer) + point_red_record.usingCurrentStyle = True + point_red_record.currentStyle = 'red' + red_record.addLayerRecord(point_red_record) + line_red_record = QgsMapThemeCollection.MapThemeLayerRecord(line_layer) + line_red_record.usingCurrentStyle = True + line_red_record.currentStyle = 'red' + red_record.addLayerRecord(line_red_record) + QgsProject.instance().mapThemeCollection().insert('red', red_record) + + marker_symbol1 = QgsMarkerSymbol.createSimple({'color': '#0000ff', 'outline_style': 'no', 'size': '5'}) + marker_symbol2 = QgsMarkerSymbol.createSimple({'color': '#0000ff', 'name': 'diamond', 'outline_style': 'no', 'size': '5'}) + marker_symbol3 = QgsMarkerSymbol.createSimple({'color': '#0000ff', 'name': 'rectangle', 'outline_style': 'no', 'size': '5'}) + + point_layer.setRenderer(QgsCategorizedSymbolRenderer('Class', [QgsRendererCategory('B52', marker_symbol1, ''), + QgsRendererCategory('Biplane', marker_symbol2, ''), + QgsRendererCategory('Jet', marker_symbol3, ''), + ])) + point_layer.styleManager().addStyleFromLayer("blue") + + line_symbol = QgsLineSymbol.createSimple({'color': '#0000ff', 'line_width': '2'}) + line_layer.setRenderer(QgsSingleSymbolRenderer(line_symbol)) + line_layer.styleManager().addStyleFromLayer("blue") + + blue_record = QgsMapThemeCollection.MapThemeRecord() + point_blue_record = QgsMapThemeCollection.MapThemeLayerRecord(point_layer) + point_blue_record.usingCurrentStyle = True + point_blue_record.currentStyle = 'blue' + blue_record.addLayerRecord(point_blue_record) + line_blue_record = QgsMapThemeCollection.MapThemeLayerRecord(line_layer) + line_blue_record.usingCurrentStyle = True + line_blue_record.currentStyle = 'blue' + blue_record.addLayerRecord(line_blue_record) + QgsProject.instance().mapThemeCollection().insert('blue', blue_record) + + layout = QgsLayout(QgsProject.instance()) + layout.initializeDefaults() + + map1 = QgsLayoutItemMap(layout) + map1.attemptSetSceneRect(QRectF(20, 20, 80, 80)) + map1.setFrameEnabled(True) + map1.setLayers([point_layer, line_layer]) + layout.addLayoutItem(map1) + map1.setExtent(point_layer.extent()) + map1.setFollowVisibilityPreset(True) + map1.setFollowVisibilityPresetName('red') + + map2 = QgsLayoutItemMap(layout) + map2.attemptSetSceneRect(QRectF(20, 120, 80, 80)) + map2.setFrameEnabled(True) + map2.setLayers([point_layer, line_layer]) + layout.addLayoutItem(map2) + map2.setExtent(point_layer.extent()) + map2.setFollowVisibilityPreset(True) + map2.setFollowVisibilityPresetName('blue') + + 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.setLinkedMap(map1) + + legend2 = QgsLayoutItemLegend(layout) + legend2.setTitle("Legend") + legend2.attemptSetSceneRect(QRectF(120, 120, 80, 80)) + legend2.setFrameEnabled(True) + legend2.setFrameStrokeWidth(QgsLayoutMeasurement(2)) + legend2.setBackgroundColor(QColor(200, 200, 200)) + legend2.setTitle('') + layout.addLayoutItem(legend2) + legend2.setLinkedMap(map2) + + checker = QgsLayoutChecker( + 'composer_legend_theme', layout) + checker.setControlPathPrefix("composer_legend") + result, message = checker.testLayout() + self.report += checker.report() + self.assertTrue(result, message) + + QgsProject.instance().clear() + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme.png new file mode 100644 index 000000000000..603ed6ef4025 Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme.png differ diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme_mask.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme_mask.png new file mode 100644 index 000000000000..c04fb3ae678e Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_theme/expected_composer_legend_theme_mask.png differ