From 0003e91f279f35724d531d4933b0b2f4215cc974 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 11 Mar 2019 15:39:58 +1000 Subject: [PATCH] Add methods to resolve parameters to layouts/layout items --- .../processing/qgsprocessingalgorithm.sip.in | 24 +++++++ .../processing/qgsprocessingparameters.sip.in | 54 +++++++++++++++- .../processing/qgsprocessingalgorithm.cpp | 10 +++ src/core/processing/qgsprocessingalgorithm.h | 20 ++++++ .../processing/qgsprocessingparameters.cpp | 53 +++++++++++++++- src/core/processing/qgsprocessingparameters.h | 48 +++++++++++++- tests/src/analysis/CMakeLists.txt | 1 + tests/src/analysis/testqgsprocessing.cpp | 63 ++++++++++++++++++- 8 files changed, 265 insertions(+), 8 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in index 41a7d446f9e8..6bfdbdc52f0c 100644 --- a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in @@ -783,6 +783,30 @@ Evaluates the parameter with matching ``name`` to a range of values. QStringList parameterAsFields( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; %Docstring Evaluates the parameter with matching ``name`` to a list of fields. +%End + + QgsPrintLayout *parameterAsLayout( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ); +%Docstring +Evaluates the parameter with matching ``name`` to a print layout. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 +%End + + QgsLayoutItem *parameterAsLayoutItem( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context, QgsPrintLayout *layout ); +%Docstring +Evaluates the parameter with matching ``name`` to a print layout item, taken from the specified ``layout``. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 %End static QString invalidSourceError( const QVariantMap ¶meters, const QString &name ); diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index a65fd83df36d..31c0280bce25 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -937,6 +937,55 @@ Evaluates the parameter with matching ``definition`` and ``value`` to a list of .. versionadded:: 3.4 %End + static QgsPrintLayout *parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); +%Docstring +Evaluates the parameter with matching ``definition`` to a print layout. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 +%End + + static QgsPrintLayout *parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ); +%Docstring +Evaluates the parameter with matching ``definition`` and ``value`` to a print layout. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 +%End + + static QgsLayoutItem *parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context, QgsPrintLayout *layout ); +%Docstring +Evaluates the parameter with matching ``definition`` to a print layout item, taken from the specified ``layout``. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 +%End + + static QgsLayoutItem *parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context, QgsPrintLayout *layout ); +%Docstring +Evaluates the parameter with matching ``definition`` and ``value`` to a print layout, taken from the specified ``layout``. + +.. warning:: + + This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + +.. versionadded:: 3.8 +%End + + static QgsProcessingParameterDefinition *parameterFromVariantMap( const QVariantMap &map ) /Factory/; %Docstring Creates a new QgsProcessingParameterDefinition using the configuration from a @@ -2756,8 +2805,9 @@ class QgsProcessingParameterLayout : QgsProcessingParameterDefinition %Docstring A print layout parameter, allowing users to select a print layout. -QgsProcessingParameterLayout should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsString()` -This will return the name of the target print layout. +QgsProcessingParameterLayout should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsLayout()` +This will return the matching layout from the context's current project. Alternatively, calling +QgsProcessingAlgorithm.parameterAsString() will return the name of the target print layout. .. versionadded:: 3.8 %End diff --git a/src/core/processing/qgsprocessingalgorithm.cpp b/src/core/processing/qgsprocessingalgorithm.cpp index be066dbcb80b..db7d2aa2f1e8 100644 --- a/src/core/processing/qgsprocessingalgorithm.cpp +++ b/src/core/processing/qgsprocessingalgorithm.cpp @@ -691,6 +691,16 @@ QStringList QgsProcessingAlgorithm::parameterAsFields( const QVariantMap ¶me return QgsProcessingParameters::parameterAsFields( parameterDefinition( name ), parameters, context ); } +QgsPrintLayout *QgsProcessingAlgorithm::parameterAsLayout( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) +{ + return QgsProcessingParameters::parameterAsLayout( parameterDefinition( name ), parameters, context ); +} + +QgsLayoutItem *QgsProcessingAlgorithm::parameterAsLayoutItem( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context, QgsPrintLayout *layout ) +{ + return QgsProcessingParameters::parameterAsLayoutItem( parameterDefinition( name ), parameters, context, layout ); +} + QString QgsProcessingAlgorithm::invalidSourceError( const QVariantMap ¶meters, const QString &name ) { if ( !parameters.contains( name ) ) diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index dee8d778fa47..87d7a1bad657 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -780,6 +780,26 @@ class CORE_EXPORT QgsProcessingAlgorithm */ QStringList parameterAsFields( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; + /** + * Evaluates the parameter with matching \a name to a print layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + QgsPrintLayout *parameterAsLayout( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ); + + /** + * Evaluates the parameter with matching \a name to a print layout item, taken from the specified \a layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + QgsLayoutItem *parameterAsLayoutItem( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context, QgsPrintLayout *layout ); + /** * Returns a user-friendly string to use as an error when a source parameter could * not be loaded. diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index f90a7be9f283..abdbdb304e96 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -31,7 +31,8 @@ #include "qgsvectorlayer.h" #include "qgsmeshlayer.h" #include "qgsapplication.h" - +#include "qgslayoutmanager.h" +#include "qgsprintlayout.h" #include @@ -1464,6 +1465,56 @@ QStringList QgsProcessingParameters::parameterAsFields( const QgsProcessingParam return resultStringList; } +QgsPrintLayout *QgsProcessingParameters::parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ) +{ + if ( !definition ) + return nullptr; + + return parameterAsLayout( definition, parameters.value( definition->name() ), context ); +} + +QgsPrintLayout *QgsProcessingParameters::parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ) +{ + const QString layoutName = parameterAsString( definition, value, context ); + if ( layoutName.isEmpty() ) + return nullptr; + + if ( !context.project() ) + return nullptr; + + QgsMasterLayoutInterface *l = context.project()->layoutManager()->layoutByName( layoutName ); + if ( l && l->layoutType() == QgsMasterLayoutInterface::PrintLayout ) + return static_cast< QgsPrintLayout * >( l ); + else + return nullptr; +} + +QgsLayoutItem *QgsProcessingParameters::parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context, QgsPrintLayout *layout ) +{ + if ( !definition ) + return nullptr; + + return parameterAsLayoutItem( definition, parameters.value( definition->name() ), context, layout ); +} + +QgsLayoutItem *QgsProcessingParameters::parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context, QgsPrintLayout *layout ) +{ + if ( !layout ) + return nullptr; + + const QString id = parameterAsString( definition, value, context ); + if ( id.isEmpty() ) + return nullptr; + + // prefer matching by uuid, since it's gauranteed to be unique. + if ( QgsLayoutItem *item = layout->itemByUuid( id ) ) + return item; + else if ( QgsLayoutItem *item = layout->itemById( id ) ) + return item; + else + return nullptr; +} + QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromVariantMap( const QVariantMap &map ) { QString type = map.value( QStringLiteral( "parameter_type" ) ).toString(); diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 9aa4cb6007f7..b6c043da5962 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -38,6 +38,8 @@ class QgsProcessingFeatureSource; class QgsProcessingOutputDefinition; class QgsProcessingFeedback; class QgsProcessingProvider; +class QgsPrintLayout; +class QgsLayoutItem; /** * \class QgsProcessingFeatureSourceDefinition @@ -989,6 +991,47 @@ class CORE_EXPORT QgsProcessingParameters */ static QStringList parameterAsFields( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ); + /** + * Evaluates the parameter with matching \a definition to a print layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + static QgsPrintLayout *parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); + + /** + * Evaluates the parameter with matching \a definition and \a value to a print layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + static QgsPrintLayout *parameterAsLayout( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ); + + /** + * Evaluates the parameter with matching \a definition to a print layout item, taken from the specified \a layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + static QgsLayoutItem *parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context, QgsPrintLayout *layout ); + + /** + * Evaluates the parameter with matching \a definition and \a value to a print layout, taken from the specified \a layout. + * + * \warning This method is not safe to run in a background thread, so it must either be used within a prepareAlgorithm + * implementation (which runs in the main thread), or the algorithm must return the FlagNoThreading flag. + * + * \since QGIS 3.8 + */ + static QgsLayoutItem *parameterAsLayoutItem( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context, QgsPrintLayout *layout ); + + /** * Creates a new QgsProcessingParameterDefinition using the configuration from a * supplied variant \a map. @@ -2591,8 +2634,9 @@ class CORE_EXPORT QgsProcessingParameterBand : public QgsProcessingParameterDefi * \ingroup core * A print layout parameter, allowing users to select a print layout. * - * QgsProcessingParameterLayout should be evaluated by calling QgsProcessingAlgorithm::parameterAsString(). - * This will return the name of the target print layout. + * QgsProcessingParameterLayout should be evaluated by calling QgsProcessingAlgorithm::parameterAsLayout(). + * This will return the matching layout from the context's current project. Alternatively, calling + * QgsProcessingAlgorithm::parameterAsString() will return the name of the target print layout. * * \since QGIS 3.8 */ diff --git a/tests/src/analysis/CMakeLists.txt b/tests/src/analysis/CMakeLists.txt index 9f57c65a94c5..70c908c03033 100644 --- a/tests/src/analysis/CMakeLists.txt +++ b/tests/src/analysis/CMakeLists.txt @@ -11,6 +11,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/core/auth ${CMAKE_SOURCE_DIR}/src/core/expression ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/layout ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/metadata ${CMAKE_SOURCE_DIR}/src/core/processing diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index a10a64b4fcce..d1f1b2826f5a 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -41,7 +41,9 @@ #include "qgsmessagelog.h" #include "qgsvectorlayer.h" #include "qgsexpressioncontextutils.h" - +#include "qgsprintlayout.h" +#include "qgslayoutmanager.h" +#include "qgslayoutitemlabel.h" class DummyAlgorithm : public QgsProcessingAlgorithm { @@ -5816,6 +5818,14 @@ void TestQgsProcessing::parameterLayout() { QgsProcessingContext context; + QgsProject p; + QgsPrintLayout *l = new QgsPrintLayout( &p ); + l->setName( "l1" ); + QgsPrintLayout *l2 = new QgsPrintLayout( &p ); + l2->setName( "l2" ); + p.layoutManager()->addLayout( l ); + p.layoutManager()->addLayout( l2 ); + // not optional! std::unique_ptr< QgsProcessingParameterLayout > def( new QgsProcessingParameterLayout( "non_optional", QString(), QString(), false ) ); QVERIFY( def->checkValueIsAcceptable( 1 ) ); @@ -5827,6 +5837,16 @@ void TestQgsProcessing::parameterLayout() QVariantMap params; params.insert( "non_optional", QString( "abcdef" ) ); QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) ); + params.insert( "non_optional", QString( "l1" ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) ); + context.setProject( &p ); + params.insert( "non_optional", QString( "abcdef" ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) ); + params.insert( "non_optional", QString( "l1" ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayout( def.get(), params, context ), l ); + params.insert( "non_optional", QString( "l2" ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayout( def.get(), params, context ), l2 ); QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) ); @@ -5932,6 +5952,18 @@ void TestQgsProcessing::parameterLayoutItem() { QgsProcessingContext context; + QgsProject p; + QgsPrintLayout *l = new QgsPrintLayout( &p ); + l->setName( "l1" ); + QgsLayoutItemLabel *label1 = new QgsLayoutItemLabel( l ); + label1->setId( "a" ); + l->addLayoutItem( label1 ); + QgsLayoutItemLabel *label2 = new QgsLayoutItemLabel( l ); + label2->setId( "b" ); + l->addLayoutItem( label2 ); + + QgsPrintLayout *l2 = new QgsPrintLayout( &p ); + // not optional! std::unique_ptr< QgsProcessingParameterLayoutItem > def( new QgsProcessingParameterLayoutItem( "non_optional", QString(), QVariant(), QString(), -1, false ) ); QVERIFY( def->checkValueIsAcceptable( 1 ) ); @@ -5941,9 +5973,34 @@ void TestQgsProcessing::parameterLayoutItem() // string QVariantMap params; - params.insert( "non_optional", QString( "a" ) ); + params.insert( "non_optional", QString( "aaaa" ) ); QString f = QgsProcessingParameters::parameterAsString( def.get(), params, context ); - QCOMPARE( f, QStringLiteral( "a" ) ); + QCOMPARE( f, QStringLiteral( "aaaa" ) ); + + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ) ); + params.insert( "non_optional", label1->uuid() ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) ); + params.insert( "non_optional", QString( "abcdef" ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ) ); + params.insert( "non_optional", label1->uuid() ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) ); + QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l2 ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 ); + params.insert( "non_optional", label1->id() ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 ); + params.insert( "non_optional", label2->uuid() ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 ); + params.insert( "non_optional", label2->id() ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 ); + // UUID matching must take precedence + label1->setId( label2->uuid() ); + params.insert( "non_optional", label2->uuid() ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 ); + label2->setId( label1->uuid() ); + params.insert( "non_optional", label1->uuid() ); + QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 ); QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );