diff --git a/python/core/qgsexpressioncontext.sip b/python/core/qgsexpressioncontext.sip index 5f67bb68ebe..f4c6dc68b01 100644 --- a/python/core/qgsexpressioncontext.sip +++ b/python/core/qgsexpressioncontext.sip @@ -76,7 +76,7 @@ class QgsExpressionContextScope * @param value initial variable value * @param readOnly true if variable should not be editable by users */ - StaticVariable( const QString &name = QString(), const QVariant &value = QVariant(), bool readOnly = false ); + StaticVariable( const QString &name = QString(), const QVariant &value = QVariant(), bool readOnly = false, bool isStatic = false ); /** Variable name */ QString name; @@ -86,6 +86,9 @@ class QgsExpressionContextScope /** True if variable should not be editable by users */ bool readOnly; + + //! A static variable can be cached for the lifetime of a context + bool isStatic; }; /** Constructor for QgsExpressionContextScope @@ -109,7 +112,7 @@ class QgsExpressionContextScope * @param value variable value * @see addVariable() */ - void setVariable( const QString &name, const QVariant &value ); + void setVariable( const QString &name, const QVariant &value, bool isStatic = false ); /** Adds a variable into the context scope. If a variable with the same name is already set then its * value is overwritten, otherwise a new variable is added to the scope. @@ -162,6 +165,14 @@ class QgsExpressionContextScope */ bool isReadOnly( const QString &name ) const; + /** + * Tests whether the variable with the specified \a name is static and can + * be cached. + * + * \note Added in QGIS 3.0 + */ + bool isStatic( const QString &name ) const; + /** Returns the count of variables contained within the scope. */ int variableCount() const; diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index c144b1d8ae0..4cfe3f6694f 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -4322,9 +4322,10 @@ const QList &QgsExpression::Functions() varFunction->setIsStaticFunction( []( const NodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) { - /* A variable node is static if it has a static name and the name is defined at prepare time - * (e.g. global or layer variables) - * It is not static if a variable is set during iteration (e.g. geom_part variable) + /* A variable node is static if it has a static name and the name can be found at prepare + * time and is tagged with isStatic. + * It is not static if a variable is set during iteration or not tagged isStatic. + * (e.g. geom_part variable) */ if ( node->args()->count() > 0 ) { @@ -4333,8 +4334,10 @@ const QList &QgsExpression::Functions() if ( !argNode->isStatic( parent, context ) ) return false; - if ( fcnGetVariable( QVariantList() << argNode->eval( parent, context ), context, parent ).isValid() ) - return true; + QString varName = argNode->eval( parent, context ).toString(); + + const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName ); + return scope ? scope->isStatic( varName ) : false; } return false; } diff --git a/src/core/qgsexpressioncontext.cpp b/src/core/qgsexpressioncontext.cpp index 07a9e4ad6a3..469bc27f57e 100644 --- a/src/core/qgsexpressioncontext.cpp +++ b/src/core/qgsexpressioncontext.cpp @@ -90,17 +90,18 @@ QgsExpressionContextScope::~QgsExpressionContextScope() qDeleteAll( mFunctions ); } -void QgsExpressionContextScope::setVariable( const QString &name, const QVariant &value ) +void QgsExpressionContextScope::setVariable( const QString &name, const QVariant &value, bool isStatic ) { if ( mVariables.contains( name ) ) { StaticVariable existing = mVariables.value( name ); existing.value = value; + existing.isStatic = isStatic; addVariable( existing ); } else { - addVariable( QgsExpressionContextScope::StaticVariable( name, value ) ); + addVariable( QgsExpressionContextScope::StaticVariable( name, value, false, isStatic ) ); } } @@ -179,6 +180,11 @@ bool QgsExpressionContextScope::isReadOnly( const QString &name ) const return hasVariable( name ) ? mVariables.value( name ).readOnly : false; } +bool QgsExpressionContextScope::isStatic( const QString &name ) const +{ + return hasVariable( name ) ? mVariables.value( name ).isStatic : false; +} + bool QgsExpressionContextScope::hasFunction( const QString &name ) const { return mFunctions.contains( name ); @@ -545,19 +551,19 @@ QgsExpressionContextScope *QgsExpressionContextUtils::globalScope() for ( QVariantMap::const_iterator it = customVariables.constBegin(); it != customVariables.constEnd(); ++it ) { - scope->setVariable( it.key(), it.value() ); + scope->setVariable( it.key(), it.value(), true ); } //add some extra global variables - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_version" ), Qgis::QGIS_VERSION, true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_version_no" ), Qgis::QGIS_VERSION_INT, true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_short_version" ), QStringLiteral( "%1.%2" ).arg( Qgis::QGIS_VERSION_INT / 10000 ).arg( Qgis::QGIS_VERSION_INT / 100 % 100 ), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_release_name" ), Qgis::QGIS_RELEASE_NAME, true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_platform" ), QgsApplication::platform(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_os_name" ), QgsApplication::osName(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_locale" ), QgsApplication::locale(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "user_account_name" ), QgsApplication::userLoginName(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "user_full_name" ), QgsApplication::userFullName(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_version" ), Qgis::QGIS_VERSION, true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_version_no" ), Qgis::QGIS_VERSION_INT, true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_short_version" ), QStringLiteral( "%1.%2" ).arg( Qgis::QGIS_VERSION_INT / 10000 ).arg( Qgis::QGIS_VERSION_INT / 100 % 100 ), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_release_name" ), Qgis::QGIS_RELEASE_NAME, true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_platform" ), QgsApplication::platform(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_os_name" ), QgsApplication::osName(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "qgis_locale" ), QgsApplication::locale(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "user_account_name" ), QgsApplication::userLoginName(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "user_full_name" ), QgsApplication::userFullName(), true, true ) ); return scope; } @@ -715,17 +721,17 @@ QgsExpressionContextScope *QgsExpressionContextUtils::projectScope( const QgsPro for ( ; it != vars.constEnd(); ++it ) { - scope->setVariable( it.key(), it.value() ); + scope->setVariable( it.key(), it.value(), true ); } //add other known project variables - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), project->title(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), project->fileInfo().filePath(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), project->fileInfo().dir().path(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), project->fileInfo().fileName(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), project->title(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), project->fileInfo().filePath(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), project->fileInfo().dir().path(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), project->fileInfo().fileName(), true, true ) ); QgsCoordinateReferenceSystem projectCrs = project->crs(); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj4(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj4(), true, true ) ); scope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( project ) ); return scope; @@ -772,14 +778,14 @@ QgsExpressionContextScope *QgsExpressionContextUtils::layerScope( const QgsMapLa QVariant varValue = variableValues.at( varIndex ); varIndex++; - scope->setVariable( variableName, varValue ); + scope->setVariable( variableName, varValue, true ); } - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_name" ), layer->name(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_id" ), layer->id(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer" ), QVariant::fromValue( QgsWeakMapLayerPointer( const_cast( layer ) ) ), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_name" ), layer->name(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_id" ), layer->id(), true, true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer" ), QVariant::fromValue( QgsWeakMapLayerPointer( const_cast( layer ) ) ), true, true ) ); - const QgsVectorLayer *vLayer = dynamic_cast< const QgsVectorLayer * >( layer ); + const QgsVectorLayer *vLayer = qobject_cast< const QgsVectorLayer * >( layer ); if ( vLayer ) { scope->setFields( vLayer->fields() ); diff --git a/src/core/qgsexpressioncontext.h b/src/core/qgsexpressioncontext.h index 78fcb012405..76dbb6eb606 100644 --- a/src/core/qgsexpressioncontext.h +++ b/src/core/qgsexpressioncontext.h @@ -127,10 +127,11 @@ class CORE_EXPORT QgsExpressionContextScope * \param value initial variable value * \param readOnly true if variable should not be editable by users */ - StaticVariable( const QString &name = QString(), const QVariant &value = QVariant(), bool readOnly = false ) + StaticVariable( const QString &name = QString(), const QVariant &value = QVariant(), bool readOnly = false, bool isStatic = false ) : name( name ) , value( value ) , readOnly( readOnly ) + , isStatic( isStatic ) {} //! Variable name @@ -141,6 +142,9 @@ class CORE_EXPORT QgsExpressionContextScope //! True if variable should not be editable by users bool readOnly; + + //! A static variable can be cached for the lifetime of a context + bool isStatic; }; /** Constructor for QgsExpressionContextScope @@ -166,7 +170,7 @@ class CORE_EXPORT QgsExpressionContextScope * \param value variable value * \see addVariable() */ - void setVariable( const QString &name, const QVariant &value ); + void setVariable( const QString &name, const QVariant &value, bool isStatic = false ); /** Adds a variable into the context scope. If a variable with the same name is already set then its * value is overwritten, otherwise a new variable is added to the scope. @@ -219,6 +223,14 @@ class CORE_EXPORT QgsExpressionContextScope */ bool isReadOnly( const QString &name ) const; + /** + * Tests whether the variable with the specified \a name is static and can + * be cached. + * + * \note Added in QGIS 3.0 + */ + bool isStatic( const QString &name ) const; + /** Returns the count of variables contained within the scope. */ int variableCount() const { return mVariables.count(); } diff --git a/tests/src/python/test_qgsatlascomposition.py b/tests/src/python/test_qgsatlascomposition.py index bf746b38572..193c1651c6b 100644 --- a/tests/src/python/test_qgsatlascomposition.py +++ b/tests/src/python/test_qgsatlascomposition.py @@ -122,7 +122,7 @@ class TestQgsAtlasComposition(unittest.TestCase): for i in range(0, self.mAtlas.numFeatures()): self.mAtlas.prepareForFeature(i) expected = "output_%d" % (i + 1) - assert self.mAtlas.currentFilename() == expected + self.assertEqual(self.mAtlas.currentFilename(), expected) self.mAtlas.endRender() def autoscale_render_test(self):