Skip to content

Commit

Permalink
[layouts] Add API to set expression variables at a multiframe level
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 27, 2019
1 parent c79eb1d commit dd62ec7
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 0 deletions.
Expand Up @@ -283,6 +283,42 @@ with the ``variables`` specified.
.. seealso:: :py:func:`layoutItemScope`

.. versionadded:: 3.0
%End

static QgsExpressionContextScope *multiFrameScope( const QgsLayoutMultiFrame *frame ) /Factory/;
%Docstring
Creates a new scope which contains variables and functions relating to a :py:class:`QgsLayoutMultiFrame`.

.. seealso:: :py:func:`setLayoutMultiFrameVariable`

.. seealso:: :py:func:`setLayoutMultiFrameVariables`

.. versionadded:: 3.10
%End

static void setLayoutMultiFrameVariable( QgsLayoutMultiFrame *frame, const QString &name, const QVariant &value );
%Docstring
Sets a layout multi ``frame`` context variable, with the given ``name`` and ``value``.
This variable will be contained within scopes retrieved via
multiFrameScope().

.. seealso:: :py:func:`setLayoutItemVariables`

.. seealso:: :py:func:`multiFrameScope`

.. versionadded:: 3.10
%End

static void setLayoutMultiFrameVariables( QgsLayoutMultiFrame *frame, const QVariantMap &variables );
%Docstring
Sets all layout multiframe context variables for an ``frame``. Existing variables will be removed and replaced
with the ``variables`` specified.

.. seealso:: :py:func:`setLayoutMultiFrameVariable`

.. seealso:: :py:func:`multiFrameScope`

.. versionadded:: 3.10
%End

static QgsExpressionContext createFeatureBasedContext( const QgsFeature &feature, const QgsFields &fields );
Expand Down
3 changes: 3 additions & 0 deletions python/core/auto_generated/layout/qgslayoutmultiframe.sip.in
Expand Up @@ -278,6 +278,9 @@ Returns the multiframe display name.
virtual QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = 0 ) /Factory/;


virtual QgsExpressionContext createExpressionContext() const;


void beginCommand( const QString &commandText, UndoCommand command = UndoNone );
%Docstring
Starts new undo command for this item.
Expand Down
62 changes: 62 additions & 0 deletions src/core/expression/qgsexpressioncontextutils.cpp
Expand Up @@ -28,6 +28,7 @@
#include "qgsexpressionutils.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutatlas.h"
#include "qgslayoutmultiframe.h"

