[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'))
This commit is contained in:
Nyall Dawson 2017-01-09 13:51:53 +10:00
parent d25fcec03d
commit f2032ea268
5 changed files with 109 additions and 0 deletions

View File

@ -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;

View File

@ -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"}
]
}

View File

@ -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

View File

@ -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.

View File

@ -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"