Skip to content

Commit f2032ea

Browse files
committed
[FEATURE] item_variables expression function inside compositions
This adds a new item_variables expression function when expressions are used inside a composition context. The function takes a single argument, the id for an item inside the composition, and returns a map of variable name to value for that item. This allows you to do things like insert text in a label fetching properties of another item in the composition, eg Insert scale of map into a label: map_get( item_variables( 'map'),'map_scale') Insert x coordinate of map center into a label: x(map_get( item_variables( 'map'),'map_extent_center'))
1 parent d25fcec commit f2032ea

5 files changed

Lines changed: 109 additions & 0 deletions

File tree

‎python/core/qgsexpressioncontext.sip‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ class QgsScopedExpressionFunction : QgsExpression::Function
2222
bool handlesNull = false,
2323
bool isContextual = true );
2424

25+
/**
26+
* Create a new QgsScopedExpressionFunction using named parameters.
27+
*
28+
* @note Added in QGIS 3.0
29+
*/
30+
QgsScopedExpressionFunction( const QString& fnname,
31+
const QgsExpression::ParameterList& params,
32+
const QString& group,
33+
const QString& helpText = QString(),
34+
bool usesGeometry = false,
35+
const QSet<QString>& referencedColumns = QSet<QString>(),
36+
bool lazyEval = false,
37+
bool handlesNull = false,
38+
bool isContextual = true );
39+
2540
virtual ~QgsScopedExpressionFunction();
2641

2742
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) = 0;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "item_variables",
3+
"type": "function",
4+
"description": "Returns a map of variables from a composer item inside this composition.",
5+
"arguments": [ {"arg":"id","description":"composer item ID"}],
6+
"examples": [ { "expression":"map_get(item_variables('main_map'), 'map_scale')", "returns":"2000"}
7+
]
8+
}

‎src/core/qgsexpressioncontext.cpp‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,41 @@ class GetNamedProjectColor : public QgsScopedExpressionFunction
609609

610610
};
611611

612+
class GetComposerItemVariables : public QgsScopedExpressionFunction
613+
{
614+
public:
615+
GetComposerItemVariables( const QgsComposition* c )
616+
: QgsScopedExpressionFunction( QStringLiteral( "item_variables" ), QgsExpression::ParameterList() << QgsExpression::Parameter( QStringLiteral( "id" ) ), QStringLiteral( "Composition" ) )
617+
, mComposition( c )
618+
{}
619+
620+
virtual QVariant func( const QVariantList& values, const QgsExpressionContext*, QgsExpression* ) override
621+
{
622+
if ( !mComposition )
623+
return QVariant();
624+
625+
QString id = values.at( 0 ).toString().toLower();
626+
627+
const QgsComposerItem* item = mComposition->getComposerItemById( id );
628+
if ( !item )
629+
return QVariant();
630+
631+
QgsExpressionContext c = item->createExpressionContext();
632+
633+
return c.variablesToMap();
634+
}
635+
636+
QgsScopedExpressionFunction* clone() const override
637+
{
638+
return new GetComposerItemVariables( mComposition );
639+
}
640+
641+
private:
642+
643+
const QgsComposition* mComposition;
644+
645+
};
646+
612647
///@endcond
613648

614649
QgsExpressionContextScope* QgsExpressionContextUtils::projectScope( const QgsProject* project )
@@ -818,6 +853,9 @@ QgsExpressionContextScope *QgsExpressionContextUtils::compositionScope( const Qg
818853
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layout_pagewidth" ), composition->paperWidth(), true ) );
819854
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layout_dpi" ), composition->printResolution(), true ) );
820855

856+
857+
scope->addFunction( QStringLiteral( "item_variables" ), new GetComposerItemVariables( composition ) );
858+
821859
return scope;
822860
}
823861

@@ -970,6 +1008,7 @@ QgsExpressionContext QgsExpressionContextUtils::createFeatureBasedContext( const
9701008
void QgsExpressionContextUtils::registerContextFunctions()
9711009
{
9721010
QgsExpression::registerFunction( new GetNamedProjectColor( nullptr ) );
1011+
QgsExpression::registerFunction( new GetComposerItemVariables( nullptr ) );
9731012
}
9741013

9751014
bool QgsScopedExpressionFunction::usesGeometry( const QgsExpression::NodeFunction* node ) const

‎src/core/qgsexpressioncontext.h‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,25 @@ class CORE_EXPORT QgsScopedExpressionFunction : public QgsExpression::Function
6363
, mReferencedColumns( referencedColumns )
6464
{}
6565

66+
/**
67+
* Create a new QgsScopedExpressionFunction using named parameters.
68+
*
69+
* @note Added in QGIS 3.0
70+
*/
71+
QgsScopedExpressionFunction( const QString& fnname,
72+
const QgsExpression::ParameterList& params,
73+
const QString& group,
74+
const QString& helpText = QString(),
75+
bool usesGeometry = false,
76+
const QSet<QString>& referencedColumns = QSet<QString>(),
77+
bool lazyEval = false,
78+
bool handlesNull = false,
79+
bool isContextual = true )
80+
: QgsExpression::Function( fnname, params, group, helpText, lazyEval, handlesNull, isContextual )
81+
, mUsesGeometry( usesGeometry )
82+
, mReferencedColumns( referencedColumns )
83+
{}
84+
6685
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) override = 0;
6786

6887
/** Returns a clone of the function.

‎tests/src/core/testqgscomposition.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include <QObject>
3232
#include "qgstest.h"
33+
#include "qgstestutils.h"
3334

3435
class TestQgsComposition : public QObject
3536
{
@@ -55,6 +56,7 @@ class TestQgsComposition : public QObject
5556
void resizeToContentsMultiPage();
5657
void georeference();
5758
void variablesEdited();
59+
void itemVariablesFunction();
5860

5961
private:
6062
QgsComposition *mComposition;
@@ -599,5 +601,31 @@ void TestQgsComposition::variablesEdited()
599601
QVERIFY( spyVariablesChanged.count() == 2 );
600602
}
601603

604+
void TestQgsComposition::itemVariablesFunction()
605+
{
606+
QgsRectangle extent( 2000, 2800, 2500, 2900 );
607+
QgsMapSettings ms;
608+
ms.setExtent( extent );
609+
QgsComposition* composition = new QgsComposition( ms, QgsProject::instance() );
610+
611+
QgsExpression e( "map_get( item_variables( 'map_id' ), 'map_scale' )" );
612+
// no map
613+
QgsExpressionContext c = composition->createExpressionContext();
614+
QVariant r = e.evaluate( &c );
615+
QVERIFY( !r.isValid() );
616+
617+
QgsComposerMap* map = new QgsComposerMap( composition );
618+
map->setNewExtent( extent );
619+
map->setSceneRect( QRectF( 30, 60, 200, 100 ) );
620+
composition->addComposerMap( map );
621+
map->setId( "map_id" );
622+
623+
c = composition->createExpressionContext();
624+
r = e.evaluate( &c );
625+
QGSCOMPARENEAR( r.toDouble(), 1.38916e+08, 100 );
626+
627+
delete composition;
628+
}
629+
602630
QGSTEST_MAIN( TestQgsComposition )
603631
#include "testqgscomposition.moc"

0 commit comments

Comments
 (0)