QgsExpressionContextScope *QgsExpressionContextUtils::globalScope()
{
Expand Down Expand Up @@ -733,6 +734,67 @@ void QgsExpressionContextUtils::setLayoutItemVariables( QgsLayoutItem *item, con
item->setCustomProperty( QStringLiteral( "variableValues" ), variableValues );
}

QgsExpressionContextScope *QgsExpressionContextUtils::multiFrameScope( const QgsLayoutMultiFrame *frame )
{
QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Multiframe Item" ) );
if ( !frame )
return scope;

//add variables defined in layout item properties
const QStringList variableNames = frame->customProperty( QStringLiteral( "variableNames" ) ).toStringList();
const QStringList variableValues = frame->customProperty( QStringLiteral( "variableValues" ) ).toStringList();

int varIndex = 0;
for ( const QString &variableName : variableNames )
{
if ( varIndex >= variableValues.length() )
{
break;
}

QVariant varValue = variableValues.at( varIndex );
varIndex++;
scope->setVariable( variableName, varValue );
}

return scope;
}

void QgsExpressionContextUtils::setLayoutMultiFrameVariable( QgsLayoutMultiFrame *frame, const QString &name, const QVariant &value )
{
if ( !frame )
return;

//write variable to layout multiframe
QStringList variableNames = frame->customProperty( QStringLiteral( "variableNames" ) ).toStringList();
QStringList variableValues = frame->customProperty( QStringLiteral( "variableValues" ) ).toStringList();

variableNames << name;
variableValues << value.toString();

frame->setCustomProperty( QStringLiteral( "variableNames" ), variableNames );
frame->setCustomProperty( QStringLiteral( "variableValues" ), variableValues );
}

void QgsExpressionContextUtils::setLayoutMultiFrameVariables( QgsLayoutMultiFrame *frame, const QVariantMap &variables )
{
if ( !frame )
return;

QStringList variableNames;
QStringList variableValues;

QVariantMap::const_iterator it = variables.constBegin();
for ( ; it != variables.constEnd(); ++it )
{
variableNames << it.key();
variableValues << it.value().toString();
}

frame->setCustomProperty( QStringLiteral( "variableNames" ), variableNames );
frame->setCustomProperty( QStringLiteral( "variableValues" ), variableValues );
}

QgsExpressionContext QgsExpressionContextUtils::createFeatureBasedContext( const QgsFeature &feature, const QgsFields &fields )
{
QgsExpressionContextScope *scope = new QgsExpressionContextScope();
Expand Down
28 changes: 28 additions & 0 deletions src/core/expression/qgsexpressioncontextutils.h
Expand Up @@ -32,6 +32,7 @@ class QgsLayoutItem;
class QgsProcessingAlgorithm;
class QgsProcessingModelAlgorithm;
class QgsProcessingContext;
class QgsLayoutMultiFrame;

/**
* \ingroup core
Expand Down Expand Up @@ -251,6 +252,33 @@ class CORE_EXPORT QgsExpressionContextUtils
*/
static void setLayoutItemVariables( QgsLayoutItem *item, const QVariantMap &variables );

/**
* Creates a new scope which contains variables and functions relating to a QgsLayoutMultiFrame.
* \see setLayoutMultiFrameVariable()
* \see setLayoutMultiFrameVariables()
* \since QGIS 3.10
*/
static QgsExpressionContextScope *multiFrameScope( const QgsLayoutMultiFrame *frame ) SIP_FACTORY;

/**
* Sets a layout multi \a frame context variable, with the given \a name and \a value.
* This variable will be contained within scopes retrieved via
* multiFrameScope().
* \see setLayoutItemVariables()
* \see multiFrameScope()
* \since QGIS 3.10
*/
static void setLayoutMultiFrameVariable( QgsLayoutMultiFrame *frame, const QString &name, const QVariant &value );

/**
* Sets all layout multiframe context variables for an \a frame. Existing variables will be removed and replaced
* with the \a variables specified.
* \see setLayoutMultiFrameVariable()
* \see multiFrameScope()
* \since QGIS 3.10
*/
static void setLayoutMultiFrameVariables( QgsLayoutMultiFrame *frame, const QVariantMap &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
Expand Down
8 changes: 8 additions & 0 deletions src/core/layout/qgslayoutmultiframe.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgslayout.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutundostack.h"
#include "qgsexpressioncontextutils.h"
#include <QtCore>

QgsLayoutMultiFrame::QgsLayoutMultiFrame( QgsLayout *layout )
Expand Down Expand Up @@ -279,6 +280,13 @@ QgsAbstractLayoutUndoCommand *QgsLayoutMultiFrame::createCommand( const QString
return new QgsLayoutMultiFrameUndoCommand( this, text, id, parent );
}

QgsExpressionContext QgsLayoutMultiFrame::createExpressionContext() const
{
QgsExpressionContext context = QgsLayoutObject::createExpressionContext();
context.appendScope( QgsExpressionContextUtils::multiFrameScope( this ) );
return context;
}

void QgsLayoutMultiFrame::beginCommand( const QString &commandText, QgsLayoutMultiFrame::UndoCommand command )
{
if ( !mLayout )
Expand Down
2 changes: 2 additions & 0 deletions src/core/layout/qgslayoutmultiframe.h
Expand Up @@ -288,6 +288,8 @@ class CORE_EXPORT QgsLayoutMultiFrame: public QgsLayoutObject, public QgsLayoutU

QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = nullptr ) override SIP_FACTORY;

QgsExpressionContext createExpressionContext() const override;

/**
* Starts new undo command for this item.
* The \a commandText should be a capitalized, imperative tense description (e.g. "Add Map Item").
Expand Down
27 changes: 27 additions & 0 deletions tests/src/core/testqgslayoutmultiframe.cpp
Expand Up @@ -26,6 +26,7 @@
#include "qgslayoutpagecollection.h"
#include "qgslayoutundostack.h"
#include "qgsreadwritecontext.h"
#include "qgsexpressioncontextutils.h"

#include <QObject>
#include "qgstest.h"
Expand All @@ -51,6 +52,7 @@ class TestQgsLayoutMultiFrame : public QObject
void deleteFrame();
void writeReadXml();
void noPageNoCrash();
void variables();

private:
QgsLayout *mLayout = nullptr;
Expand Down Expand Up @@ -651,5 +653,30 @@ void TestQgsLayoutMultiFrame::noPageNoCrash()
QCOMPARE( html->frameCount(), 1 );
}

void TestQgsLayoutMultiFrame::variables()
{
QgsLayout l( QgsProject::instance() );

QgsLayoutItemHtml *html = new QgsLayoutItemHtml( &l );
std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::multiFrameScope( html ) );
int before = scope->variableCount();

QgsExpressionContextUtils::setLayoutMultiFrameVariable( html, QStringLiteral( "var" ), 5 );
scope.reset( QgsExpressionContextUtils::multiFrameScope( html ) );
QCOMPARE( scope->variableCount(), before + 1 );
QCOMPARE( scope->variable( QStringLiteral( "var" ) ).toInt(), 5 );

QVariantMap vars;
vars.insert( QStringLiteral( "var2" ), 7 );
QgsExpressionContextUtils::setLayoutMultiFrameVariables( html, vars );
scope.reset( QgsExpressionContextUtils::multiFrameScope( html ) );
QCOMPARE( scope->variableCount(), before + 1 );
QVERIFY( !scope->hasVariable( QStringLiteral( "var" ) ) );
QCOMPARE( scope->variable( QStringLiteral( "var2" ) ).toInt(), 7 );

QgsExpressionContext context = html->createExpressionContext();
QCOMPARE( context.variable( QStringLiteral( "var2" ) ).toInt(), 7 );
}

QGSTEST_MAIN( TestQgsLayoutMultiFrame )
#include "testqgslayoutmultiframe.moc"

0 comments on commit dd62ec7

Please sign in to comment.