From b674a7c35d4cb42f40ab1c40b85fc1808eeeb285 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 9 Jan 2024 11:46:15 +1000 Subject: [PATCH] Add method to format processing output values as strings --- .../processing/qgsprocessingoutputs.sip.in | 27 +++ .../processing/qgsprocessingoutputs.sip.in | 27 +++ src/core/processing/qgsprocessingoutputs.cpp | 120 +++++++++++ src/core/processing/qgsprocessingoutputs.h | 23 +++ tests/src/analysis/testqgsprocessing.cpp | 191 ++++++++++++++++++ 5 files changed, 388 insertions(+) diff --git a/python/PyQt6/core/auto_generated/processing/qgsprocessingoutputs.sip.in b/python/PyQt6/core/auto_generated/processing/qgsprocessingoutputs.sip.in index 33bfd0409fc..02d014c5a9f 100644 --- a/python/PyQt6/core/auto_generated/processing/qgsprocessingoutputs.sip.in +++ b/python/PyQt6/core/auto_generated/processing/qgsprocessingoutputs.sip.in @@ -10,6 +10,8 @@ + + class QgsProcessingOutputDefinition { %Docstring(signature="appended") @@ -121,6 +123,19 @@ Returns ``True`` if the output was automatically created when adding a parameter .. seealso:: :py:func:`setAutoCreated` .. versionadded:: 3.14 +%End + + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; +%Docstring +Returns a string version of the parameter output ``value`` (if possible). + +:param value: value to convert +:param context: processing context + +:return: - value converted to string + - ok: will be set to ``True`` if value could be represented as a string. + +.. versionadded:: 3.36 %End protected: @@ -159,6 +174,7 @@ Returns the type name for the output class. virtual QString type() const; + }; class QgsProcessingOutputVectorLayer : QgsProcessingOutputDefinition @@ -284,6 +300,9 @@ Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputHtml : QgsProcessingOutputDefinition @@ -336,6 +355,9 @@ Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputNumber : QgsProcessingOutputDefinition @@ -361,6 +383,9 @@ Constructor for QgsProcessingOutputNumber. Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputString : QgsProcessingOutputDefinition @@ -411,6 +436,8 @@ Constructor for :py:class:`QgsProcessingOutputNumber`. Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + }; class QgsProcessingOutputFolder : QgsProcessingOutputDefinition diff --git a/python/core/auto_generated/processing/qgsprocessingoutputs.sip.in b/python/core/auto_generated/processing/qgsprocessingoutputs.sip.in index 33bfd0409fc..02d014c5a9f 100644 --- a/python/core/auto_generated/processing/qgsprocessingoutputs.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingoutputs.sip.in @@ -10,6 +10,8 @@ + + class QgsProcessingOutputDefinition { %Docstring(signature="appended") @@ -121,6 +123,19 @@ Returns ``True`` if the output was automatically created when adding a parameter .. seealso:: :py:func:`setAutoCreated` .. versionadded:: 3.14 +%End + + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; +%Docstring +Returns a string version of the parameter output ``value`` (if possible). + +:param value: value to convert +:param context: processing context + +:return: - value converted to string + - ok: will be set to ``True`` if value could be represented as a string. + +.. versionadded:: 3.36 %End protected: @@ -159,6 +174,7 @@ Returns the type name for the output class. virtual QString type() const; + }; class QgsProcessingOutputVectorLayer : QgsProcessingOutputDefinition @@ -284,6 +300,9 @@ Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputHtml : QgsProcessingOutputDefinition @@ -336,6 +355,9 @@ Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputNumber : QgsProcessingOutputDefinition @@ -361,6 +383,9 @@ Constructor for QgsProcessingOutputNumber. Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + + }; class QgsProcessingOutputString : QgsProcessingOutputDefinition @@ -411,6 +436,8 @@ Constructor for :py:class:`QgsProcessingOutputNumber`. Returns the type name for the output class. %End virtual QString type() const; + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; + }; class QgsProcessingOutputFolder : QgsProcessingOutputDefinition diff --git a/src/core/processing/qgsprocessingoutputs.cpp b/src/core/processing/qgsprocessingoutputs.cpp index d12d2cb329a..0ca41aeb870 100644 --- a/src/core/processing/qgsprocessingoutputs.cpp +++ b/src/core/processing/qgsprocessingoutputs.cpp @@ -16,12 +16,30 @@ ***************************************************************************/ #include "qgsprocessingoutputs.h" +#include "qgsvariantutils.h" QgsProcessingOutputDefinition::QgsProcessingOutputDefinition( const QString &name, const QString &description ) : mName( name ) , mDescription( description ) {} +QString QgsProcessingOutputDefinition::valueAsString( const QVariant &value, QgsProcessingContext &, bool &ok ) const +{ + if ( QgsVariantUtils::isNull( value ) ) + { + ok = true; + return QObject::tr( "NULL" ); + } + + if ( value.type() == QVariant::String ) + { + ok = true; + return value.toString(); + } + ok = false; + return QString(); +} + QgsProcessingOutputVectorLayer::QgsProcessingOutputVectorLayer( const QString &name, const QString &description, QgsProcessing::SourceType type ) : QgsProcessingOutputDefinition( name, description ) , mDataType( type ) @@ -57,6 +75,24 @@ QgsProcessingOutputNumber::QgsProcessingOutputNumber( const QString &name, const : QgsProcessingOutputDefinition( name, description ) {} +QString QgsProcessingOutputNumber::valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = false; + switch ( value.type() ) + { + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + ok = true; + return value.toString(); + default: + break; + } + return QgsProcessingOutputDefinition::valueAsString( value, context, ok ); +} + QgsProcessingOutputString::QgsProcessingOutputString( const QString &name, const QString &description ) : QgsProcessingOutputDefinition( name, description ) {} @@ -65,6 +101,17 @@ QgsProcessingOutputBoolean::QgsProcessingOutputBoolean( const QString &name, con : QgsProcessingOutputDefinition( name, description ) {} +QString QgsProcessingOutputBoolean::valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = false; + if ( value.type() == QVariant::Bool ) + { + ok = true; + return value.toBool() ? QObject::tr( "True" ) : QObject::tr( "False" ); + } + return QgsProcessingOutputDefinition::valueAsString( value, context, ok ); +} + QgsProcessingOutputFolder::QgsProcessingOutputFolder( const QString &name, const QString &description ) : QgsProcessingOutputDefinition( name, description ) {} @@ -91,6 +138,37 @@ QString QgsProcessingOutputMultipleLayers::type() const return typeName(); } +QString QgsProcessingOutputMultipleLayers::valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = false; + switch ( value.type() ) + { + case QVariant::List: + { + ok = true; + const QVariantList list = value.toList(); + + QStringList layerNames; + for ( const QVariant &v : list ) + { + layerNames << v.toString(); + } + return layerNames.join( QStringLiteral( ", " ) ); + } + + case QVariant::StringList: + { + ok = true; + const QStringList list = value.toStringList(); + return list.join( QStringLiteral( ", " ) ); + } + + default: + break; + } + return QgsProcessingOutputDefinition::valueAsString( value, context, ok ); +} + QgsProcessingOutputConditionalBranch::QgsProcessingOutputConditionalBranch( const QString &name, const QString &description ) : QgsProcessingOutputDefinition( name, description ) {} @@ -105,3 +183,45 @@ QString QgsProcessingOutputVariant::type() const { return typeName(); } + +QString QgsProcessingOutputVariant::valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = false; + switch ( value.type() ) + { + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + ok = true; + return value.toString(); + case QVariant::Bool: + ok = true; + return value.toBool() ? QObject::tr( "True" ) : QObject::tr( "False" ); + + case QVariant::List: + { + ok = true; + const QVariantList list = value.toList(); + + QStringList names; + for ( const QVariant &v : list ) + { + names << v.toString(); + } + return names.join( QStringLiteral( ", " ) ); + } + + case QVariant::StringList: + { + ok = true; + const QStringList list = value.toStringList(); + return list.join( QStringLiteral( ", " ) ); + } + + default: + break; + } + return QgsProcessingOutputDefinition::valueAsString( value, context, ok ); +} diff --git a/src/core/processing/qgsprocessingoutputs.h b/src/core/processing/qgsprocessingoutputs.h index 7a91b2bf285..f79e8319020 100644 --- a/src/core/processing/qgsprocessingoutputs.h +++ b/src/core/processing/qgsprocessingoutputs.h @@ -21,6 +21,9 @@ #include "qgis_core.h" #include "qgis.h" #include "qgsprocessing.h" + +class QgsProcessingContext; + // // Output definitions // @@ -132,6 +135,18 @@ class CORE_EXPORT QgsProcessingOutputDefinition */ bool autoCreated() const { return mAutoCreated; } + /** + * Returns a string version of the parameter output \a value (if possible). + * + * \param value value to convert + * \param context processing context + * \param ok will be set to TRUE if value could be represented as a string. + * \returns value converted to string + * + * \since QGIS 3.36 + */ + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + protected: //! Output name @@ -171,6 +186,7 @@ class CORE_EXPORT QgsProcessingOutputMapLayer : public QgsProcessingOutputDefini static QString typeName() { return QStringLiteral( "outputLayer" ); } QString type() const override; + }; /** @@ -282,6 +298,8 @@ class CORE_EXPORT QgsProcessingOutputMultipleLayers : public QgsProcessingOutput */ static QString typeName() { return QStringLiteral( "outputMultilayer" ); } QString type() const override; + QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const override; + }; /** @@ -327,6 +345,8 @@ class CORE_EXPORT QgsProcessingOutputVariant : public QgsProcessingOutputDefinit */ static QString typeName() { return QStringLiteral( "outputVariant" ); } QString type() const override; + QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const override; + }; /** @@ -349,6 +369,8 @@ class CORE_EXPORT QgsProcessingOutputNumber : public QgsProcessingOutputDefiniti */ static QString typeName() { return QStringLiteral( "outputNumber" ); } QString type() const override { return typeName(); } + QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const override; + }; /** @@ -393,6 +415,7 @@ class CORE_EXPORT QgsProcessingOutputBoolean : public QgsProcessingOutputDefinit */ static QString typeName() { return QStringLiteral( "outputBoolean" ); } QString type() const override { return typeName(); } + QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const override; }; /** diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index f962d0a0bac..aead5cf5c4d 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -818,6 +818,7 @@ class TestQgsProcessing: public QgsTest void formatHelp(); void preprocessParameters(); void guiDefaultParameterValues(); + void testOutputs(); private: @@ -12782,5 +12783,195 @@ void TestQgsProcessing::guiDefaultParameterValues() s.remove( QStringLiteral( "/Processing/DefaultGuiParam/testAlgorithm/testStringParameter" ) ); } +void TestQgsProcessing::testOutputs() +{ + QgsProcessingContext context; + bool ok = false; + QgsProcessingOutputVectorLayer outputVectorLayer( QStringLiteral( "vl" ) ); + QCOMPARE( outputVectorLayer.valueAsString( QStringLiteral( "/home/test/test.shp" ), context, ok ), + QStringLiteral( "/home/test/test.shp" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVectorLayer.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputRasterLayer outputRasterLayer( QStringLiteral( "rl" ) ); + QCOMPARE( outputRasterLayer.valueAsString( QStringLiteral( "/home/test/test.tiff" ), context, ok ), + QStringLiteral( "/home/test/test.tiff" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputRasterLayer.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputPointCloudLayer outputPointCloudLayer( QStringLiteral( "pcl" ) ); + QCOMPARE( outputPointCloudLayer.valueAsString( QStringLiteral( "/home/test/test.laz" ), context, ok ), + QStringLiteral( "/home/test/test.laz" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputPointCloudLayer.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputVectorTileLayer outputVectorTileLayer( QStringLiteral( "vtl" ) ); + QCOMPARE( outputVectorTileLayer.valueAsString( QStringLiteral( "/home/test/test.mbtiles" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVectorTileLayer.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputMapLayer outputMapLayer( QStringLiteral( "ml" ) ); + QCOMPARE( outputMapLayer.valueAsString( QStringLiteral( "/home/test/test.mbtiles" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputMapLayer.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputMultipleLayers outputMultipleLayers( QStringLiteral( "ml" ) ); + QCOMPARE( outputMultipleLayers.valueAsString( QStringLiteral( "/home/test/test.mbtiles" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputMultipleLayers.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + QCOMPARE( outputMultipleLayers.valueAsString( QStringList() << QStringLiteral( "/home/test/test.mbtiles" ) + << QStringLiteral( "/home/test/test.shp" ) + << QStringLiteral( "/home/test/test.tif" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles, /home/test/test.shp, /home/test/test.tif" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputMultipleLayers.valueAsString( QVariantList() << QStringLiteral( "/home/test/test.mbtiles" ) + << QStringLiteral( "/home/test/test.shp" ) + << QStringLiteral( "/home/test/test.tif" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles, /home/test/test.shp, /home/test/test.tif" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputHtml outputHtml( QStringLiteral( "html" ) ); + QCOMPARE( outputHtml.valueAsString( QStringLiteral( "/home/test/test.html" ), context, ok ), + QStringLiteral( "/home/test/test.html" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputHtml.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputFile outputFile( QStringLiteral( "file" ) ); + QCOMPARE( outputFile.valueAsString( QStringLiteral( "/home/test/test.txt" ), context, ok ), + QStringLiteral( "/home/test/test.txt" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputFile.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputFolder outputFolder( QStringLiteral( "folder" ) ); + QCOMPARE( outputFolder.valueAsString( QStringLiteral( "/home/test" ), context, ok ), + QStringLiteral( "/home/test" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputFolder.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputNumber outputNumber( QStringLiteral( "number" ) ); + QCOMPARE( outputNumber.valueAsString( QStringLiteral( "xxx" ), context, ok ), + QStringLiteral( "xxx" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputNumber.valueAsString( 5, context, ok ), + QStringLiteral( "5" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputNumber.valueAsString( 5.5, context, ok ), + QStringLiteral( "5.5" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputNumber.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputString outputString( QStringLiteral( "string" ) ); + QCOMPARE( outputString.valueAsString( QStringLiteral( "lalala" ), context, ok ), + QStringLiteral( "lalala" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputString.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputBoolean outputBoolean( QStringLiteral( "string" ) ); + QCOMPARE( outputBoolean.valueAsString( QStringLiteral( "lalala" ), context, ok ), + QStringLiteral( "lalala" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputBoolean.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputBoolean.valueAsString( QVariant( true ), context, ok ), + QStringLiteral( "True" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputBoolean.valueAsString( QVariant( false ), context, ok ), + QStringLiteral( "False" ) ); + QVERIFY( ok ); + ok = false; + + QgsProcessingOutputVariant outputVariant( QStringLiteral( "variant" ) ); + QCOMPARE( outputVariant.valueAsString( QStringLiteral( "lalala" ), context, ok ), + QStringLiteral( "lalala" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( QVariant(), context, ok ), + QStringLiteral( "NULL" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( QVariant( true ), context, ok ), + QStringLiteral( "True" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( QVariant( false ), context, ok ), + QStringLiteral( "False" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( 5, context, ok ), + QStringLiteral( "5" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( 5.5, context, ok ), + QStringLiteral( "5.5" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( QStringList() << QStringLiteral( "/home/test/test.mbtiles" ) + << QStringLiteral( "/home/test/test.shp" ) + << QStringLiteral( "/home/test/test.tif" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles, /home/test/test.shp, /home/test/test.tif" ) ); + QVERIFY( ok ); + ok = false; + QCOMPARE( outputVariant.valueAsString( QVariantList() << QStringLiteral( "/home/test/test.mbtiles" ) + << QStringLiteral( "/home/test/test.shp" ) + << QStringLiteral( "/home/test/test.tif" ), context, ok ), + QStringLiteral( "/home/test/test.mbtiles, /home/test/test.shp, /home/test/test.tif" ) ); + QVERIFY( ok ); + ok = false; +} + QGSTEST_MAIN( TestQgsProcessing ) #include "testqgsprocessing.moc"