Skip to content

Commit b43943a

Browse files
roya0045nyalldawson
authored andcommittedJan 18, 2019
[FEATURE] New expression variables for legend items
Adds new variables for use in data defined expressions for layout legend items, including - @legend_title - @legend_column_count - @legend_split_layers - @legend_wrap_string - @legend_filter_by_map - @legend_filter_out_atlas Additionally, if the legend is linked to a map, then expressions used in that legend will also have access to the linked variables, including @map_scale, @map_extent, etc.
1 parent 7c5dcd6 commit b43943a

File tree

5 files changed

+79
-1
lines changed

5 files changed

+79
-1
lines changed
 

‎python/core/auto_generated/layout/qgslayoutitemlegend.sip.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@ Returns the legend's renderer settings object.
488488
virtual void finalizeRestoreFromXml();
489489

490490

491+
virtual QgsExpressionContext createExpressionContext() const;
492+
493+
491494

492495
public slots:
493496

‎src/core/expression/qgsexpression.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,15 @@ void QgsExpression::initVariableHelp()
757757
// map canvas item variables
758758
sVariableHelpTexts.insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
759759

760+
// legend canvas item variables
761+
sVariableHelpTexts.insert( QStringLiteral( "legend_title" ), QCoreApplication::translate( "variable_help", "Title of the legend." ) );
762+
sVariableHelpTexts.insert( QStringLiteral( "legend_column_count" ), QCoreApplication::translate( "variable_help", "Number of column in the legend." ) );
763+
sVariableHelpTexts.insert( QStringLiteral( "legend_split_layers" ), QCoreApplication::translate( "variable_help", "Boolean indicating if layers can be split in the legend." ) );
764+
sVariableHelpTexts.insert( QStringLiteral( "legend_wrap_string" ), QCoreApplication::translate( "variable_help", "Characters used to wrap the legend text." ) );
765+
sVariableHelpTexts.insert( QStringLiteral( "legend_filter_by_map" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the content of the legend is filtered by the map." ) );
766+
sVariableHelpTexts.insert( QStringLiteral( "legend_filter_out_atlas" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the Atlas is filtered out of the legend." ) );
767+
768+
760769
// map tool capture variables
761770
sVariableHelpTexts.insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
762771
"<p>An array with an item for each snapped point.</p>"

‎src/core/layout/qgslayoutitemlegend.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,34 @@ void QgsLayoutItemLegend::onAtlasEnded()
818818
updateFilterByMap();
819819
}
820820

821+
QgsExpressionContext QgsLayoutItemLegend::createExpressionContext() const
822+
{
823+
QgsExpressionContext context = QgsLayoutItem::createExpressionContext();
824+
825+
// We only want the last scope from the map's expression context, as this contains
826+
// the map specific variables. We don't want the rest of the map's context, because that
827+
// will contain duplicate global, project, layout, etc scopes.
828+
829+
if ( mMap )
830+
{
831+
context.appendScope( mMap->createExpressionContext().popScope() );
832+
}
833+
834+
835+
QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
836+
837+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
838+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
839+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
840+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
841+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
842+
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
843+
844+
context.appendScope( scope );
845+
846+
return context;
847+
}
848+
821849
// -------------------------------------------------------------------------
822850
#include "qgslayertreemodellegendnode.h"
823851
#include "qgsvectorlayer.h"

‎src/core/layout/qgslayoutitemlegend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
436436

437437
void finalizeRestoreFromXml() override;
438438

439+
QgsExpressionContext createExpressionContext() const override;
440+
439441

440442
public slots:
441443

‎tests/src/python/test_qgslayoutlegend.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
QgsLayoutMeasurement,
3030
QgsLayoutItem,
3131
QgsLayoutPoint,
32-
QgsLayoutSize)
32+
QgsLayoutSize,
33+
QgsExpression)
3334
from qgis.testing import (start_app,
3435
unittest
3536
)
@@ -269,6 +270,41 @@ def testDataDefinedColumnCount(self):
269270
self.assertEqual(legend.columnCount(), 2)
270271
self.assertEqual(legend.legendSettings().columnCount(), 5)
271272

273+
def testLegendScopeVariables(self):
274+
layout = QgsLayout(QgsProject.instance())
275+
layout.initializeDefaults()
276+
277+
legend = QgsLayoutItemLegend(layout)
278+
legend.setTitle("Legend")
279+
layout.addLayoutItem(legend)
280+
281+
legend.setColumnCount(2)
282+
legend.setWrapString('d')
283+
legend.setLegendFilterOutAtlas(True)
284+
285+
expc = legend.createExpressionContext()
286+
exp1 = QgsExpression("@legend_title")
287+
self.assertEqual(exp1.evaluate(expc), "Legend")
288+
exp2 = QgsExpression("@legend_column_count")
289+
self.assertEqual(exp2.evaluate(expc), 2)
290+
exp3 = QgsExpression("@legend_wrap_string")
291+
self.assertEqual(exp3.evaluate(expc), 'd')
292+
exp4 = QgsExpression("@legend_split_layers")
293+
self.assertEqual(exp4.evaluate(expc), False)
294+
exp5 = QgsExpression("@legend_filter_out_atlas")
295+
self.assertEqual(exp5.evaluate(expc), True)
296+
297+
map = QgsLayoutItemMap(layout)
298+
map.attemptSetSceneRect(QRectF(20, 20, 80, 80))
299+
map.setFrameEnabled(True)
300+
map.setExtent(QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125))
301+
layout.addLayoutItem(map)
302+
map.setScale(15000)
303+
legend.setLinkedMap(map)
304+
expc2 = legend.createExpressionContext()
305+
exp6 = QgsExpression("@map_scale")
306+
self.assertAlmostEqual(exp6.evaluate(expc2), 15000, 2)
307+
272308

273309
if __name__ == '__main__':
274310
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.