Skip to content
Permalink
Browse files

Merge pull request #3968 from nyalldawson/item_vars

[FEATURE] item_variables expression function inside compositions
  • Loading branch information
nyalldawson committed Jan 11, 2017
2 parents a7806d1 + db1009c commit 3e2b4fc421ca418ae908e60d39beaa736485f2c2
@@ -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;
@@ -247,6 +262,13 @@ class QgsExpressionContext
*/
QVariant variable( const QString& name ) const;

/**
* Returns a map of variable name to value representing all the expression variables
* contained by the context.
* @note added in QGIS 3.0
*/
QVariantMap variablesToMap() const;

/** Returns true if the specified variable name is intended to be highlighted to the
* user. This is used by the expression builder to more prominently display the
* variable.
@@ -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"}
]
}
@@ -1834,6 +1834,13 @@ QgsExpressionContext QgsComposerMap::createExpressionContext() const
QgsGeometry centerPoint = QgsGeometry::fromPoint( extent.center() );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_center" ), QVariant::fromValue( centerPoint ), true ) );

if ( mComposition )
{
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mComposition->mapSettings().destinationCrs().authid(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mComposition->mapSettings().destinationCrs().toProj4(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mComposition->mapSettings().mapUnits() ), true ) );
}

context.appendScope( scope );

return context;
@@ -5825,6 +5825,9 @@ void QgsExpression::initVariableHelp()
gVariableHelpTexts.insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
gVariableHelpTexts.insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
gVariableHelpTexts.insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
gVariableHelpTexts.insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
gVariableHelpTexts.insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (full definition)." ) );
gVariableHelpTexts.insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );

gVariableHelpTexts.insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
gVariableHelpTexts.insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
@@ -274,6 +274,17 @@ QVariant QgsExpressionContext::variable( const QString& name ) const
return scope ? scope->variable( name ) : QVariant();
}

QVariantMap QgsExpressionContext::variablesToMap() const
{
QStringList names = variableNames();
QVariantMap m;
Q_FOREACH ( const QString& name, names )
{
m.insert( name, variable( name ) );
}
return m;
}

bool QgsExpressionContext::isHighlightedVariable( const QString &name ) const
{
return mHighlightedVariables.contains( name );
@@ -598,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 )
@@ -757,6 +803,10 @@ QgsExpressionContextScope* QgsExpressionContextUtils::mapSettingsScope( const Qg
QgsGeometry centerPoint = QgsGeometry::fromPoint( mapSettings.visibleExtent().center() );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_center" ), QVariant::fromValue( centerPoint ), true ) );

scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mapSettings.destinationCrs().authid(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mapSettings.destinationCrs().toProj4(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mapSettings.mapUnits() ), true ) );

return scope;
}

@@ -807,6 +857,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;
}

@@ -959,6 +1012,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.
@@ -303,6 +322,13 @@ class CORE_EXPORT QgsExpressionContext
*/
QVariant variable( const QString& name ) const;

/**
* Returns a map of variable name to value representing all the expression variables
* contained by the context.
* @note added in QGIS 3.0
*/
QVariantMap variablesToMap() const;

/** Returns true if the specified variable name is intended to be highlighted to the
* user. This is used by the expression builder to more prominently display the
* variable.
@@ -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,43 @@ 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 );

QgsExpression e2( "map_get( item_variables( 'map_id' ), 'map_crs' )" );
r = e2.evaluate( &c );
QCOMPARE( r.toString(), QString( "EPSG:4326" ) );

QgsExpression e3( "map_get( item_variables( 'map_id' ), 'map_crs_definition' )" );
r = e3.evaluate( &c );
QCOMPARE( r.toString(), QString( "+proj=longlat +datum=WGS84 +no_defs" ) );

QgsExpression e4( "map_get( item_variables( 'map_id' ), 'map_units' )" );
r = e4.evaluate( &c );
QCOMPARE( r.toString(), QString( "degrees" ) );

delete composition;
}

QGSTEST_MAIN( TestQgsComposition )
#include "testqgscomposition.moc"
@@ -51,6 +51,8 @@ class TestQgsExpressionContext : public QObject

void cache();

void valuesAsMap();

private:

class GetTestValueFunction : public QgsScopedExpressionFunction
@@ -688,5 +690,35 @@ void TestQgsExpressionContext::cache()
QVERIFY( !c.cachedValue( "test" ).isValid() );
}

void TestQgsExpressionContext::valuesAsMap()
{
QgsExpressionContext context;

//test retrieving from empty context
QVERIFY( context.variablesToMap().isEmpty() );

//add a scope to the context
QgsExpressionContextScope* s1 = new QgsExpressionContextScope();
s1->setVariable( "v1", "t1" );
s1->setVariable( "v2", "t2" );
context << s1;

QVariantMap m = context.variablesToMap();
QCOMPARE( m.size(), 2 );
QCOMPARE( m.value( "v1" ).toString(), QString( "t1" ) );
QCOMPARE( m.value( "v2" ).toString(), QString( "t2" ) );

QgsExpressionContextScope* s2 = new QgsExpressionContextScope();
s2->setVariable( "v2", "t2a" );
s2->setVariable( "v3", "t3" );
context << s2;

m = context.variablesToMap();
QCOMPARE( m.size(), 3 );
QCOMPARE( m.value( "v1" ).toString(), QString( "t1" ) );
QCOMPARE( m.value( "v2" ).toString(), QString( "t2a" ) );
QCOMPARE( m.value( "v3" ).toString(), QString( "t3" ) );
}

QGSTEST_MAIN( TestQgsExpressionContext )
#include "testqgsexpressioncontext.moc"

0 comments on commit 3e2b4fc

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