Skip to content
Permalink
Browse files

[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'))
  • Loading branch information
nyalldawson committed Jan 11, 2017
1 parent d25fcec commit f2032ea2688715163a631cc0860b9e5f97257cfc
@@ -22,6 +22,21 @@ class QgsScopedExpressionFunction : QgsExpression::Function
bool handlesNull = false,
bool isContextual = true );

/**
* Create a new QgsScopedExpressionFunction using named parameters.
*
* @note Added in QGIS 3.0
*/
QgsScopedExpressionFunction( const QString& fnname,
const QgsExpression::ParameterList& params,
const QString& group,
const QString& helpText = QString(),
bool usesGeometry = false,
const QSet<QString>& referencedColumns = QSet<QString>(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = true );

virtual ~QgsScopedExpressionFunction();

virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) = 0;
@@ -0,0 +1,8 @@
{
"name": "item_variables",
"type": "function",
"description": "Returns a map of variables from a composer item inside this composition.",
"arguments": [ {"arg":"id","description":"composer item ID"}],
"examples": [ { "expression":"map_get(item_variables('main_map'), 'map_scale')", "returns":"2000"}
]
}
@@ -609,6 +609,41 @@ class GetNamedProjectColor : public QgsScopedExpressionFunction

};

class GetComposerItemVariables : public QgsScopedExpressionFunction
{
public:
GetComposerItemVariables( const QgsComposition* c )
: QgsScopedExpressionFunction( QStringLiteral( "item_variables" ), QgsExpression::ParameterList() << QgsExpression::Parameter( QStringLiteral( "id" ) ), QStringLiteral( "Composition" ) )
, mComposition( c )
{}

virtual QVariant func( const QVariantList& values, const QgsExpressionContext*, QgsExpression* ) override
{
if ( !mComposition )
return QVariant();

QString id = values.at( 0 ).toString().toLower();

const QgsComposerItem* item = mComposition->getComposerItemById( id );
if ( !item )
return QVariant();

QgsExpressionContext c = item->createExpressionContext();

return c.variablesToMap();
}

QgsScopedExpressionFunction* clone() const override
{
return new GetComposerItemVariables( mComposition );
}

private:

const QgsComposition* mComposition;

};

///@endcond

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


scope->addFunction( QStringLiteral( "item_variables" ), new GetComposerItemVariables( composition ) );

return scope;
}

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

bool QgsScopedExpressionFunction::usesGeometry( const QgsExpression::NodeFunction* node ) const
@@ -63,6 +63,25 @@ class CORE_EXPORT QgsScopedExpressionFunction : public QgsExpression::Function
, mReferencedColumns( referencedColumns )
{}

/**
* Create a new QgsScopedExpressionFunction using named parameters.
*
* @note Added in QGIS 3.0
*/
QgsScopedExpressionFunction( const QString& fnname,
const QgsExpression::ParameterList& params,
const QString& group,
const QString& helpText = QString(),
bool usesGeometry = false,
const QSet<QString>& referencedColumns = QSet<QString>(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = true )
: QgsExpression::Function( fnname, params, group, helpText, lazyEval, handlesNull, isContextual )
, mUsesGeometry( usesGeometry )
, mReferencedColumns( referencedColumns )
{}

virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) override = 0;

/** Returns a clone of the function.
@@ -30,6 +30,7 @@

#include <QObject>
#include "qgstest.h"
#include "qgstestutils.h"

class TestQgsComposition : public QObject
{
@@ -55,6 +56,7 @@ class TestQgsComposition : public QObject
void resizeToContentsMultiPage();
void georeference();
void variablesEdited();
void itemVariablesFunction();

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

void TestQgsComposition::itemVariablesFunction()
{
QgsRectangle extent( 2000, 2800, 2500, 2900 );
QgsMapSettings ms;
ms.setExtent( extent );
QgsComposition* composition = new QgsComposition( ms, QgsProject::instance() );

QgsExpression e( "map_get( item_variables( 'map_id' ), 'map_scale' )" );
// no map
QgsExpressionContext c = composition->createExpressionContext();
QVariant r = e.evaluate( &c );
QVERIFY( !r.isValid() );

QgsComposerMap* map = new QgsComposerMap( composition );
map->setNewExtent( extent );
map->setSceneRect( QRectF( 30, 60, 200, 100 ) );
composition->addComposerMap( map );
map->setId( "map_id" );

c = composition->createExpressionContext();
r = e.evaluate( &c );
QGSCOMPARENEAR( r.toDouble(), 1.38916e+08, 100 );

delete composition;
}

QGSTEST_MAIN( TestQgsComposition )
#include "testqgscomposition.moc"

0 comments on commit f2032ea

Please sign in to comment.
You can’t perform that action at this time.