From a0e35a3893f44e8bfc19a12c835458c533217be5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 13 Aug 2015 09:42:51 +1000 Subject: [PATCH] Switch some composer expressions to contexts --- python/core/composer/qgscomposeritem.sip | 6 + python/core/qgsexpressioncontext.sip | 50 +++++++ src/app/composer/qgscomposeritemwidget.cpp | 13 ++ src/app/composer/qgscomposeritemwidget.h | 5 + src/app/composer/qgscompositionwidget.cpp | 14 ++ src/app/composer/qgscompositionwidget.h | 2 + src/core/composer/qgsatlascomposition.cpp | 40 ++++- src/core/composer/qgsatlascomposition.h | 5 +- src/core/composer/qgscomposeritem.cpp | 19 +++ src/core/composer/qgscomposeritem.h | 7 + src/core/composer/qgscomposerlabel.cpp | 23 ++- src/core/qgsexpressioncontext.cpp | 164 +++++++++++++++++++++ src/core/qgsexpressioncontext.h | 59 ++++++++ src/ui/qgscomposeritemwidgetbase.ui | 31 ++++ src/ui/qgscompositionwidgetbase.ui | 58 ++++++-- 15 files changed, 470 insertions(+), 26 deletions(-) diff --git a/python/core/composer/qgscomposeritem.sip b/python/core/composer/qgscomposeritem.sip index 9713e0fdbcb..b35c5cc6554 100644 --- a/python/core/composer/qgscomposeritem.sip +++ b/python/core/composer/qgscomposeritem.sip @@ -630,6 +630,12 @@ class QgsComposerItem : QgsComposerObject, QGraphicsRectItem */ void setCurrentExportLayer( const int layerIdx = -1 ); + /** Creates an expression context relating to the item's current state. The context includes + * scopes for global, project, composition, atlas and item properties. + * @note added in QGIS 2.12 + */ + QgsExpressionContext* createExpressionContext() const /Factory/; + public slots: /** Sets the item rotation * @deprecated Use setItemRotation( double rotation ) instead diff --git a/python/core/qgsexpressioncontext.sip b/python/core/qgsexpressioncontext.sip index 142bef735de..15b15817306 100644 --- a/python/core/qgsexpressioncontext.sip +++ b/python/core/qgsexpressioncontext.sip @@ -404,6 +404,56 @@ class QgsExpressionContextUtils */ static void setLayerVariables( QgsMapLayer* layer, const QgsStringMap variables ); + /** Creates a new scope which contains variables and functions relating to a QgsComposition. + * For instance, number of pages and page sizes. + * @param composition source composition + */ + static QgsExpressionContextScope* compositionScope( const QgsComposition *composition ) /Factory/; + + /** Sets a composition context variable. This variable will be contained within scopes retrieved via + * compositionScope(). + * @param composition target composition + * @param name variable name + * @param value variable value + * @see setCompositionVariables() + * @see compositionScope() + */ + static void setCompositionVariable( QgsComposition* composition, const QString& name, const QVariant& value ); + + /** Sets all composition context variables. Existing composition variables will be removed and replaced + * with the variables specified. + * @param composition target composition + * @param variables new set of layer variables + * @see setCompositionVariable() + * @see compositionScope() + */ + static void setCompositionVariables( QgsComposition* composition, const QgsStringMap variables ); + + /** Creates a new scope which contains variables and functions relating to a QgsComposerItem. + * For instance, item size and position. + * @param composerItem source composer item + */ + static QgsExpressionContextScope* composerItemScope( const QgsComposerItem *composerItem ) /Factory/; + + /** Sets a composer item context variable. This variable will be contained within scopes retrieved via + * composerItemScope(). + * @param composerItem target composer item + * @param name variable name + * @param value variable value + * @see setComposerItemVariables() + * @see composerItemScope() + */ + static void setComposerItemVariable( QgsComposerItem* composerItem, const QString& name, const QVariant& value ); + + /** Sets all composition context variables. Existing compositoin variables will be removed and replaced + * with the variables specified. + * @param composerItem target composer item + * @param variables new set of layer variables + * @see setComposerItemVariable() + * @see composerItemScope() + */ + static void setComposerItemVariables( QgsComposerItem* composerItem, const QgsStringMap variables ); + /** Registers all known core functions provided by QgsExpressionContextScope objects. */ static void registerContextFunctions(); diff --git a/src/app/composer/qgscomposeritemwidget.cpp b/src/app/composer/qgscomposeritemwidget.cpp index 2d88eaee48e..43a860746bf 100644 --- a/src/app/composer/qgscomposeritemwidget.cpp +++ b/src/app/composer/qgscomposeritemwidget.cpp @@ -22,6 +22,7 @@ #include "qgscomposition.h" #include "qgspoint.h" #include "qgsdatadefinedbutton.h" +#include "qgsexpressioncontext.h" #include #include @@ -140,6 +141,13 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* connect( mTransparencySlider, SIGNAL( valueChanged( int ) ), mTransparencySpnBx, SLOT( setValue( int ) ) ); + QgsExpressionContext* context = mItem->createExpressionContext(); + mVariableEditor->setContext( context ); + mVariableEditor->setEditableScopeIndex( context->scopeCount() - 1 ); + delete context; + + connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) ); + //connect atlas signals to data defined buttons QgsAtlasComposition* atlas = atlasComposition(); if ( atlas ) @@ -260,6 +268,11 @@ void QgsComposerItemWidget::changeItemPosition() mItem->endCommand(); } +void QgsComposerItemWidget::variablesChanged() +{ + QgsExpressionContextUtils::setComposerItemVariables( mItem, mVariableEditor->variablesInActiveScope() ); +} + QgsComposerItem::ItemPositionMode QgsComposerItemWidget::positionMode() const { if ( mUpperLeftCheckBox->checkState() == Qt::Checked ) diff --git a/src/app/composer/qgscomposeritemwidget.h b/src/app/composer/qgscomposeritemwidget.h index 0f2bbc3bc98..f5e884c9271 100644 --- a/src/app/composer/qgscomposeritemwidget.h +++ b/src/app/composer/qgscomposeritemwidget.h @@ -139,6 +139,11 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo // void changeItemTransparency( int value ); void changeItemPosition(); + private slots: + + void variablesChanged(); + + }; #endif //QGSCOMPOSERITEMWIDGET_H diff --git a/src/app/composer/qgscompositionwidget.cpp b/src/app/composer/qgscompositionwidget.cpp index 13c072ac7ae..2a810839478 100644 --- a/src/app/composer/qgscompositionwidget.cpp +++ b/src/app/composer/qgscompositionwidget.cpp @@ -22,6 +22,7 @@ #include "qgsstylev2.h" #include "qgssymbolv2selectordialog.h" #include "qgssymbollayerv2utils.h" +#include "qgsexpressioncontext.h" #include #include #include //for screen resolution @@ -44,6 +45,14 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c ) //read with/height from composition and find suitable entries to display displayCompositionWidthHeight(); + mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() ); + mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope() ); + mVariableEditor->context()->appendScope( QgsExpressionContextUtils::compositionScope( mComposition ) ); + mVariableEditor->reloadContext(); + mVariableEditor->setEditableScopeIndex( 2 ); + + connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) ); + if ( mComposition ) { mNumPagesSpinBox->setValue( mComposition->numPages() ); @@ -170,6 +179,11 @@ void QgsCompositionWidget::populateDataDefinedButtons() mPaperOrientationDDBtn->blockSignals( false ); } +void QgsCompositionWidget::variablesChanged() +{ + QgsExpressionContextUtils::setCompositionVariables( mComposition, mVariableEditor->variablesInActiveScope() ); +} + void QgsCompositionWidget::setDataDefinedProperty( const QgsDataDefinedButton* ddBtn, QgsComposerObject::DataDefinedProperty property ) { if ( !mComposition ) diff --git a/src/app/composer/qgscompositionwidget.h b/src/app/composer/qgscompositionwidget.h index 023a48bc2bd..368e9ecc193 100644 --- a/src/app/composer/qgscompositionwidget.h +++ b/src/app/composer/qgscompositionwidget.h @@ -83,6 +83,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase /** Initializes data defined buttons to current atlas coverage layer*/ void populateDataDefinedButtons(); + void variablesChanged(); + private: QgsComposition* mComposition; QMap mPaperMap; diff --git a/src/core/composer/qgsatlascomposition.cpp b/src/core/composer/qgsatlascomposition.cpp index ca6dc99db21..8fc05919ad8 100644 --- a/src/core/composer/qgsatlascomposition.cpp +++ b/src/core/composer/qgsatlascomposition.cpp @@ -27,6 +27,7 @@ #include "qgsmaplayerregistry.h" #include "qgsproject.h" #include "qgsmessagelog.h" +#include "qgsexpressioncontext.h" QgsAtlasComposition::QgsAtlasComposition( QgsComposition* composition ) : mComposition( composition ) @@ -218,6 +219,8 @@ int QgsAtlasComposition::updateFeatures() return 0; } + QgsExpressionContext expressionContext = createExpressionContext(); + updateFilenameExpression(); // select all features with all attributes @@ -248,7 +251,7 @@ int QgsAtlasComposition::updateFeatures() { nameExpression.reset( 0 ); } - nameExpression->prepare( mCoverageLayer->pendingFields() ); + nameExpression->prepare( &expressionContext ); } // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process @@ -260,10 +263,12 @@ int QgsAtlasComposition::updateFeatures() while ( fit.nextFeature( feat ) ) { + expressionContext.setFeature( feat ); + QString pageName; if ( !nameExpression.isNull() ) { - QVariant result = nameExpression->evaluate( &feat, mCoverageLayer->fields() ); + QVariant result = nameExpression->evaluate( &expressionContext ); if ( nameExpression->hasEvalError() ) { QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) ); @@ -446,13 +451,15 @@ bool QgsAtlasComposition::prepareForFeature( const int featureI, const bool upda // retrieve the next feature, based on its id mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature ); + QgsExpressionContext expressionContext = createExpressionContext(); + QgsExpression::setSpecialColumn( "$atlasfeatureid", mCurrentFeature.id() ); QgsExpression::setSpecialColumn( "$atlasgeometry", QVariant::fromValue( *mCurrentFeature.constGeometry() ) ); QgsExpression::setSpecialColumn( "$atlasfeature", QVariant::fromValue( mCurrentFeature ) ); QgsExpression::setSpecialColumn( "$feature", QVariant(( int )featureI + 1 ) ); // generate filename for current feature - if ( !evalFeatureFilename() ) + if ( !evalFeatureFilename( expressionContext ) ) { //error evaluating filename return false; @@ -820,6 +827,23 @@ bool QgsAtlasComposition::setFilenamePattern( const QString& pattern ) return updateFilenameExpression(); } +QgsExpressionContext QgsAtlasComposition::createExpressionContext() +{ + QgsExpressionContext expressionContext; + expressionContext << QgsExpressionContextUtils::globalScope() + << QgsExpressionContextUtils::projectScope(); + if ( mComposition ) + expressionContext << QgsExpressionContextUtils::compositionScope( mComposition ); + + expressionContext << new QgsExpressionContextScope( "Atlas" ); + if ( mCoverageLayer ) + expressionContext.lastScope()->setFields( mCoverageLayer->fields() ); + if ( mComposition->atlasMode() != QgsComposition::AtlasOff ) + expressionContext.lastScope()->setFeature( mCurrentFeature ); + + return expressionContext; +} + bool QgsAtlasComposition::updateFilenameExpression() { if ( !mCoverageLayer ) @@ -827,7 +851,7 @@ bool QgsAtlasComposition::updateFilenameExpression() return false; } - const QgsFields& fields = mCoverageLayer->fields(); + QgsExpressionContext expressionContext = createExpressionContext(); if ( mFilenamePattern.size() > 0 ) { @@ -841,23 +865,23 @@ bool QgsAtlasComposition::updateFilenameExpression() } // prepare the filename expression - mFilenameExpr->prepare( fields ); + mFilenameExpr->prepare( &expressionContext ); } //if atlas preview is currently enabled, regenerate filename for current feature if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas ) { - evalFeatureFilename(); + evalFeatureFilename( expressionContext ); } return true; } -bool QgsAtlasComposition::evalFeatureFilename() +bool QgsAtlasComposition::evalFeatureFilename( const QgsExpressionContext &context ) { //generate filename for current atlas feature if ( mFilenamePattern.size() > 0 && !mFilenameExpr.isNull() ) { - QVariant filenameRes = mFilenameExpr->evaluate( &mCurrentFeature, mCoverageLayer->fields() ); + QVariant filenameRes = mFilenameExpr->evaluate( &context ); if ( mFilenameExpr->hasEvalError() ) { QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpr->evalErrorString() ), tr( "Composer" ) ); diff --git a/src/core/composer/qgsatlascomposition.h b/src/core/composer/qgsatlascomposition.h index e104c2f3e84..fd98fc33604 100644 --- a/src/core/composer/qgsatlascomposition.h +++ b/src/core/composer/qgsatlascomposition.h @@ -29,6 +29,7 @@ class QgsComposerMap; class QgsComposition; class QgsVectorLayer; class QgsExpression; +class QgsExpressionContext; /** \ingroup MapComposer * Class used to render an Atlas, iterating over geometry features. @@ -338,7 +339,7 @@ class CORE_EXPORT QgsAtlasComposition : public QObject /** Evaluates filename for current feature * @returns true if feature filename was successfully evaluated */ - bool evalFeatureFilename(); + bool evalFeatureFilename( const QgsExpressionContext &context ); QgsComposition* mComposition; @@ -396,6 +397,8 @@ class CORE_EXPORT QgsAtlasComposition : public QObject //computes the extent of the current feature, in the crs of the specified map void computeExtent( QgsComposerMap *map ); + QgsExpressionContext createExpressionContext(); + //list of predefined scales QVector mPredefinedScales; }; diff --git a/src/core/composer/qgscomposeritem.cpp b/src/core/composer/qgscomposeritem.cpp index 003d9e01e6c..24d41cc6356 100644 --- a/src/core/composer/qgscomposeritem.cpp +++ b/src/core/composer/qgscomposeritem.cpp @@ -40,6 +40,7 @@ #include "qgslogger.h" #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance #include "qgsmaprenderer.h" //for getCompositionMode +#include "qgsexpressioncontext.h" #include @@ -846,6 +847,24 @@ bool QgsComposerItem::shouldDrawItem() const return !mEvaluatedExcludeFromExports; } +QgsExpressionContext* QgsComposerItem::createExpressionContext() const +{ + QgsExpressionContext* context = new QgsExpressionContext(); + context->appendScope( QgsExpressionContextUtils::globalScope() ); + context->appendScope( QgsExpressionContextUtils::projectScope() ); + if ( mComposition ) + { + context->appendScope( QgsExpressionContextUtils::compositionScope( mComposition ) ); + if ( mComposition->atlasComposition().enabled() ) + { + context->appendScope( QgsExpressionContextUtils::atlasScope( &mComposition->atlasComposition() ) ); + } + } + + context->appendScope( QgsExpressionContextUtils::composerItemScope( this ) ); + return context; +} + void QgsComposerItem::drawBackground( QPainter* p ) { if ( mBackground && p ) diff --git a/src/core/composer/qgscomposeritem.h b/src/core/composer/qgscomposeritem.h index ab82f67815b..312b2fc1756 100644 --- a/src/core/composer/qgscomposeritem.h +++ b/src/core/composer/qgscomposeritem.h @@ -31,6 +31,7 @@ class QGraphicsLineItem; class QgsComposerItemGroup; class QgsDataDefined; class QgsComposition; +class QgsExpressionContext; /** \ingroup MapComposer * A item that forms part of a map composition. @@ -582,6 +583,12 @@ class CORE_EXPORT QgsComposerItem: public QgsComposerObject, public QGraphicsRec */ virtual void setCurrentExportLayer( const int layerIdx = -1 ) { mCurrentExportLayer = layerIdx; } + /** Creates an expression context relating to the item's current state. The context includes + * scopes for global, project, composition, atlas and item properties. + * @note added in QGIS 2.12 + */ + QgsExpressionContext* createExpressionContext() const; + public slots: /** Sets the item rotation * @deprecated Use setItemRotation( double rotation ) instead diff --git a/src/core/composer/qgscomposerlabel.cpp b/src/core/composer/qgscomposerlabel.cpp index 897e3518080..1694a43109b 100644 --- a/src/core/composer/qgscomposerlabel.cpp +++ b/src/core/composer/qgscomposerlabel.cpp @@ -25,6 +25,7 @@ #include "qgsproject.h" #include "qgsdistancearea.h" #include "qgsfontutils.h" +#include "qgsexpressioncontext.h" #include "qgswebview.h" #include "qgswebframe.h" @@ -269,19 +270,16 @@ void QgsComposerLabel::refreshExpressionContext() if ( !mComposition ) return; + QgsVectorLayer* layer = 0; if ( mComposition->atlasComposition().enabled() ) { - mExpressionLayer = mComposition->atlasComposition().coverageLayer(); - if ( mComposition->atlasMode() != QgsComposition::AtlasOff ) - { - mExpressionFeature.reset( new QgsFeature( mComposition->atlasComposition().feature() ) ); - } + layer = mComposition->atlasComposition().coverageLayer(); } //setup distance area conversion - if ( mExpressionLayer ) + if ( layer ) { - mDistanceArea->setSourceCrs( mExpressionLayer->crs().srsid() ); + mDistanceArea->setSourceCrs( layer->crs().srsid() ); } else { @@ -300,7 +298,16 @@ QString QgsComposerLabel::displayText() const replaceDateText( displayText ); QMap subs = mSubstitutions; subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 ); - return QgsExpression::replaceExpressionText( displayText, mExpressionFeature.data(), mExpressionLayer, &subs, mDistanceArea ); + + QScopedPointer context( createExpressionContext() ); + //overwrite layer/feature if they have been set via setExpressionContext + //TODO remove when setExpressionContext is removed + if ( mExpressionFeature.data() ) + context->setFeature( *mExpressionFeature.data() ); + if ( mExpressionLayer ) + context->setFields( mExpressionLayer->fields() ); + + return QgsExpression::replaceExpressionText( displayText, context.data(), &subs, mDistanceArea ); } void QgsComposerLabel::replaceDateText( QString& text ) const diff --git a/src/core/qgsexpressioncontext.cpp b/src/core/qgsexpressioncontext.cpp index 0e7e10fe91a..1d17f865530 100644 --- a/src/core/qgsexpressioncontext.cpp +++ b/src/core/qgsexpressioncontext.cpp @@ -22,6 +22,9 @@ #include "qgsproject.h" #include "qgssymbollayerv2utils.h" #include "qgsgeometry.h" +#include "qgscomposition.h" +#include "qgscomposeritem.h" +#include "qgsatlascomposition.h" #include #include @@ -586,6 +589,167 @@ void QgsExpressionContextUtils::setLayerVariables( QgsMapLayer* layer, const Qgs layer->setCustomProperty( "variableValues", variableValues ); } +QgsExpressionContextScope *QgsExpressionContextUtils::compositionScope( const QgsComposition *composition ) +{ + QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Composition" ) ); + if ( !composition ) + return scope; + + //add variables defined in composition properties + QStringList variableNames = composition->customProperty( "variableNames" ).toStringList(); + QStringList variableValues = composition->customProperty( "variableValues" ).toStringList(); + + int varIndex = 0; + foreach ( QString variableName, variableNames ) + { + if ( varIndex >= variableValues.length() ) + { + break; + } + + QVariant varValue = variableValues.at( varIndex ); + varIndex++; + scope->setVariable( variableName, varValue ); + } + + //add known composition context variables + scope->addVariable( QgsExpressionContextScope::StaticVariable( "composer_numpages", composition->numPages(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "composer_page_height", composition->paperHeight(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "composer_page_width", composition->paperWidth(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "composer_dpi", composition->printResolution(), true ) ); + + return scope; +} + +void QgsExpressionContextUtils::setCompositionVariable( QgsComposition* composition, const QString& name, const QVariant& value ) +{ + if ( !composition ) + return; + + //write variable to composition + QStringList variableNames = composition->customProperty( "variableNames" ).toStringList(); + QStringList variableValues = composition->customProperty( "variableValues" ).toStringList(); + + variableNames << name; + variableValues << value.toString(); + + composition->setCustomProperty( "variableNames", variableNames ); + composition->setCustomProperty( "variableValues", variableValues ); +} + +void QgsExpressionContextUtils::setCompositionVariables( QgsComposition* composition, const QgsStringMap variables ) +{ + if ( !composition ) + return; + + QStringList variableNames; + QStringList variableValues; + + Q_FOREACH ( QString variable, variables.keys() ) + { + variableNames << variable; + variableValues << variables.value( variable ); + } + + composition->setCustomProperty( "variableNames", variableNames ); + composition->setCustomProperty( "variableValues", variableValues ); +} + +QgsExpressionContextScope* QgsExpressionContextUtils::atlasScope( const QgsAtlasComposition* atlas ) +{ + QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Atlas" ) ); + if ( !atlas ) + return scope; + + //add known atlas variables + scope->addVariable( QgsExpressionContextScope::StaticVariable( "atlas_numpages", atlas->numFeatures(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "atlas_page", atlas->currentFeatureNumber() + 1, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "atlas_filename", atlas->currentFilename(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "atlas_pagename", atlas->currentPageName(), true ) ); + + if ( atlas->enabled() && atlas->coverageLayer() ) + { + scope->setFields( atlas->coverageLayer()->fields() ); + } + + if ( atlas->enabled() ) + { + QgsFeature atlasFeature = atlas->feature(); + scope->setFeature( atlasFeature ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "atlas_featureid", atlasFeature.id(), true ) ); + } + + return scope; +} + +QgsExpressionContextScope *QgsExpressionContextUtils::composerItemScope( const QgsComposerItem *composerItem ) +{ + QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Composer Item" ) ); + if ( !composerItem ) + return scope; + + //add variables defined in composer item properties + QStringList variableNames = composerItem->customProperty( "variableNames" ).toStringList(); + QStringList variableValues = composerItem->customProperty( "variableValues" ).toStringList(); + + int varIndex = 0; + foreach ( QString variableName, variableNames ) + { + if ( varIndex >= variableValues.length() ) + { + break; + } + + QVariant varValue = variableValues.at( varIndex ); + varIndex++; + scope->setVariable( variableName, varValue ); + } + + //add known composer item context variables + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_id", composerItem->id(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_uuid", composerItem->uuid(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_left", composerItem->sceneBoundingRect().left(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_top", composerItem->sceneBoundingRect().top(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_width", composerItem->sceneBoundingRect().width(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( "item_height", composerItem->sceneBoundingRect().height(), true ) ); + + return scope; +} + +void QgsExpressionContextUtils::setComposerItemVariable( QgsComposerItem* composerItem, const QString& name, const QVariant& value ) +{ + if ( !composerItem ) + return; + + //write variable to composer item + QStringList variableNames = composerItem->customProperty( "variableNames" ).toStringList(); + QStringList variableValues = composerItem->customProperty( "variableValues" ).toStringList(); + + variableNames << name; + variableValues << value.toString(); + + composerItem->setCustomProperty( "variableNames", variableNames ); + composerItem->setCustomProperty( "variableValues", variableValues ); +} + +void QgsExpressionContextUtils::setComposerItemVariables( QgsComposerItem* composerItem, const QgsStringMap variables ) +{ + if ( !composerItem ) + return; + + QStringList variableNames; + QStringList variableValues; + + Q_FOREACH ( QString variable, variables.keys() ) + { + variableNames << variable; + variableValues << variables.value( variable ); + } + + composerItem->setCustomProperty( "variableNames", variableNames ); + composerItem->setCustomProperty( "variableValues", variableValues ); +} + QgsExpressionContext QgsExpressionContextUtils::createFeatureBasedContext( const QgsFeature &feature, const QgsFields &fields ) { QgsExpressionContextScope* scope = new QgsExpressionContextScope(); diff --git a/src/core/qgsexpressioncontext.h b/src/core/qgsexpressioncontext.h index 53d8d015327..a9c7b974442 100644 --- a/src/core/qgsexpressioncontext.h +++ b/src/core/qgsexpressioncontext.h @@ -24,6 +24,9 @@ class QgsExpression; class QgsMapLayer; +class QgsComposition; +class QgsComposerItem; +class QgsAtlasComposition; /** \ingroup core * \class QgsScopedExpressionFunction @@ -430,6 +433,62 @@ class CORE_EXPORT QgsExpressionContextUtils */ static void setLayerVariables( QgsMapLayer* layer, const QgsStringMap variables ); + /** Creates a new scope which contains variables and functions relating to a QgsComposition. + * For instance, number of pages and page sizes. + * @param composition source composition + */ + static QgsExpressionContextScope* compositionScope( const QgsComposition *composition ); + + /** Sets a composition context variable. This variable will be contained within scopes retrieved via + * compositionScope(). + * @param composition target composition + * @param name variable name + * @param value variable value + * @see setCompositionVariables() + * @see compositionScope() + */ + static void setCompositionVariable( QgsComposition* composition, const QString& name, const QVariant& value ); + + /** Sets all composition context variables. Existing composition variables will be removed and replaced + * with the variables specified. + * @param composition target composition + * @param variables new set of layer variables + * @see setCompositionVariable() + * @see compositionScope() + */ + static void setCompositionVariables( QgsComposition* composition, const QgsStringMap variables ); + + /** Creates a new scope which contains variables and functions relating to a QgsAtlasComposition. + * For instance, current page name and number. + * @param atlas source atlas + */ + static QgsExpressionContextScope* atlasScope( const QgsAtlasComposition* atlas ); + + /** Creates a new scope which contains variables and functions relating to a QgsComposerItem. + * For instance, item size and position. + * @param composerItem source composer item + */ + static QgsExpressionContextScope* composerItemScope( const QgsComposerItem *composerItem ); + + /** Sets a composer item context variable. This variable will be contained within scopes retrieved via + * composerItemScope(). + * @param composerItem target composer item + * @param name variable name + * @param value variable value + * @see setComposerItemVariables() + * @see composerItemScope() + */ + static void setComposerItemVariable( QgsComposerItem* composerItem, const QString& name, const QVariant& value ); + + /** Sets all composition context variables. Existing compositoin variables will be removed and replaced + * with the variables specified. + * @param composerItem target composer item + * @param variables new set of layer variables + * @see setComposerItemVariable() + * @see composerItemScope() + */ + static void setComposerItemVariables( QgsComposerItem* composerItem, const QgsStringMap variables ); + /** Helper function for creating an expression context which contains just a feature and fields * collection. Generally this method should not be used as the created context does not include * standard scopes such as the global and project scopes. diff --git a/src/ui/qgscomposeritemwidgetbase.ui b/src/ui/qgscomposeritemwidgetbase.ui index bcc6c014400..8d50fe4b467 100644 --- a/src/ui/qgscomposeritemwidgetbase.ui +++ b/src/ui/qgscomposeritemwidgetbase.ui @@ -703,6 +703,25 @@ + + + + Variables + + + + + + + 0 + 200 + + + + + + + @@ -759,6 +778,18 @@ QComboBox
qgsblendmodecombobox.h
+ + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
+ + QgsVariableEditorWidget + QWidget +
qgsvariableeditorwidget.h
+ 1 +
mGeneralOptionsGroupBox diff --git a/src/ui/qgscompositionwidgetbase.ui b/src/ui/qgscompositionwidgetbase.ui index 2529a32a83e..9b6603770ca 100644 --- a/src/ui/qgscompositionwidgetbase.ui +++ b/src/ui/qgscompositionwidgetbase.ui @@ -23,7 +23,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -50,9 +59,9 @@ 0 - 0 + -337 327 - 586 + 841 @@ -137,7 +146,7 @@ 99999.000000000000000 - + false @@ -180,7 +189,7 @@ 99999.000000000000000 - + false @@ -234,7 +243,7 @@ 1 - + false @@ -317,7 +326,7 @@ 3000 - + false @@ -406,7 +415,7 @@ 9999.000000000000000 - + false @@ -466,7 +475,7 @@ 200 - + false @@ -474,6 +483,25 @@ + + + + Variables + + + + + + + 0 + 200 + + + + + + + @@ -495,6 +523,18 @@ + + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
+ + QgsVariableEditorWidget + QWidget +
qgsvariableeditorwidget.h
+ 1 +
QgsDataDefinedButton QToolButton