Skip to content

Commit

Permalink
Switch some composer expressions to contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Aug 22, 2015
1 parent f9a7820 commit a0e35a3
Show file tree
Hide file tree
Showing 15 changed files with 470 additions and 26 deletions.
6 changes: 6 additions & 0 deletions python/core/composer/qgscomposeritem.sip
Expand Up @@ -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
Expand Down
50 changes: 50 additions & 0 deletions python/core/qgsexpressioncontext.sip
Expand Up @@ -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();
Expand Down
13 changes: 13 additions & 0 deletions src/app/composer/qgscomposeritemwidget.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgscomposition.h"
#include "qgspoint.h"
#include "qgsdatadefinedbutton.h"
#include "qgsexpressioncontext.h"
#include <QColorDialog>
#include <QPen>

Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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 )
Expand Down
5 changes: 5 additions & 0 deletions src/app/composer/qgscomposeritemwidget.h
Expand Up @@ -139,6 +139,11 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo
// void changeItemTransparency( int value );
void changeItemPosition();

private slots:

void variablesChanged();


};

#endif //QGSCOMPOSERITEMWIDGET_H
14 changes: 14 additions & 0 deletions src/app/composer/qgscompositionwidget.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgsstylev2.h"
#include "qgssymbolv2selectordialog.h"
#include "qgssymbollayerv2utils.h"
#include "qgsexpressioncontext.h"
#include <QColorDialog>
#include <QWidget>
#include <QPrinter> //for screen resolution
Expand All @@ -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() );
Expand Down Expand Up @@ -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 )
Expand Down
2 changes: 2 additions & 0 deletions src/app/composer/qgscompositionwidget.h
Expand Up @@ -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<QString, QgsCompositionPaper> mPaperMap;
Expand Down
40 changes: 32 additions & 8 deletions src/core/composer/qgsatlascomposition.cpp
Expand Up @@ -27,6 +27,7 @@
#include "qgsmaplayerregistry.h"
#include "qgsproject.h"
#include "qgsmessagelog.h"
#include "qgsexpressioncontext.h"

QgsAtlasComposition::QgsAtlasComposition( QgsComposition* composition )
: mComposition( composition )
Expand Down Expand Up @@ -218,6 +219,8 @@ int QgsAtlasComposition::updateFeatures()
return 0;
}

QgsExpressionContext expressionContext = createExpressionContext();

updateFilenameExpression();

// select all features with all attributes
Expand Down Expand Up @@ -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
Expand All @@ -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" ) );
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -820,14 +827,31 @@ 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 )
{
return false;
}

const QgsFields& fields = mCoverageLayer->fields();
QgsExpressionContext expressionContext = createExpressionContext();

if ( mFilenamePattern.size() > 0 )
{
Expand All @@ -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" ) );
Expand Down
5 changes: 4 additions & 1 deletion src/core/composer/qgsatlascomposition.h
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<qreal> mPredefinedScales;
};
Expand Down
19 changes: 19 additions & 0 deletions src/core/composer/qgscomposeritem.cpp
Expand Up @@ -40,6 +40,7 @@
#include "qgslogger.h"
#include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
#include "qgsmaprenderer.h" //for getCompositionMode
#include "qgsexpressioncontext.h"

#include <cmath>

Expand Down Expand Up @@ -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 )
Expand Down
7 changes: 7 additions & 0 deletions src/core/composer/qgscomposeritem.h
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit a0e35a3

Please sign in to comment.