Ensure atlas expression evaluation has access to coverage layer scope

Fixes #31807
This commit is contained in:
Nyall Dawson 2019-09-17 15:01:14 +10:00
parent 421e68cf5a
commit 3de0593957
4 changed files with 35 additions and 6 deletions

View File

@ -8,7 +8,7 @@
class QgsLayoutAtlas : QObject, QgsAbstractLayoutIterator, QgsLayoutSerializableObject class QgsLayoutAtlas : QObject, QgsAbstractLayoutIterator, QgsLayoutSerializableObject, QgsExpressionContextGenerator
{ {
%Docstring %Docstring
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector layer. Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector layer.
@ -277,6 +277,9 @@ number of matching features.
Returns the current feature number, where a value of 0 corresponds to the first feature. Returns the current feature number, where a value of 0 corresponds to the first feature.
%End %End
virtual QgsExpressionContext createExpressionContext() const;
public slots: public slots:
virtual bool next(); virtual bool next();

View File

@ -439,7 +439,7 @@ QString QgsLayoutAtlas::currentFilename() const
return mCurrentFilename; return mCurrentFilename;
} }
QgsExpressionContext QgsLayoutAtlas::createExpressionContext() QgsExpressionContext QgsLayoutAtlas::createExpressionContext() const
{ {
QgsExpressionContext expressionContext; QgsExpressionContext expressionContext;
expressionContext << QgsExpressionContextUtils::globalScope(); expressionContext << QgsExpressionContextUtils::globalScope();
@ -447,10 +447,10 @@ QgsExpressionContext QgsLayoutAtlas::createExpressionContext()
expressionContext << QgsExpressionContextUtils::projectScope( mLayout->project() ) expressionContext << QgsExpressionContextUtils::projectScope( mLayout->project() )
<< QgsExpressionContextUtils::layoutScope( mLayout ); << QgsExpressionContextUtils::layoutScope( mLayout );
expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( this ) ); expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( const_cast< QgsLayoutAtlas * >( this ) ) );
if ( mCoverageLayer ) if ( mCoverageLayer )
expressionContext.lastScope()->setFields( mCoverageLayer->fields() ); expressionContext.appendScope( mCoverageLayer->createExpressionContextScope() );
if ( mLayout && mEnabled ) if ( mLayout && mEnabled )
expressionContext.lastScope()->setFeature( mCurrentFeature ); expressionContext.lastScope()->setFeature( mCurrentFeature );

View File

@ -20,6 +20,7 @@
#include "qgsvectorlayerref.h" #include "qgsvectorlayerref.h"
#include "qgslayoutserializableobject.h" #include "qgslayoutserializableobject.h"
#include "qgsabstractlayoutiterator.h" #include "qgsabstractlayoutiterator.h"
#include "qgsexpressioncontextgenerator.h"
#include <QObject> #include <QObject>
class QgsLayout; class QgsLayout;
@ -37,7 +38,7 @@ class QgsLayout;
* *
* \since QGIS 3.0 * \since QGIS 3.0
*/ */
class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutIterator, public QgsLayoutSerializableObject class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutIterator, public QgsLayoutSerializableObject, public QgsExpressionContextGenerator
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -252,6 +253,8 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutItera
*/ */
int currentFeatureNumber() const { return mCurrentFeatureNo; } int currentFeatureNumber() const { return mCurrentFeatureNo; }
QgsExpressionContext createExpressionContext() const override;
public slots: public slots:
bool next() override; bool next() override;
@ -390,7 +393,6 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutItera
int mCurrentFeatureNo = -1; int mCurrentFeatureNo = -1;
QgsFeature mCurrentFeature; QgsFeature mCurrentFeature;
QgsExpressionContext createExpressionContext();
friend class AtlasFeatureSorter; friend class AtlasFeatureSorter;
}; };

View File

@ -67,6 +67,8 @@ class TestQgsLayoutAtlas : public QObject
// test removing coverage layer while atlas is enabled // test removing coverage layer while atlas is enabled
void test_remove_layer(); void test_remove_layer();
void context();
private: private:
QgsPrintLayout *mLayout = nullptr; QgsPrintLayout *mLayout = nullptr;
QgsLayoutItemLabel *mLabel1 = nullptr; QgsLayoutItemLabel *mLabel1 = nullptr;
@ -426,5 +428,27 @@ void TestQgsLayoutAtlas::test_remove_layer()
QVERIFY( spyToggled.count() == 1 ); QVERIFY( spyToggled.count() == 1 );
} }
void TestQgsLayoutAtlas::context()
{
std::unique_ptr< QgsVectorLayer> vl2( new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=id:integer&field=labelx:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
QgsFeature f;
QVERIFY( vl2->dataProvider()->addFeature( f ) );
QgsFeature f2;
QVERIFY( vl2->dataProvider()->addFeature( f2 ) );
mAtlas->setCoverageLayer( vl2.get() );
mAtlas->setEnabled( true );
QgsExpressionContext context = mAtlas->createExpressionContext();
QVERIFY( context.hasVariable( QStringLiteral( "project_title" ) ) );
QVERIFY( context.hasVariable( QStringLiteral( "layout_name" ) ) );
QVERIFY( context.hasVariable( QStringLiteral( "atlas_totalfeatures" ) ) );
QVERIFY( context.hasVariable( QStringLiteral( "layer_id" ) ) );
QCOMPARE( context.fields().at( 1 ).name(), QStringLiteral( "labelx" ) );
QVERIFY( context.hasFeature() );
mAtlas->setCoverageLayer( nullptr );
}
QGSTEST_MAIN( TestQgsLayoutAtlas ) QGSTEST_MAIN( TestQgsLayoutAtlas )
#include "testqgslayoutatlas.moc" #include "testqgslayoutatlas.moc"