From 12b46709ade3eeb982f650a2ed2a1634a1d3a0d6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 10:50:01 +1000 Subject: [PATCH 01/11] Add api to convert a processing parameter value to a value which is compatible with storage in a JSON object --- .../processing/qgsprocessingparameters.sip.in | 15 + .../processing/qgsprocessingparameters.cpp | 200 +++++++++++ src/core/processing/qgsprocessingparameters.h | 14 + tests/src/analysis/testqgsprocessing.cpp | 313 +++++++++++++++++- 4 files changed, 540 insertions(+), 2 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index 964834ba89a3..8531162e5f5f 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -515,6 +515,21 @@ layers and other factors within the context. %Docstring Returns a string version of the parameter input ``value``, which is suitable for use as an input parameter value when running an algorithm directly from a Python command. + +.. seealso:: :py:func:`valueAsJsonObject` +%End + + virtual QVariant valueAsJsonObject( const QVariant &value, QgsProcessingContext &context ) const; +%Docstring +Returns a version of the parameter input ``value``, which is suitable for use in a JSON object. + +This method must return only simple values which can be losslessly encapsulated in a serialized +JSON map. For instance, and QGIS class values (such as :py:class:`QgsCoordinateReferenceSystem`) must be +converted to a simple string or numeric value equivalent. + +.. seealso:: :py:func:`valueAsPythonString` + +.. versionadded:: 3.24 %End virtual QString valueAsPythonComment( const QVariant &value, QgsProcessingContext &context ) const; diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 5a39c3c37438..a16ae4e9a64c 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2449,6 +2449,206 @@ QString QgsProcessingParameterDefinition::valueAsPythonString( const QVariant &v return QgsProcessingUtils::stringToPythonLiteral( value.toString() ); } +QVariant QgsProcessingParameterDefinition::valueAsJsonObject( const QVariant &value, QgsProcessingContext &context ) const +{ + if ( !value.isValid() ) + return value; + + // dive into map and list types and convert each value + if ( value.type() == QVariant::Type::Map ) + { + const QVariantMap sourceMap = value.toMap(); + QVariantMap resultMap; + for ( auto it = sourceMap.constBegin(); it != sourceMap.constEnd(); it++ ) + { + resultMap[ it.key() ] = valueAsJsonObject( it.value(), context ); + } + return resultMap; + } + else if ( value.type() == QVariant::Type::List || value.type() == QVariant::Type::StringList ) + { + const QVariantList sourceList = value.toList(); + QVariantList resultList; + resultList.reserve( sourceList.size() ); + for ( const QVariant &v : sourceList ) + { + resultList.push_back( valueAsJsonObject( v, context ) ); + } + return resultList; + } + else + { + switch ( value.userType() ) + { + // simple types which can be directly represented in JSON -- not that strings are NOT handled here yet! + case QMetaType::Bool: + case QMetaType::Char: + case QMetaType::Int: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::UShort: + return value; + + default: + break; + } + + + if ( value.userType() == QMetaType::type( "QgsProperty" ) ) + { + const QgsProperty prop = value.value< QgsProperty >(); + switch ( prop.propertyType() ) + { + case QgsProperty::InvalidProperty: + return QVariant(); + case QgsProperty::StaticProperty: + return valueAsJsonObject( prop.staticValue(), context ); + + // these are not supported for serialization + case QgsProperty::FieldBasedProperty: + case QgsProperty::ExpressionBasedProperty: + QgsDebugMsg( QStringLiteral( "could not convert expression/field based property to JSON object" ) ); + return QVariant(); + } + } + + // value may be a CRS + if ( value.userType() == QMetaType::type( "QgsCoordinateReferenceSystem" ) ) + { + const QgsCoordinateReferenceSystem crs = value.value< QgsCoordinateReferenceSystem >(); + if ( !crs.isValid() ) + return QString(); + else if ( !crs.authid().isEmpty() ) + return crs.authid(); + else + return crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ); + } + else if ( value.userType() == QMetaType::type( "QgsRectangle" ) ) + { + const QgsRectangle r = value.value(); + return QStringLiteral( "%1, %3, %2, %4" ).arg( qgsDoubleToString( r.xMinimum() ), + qgsDoubleToString( r.yMinimum() ), + qgsDoubleToString( r.xMaximum() ), + qgsDoubleToString( r.yMaximum() ) ); + } + else if ( value.userType() == QMetaType::type( "QgsReferencedRectangle" ) ) + { + const QgsReferencedRectangle r = value.value(); + return QStringLiteral( "%1, %3, %2, %4 [%5]" ).arg( qgsDoubleToString( r.xMinimum() ), + qgsDoubleToString( r.yMinimum() ), + qgsDoubleToString( r.xMaximum() ), + qgsDoubleToString( r.yMaximum() ), r.crs().authid() ); + } + else if ( value.userType() == QMetaType::type( "QgsGeometry" ) ) + { + const QgsGeometry g = value.value(); + if ( !g.isNull() ) + { + return g.asWkt(); + } + else + { + return QString(); + } + } + else if ( value.userType() == QMetaType::type( "QgsReferencedGeometry" ) ) + { + const QgsReferencedGeometry g = value.value(); + if ( !g.isNull() ) + { + if ( !g.crs().isValid() ) + return g.asWkt(); + else + return QStringLiteral( "CRS=%1;%2" ).arg( g.crs().authid().isEmpty() ? g.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : g.crs().authid(), g.asWkt() ); + } + else + { + return QString(); + } + } + else if ( value.userType() == QMetaType::type( "QgsPointXY" ) ) + { + const QgsPointXY r = value.value(); + return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( r.x() ), + qgsDoubleToString( r.y() ) ); + } + else if ( value.userType() == QMetaType::type( "QgsReferencedPointXY" ) ) + { + const QgsReferencedPointXY r = value.value(); + return QStringLiteral( "%1,%2 [%3]" ).arg( qgsDoubleToString( r.x() ), + qgsDoubleToString( r.y() ), + r.crs().authid() ); + } + else if ( value.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) ) + { + const QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast( value ); + + // TODO -- we could consider also serializating the additional properties like invalid feature handling, limits, etc + return valueAsJsonObject( fromVar.source, context ); + } + else if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) ) + { + const QgsProcessingOutputLayerDefinition fromVar = qvariant_cast( value ); + return valueAsJsonObject( fromVar.sink, context ); + } + else if ( value.userType() == QMetaType::type( "QColor" ) ) + { + const QColor fromVar = value.value< QColor >(); + if ( !fromVar.isValid() ) + return QString(); + + return QStringLiteral( "rgba( %1, %2, %3, %4 )" ).arg( fromVar.red() ).arg( fromVar.green() ).arg( fromVar.blue() ).arg( QString::number( fromVar.alphaF(), 'f', 2 ) ); + } + else if ( value.userType() == QMetaType::type( "QDateTime" ) ) + { + const QDateTime fromVar = value.toDateTime(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + else if ( value.userType() == QMetaType::type( "QDate" ) ) + { + const QDate fromVar = value.toDate(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + else if ( value.userType() == QMetaType::type( "QTime" ) ) + { + const QTime fromVar = value.toTime(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + + // value may be a map layer + QVariantMap p; + p.insert( name(), value ); + if ( QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context ) ) + { + const QString source = QgsProcessingUtils::normalizeLayerSource( layer->source() ); + if ( !source.isEmpty() ) + return source; + return layer->id(); + } + + // now we handle strings, after any other specific logic has already been applied + if ( value.userType() == QMetaType::QString ) + return value; + } + + // unhandled type + Q_ASSERT_X( false, "QgsProcessingParameterDefinition::valueAsJsonObject", QStringLiteral( "unsupported variant type %1" ).arg( QMetaType::typeName( value.userType() ) ).toLocal8Bit() ); + return value; +} + QString QgsProcessingParameterDefinition::valueAsPythonComment( const QVariant &, QgsProcessingContext & ) const { return QString(); diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 1d7f37362f77..0f467bac5e0e 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -611,9 +611,23 @@ class CORE_EXPORT QgsProcessingParameterDefinition /** * Returns a string version of the parameter input \a value, which is suitable for use as an input * parameter value when running an algorithm directly from a Python command. + * + * \see valueAsJsonObject() */ virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; + /** + * Returns a version of the parameter input \a value, which is suitable for use in a JSON object. + * + * This method must return only simple values which can be losslessly encapsulated in a serialized + * JSON map. For instance, and QGIS class values (such as QgsCoordinateReferenceSystem) must be + * converted to a simple string or numeric value equivalent. + * + * \see valueAsPythonString() + * \since QGIS 3.24 + */ + virtual QVariant valueAsJsonObject( const QVariant &value, QgsProcessingContext &context ) const; + /** * Returns a Python comment explaining a parameter \a value, or an empty string if no comment is required. * diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 8df608bc9e6c..9ea6d0fa94f0 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -24,6 +24,7 @@ #include "qgsprocessingmodelalgorithm.h" #include "qgsprocessingmodelgroupbox.h" #include "qgsnativealgorithms.h" +#include "qgsconfig.h" #include #include #include @@ -57,7 +58,8 @@ #include "qgsdxfexport.h" #include "qgspointcloudlayer.h" #include "qgsannotationlayer.h" -#include "qgsconfig.h" +#include "qgsjsonutils.h" +#include "json.hpp" class DummyAlgorithm : public QgsProcessingAlgorithm { @@ -2621,6 +2623,12 @@ void TestQgsProcessing::parameterBoolean() QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('non_optional_default_false', '', defaultValue=None)" ) ); + QCOMPARE( def->valueAsJsonObject( false, context ), QVariant( false ) ); + QCOMPARE( def->valueAsJsonObject( true, context ), QVariant( true ) ); + QCOMPARE( def->valueAsJsonObject( "false", context ), QVariant( QStringLiteral( "false" ) ) ); + QCOMPARE( def->valueAsJsonObject( "true", context ), QVariant( QStringLiteral( "true" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QString code = def->asScriptCode(); QCOMPARE( code, QStringLiteral( "##non_optional_default_false=boolean false" ) ); std::unique_ptr< QgsProcessingParameterBoolean > fromCode( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); @@ -2861,6 +2869,16 @@ void TestQgsProcessing::parameterCrs() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QgsCoordinateReferenceSystem( "EPSG:3111" ), context ), QVariant( QStringLiteral( "EPSG:3111" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsCoordinateReferenceSystem(), context ), QVariant( QString() ) ); + QCOMPARE( def->valueAsJsonObject( "EPSG:12003", context ), QVariant( QStringLiteral( "EPSG:12003" ) ) ); + QCOMPARE( def->valueAsJsonObject( "ProjectCrs", context ), QVariant( QStringLiteral( "ProjectCrs" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->valueAsJsonObject( raster1, context ), QVariant( QString( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ) ); + QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( QString( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterCrs fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -2985,6 +3003,12 @@ void TestQgsProcessing::parameterMapLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( raster1, context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='')" ) ); @@ -3373,6 +3397,18 @@ void TestQgsProcessing::parameterExtent() QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); QCOMPARE( def->valueAsPythonString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QStringLiteral( "QgsGeometry.fromWkt('LineString (10 10, 20 20)')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "1,2,3,4", context ), QVariant( QStringLiteral( "1,2,3,4" ) ) ); + QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ); + QCOMPARE( def->valueAsJsonObject( raster2, context ), QVariant( testDataDir + QStringLiteral( "landsat.tif" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsRectangle( 11, 12, 13, 14 ), context ), QVariant( QStringLiteral( "11, 13, 12, 14" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QVariant( QStringLiteral( "11, 13, 12, 14 [EPSG:4326]" ) ) ); + QCOMPARE( def->valueAsJsonObject( "1,2,3,4 [EPSG:4326]", context ), QVariant( QStringLiteral( "1,2,3,4 [EPSG:4326]" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QVariant( QStringLiteral( "LineString (10 10, 20 20)" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExtent('non_optional', '', defaultValue='1,2,3,4')" ) ); @@ -3559,6 +3595,13 @@ void TestQgsProcessing::parameterPoint() QCOMPARE( def->valueAsPythonString( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11,12 [EPSG:4326]'" ) ); QCOMPARE( def->valueAsPythonString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QStringLiteral( "QgsGeometry.fromWkt('LineString (10 10, 20 20)')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "1,2", context ), QVariant( QStringLiteral( "1,2" ) ) ); + QCOMPARE( def->valueAsJsonObject( "1,2 [EPSG:4326]", context ), QVariant( QStringLiteral( "1,2 [EPSG:4326]" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsPointXY( 11, 12 ), context ), QVariant( QStringLiteral( "11,12" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QVariant( QStringLiteral( "11,12 [EPSG:4326]" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QVariant( QStringLiteral( "LineString (10 10, 20 20)" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPoint('non_optional', '', defaultValue='1,2')" ) ); @@ -3730,6 +3773,13 @@ void TestQgsProcessing::parameterGeometry() QgsCoordinateReferenceSystem( "EPSG:4326" ) ), context ), QStringLiteral( "'CRS=EPSG:4326;LineString (10 10, 20 20)'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "LineString( 10 10, 20 20)", context ), QVariant( QStringLiteral( "LineString( 10 10, 20 20)" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QVariant( QStringLiteral( "LineString (10 10, 20 20)" ) ) ); + QCOMPARE( def->valueAsJsonObject( QgsReferencedGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), + QgsCoordinateReferenceSystem( "EPSG:4326" ) ), context ), + QVariant( QStringLiteral( "CRS=EPSG:4326;LineString (10 10, 20 20)" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('non_optional', '', defaultValue='Point(1 2)')" ) ); @@ -3873,6 +3923,11 @@ void TestQgsProcessing::parameterFile() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "bricks.bmp", context ), QVariant( QStringLiteral( "bricks.bmp" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, extension='.bmp', defaultValue='abc.bmp')" ) ); @@ -3913,6 +3968,11 @@ void TestQgsProcessing::parameterFile() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "bricks.png", context ), QVariant( QStringLiteral( "bricks.png" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, fileFilter='PNG Files (*.png *.PNG)', defaultValue='abc.bmp')" ) ); @@ -4035,6 +4095,15 @@ void TestQgsProcessing::parameterMatrix() QCOMPARE( def->valueAsPythonString( "1,2,3", context ), QStringLiteral( "[1,2,3]" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << 1 << 2.5 << 3, context ), QVariant( QVariantList( { 1, 2.5, 3} ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ), context ), QVariant( QVariantList( {1, 2, 3, 1, 2, 3} ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << ( QVariantList() << 1 << QStringLiteral( "value" ) << 3 ) << ( QVariantList() << 1 << 2 << QStringLiteral( "it's a value" ) ), context ), QVariant( QVariantList( {1, "value", 3, 1, 2, "it's a value"} ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << ( QVariantList() << 1 << QVariant() << 3 ) << ( QVariantList() << QVariant() << 2 << 3 ), context ), QVariant( QVariantList( {1, QVariant(), 3, QVariant(), 2, 3} ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << ( QVariantList() << 1 << QString( "" ) << 3 ) << ( QVariantList() << 1 << 2 << QString( "" ) ), context ), QVariant( QVariantList( {1, QString(), 3, 1, 2, QString()} ) ) ); + QCOMPARE( def->valueAsJsonObject( "1,2,3", context ), QVariant( QStringLiteral( "1,2,3" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('non_optional', '', numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=None)" ) ); @@ -4219,6 +4288,13 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "layer12312312", context ), QVariant( QStringLiteral( "layer12312312" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringList() << r1->id() << raster2, context ), QVariant( QVariantList( { QString( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ), QString( testDataDir + QStringLiteral( "landsat.tif" ) ) } ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('non_optional', '', layerType=QgsProcessing.TypeMapLayer, defaultValue='')" ) ); @@ -4410,6 +4486,11 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( def->valueAsPythonString( "layer12312312", context ), QStringLiteral( "'layer12312312'" ) ); QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "B", context ), QStringLiteral( "['a','B']" ) ); QCOMPARE( def->valueAsPythonString( QVariantList() << "c" << "d", context ), QStringLiteral( "['c','d']" ) ); + + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "layer12312312", context ), QVariant( QStringLiteral( "layer12312312" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringList() << "a" << "B", context ), QVariant( QVariantList( { QStringLiteral( "a" ), QStringLiteral( "B" ) } ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << "c" << "d", context ), QVariant( QVariantList( { QStringLiteral( "c" ), QStringLiteral( "d" ) } ) ) ); } void TestQgsProcessing::parameterDistance() @@ -4473,6 +4554,10 @@ void TestQgsProcessing::parameterDistance() QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterDistance fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4573,6 +4658,10 @@ void TestQgsProcessing::parameterDuration() QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterDuration fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4664,6 +4753,10 @@ void TestQgsProcessing::parameterScale() QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( "5" ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterScale fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4771,6 +4864,10 @@ void TestQgsProcessing::parameterNumber() QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( "5" ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterNumber('non_optional', '', type=QgsProcessingParameterNumber.Double, minValue=11, maxValue=21, defaultValue=5)" ) ); @@ -4896,6 +4993,10 @@ void TestQgsProcessing::parameterRange() QCOMPARE( def->valueAsPythonString( QVariantList() << 1.1 << 2, context ), QStringLiteral( "[1.1,2]" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( "1.1,2", context ), QVariant( QStringLiteral( "1.1,2" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << 1.1 << 2, context ), QVariant( QVariantList( { QVariant( 1.1 ), QVariant( 2 )} ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('non_optional', '', type=QgsProcessingParameterNumber.Double, defaultValue=[5,6])" ) ); @@ -5062,6 +5163,12 @@ void TestQgsProcessing::parameterRasterLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( raster1, context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRasterLayer('non_optional', '', defaultValue=None)" ) ); @@ -5179,6 +5286,10 @@ void TestQgsProcessing::parameterEnum() QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=False, usesStaticStrings=False, defaultValue=2)" ) ); @@ -5242,6 +5353,10 @@ void TestQgsProcessing::parameterEnum() QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2, context ), QStringLiteral( "[1,2]" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "1,2" ), context ), QStringLiteral( "[1,2]" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << 1 << 2, context ), QVariant( QVariantList( {1, 2} ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1,2" ), context ), QVariant( QStringLiteral( "1,2" ) ) ); + QCOMPARE( def->valueAsPythonComment( QVariant(), context ), QString() ); QCOMPARE( def->valueAsPythonComment( 2, context ), QStringLiteral( "C" ) ); QCOMPARE( def->valueAsPythonComment( QVariantList() << 1 << 2, context ), QStringLiteral( "B,C" ) ); @@ -5498,6 +5613,13 @@ void TestQgsProcessing::parameterString() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('non_optional', '', multiLine=False, defaultValue=None)" ) ); @@ -5652,6 +5774,13 @@ void TestQgsProcessing::parameterAuthConfig() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAuthConfig('non_optional', '', defaultValue='')" ) ); @@ -5767,6 +5896,11 @@ void TestQgsProcessing::parameterExpression() QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExpression('non_optional', '', parentLayerParameterName='', defaultValue='1+1')" ) ); @@ -5855,6 +5989,10 @@ void TestQgsProcessing::parameterField() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "probably\'invalid\"field", context ), QStringLiteral( "'probably\\'invalid\"field'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"field", context ), QVariant( QStringLiteral( "probably\'invalid\"field" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) ); @@ -5955,7 +6093,9 @@ void TestQgsProcessing::parameterField() QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) ); - QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) ); + + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringList() << "a" << "b", context ), QVariant( QVariantList( {QStringLiteral( "a" ), QStringLiteral( "b" ) } ) ) ); QVariantMap map = def->toVariantMap(); QgsProcessingParameterField fromMap( "x" ); @@ -6150,6 +6290,12 @@ void TestQgsProcessing::parameterVectorLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( vector1, context ), QVariant( testDataDir + QStringLiteral( "multipoint.shp" ) ) ); + QCOMPARE( def->valueAsJsonObject( v1->id(), context ), QVariant( testDataDir + QStringLiteral( "multipoint.shp" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( v1 ), context ), QVariant( testDataDir + QStringLiteral( "multipoint.shp" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -6279,6 +6425,12 @@ void TestQgsProcessing::parameterMeshLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.2dm" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.2dm'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( mesh, context ), QVariant( testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ) ); + QCOMPARE( def->valueAsJsonObject( m1->id(), context ), QVariant( testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( m1 ), context ), QVariant( testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.2dm" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.2dm" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -6426,6 +6578,25 @@ void TestQgsProcessing::parameterFeatureSource() QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ), context ), QStringLiteral( "'postgres://uri=\\'complex\\' username=\"complex\"'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QVariant( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc'def" ) ), context ), QVariant( QStringLiteral( "abc'def" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( v2->id() ) ), context ), QVariant( vector2 ) ); + QCOMPARE( def->valueAsJsonObject( v2->id(), context ), QVariant( vector2 ) ); + // currently limits, flags, etc from feature source definitions cannot be serialized to JSON... + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), true ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "dbname='mydb' host=localhost port=5432 sslmode=disable key='id'" ), true ) ), context ), QVariant( QStringLiteral( "dbname='mydb' host=localhost port=5432 sslmode=disable key='id'" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, 11 ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1 ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( v2 ), context ), QVariant( vector2 ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ), context ), QVariant( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFeatureSource fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -6554,6 +6725,13 @@ void TestQgsProcessing::parameterFeatureSink() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) ); QCOMPARE( def->generateTemporaryDestination(), QStringLiteral( "memory:" ) ); def->setSupportsNonFileBasedOutput( false ); @@ -6725,6 +6903,13 @@ void TestQgsProcessing::parameterVectorOut() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) ); QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".gpkg" ) ) ); QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) ); @@ -6913,6 +7098,7 @@ void TestQgsProcessing::parameterRasterOut() QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif", &context ) ); QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); QCOMPARE( def->defaultFileExtension(), QStringLiteral( "tif" ) ); QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".tif" ) ) ); QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) ); @@ -6936,6 +7122,12 @@ void TestQgsProcessing::parameterRasterOut() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterRasterDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7047,6 +7239,7 @@ void TestQgsProcessing::parameterPointCloudOut() QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.las", &context ) ); QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); QCOMPARE( def->defaultFileExtension(), QStringLiteral( "las" ) ); QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".las" ) ) ); QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) ); @@ -7070,6 +7263,12 @@ void TestQgsProcessing::parameterPointCloudOut() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterPointCloudDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7223,6 +7422,13 @@ void TestQgsProcessing::parameterFileOut() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFileDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7323,6 +7529,11 @@ void TestQgsProcessing::parameterFolderOut() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\" ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFolderDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7394,6 +7605,9 @@ void TestQgsProcessing::parameterBand() QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('non_optional', '', parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) ); QString code = def->asScriptCode(); @@ -7441,6 +7655,10 @@ void TestQgsProcessing::parameterBand() QCOMPARE( def->valueAsPythonString( QStringList() << "1" << "2", context ), QStringLiteral( "[1,2]" ) ); QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2, context ), QStringLiteral( "[1,2]" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringList() << "1" << "2", context ), QVariant( QVariantList( { "1", "2" } ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariantList() << 1 << 2, context ), QVariant( QVariantList( { 1, 2} ) ) ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterBand fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7534,6 +7752,13 @@ void TestQgsProcessing::parameterLayout() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayout('non_optional', '', defaultValue=None)" ) ); @@ -7686,6 +7911,10 @@ void TestQgsProcessing::parameterLayoutItem() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "probably\'invalid\"item", context ), QStringLiteral( "'probably\\'invalid\"item'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"item", context ), QVariant( QStringLiteral( "probably\'invalid\"item" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', parentLayoutParameterName='', defaultValue=None)" ) ); @@ -7812,6 +8041,12 @@ void TestQgsProcessing::parameterColor() QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0 ), context ), QStringLiteral( "QColor(255, 0, 0)" ) ); QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0, 100 ), context ), QStringLiteral( "QColor(255, 0, 0, 100)" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "#ff0000" ), context ), QVariant( QStringLiteral( "#ff0000" ) ) ); + QCOMPARE( def->valueAsJsonObject( QColor(), context ), QVariant( QString() ) ); + QCOMPARE( def->valueAsJsonObject( QColor( 255, 0, 0 ), context ), QVariant( QStringLiteral( "rgba( 255, 0, 0, 1.00 )" ) ) ); + QCOMPARE( def->valueAsJsonObject( QColor( 255, 0, 0, 100 ), context ), QVariant( QStringLiteral( "rgba( 255, 0, 0, 0.39 )" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=None)" ) ); @@ -7959,6 +8194,13 @@ void TestQgsProcessing::parameterCoordinateOperation() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCoordinateOperation('non_optional', '', sourceCrsParameterName='src', destinationCrsParameterName='dest', staticSourceCrs=QgsCoordinateReferenceSystem('EPSG:7855'), staticDestinationCrs=QgsCoordinateReferenceSystem('EPSG:28355'), defaultValue=None)" ) ); @@ -8080,6 +8322,13 @@ void TestQgsProcessing::parameterMapTheme() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapTheme('non_optional', '', defaultValue=None)" ) ); @@ -8200,6 +8449,13 @@ void TestQgsProcessing::parameterProviderConnection() QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterProviderConnection('non_optional', '', 'postgres', defaultValue=None)" ) ); @@ -8322,6 +8578,10 @@ void TestQgsProcessing::parameterDatabaseSchema() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "probably\'invalid\"schema", context ), QStringLiteral( "'probably\\'invalid\"schema'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"schema", context ), QVariant( QStringLiteral( "probably\'invalid\"schema" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseSchema('non_optional', '', connectionParameterName='', defaultValue=None)" ) ); @@ -8400,6 +8660,10 @@ void TestQgsProcessing::parameterDatabaseTable() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( "probably\'invalid\"schema", context ), QStringLiteral( "'probably\\'invalid\"schema'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); + QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"schema", context ), QVariant( QStringLiteral( "probably\'invalid\"schema" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('non_optional', '', connectionParameterName='', schemaParameterName='', defaultValue=None)" ) ); @@ -8577,6 +8841,12 @@ void TestQgsProcessing::parameterAggregate() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QVariant( QVariantList() << map << map2 ), context ), QStringLiteral( "[{'aggregate': 'e','input': 'i','name': 'n','type': 't'},{'aggregate': 'e2','input': 'i2','name': 'n2','type': 't2'}]" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( 5, context ), 5 ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QStringLiteral( "abc" ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "abc\ndef" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant( QVariantList() << map << map2 ), context ), QVariant( QVariantList() << map << map2 ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAggregate('non_optional', '', parentLayerParameterName='parent')" ) ); @@ -8647,6 +8917,9 @@ void TestQgsProcessing::parameterTinInputLayers() const QString valueAsPythonString = def->valueAsPythonString( layerList, context ); QCOMPARE( valueAsPythonString, QStringLiteral( "[{'source': 'PointLayerForTin','type': 0,'attributeIndex': -1}]" ) ); + QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( layerList, context ) ).dump() ), + QStringLiteral( "[{\"attributeIndex\":-1,\"source\":\"%1\",\"type\":0}]" ).arg( vectorLayer->source() ) ); + const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterTinInputLayers('tin input layer', '')" ) ); } @@ -8694,6 +8967,8 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( valueAsPythonString, QStringLiteral( "[0,5]" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList() << 0 << 5 ); + QCOMPARE( def->valueAsJsonObject( groupsList, context ), QVariant( QVariantList( {0, 5 } ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', dataType=[QgsMeshDatasetGroupMetadata.DataOnVertices])" ) ); @@ -8724,6 +8999,8 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( valueAsPythonString, QStringLiteral( "[2,6]" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList() << 2 << 6 ); + QCOMPARE( def->valueAsJsonObject( groupsList, context ), QVariant( QVariantList( {2, 6 } ) ) ); + QVERIFY( !def->dependsOnOtherParameters().isEmpty() ); QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) ); @@ -8757,6 +9034,8 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( def->valueAsPythonString( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ), context ), QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2020, 1, 1), QTime(10, 0, 0))}" ) ); + QCOMPARE( def->valueAsJsonObject( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ), context ), QVariant( QStringLiteral( "2020-01-01T10:00:00" ) ) ); + QVariantMap value; QVERIFY( !def->checkValueIsAcceptable( value ) ); value[QStringLiteral( "test" )] = QStringLiteral( "test" ); @@ -8769,11 +9048,15 @@ void TestQgsProcessing::parameterMeshDatasetTime() value[QStringLiteral( "type" )] = QStringLiteral( "static" ); QVERIFY( def->checkValueIsAcceptable( value ) ); QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'static'}" ) ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "static" ) ); value[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" ); QVERIFY( def->checkValueIsAcceptable( value ) ); QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'current-context-time'}" ) ); + QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), + QStringLiteral( "{\"type\":\"current-context-time\"}" ) ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "current-context-time" ) ); value[QStringLiteral( "type" )] = QStringLiteral( "defined-date-time" ); @@ -8781,6 +9064,8 @@ void TestQgsProcessing::parameterMeshDatasetTime() value[QStringLiteral( "value" )] = QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) ); QVERIFY( def->checkValueIsAcceptable( value ) ); QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2123, 1, 2), QTime(1, 2, 3))}" ) ); + QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), + QStringLiteral( "{\"type\":\"defined-date-time\",\"value\":\"2123-01-02T01:02:03\"}" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "defined-date-time" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ), QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) ) ); QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ).isValid() ); @@ -8791,6 +9076,8 @@ void TestQgsProcessing::parameterMeshDatasetTime() value[QStringLiteral( "value" )] = QVariantList() << 1 << 5; QVERIFY( def->checkValueIsAcceptable( value ) ); QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'dataset-time-step','value': [1,5]}" ) ); + QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), + QStringLiteral( "{\"type\":\"dataset-time-step\",\"value\":[1,5]}" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "dataset-time-step" ) ); QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ).isValid() ); QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ) == QgsMeshDatasetIndex( 1, 5 ) ); @@ -8874,6 +9161,11 @@ void TestQgsProcessing::parameterDateTime() QCOMPARE( def->valueAsPythonString( QStringLiteral( "2015-12-31" ), context ), QStringLiteral( "2015-12-31" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QDateTime( QDate( 2014, 12, 31 ), QTime( 0, 0, 0 ) ), context ), QVariant( QStringLiteral( "2014-12-31T00:00:00" ) ) ); + QCOMPARE( def->valueAsJsonObject( QDateTime( QDate( 2014, 12, 31 ), QTime( 12, 11, 10 ) ), context ), QVariant( QStringLiteral( "2014-12-31T12:11:10" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "2015-12-31" ), context ), QVariant( QStringLiteral( "2015-12-31" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.DateTime, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDateTime(QDate(2010, 4, 3), QTime(12, 11, 10)))" ) ); @@ -9008,6 +9300,7 @@ void TestQgsProcessing::parameterDateTime() QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "2015-12-31" ) ) ); QCOMPARE( def->valueAsPythonString( QDate( 2014, 12, 31 ), context ), QStringLiteral( "QDate(2014, 12, 31)" ) ); + QCOMPARE( def->valueAsJsonObject( QDate( 2014, 12, 31 ), context ), QVariant( QStringLiteral( "2014-12-31" ) ) ); pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Date, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDate(2010, 4, 3))" ) ); @@ -9112,6 +9405,7 @@ void TestQgsProcessing::parameterDateTime() QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "10:40:01" ) ) ); QCOMPARE( def->valueAsPythonString( QTime( 13, 14, 15 ), context ), QStringLiteral( "QTime(13, 14, 15)" ) ); + QCOMPARE( def->valueAsJsonObject( QTime( 13, 14, 15 ), context ), QVariant( QStringLiteral( "13:14:15" ) ) ); pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Time, minValue=QDateTime(QDate(1, 1, 1), QTime(10, 0, 0)), maxValue=QDateTime(QDate(1, 1, 1), QTime(11, 0, 0)), defaultValue=QTime(12, 11, 13))" ) ); @@ -9209,6 +9503,8 @@ void TestQgsProcessing::parameterDxfLayers() const QString valueAsPythonString = def->valueAsPythonString( layerList, context ); QCOMPARE( valueAsPythonString, QStringLiteral( "[{'layer': '%1','attributeIndex': -1}]" ).arg( vectorLayer->source() ) ); + QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( layerList, context ) ).dump() ), + QStringLiteral( "[{\"attributeIndex\":-1,\"layer\":\"%1\"}]" ).arg( vectorLayer->source() ) ); const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDxfLayers('dxf input layer', '')" ) ); @@ -9300,6 +9596,11 @@ void TestQgsProcessing::parameterAnnotationLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( al ), context ), QStringLiteral( "'%1'" ).arg( al->id() ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "main" ), context ), QVariant( QStringLiteral( "main" ) ) ); + QCOMPARE( def->valueAsJsonObject( al->id(), context ), QVariant( al->id() ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( al ), context ), QVariant( al->id() ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAnnotationLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -9395,7 +9696,9 @@ void TestQgsProcessing::parameterPointCloudLayer() QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) ); QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) ); +#ifdef HAVE_PDAL QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.las" ) ) ); +#endif QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) ); // using existing map layer ID @@ -9430,6 +9733,12 @@ void TestQgsProcessing::parameterPointCloudLayer() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.las" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.las'" ) ); + QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + QCOMPARE( def->valueAsJsonObject( pointCloud, context ), QVariant( testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ) ); + QCOMPARE( def->valueAsJsonObject( pc1->id(), context ), QVariant( testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ) ); + QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( pc1 ), context ), QVariant( testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ) ); + QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.las" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.las" ) ) ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPointCloudLayer('non_optional', '', defaultValue='somelayer')" ) ); From 700075f987ef7d995ac4b2901a6e2b03925b8ebc Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 12:08:20 +1000 Subject: [PATCH 02/11] Add api to convert a processing parameter value to a string, if possible Designed for use in converting an algorithm's parameters for representation as a qgis_process command line --- .../processing/qgsprocessingparameters.sip.in | 21 + .../processing/qgsprocessingparameters.cpp | 175 ++++++ src/core/processing/qgsprocessingparameters.h | 16 + tests/src/analysis/testqgsprocessing.cpp | 594 ++++++++++++++++++ 4 files changed, 806 insertions(+) diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index 8531162e5f5f..b1430d14cb53 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -517,6 +517,8 @@ Returns a string version of the parameter input ``value``, which is suitable for parameter value when running an algorithm directly from a Python command. .. seealso:: :py:func:`valueAsJsonObject` + +.. seealso:: :py:func:`valueAsString` %End virtual QVariant valueAsJsonObject( const QVariant &value, QgsProcessingContext &context ) const; @@ -529,6 +531,25 @@ converted to a simple string or numeric value equivalent. .. seealso:: :py:func:`valueAsPythonString` +.. seealso:: :py:func:`valueAsString` + +.. versionadded:: 3.24 +%End + + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; +%Docstring +Returns a string version of the parameter input ``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 + +.. seealso:: :py:func:`valueAsJsonObject` + +.. seealso:: :py:func:`valueAsPythonString` + .. versionadded:: 3.24 %End diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index a16ae4e9a64c..5901490abd36 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2649,6 +2649,181 @@ QVariant QgsProcessingParameterDefinition::valueAsJsonObject( const QVariant &va return value; } +QString QgsProcessingParameterDefinition::valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = true; + + if ( !value.isValid() ) + return QString(); + + switch ( value.userType() ) + { + // simple types which can be directly represented in JSON -- not that strings are NOT handled here yet! + case QMetaType::Bool: + case QMetaType::Char: + case QMetaType::Int: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::UShort: + return value.toString(); + + default: + break; + } + + if ( value.userType() == QMetaType::type( "QgsProperty" ) ) + { + const QgsProperty prop = value.value< QgsProperty >(); + switch ( prop.propertyType() ) + { + case QgsProperty::InvalidProperty: + return QString(); + case QgsProperty::StaticProperty: + return valueAsString( prop.staticValue(), context, ok ); + + // these are not supported for serialization + case QgsProperty::FieldBasedProperty: + case QgsProperty::ExpressionBasedProperty: + QgsDebugMsg( QStringLiteral( "could not convert expression/field based property to string" ) ); + return QString(); + } + } + + // value may be a CRS + if ( value.userType() == QMetaType::type( "QgsCoordinateReferenceSystem" ) ) + { + const QgsCoordinateReferenceSystem crs = value.value< QgsCoordinateReferenceSystem >(); + if ( !crs.isValid() ) + return QString(); + else if ( !crs.authid().isEmpty() ) + return crs.authid(); + else + return crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ); + } + else if ( value.userType() == QMetaType::type( "QgsRectangle" ) ) + { + const QgsRectangle r = value.value(); + return QStringLiteral( "%1, %3, %2, %4" ).arg( qgsDoubleToString( r.xMinimum() ), + qgsDoubleToString( r.yMinimum() ), + qgsDoubleToString( r.xMaximum() ), + qgsDoubleToString( r.yMaximum() ) ); + } + else if ( value.userType() == QMetaType::type( "QgsReferencedRectangle" ) ) + { + const QgsReferencedRectangle r = value.value(); + return QStringLiteral( "%1, %3, %2, %4 [%5]" ).arg( qgsDoubleToString( r.xMinimum() ), + qgsDoubleToString( r.yMinimum() ), + qgsDoubleToString( r.xMaximum() ), + qgsDoubleToString( r.yMaximum() ), r.crs().authid() ); + } + else if ( value.userType() == QMetaType::type( "QgsGeometry" ) ) + { + const QgsGeometry g = value.value(); + if ( !g.isNull() ) + { + return g.asWkt(); + } + else + { + return QString(); + } + } + else if ( value.userType() == QMetaType::type( "QgsReferencedGeometry" ) ) + { + const QgsReferencedGeometry g = value.value(); + if ( !g.isNull() ) + { + if ( !g.crs().isValid() ) + return g.asWkt(); + else + return QStringLiteral( "CRS=%1;%2" ).arg( g.crs().authid().isEmpty() ? g.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : g.crs().authid(), g.asWkt() ); + } + else + { + return QString(); + } + } + else if ( value.userType() == QMetaType::type( "QgsPointXY" ) ) + { + const QgsPointXY r = value.value(); + return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( r.x() ), + qgsDoubleToString( r.y() ) ); + } + else if ( value.userType() == QMetaType::type( "QgsReferencedPointXY" ) ) + { + const QgsReferencedPointXY r = value.value(); + return QStringLiteral( "%1,%2 [%3]" ).arg( qgsDoubleToString( r.x() ), + qgsDoubleToString( r.y() ), + r.crs().authid() ); + } + else if ( value.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) ) + { + const QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast( value ); + return valueAsString( fromVar.source, context, ok ); + } + else if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) ) + { + const QgsProcessingOutputLayerDefinition fromVar = qvariant_cast( value ); + return valueAsString( fromVar.sink, context, ok ); + } + else if ( value.userType() == QMetaType::type( "QColor" ) ) + { + const QColor fromVar = value.value< QColor >(); + if ( !fromVar.isValid() ) + return QString(); + + return QStringLiteral( "rgba( %1, %2, %3, %4 )" ).arg( fromVar.red() ).arg( fromVar.green() ).arg( fromVar.blue() ).arg( QString::number( fromVar.alphaF(), 'f', 2 ) ); + } + else if ( value.userType() == QMetaType::type( "QDateTime" ) ) + { + const QDateTime fromVar = value.toDateTime(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + else if ( value.userType() == QMetaType::type( "QDate" ) ) + { + const QDate fromVar = value.toDate(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + else if ( value.userType() == QMetaType::type( "QTime" ) ) + { + const QTime fromVar = value.toTime(); + if ( !fromVar.isValid() ) + return QString(); + + return fromVar.toString( Qt::ISODate ); + } + + // value may be a map layer + QVariantMap p; + p.insert( name(), value ); + if ( QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context ) ) + { + const QString source = QgsProcessingUtils::normalizeLayerSource( layer->source() ); + if ( !source.isEmpty() ) + return source; + return layer->id(); + } + + // now we handle strings, after any other specific logic has already been applied + if ( value.userType() == QMetaType::QString ) + return value.toString(); + + // unhandled type + QgsDebugMsg( QStringLiteral( "unsupported variant type %1" ).arg( QMetaType::typeName( value.userType() ) ) ); + ok = false; + return value.toString(); +} + QString QgsProcessingParameterDefinition::valueAsPythonComment( const QVariant &, QgsProcessingContext & ) const { return QString(); diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 0f467bac5e0e..bf851c562ec7 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -613,6 +613,7 @@ class CORE_EXPORT QgsProcessingParameterDefinition * parameter value when running an algorithm directly from a Python command. * * \see valueAsJsonObject() + * \see valueAsString() */ virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; @@ -624,10 +625,25 @@ class CORE_EXPORT QgsProcessingParameterDefinition * converted to a simple string or numeric value equivalent. * * \see valueAsPythonString() + * \see valueAsString() * \since QGIS 3.24 */ virtual QVariant valueAsJsonObject( const QVariant &value, QgsProcessingContext &context ) const; + /** + * Returns a string version of the parameter input \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 + * + * \see valueAsJsonObject() + * \see valueAsPythonString() + * \since QGIS 3.24 + */ + virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + /** * Returns a Python comment explaining a parameter \a value, or an empty string if no comment is required. * diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 9ea6d0fa94f0..04e2edaa215c 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -2629,6 +2629,18 @@ void TestQgsProcessing::parameterBoolean() QCOMPARE( def->valueAsJsonObject( "true", context ), QVariant( QStringLiteral( "true" ) ) ); QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + bool ok = false; + QCOMPARE( def->valueAsString( false, context, ok ), QStringLiteral( "false" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( true, context, ok ), QStringLiteral( "true" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "false", context, ok ), QStringLiteral( "false" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "true", context, ok ), QStringLiteral( "true" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QString code = def->asScriptCode(); QCOMPARE( code, QStringLiteral( "##non_optional_default_false=boolean false" ) ); std::unique_ptr< QgsProcessingParameterBoolean > fromCode( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); @@ -2879,6 +2891,26 @@ void TestQgsProcessing::parameterCrs() QCOMPARE( def->valueAsJsonObject( r1->id(), context ), QVariant( QString( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ) ); QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsCoordinateReferenceSystem( "EPSG:3111" ), context, ok ), QStringLiteral( "EPSG:3111" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsCoordinateReferenceSystem(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "EPSG:12003", context, ok ), QStringLiteral( "EPSG:12003" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "ProjectCrs", context, ok ), QStringLiteral( "ProjectCrs" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( raster1, context, ok ), QString( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( r1->id(), context, ok ), QString( testDataDir + QStringLiteral( "landsat_4326.tif" ) ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterCrs fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -3009,6 +3041,18 @@ void TestQgsProcessing::parameterMapLayer() QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( raster1, context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( r1->id(), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( r1 ), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='')" ) ); @@ -3409,6 +3453,30 @@ void TestQgsProcessing::parameterExtent() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); QCOMPARE( def->valueAsJsonObject( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QVariant( QStringLiteral( "LineString (10 10, 20 20)" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "1,2,3,4", context, ok ), QStringLiteral( "1,2,3,4" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( r1->id(), context, ok ), testDataDir + QStringLiteral( "landsat_4326.tif" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( r1 ), context, ok ), testDataDir + QStringLiteral( "landsat_4326.tif" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( raster2, context, ok ), testDataDir + QStringLiteral( "landsat.tif" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsRectangle( 11, 12, 13, 14 ), context, ok ), QStringLiteral( "11, 13, 12, 14" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context, ok ), QStringLiteral( "11, 13, 12, 14 [EPSG:4326]" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "1,2,3,4 [EPSG:4326]", context, ok ), QStringLiteral( "1,2,3,4 [EPSG:4326]" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context, ok ), QStringLiteral( "LineString (10 10, 20 20)" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExtent('non_optional', '', defaultValue='1,2,3,4')" ) ); @@ -3602,6 +3670,20 @@ void TestQgsProcessing::parameterPoint() QCOMPARE( def->valueAsJsonObject( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QVariant( QStringLiteral( "11,12 [EPSG:4326]" ) ) ); QCOMPARE( def->valueAsJsonObject( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QVariant( QStringLiteral( "LineString (10 10, 20 20)" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "1,2", context, ok ), QStringLiteral( "1,2" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "1,2 [EPSG:4326]", context, ok ), QStringLiteral( "1,2 [EPSG:4326]" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsPointXY( 11, 12 ), context, ok ), QStringLiteral( "11,12" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context, ok ), QStringLiteral( "11,12 [EPSG:4326]" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context, ok ), QStringLiteral( "LineString (10 10, 20 20)" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPoint('non_optional', '', defaultValue='1,2')" ) ); @@ -3780,6 +3862,18 @@ void TestQgsProcessing::parameterGeometry() QgsCoordinateReferenceSystem( "EPSG:4326" ) ), context ), QVariant( QStringLiteral( "CRS=EPSG:4326;LineString (10 10, 20 20)" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "LineString( 10 10, 20 20)", context, ok ), QStringLiteral( "LineString( 10 10, 20 20)" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context, ok ), QStringLiteral( "LineString (10 10, 20 20)" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QgsReferencedGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), + QgsCoordinateReferenceSystem( "EPSG:4326" ) ), context, ok ), + QStringLiteral( "CRS=EPSG:4326;LineString (10 10, 20 20)" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('non_optional', '', defaultValue='Point(1 2)')" ) ); @@ -3928,6 +4022,16 @@ void TestQgsProcessing::parameterFile() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "bricks.bmp", context, ok ), QStringLiteral( "bricks.bmp" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, extension='.bmp', defaultValue='abc.bmp')" ) ); @@ -3973,6 +4077,16 @@ void TestQgsProcessing::parameterFile() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "bricks.png", context, ok ), QStringLiteral( "bricks.png" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, fileFilter='PNG Files (*.png *.PNG)', defaultValue='abc.bmp')" ) ); @@ -4104,6 +4218,24 @@ void TestQgsProcessing::parameterMatrix() QCOMPARE( def->valueAsJsonObject( QVariantList() << ( QVariantList() << 1 << QString( "" ) << 3 ) << ( QVariantList() << 1 << 2 << QString( "" ) ), context ), QVariant( QVariantList( {1, QString(), 3, 1, 2, QString()} ) ) ); QCOMPARE( def->valueAsJsonObject( "1,2,3", context ), QVariant( QStringLiteral( "1,2,3" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariantList() << 1 << 2.5 << 3, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ), context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << ( QVariantList() << 1 << QStringLiteral( "value" ) << 3 ) << ( QVariantList() << 1 << 2 << QStringLiteral( "it's a value" ) ), context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << ( QVariantList() << 1 << QVariant() << 3 ) << ( QVariantList() << QVariant() << 2 << 3 ), context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << ( QVariantList() << 1 << QString( "" ) << 3 ) << ( QVariantList() << 1 << 2 << QString( "" ) ), context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( "1,2,3", context, ok ), QVariant( QStringLiteral( "1,2,3" ) ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('non_optional', '', numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=None)" ) ); @@ -4295,6 +4427,20 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( def->valueAsJsonObject( QStringList() << r1->id() << raster2, context ), QVariant( QVariantList( { QString( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ), QString( testDataDir + QStringLiteral( "landsat.tif" ) ) } ) ) ); QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "layer12312312", context, ok ), QStringLiteral( "layer12312312" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( r1 ), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( r1->id(), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringList() << r1->id() << raster2, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('non_optional', '', layerType=QgsProcessing.TypeMapLayer, defaultValue='')" ) ); @@ -4491,6 +4637,16 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( def->valueAsJsonObject( "layer12312312", context ), QVariant( QStringLiteral( "layer12312312" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringList() << "a" << "B", context ), QVariant( QVariantList( { QStringLiteral( "a" ), QStringLiteral( "B" ) } ) ) ); QCOMPARE( def->valueAsJsonObject( QVariantList() << "c" << "d", context ), QVariant( QVariantList( { QStringLiteral( "c" ), QStringLiteral( "d" ) } ) ) ); + + ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "layer12312312", context, ok ), QStringLiteral( "layer12312312" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringList() << "a" << "B", context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << "c" << "d", context, ok ), QString() ); + QVERIFY( !ok ); } void TestQgsProcessing::parameterDistance() @@ -4558,6 +4714,14 @@ void TestQgsProcessing::parameterDistance() QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1.1" ), context, ok ), QStringLiteral( "1.1" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterDistance fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4662,6 +4826,14 @@ void TestQgsProcessing::parameterDuration() QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1.1" ), context, ok ), QStringLiteral( "1.1" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterDuration fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4757,6 +4929,14 @@ void TestQgsProcessing::parameterScale() QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( "5" ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1.1" ), context, ok ), QStringLiteral( "1.1" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterScale fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -4868,6 +5048,14 @@ void TestQgsProcessing::parameterNumber() QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( "5" ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1.1" ), context, ok ), QStringLiteral( "1.1" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterNumber('non_optional', '', type=QgsProcessingParameterNumber.Double, minValue=11, maxValue=21, defaultValue=5)" ) ); @@ -4997,6 +5185,14 @@ void TestQgsProcessing::parameterRange() QCOMPARE( def->valueAsJsonObject( "1.1,2", context ), QVariant( QStringLiteral( "1.1,2" ) ) ); QCOMPARE( def->valueAsJsonObject( QVariantList() << 1.1 << 2, context ), QVariant( QVariantList( { QVariant( 1.1 ), QVariant( 2 )} ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "1.1,2", context, ok ), QStringLiteral( "1.1,2" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariantList() << 1.1 << 2, context, ok ), QString() ); + QVERIFY( !ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('non_optional', '', type=QgsProcessingParameterNumber.Double, defaultValue=[5,6])" ) ); @@ -5169,6 +5365,18 @@ void TestQgsProcessing::parameterRasterLayer() QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( r1 ), context ), QVariant( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( raster1, context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( r1->id(), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( r1 ), context, ok ), testDataDir + QStringLiteral( "tenbytenraster.asc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRasterLayer('non_optional', '', defaultValue=None)" ) ); @@ -5290,6 +5498,14 @@ void TestQgsProcessing::parameterEnum() QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1.1" ), context ), QVariant( QStringLiteral( "1.1" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1.1" ), context, ok ), QStringLiteral( "1.1" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=False, usesStaticStrings=False, defaultValue=2)" ) ); @@ -5357,6 +5573,13 @@ void TestQgsProcessing::parameterEnum() QCOMPARE( def->valueAsJsonObject( QVariantList() << 1 << 2, context ), QVariant( QVariantList( {1, 2} ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "1,2" ), context ), QVariant( QStringLiteral( "1,2" ) ) ); + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariantList() << 1 << 2, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "1,2" ), context, ok ), QStringLiteral( "1,2" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsPythonComment( QVariant(), context ), QString() ); QCOMPARE( def->valueAsPythonComment( 2, context ), QStringLiteral( "C" ) ); QCOMPARE( def->valueAsPythonComment( QVariantList() << 1 << 2, context ), QStringLiteral( "B,C" ) ); @@ -5620,6 +5843,20 @@ void TestQgsProcessing::parameterString() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('non_optional', '', multiLine=False, defaultValue=None)" ) ); @@ -5781,6 +6018,20 @@ void TestQgsProcessing::parameterAuthConfig() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAuthConfig('non_optional', '', defaultValue='')" ) ); @@ -5901,6 +6152,16 @@ void TestQgsProcessing::parameterExpression() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QVariant( QStringLiteral( "abc\ndef" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExpression('non_optional', '', parentLayerParameterName='', defaultValue='1+1')" ) ); @@ -5993,6 +6254,16 @@ void TestQgsProcessing::parameterField() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"field", context ), QVariant( QStringLiteral( "probably\'invalid\"field" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "probably\'invalid\"field" ), context, ok ), QStringLiteral( "probably\'invalid\"field" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) ); @@ -6097,6 +6368,11 @@ void TestQgsProcessing::parameterField() QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); QCOMPARE( def->valueAsJsonObject( QStringList() << "a" << "b", context ), QVariant( QVariantList( {QStringLiteral( "a" ), QStringLiteral( "b" ) } ) ) ); + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringList() << "a" << "b", context, ok ), QString() ); + QVERIFY( !ok ); + QVariantMap map = def->toVariantMap(); QgsProcessingParameterField fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -6296,6 +6572,18 @@ void TestQgsProcessing::parameterVectorLayer() QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( v1 ), context ), QVariant( testDataDir + QStringLiteral( "multipoint.shp" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( vector1, context, ok ), testDataDir + QStringLiteral( "multipoint.shp" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( v1->id(), context, ok ), testDataDir + QStringLiteral( "multipoint.shp" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( v1 ), context, ok ), testDataDir + QStringLiteral( "multipoint.shp" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -6431,6 +6719,18 @@ void TestQgsProcessing::parameterMeshLayer() QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( m1 ), context ), QVariant( testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.2dm" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.2dm" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( mesh, context, ok ), testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( m1->id(), context, ok ), testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( m1 ), context, ok ), testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.2dm" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.2dm" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -6597,6 +6897,43 @@ void TestQgsProcessing::parameterFeatureSource() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ), context ), QVariant( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc'def" ) ), context, ok ), QStringLiteral( "abc'def" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( v2->id() ) ), context, ok ), vector2 ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( v2->id(), context, ok ), vector2 ); + QVERIFY( ok ); + // currently limits, flags, etc from feature source definitions cannot be serialized to string + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), true ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "dbname='mydb' host=localhost port=5432 sslmode=disable key='id'" ), true ) ), context, ok ), QStringLiteral( "dbname='mydb' host=localhost port=5432 sslmode=disable key='id'" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, 11 ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1 ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( v2 ), context, ok ), vector2 ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ), context, ok ), QStringLiteral( "postgres://uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFeatureSource fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -6732,6 +7069,20 @@ void TestQgsProcessing::parameterFeatureSink() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) ); QCOMPARE( def->generateTemporaryDestination(), QStringLiteral( "memory:" ) ); def->setSupportsNonFileBasedOutput( false ); @@ -6910,6 +7261,20 @@ void TestQgsProcessing::parameterVectorOut() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) ); QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".gpkg" ) ) ); QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) ); @@ -7128,6 +7493,18 @@ void TestQgsProcessing::parameterRasterOut() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterRasterDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7240,6 +7617,9 @@ void TestQgsProcessing::parameterPointCloudOut() QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) ); QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); QCOMPARE( def->defaultFileExtension(), QStringLiteral( "las" ) ); QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".las" ) ) ); QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) ); @@ -7269,6 +7649,17 @@ void TestQgsProcessing::parameterPointCloudOut() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterPointCloudDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7429,6 +7820,20 @@ void TestQgsProcessing::parameterFileOut() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFileDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7534,6 +7939,16 @@ void TestQgsProcessing::parameterFolderOut() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\" ), context, ok ), QStringLiteral( "c:\\test\\new data\\" ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterFolderDestination fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7608,6 +8023,12 @@ void TestQgsProcessing::parameterBand() QCOMPARE( def->valueAsJsonObject( QVariant(), context ), QVariant() ); QCOMPARE( def->valueAsJsonObject( 5, context ), QVariant( 5 ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('non_optional', '', parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) ); QString code = def->asScriptCode(); @@ -7659,6 +8080,13 @@ void TestQgsProcessing::parameterBand() QCOMPARE( def->valueAsJsonObject( QStringList() << "1" << "2", context ), QVariant( QVariantList( { "1", "2" } ) ) ); QCOMPARE( def->valueAsJsonObject( QVariantList() << 1 << 2, context ), QVariant( QVariantList( { 1, 2} ) ) ); + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringList() << "1" << "2", context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( def->valueAsString( QVariantList() << 1 << 2, context, ok ), QString() ); + QVERIFY( !ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterBand fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -7759,6 +8187,20 @@ void TestQgsProcessing::parameterLayout() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayout('non_optional', '', defaultValue=None)" ) ); @@ -7915,6 +8357,14 @@ void TestQgsProcessing::parameterLayoutItem() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"item", context ), QVariant( QStringLiteral( "probably\'invalid\"item" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "probably\'invalid\"item", context, ok ), QStringLiteral( "probably\'invalid\"item" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', parentLayoutParameterName='', defaultValue=None)" ) ); @@ -8047,6 +8497,18 @@ void TestQgsProcessing::parameterColor() QCOMPARE( def->valueAsJsonObject( QColor( 255, 0, 0 ), context ), QVariant( QStringLiteral( "rgba( 255, 0, 0, 1.00 )" ) ) ); QCOMPARE( def->valueAsJsonObject( QColor( 255, 0, 0, 100 ), context ), QVariant( QStringLiteral( "rgba( 255, 0, 0, 0.39 )" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "#ff0000" ), context, ok ), QStringLiteral( "#ff0000" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QColor(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QColor( 255, 0, 0 ), context, ok ), QStringLiteral( "rgba( 255, 0, 0, 1.00 )" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QColor( 255, 0, 0, 100 ), context, ok ), QStringLiteral( "rgba( 255, 0, 0, 0.39 )" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=None)" ) ); @@ -8201,6 +8663,18 @@ void TestQgsProcessing::parameterCoordinateOperation() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCoordinateOperation('non_optional', '', sourceCrsParameterName='src', destinationCrsParameterName='dest', staticSourceCrs=QgsCoordinateReferenceSystem('EPSG:7855'), staticDestinationCrs=QgsCoordinateReferenceSystem('EPSG:28355'), defaultValue=None)" ) ); @@ -8329,6 +8803,18 @@ void TestQgsProcessing::parameterMapTheme() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapTheme('non_optional', '', defaultValue=None)" ) ); @@ -8456,6 +8942,18 @@ void TestQgsProcessing::parameterProviderConnection() QCOMPARE( def->valueAsJsonObject( "uri='complex' username=\"complex\"", context ), QVariant( QStringLiteral( "uri='complex' username=\"complex\"" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.dat" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.dat" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterProviderConnection('non_optional', '', 'postgres', defaultValue=None)" ) ); @@ -8582,6 +9080,14 @@ void TestQgsProcessing::parameterDatabaseSchema() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"schema", context ), QVariant( QStringLiteral( "probably\'invalid\"schema" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "probably\'invalid\"schema", context, ok ), QStringLiteral( "probably\'invalid\"schema" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseSchema('non_optional', '', connectionParameterName='', defaultValue=None)" ) ); @@ -8664,6 +9170,14 @@ void TestQgsProcessing::parameterDatabaseTable() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc" ), context ), QVariant( QStringLiteral( "abc" ) ) ); QCOMPARE( def->valueAsJsonObject( "probably\'invalid\"schema", context ), QVariant( QStringLiteral( "probably\'invalid\"schema" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( "probably\'invalid\"schema", context, ok ), QStringLiteral( "probably\'invalid\"schema" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('non_optional', '', connectionParameterName='', schemaParameterName='', defaultValue=None)" ) ); @@ -8847,6 +9361,18 @@ void TestQgsProcessing::parameterAggregate() QCOMPARE( def->valueAsJsonObject( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "abc\ndef" ) ); QCOMPARE( def->valueAsJsonObject( QVariant( QVariantList() << map << map2 ), context ), QVariant( QVariantList() << map << map2 ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( 5, context, ok ), QStringLiteral( "5" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc" ), context, ok ), QStringLiteral( "abc" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "abc\ndef" ), context, ok ), QStringLiteral( "abc\ndef" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant( QVariantList() << map << map2 ), context, ok ), QString() ); + QVERIFY( !ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAggregate('non_optional', '', parentLayerParameterName='parent')" ) ); @@ -8920,6 +9446,10 @@ void TestQgsProcessing::parameterTinInputLayers() QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( layerList, context ) ).dump() ), QStringLiteral( "[{\"attributeIndex\":-1,\"source\":\"%1\",\"type\":0}]" ).arg( vectorLayer->source() ) ); + bool ok = false; + QCOMPARE( def->valueAsString( layerList, context, ok ), QString() ); + QVERIFY( !ok ); + const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterTinInputLayers('tin input layer', '')" ) ); } @@ -8969,6 +9499,10 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( def->valueAsJsonObject( groupsList, context ), QVariant( QVariantList( {0, 5 } ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( groupsList, context, ok ), QString() ); + QVERIFY( !ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', dataType=[QgsMeshDatasetGroupMetadata.DataOnVertices])" ) ); @@ -9001,6 +9535,9 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( def->valueAsJsonObject( groupsList, context ), QVariant( QVariantList( {2, 6 } ) ) ); + QCOMPARE( def->valueAsString( groupsList, context, ok ), QString() ); + QVERIFY( !ok ); + QVERIFY( !def->dependsOnOtherParameters().isEmpty() ); QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) ); @@ -9036,6 +9573,10 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( def->valueAsJsonObject( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ), context ), QVariant( QStringLiteral( "2020-01-01T10:00:00" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ), context, ok ), QStringLiteral( "2020-01-01T10:00:00" ) ); + QVERIFY( ok ); + QVariantMap value; QVERIFY( !def->checkValueIsAcceptable( value ) ); value[QStringLiteral( "test" )] = QStringLiteral( "test" ); @@ -9049,6 +9590,9 @@ void TestQgsProcessing::parameterMeshDatasetTime() QVERIFY( def->checkValueIsAcceptable( value ) ); QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'static'}" ) ); + QCOMPARE( def->valueAsString( value, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "static" ) ); value[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" ); @@ -9057,6 +9601,9 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), QStringLiteral( "{\"type\":\"current-context-time\"}" ) ); + QCOMPARE( def->valueAsString( value, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "current-context-time" ) ); value[QStringLiteral( "type" )] = QStringLiteral( "defined-date-time" ); @@ -9066,6 +9613,10 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2123, 1, 2), QTime(1, 2, 3))}" ) ); QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), QStringLiteral( "{\"type\":\"defined-date-time\",\"value\":\"2123-01-02T01:02:03\"}" ) ); + + QCOMPARE( def->valueAsString( value, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "defined-date-time" ) ); QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ), QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) ) ); QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ).isValid() ); @@ -9078,6 +9629,10 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'dataset-time-step','value': [1,5]}" ) ); QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( value, context ) ).dump() ), QStringLiteral( "{\"type\":\"dataset-time-step\",\"value\":[1,5]}" ) ); + + QCOMPARE( def->valueAsString( value, context, ok ), QString() ); + QVERIFY( !ok ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "dataset-time-step" ) ); QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ).isValid() ); QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ) == QgsMeshDatasetIndex( 1, 5 ) ); @@ -9166,6 +9721,16 @@ void TestQgsProcessing::parameterDateTime() QCOMPARE( def->valueAsJsonObject( QDateTime( QDate( 2014, 12, 31 ), QTime( 12, 11, 10 ) ), context ), QVariant( QStringLiteral( "2014-12-31T12:11:10" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "2015-12-31" ), context ), QVariant( QStringLiteral( "2015-12-31" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QDateTime( QDate( 2014, 12, 31 ), QTime( 0, 0, 0 ) ), context, ok ), QStringLiteral( "2014-12-31T00:00:00" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QDateTime( QDate( 2014, 12, 31 ), QTime( 12, 11, 10 ) ), context, ok ), QStringLiteral( "2014-12-31T12:11:10" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "2015-12-31" ), context, ok ), QStringLiteral( "2015-12-31" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.DateTime, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDateTime(QDate(2010, 4, 3), QTime(12, 11, 10)))" ) ); @@ -9301,6 +9866,8 @@ void TestQgsProcessing::parameterDateTime() QCOMPARE( def->valueAsPythonString( QDate( 2014, 12, 31 ), context ), QStringLiteral( "QDate(2014, 12, 31)" ) ); QCOMPARE( def->valueAsJsonObject( QDate( 2014, 12, 31 ), context ), QVariant( QStringLiteral( "2014-12-31" ) ) ); + QCOMPARE( def->valueAsString( QDate( 2014, 12, 31 ), context, ok ), QStringLiteral( "2014-12-31" ) ); + QVERIFY( ok ); pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Date, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDate(2010, 4, 3))" ) ); @@ -9406,6 +9973,8 @@ void TestQgsProcessing::parameterDateTime() QCOMPARE( def->valueAsPythonString( QTime( 13, 14, 15 ), context ), QStringLiteral( "QTime(13, 14, 15)" ) ); QCOMPARE( def->valueAsJsonObject( QTime( 13, 14, 15 ), context ), QVariant( QStringLiteral( "13:14:15" ) ) ); + QCOMPARE( def->valueAsString( QTime( 13, 14, 15 ), context, ok ), QStringLiteral( "13:14:15" ) ); + QVERIFY( ok ); pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Time, minValue=QDateTime(QDate(1, 1, 1), QTime(10, 0, 0)), maxValue=QDateTime(QDate(1, 1, 1), QTime(11, 0, 0)), defaultValue=QTime(12, 11, 13))" ) ); @@ -9505,6 +10074,9 @@ void TestQgsProcessing::parameterDxfLayers() QCOMPARE( valueAsPythonString, QStringLiteral( "[{'layer': '%1','attributeIndex': -1}]" ).arg( vectorLayer->source() ) ); QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( def->valueAsJsonObject( layerList, context ) ).dump() ), QStringLiteral( "[{\"attributeIndex\":-1,\"layer\":\"%1\"}]" ).arg( vectorLayer->source() ) ); + bool ok = false; + QCOMPARE( def->valueAsString( layerList, context, ok ), QString() ); + QVERIFY( !ok ); const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDxfLayers('dxf input layer', '')" ) ); @@ -9601,6 +10173,16 @@ void TestQgsProcessing::parameterAnnotationLayer() QCOMPARE( def->valueAsJsonObject( al->id(), context ), QVariant( al->id() ) ); QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( al ), context ), QVariant( al->id() ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "main" ), context, ok ), QStringLiteral( "main" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( al->id(), context, ok ), al->id() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( al ), context, ok ), al->id() ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAnnotationLayer('non_optional', '', defaultValue='somelayer')" ) ); @@ -9739,6 +10321,18 @@ void TestQgsProcessing::parameterPointCloudLayer() QCOMPARE( def->valueAsJsonObject( QVariant::fromValue( pc1 ), context ), QVariant( testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ) ); QCOMPARE( def->valueAsJsonObject( QStringLiteral( "c:\\test\\new data\\test.las" ), context ), QVariant( QStringLiteral( "c:\\test\\new data\\test.las" ) ) ); + bool ok = false; + QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( pointCloud, context, ok ), testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( pc1->id(), context, ok ), testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QVariant::fromValue( pc1 ), context, ok ), testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json" ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsString( QStringLiteral( "c:\\test\\new data\\test.las" ), context, ok ), QStringLiteral( "c:\\test\\new data\\test.las" ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPointCloudLayer('non_optional', '', defaultValue='somelayer')" ) ); From c157ad35b6d532a5282862613afe7873bd88980c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 14:08:04 +1000 Subject: [PATCH 03/11] Add method to convert QgsProcessingContext settings to equivalent qgis_process arguments --- .../auto_additions/qgsprocessingcontext.py | 5 +++ .../processing/qgsprocessingcontext.sip.in | 16 ++++++++++ src/core/processing/qgsprocessingcontext.cpp | 18 +++++++++++ src/core/processing/qgsprocessingcontext.h | 19 ++++++++++++ tests/src/analysis/testqgsprocessing.cpp | 31 +++++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 python/core/auto_additions/qgsprocessingcontext.py diff --git a/python/core/auto_additions/qgsprocessingcontext.py b/python/core/auto_additions/qgsprocessingcontext.py new file mode 100644 index 000000000000..cd3a8731da73 --- /dev/null +++ b/python/core/auto_additions/qgsprocessingcontext.py @@ -0,0 +1,5 @@ +# The following has been generated automatically from src/core/processing/qgsprocessingcontext.h +# monkey patching scoped based enum +QgsProcessingContext.ProcessArgumentFlag.IncludeProjectPath.__doc__ = "Include the associated project path argument" +QgsProcessingContext.ProcessArgumentFlag.__doc__ = 'Flags controlling the results given by :py:func:`~QgsProcessingContext.asQgisProcessArguments`.\n\n.. versionadded:: 3.24\n\n' + '* ``IncludeProjectPath``: ' + QgsProcessingContext.ProcessArgumentFlag.IncludeProjectPath.__doc__ +# -- diff --git a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in index 796c5a280ce9..65a38f327fc6 100644 --- a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in @@ -585,6 +585,20 @@ Sets the logging ``level`` for algorithms to use when pushing feedback messages .. seealso:: :py:func:`logLevel` .. versionadded:: 3.20 +%End + + enum class ProcessArgumentFlag + { + IncludeProjectPath, + }; + typedef QFlags ProcessArgumentFlags; + + + QStringList asQgisProcessArguments( QgsProcessingContext::ProcessArgumentFlags flags = QgsProcessingContext::ProcessArgumentFlags() ) const; +%Docstring +Returns list of the equivalent qgis_process arguments representing the settings from the context. + +.. versionadded:: 3.24 %End private: @@ -593,6 +607,8 @@ Sets the logging ``level`` for algorithms to use when pushing feedback messages QFlags operator|(QgsProcessingContext::Flag f1, QFlags f2); +QFlags operator|(QgsProcessingContext::ProcessArgumentFlag f1, QFlags f2); + class QgsProcessingLayerPostProcessorInterface diff --git a/src/core/processing/qgsprocessingcontext.cpp b/src/core/processing/qgsprocessingcontext.cpp index 4c52a4c46a06..46891fc40cc4 100644 --- a/src/core/processing/qgsprocessingcontext.cpp +++ b/src/core/processing/qgsprocessingcontext.cpp @@ -139,6 +139,24 @@ void QgsProcessingContext::setLogLevel( LogLevel level ) mLogLevel = level; } +QStringList QgsProcessingContext::asQgisProcessArguments( QgsProcessingContext::ProcessArgumentFlags flags ) const +{ + QStringList res; + if ( mDistanceUnit != QgsUnitTypes::DistanceUnknownUnit ) + res << QStringLiteral( "--distance_units=%1" ).arg( QgsUnitTypes::encodeUnit( mDistanceUnit ) ); + if ( mAreaUnit != QgsUnitTypes::AreaUnknownUnit ) + res << QStringLiteral( "--area_units=%1" ).arg( QgsUnitTypes::encodeUnit( mAreaUnit ) ); + if ( !mEllipsoid.isEmpty() ) + res << QStringLiteral( "--ellipsoid=%1" ).arg( mEllipsoid ); + + if ( mProject && flags & ProcessArgumentFlag::IncludeProjectPath ) + { + res << QStringLiteral( "--project_path=%1" ).arg( mProject->fileName() ); + } + + return res; +} + QgsDateTimeRange QgsProcessingContext::currentTimeRange() const { return mCurrentTimeRange; diff --git a/src/core/processing/qgsprocessingcontext.h b/src/core/processing/qgsprocessingcontext.h index 77831bb71dfe..ec7bc57a1c5a 100644 --- a/src/core/processing/qgsprocessingcontext.h +++ b/src/core/processing/qgsprocessingcontext.h @@ -657,6 +657,24 @@ class CORE_EXPORT QgsProcessingContext */ void setLogLevel( LogLevel level ); + /** + * Flags controlling the results given by asQgisProcessArguments(). + * + * \since QGIS 3.24 + */ + enum class ProcessArgumentFlag : int + { + IncludeProjectPath = 1 << 0, //!< Include the associated project path argument + }; + Q_DECLARE_FLAGS( ProcessArgumentFlags, ProcessArgumentFlag ) + + /** + * Returns list of the equivalent qgis_process arguments representing the settings from the context. + * + * \since QGIS 3.24 + */ + QStringList asQgisProcessArguments( QgsProcessingContext::ProcessArgumentFlags flags = QgsProcessingContext::ProcessArgumentFlags() ) const; + private: QgsProcessingContext::Flags mFlags = QgsProcessingContext::Flags(); @@ -694,6 +712,7 @@ class CORE_EXPORT QgsProcessingContext }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::ProcessArgumentFlags ) /** diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 04e2edaa215c..e52c93fadce2 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -608,6 +608,7 @@ class TestQgsProcessing: public QObject void encodeDecodeUriProvider(); void normalizeLayerSource(); void context(); + void contextToProcessArguments(); void feedback(); void mapLayers(); void mapLayerFromStore(); @@ -1261,6 +1262,36 @@ void TestQgsProcessing::context() QVERIFY( !context2.temporaryLayerStore()->mapLayer( id ) ); } +void TestQgsProcessing::contextToProcessArguments() +{ + // test converting QgsProcessingContext settings to qgis_process arguments + QgsProcessingContext context; + + QCOMPARE( context.asQgisProcessArguments(), QStringList() ); + context.setDistanceUnit( QgsUnitTypes::DistanceKilometers ); + QCOMPARE( context.asQgisProcessArguments(), QStringList( {QStringLiteral( "--distance_units=km" )} ) ); + + context.setAreaUnit( QgsUnitTypes::AreaHectares ); + QCOMPARE( context.asQgisProcessArguments(), QStringList( {QStringLiteral( "--distance_units=km" ), QStringLiteral( "--area_units=ha" )} ) ); + + context.setEllipsoid( QStringLiteral( "EPSG:7019" ) ); + QCOMPARE( context.asQgisProcessArguments(), QStringList( {QStringLiteral( "--distance_units=km" ), QStringLiteral( "--area_units=ha" ), QStringLiteral( "--ellipsoid=EPSG:7019" )} ) ); + + QgsProject p; + QgsProcessingContext context2; + QVERIFY( p.read( TEST_DATA_DIR + QStringLiteral( "/projects/custom_crs.qgs" ) ) ); + context2.setProject( &p ); + + // by default we don't include the project path argument + QCOMPARE( context2.asQgisProcessArguments(), QStringList( {QStringLiteral( "--distance_units=meters" ), QStringLiteral( "--area_units=m2" ), QStringLiteral( "--ellipsoid=NONE" )} ) ); + + QCOMPARE( context2.asQgisProcessArguments( QgsProcessingContext::ProcessArgumentFlag::IncludeProjectPath ), QStringList( + { + QStringLiteral( "--distance_units=meters" ), QStringLiteral( "--area_units=m2" ), QStringLiteral( "--ellipsoid=NONE" ), + QStringLiteral( "--project_path=%1" ).arg( TEST_DATA_DIR + QStringLiteral( "/projects/custom_crs.qgs" ) ) + } ) ); +} + void TestQgsProcessing::feedback() { QgsProcessingFeedback f; From bfd707f3e5ce7a157a700d1373553f001cbc0191 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 15:13:26 +1000 Subject: [PATCH 04/11] Add method to convert parameter value to a list of strings --- .../processing/qgsprocessingparameters.sip.in | 23 ++++++- .../processing/qgsprocessingparameters.cpp | 25 ++++++++ src/core/processing/qgsprocessingparameters.h | 18 +++++- tests/src/analysis/testqgsprocessing.cpp | 62 +++++++++++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index b1430d14cb53..bd23d41acbac 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -544,7 +544,28 @@ Returns a string version of the parameter input ``value`` (if possible). :param context: processing context :return: - value converted to string - - ok: will be set to ``True`` if value could be represented as a string + - ok: will be set to ``True`` if value could be represented as a string. + +.. seealso:: :py:func:`valueAsStringList` + +.. seealso:: :py:func:`valueAsJsonObject` + +.. seealso:: :py:func:`valueAsPythonString` + +.. versionadded:: 3.24 +%End + + virtual QStringList valueAsStringList( const QVariant &value, QgsProcessingContext &context, bool &ok /Out/ ) const; +%Docstring +Returns a string list version of the parameter input ``value`` (if possible). + +:param value: value to convert +:param context: processing context + +:return: - value converted to string list + - ok: will be set to ``True`` if value could be represented as a string list + +.. seealso:: :py:func:`valueAsString` .. seealso:: :py:func:`valueAsJsonObject` diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 5901490abd36..d7c31922677d 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2824,6 +2824,31 @@ QString QgsProcessingParameterDefinition::valueAsString( const QVariant &value, return value.toString(); } +QStringList QgsProcessingParameterDefinition::valueAsStringList( const QVariant &value, QgsProcessingContext &context, bool &ok ) const +{ + ok = true; + if ( !value.isValid( ) ) + return QStringList(); + + if ( value.type() == QVariant::Type::List || value.type() == QVariant::Type::StringList ) + { + const QVariantList sourceList = value.toList(); + QStringList resultList; + resultList.reserve( sourceList.size() ); + for ( const QVariant &v : sourceList ) + { + resultList.append( valueAsStringList( v, context, ok ) ); + } + return resultList; + } + + const QString res = valueAsString( value, context, ok ); + if ( !ok ) + return QStringList(); + + return {res}; +} + QString QgsProcessingParameterDefinition::valueAsPythonComment( const QVariant &, QgsProcessingContext & ) const { return QString(); diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index bf851c562ec7..c26a2f7fd2bd 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -635,15 +635,31 @@ class CORE_EXPORT QgsProcessingParameterDefinition * * \param value value to convert * \param context processing context - * \param ok will be set to TRUE if value could be represented as a string + * \param ok will be set to TRUE if value could be represented as a string. * \returns value converted to string * + * \see valueAsStringList() * \see valueAsJsonObject() * \see valueAsPythonString() * \since QGIS 3.24 */ virtual QString valueAsString( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + /** + * Returns a string list version of the parameter input \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 list + * \returns value converted to string list + * + * \see valueAsString() + * \see valueAsJsonObject() + * \see valueAsPythonString() + * \since QGIS 3.24 + */ + virtual QStringList valueAsStringList( const QVariant &value, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + /** * Returns a Python comment explaining a parameter \a value, or an empty string if no comment is required. * diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index e52c93fadce2..2e9cf6a0f764 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -2672,6 +2672,17 @@ void TestQgsProcessing::parameterBoolean() QCOMPARE( def->valueAsString( QVariant(), context, ok ), QString() ); QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( false, context, ok ), QStringList( {QStringLiteral( "false" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( true, context, ok ), QStringList( {QStringLiteral( "true" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( "false", context, ok ), QStringList( {QStringLiteral( "false" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( "true", context, ok ), QStringList( {QStringLiteral( "true" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariant(), context, ok ), QStringList() ); + QVERIFY( ok ); + QString code = def->asScriptCode(); QCOMPARE( code, QStringLiteral( "##non_optional_default_false=boolean false" ) ); std::unique_ptr< QgsProcessingParameterBoolean > fromCode( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); @@ -4267,6 +4278,17 @@ void TestQgsProcessing::parameterMatrix() QCOMPARE( def->valueAsString( "1,2,3", context, ok ), QVariant( QStringLiteral( "1,2,3" ) ) ); QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << 1 << 2.5 << 3, context, ok ), QStringList( {QStringLiteral( "1" ), QStringLiteral( "2.5" ), QStringLiteral( "3" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ), context, ok ), QStringList( {QStringLiteral( "1" ), QStringLiteral( "2" ), QStringLiteral( "3" ), QStringLiteral( "1" ), QStringLiteral( "2" ), QStringLiteral( "3" )} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << ( QVariantList() << 1 << QStringLiteral( "value" ) << 3 ) << ( QVariantList() << 1 << 2 << QStringLiteral( "it's a value" ) ), context, ok ), QStringList( {"1", "value", "3", "1", "2", "it's a value" } ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << ( QVariantList() << 1 << QVariant() << 3 ) << ( QVariantList() << QVariant() << 2 << 3 ), context, ok ), QStringList( {"1", "3", "2", "3"} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << ( QVariantList() << 1 << QString( "" ) << 3 ) << ( QVariantList() << 1 << 2 << QString( "" ) ), context, ok ), QStringList( {"1", "", "3", "1", "2", "" } ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('non_optional', '', numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=None)" ) ); @@ -4472,6 +4494,9 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( def->valueAsString( "uri='complex' username=\"complex\"", context, ok ), QStringLiteral( "uri='complex' username=\"complex\"" ) ); QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QStringList() << r1->id() << raster2, context, ok ), QStringList( { QString( testDataDir + QStringLiteral( "tenbytenraster.asc" ) ), QString( testDataDir + QStringLiteral( "landsat.tif" ) ) } ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('non_optional', '', layerType=QgsProcessing.TypeMapLayer, defaultValue='')" ) ); @@ -4678,6 +4703,11 @@ void TestQgsProcessing::parameterLayerList() QVERIFY( !ok ); QCOMPARE( def->valueAsString( QVariantList() << "c" << "d", context, ok ), QString() ); QVERIFY( !ok ); + + QCOMPARE( def->valueAsStringList( QStringList() << "a" << "B", context, ok ), QStringList( {"a", "B"} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << "c" << "d", context, ok ), QStringList( {"c", "d"} ) ); + QVERIFY( ok ); } void TestQgsProcessing::parameterDistance() @@ -5224,6 +5254,9 @@ void TestQgsProcessing::parameterRange() QCOMPARE( def->valueAsString( QVariantList() << 1.1 << 2, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << 1.1 << 2, context, ok ), QStringList( {"1.1", "2"} ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('non_optional', '', type=QgsProcessingParameterNumber.Double, defaultValue=[5,6])" ) ); @@ -5611,6 +5644,9 @@ void TestQgsProcessing::parameterEnum() QCOMPARE( def->valueAsString( QStringLiteral( "1,2" ), context, ok ), QStringLiteral( "1,2" ) ); QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << 1 << 2, context, ok ), QStringList( {"1", "2"} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsPythonComment( QVariant(), context ), QString() ); QCOMPARE( def->valueAsPythonComment( 2, context ), QStringLiteral( "C" ) ); QCOMPARE( def->valueAsPythonComment( QVariantList() << 1 << 2, context ), QStringLiteral( "B,C" ) ); @@ -6404,6 +6440,9 @@ void TestQgsProcessing::parameterField() QCOMPARE( def->valueAsString( QStringList() << "a" << "b", context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( QStringList() << "a" << "b", context, ok ), QStringList( {"a", "b"} ) ); + QVERIFY( ok ); + QVariantMap map = def->toVariantMap(); QgsProcessingParameterField fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -8118,6 +8157,11 @@ void TestQgsProcessing::parameterBand() QCOMPARE( def->valueAsString( QVariantList() << 1 << 2, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( QStringList() << "1" << "2", context, ok ), QStringList( {"1", "2"} ) ); + QVERIFY( ok ); + QCOMPARE( def->valueAsStringList( QVariantList() << 1 << 2, context, ok ), QStringList( {"1", "2"} ) ); + QVERIFY( ok ); + const QVariantMap map = def->toVariantMap(); QgsProcessingParameterBand fromMap( "x" ); QVERIFY( fromMap.fromVariantMap( map ) ); @@ -9404,6 +9448,9 @@ void TestQgsProcessing::parameterAggregate() QCOMPARE( def->valueAsString( QVariant( QVariantList() << map << map2 ), context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( QVariant( QVariantList() << map << map2 ), context, ok ), QStringList() ); + QVERIFY( !ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAggregate('non_optional', '', parentLayerParameterName='parent')" ) ); @@ -9481,6 +9528,9 @@ void TestQgsProcessing::parameterTinInputLayers() QCOMPARE( def->valueAsString( layerList, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( layerList, context, ok ), QStringList() ); + QVERIFY( !ok ); + const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterTinInputLayers('tin input layer', '')" ) ); } @@ -9534,6 +9584,9 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( def->valueAsString( groupsList, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( groupsList, context, ok ), QStringList( {"0", "5"} ) ); + QVERIFY( ok ); + QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', dataType=[QgsMeshDatasetGroupMetadata.DataOnVertices])" ) ); @@ -9569,6 +9622,9 @@ void TestQgsProcessing::parameterMeshDatasetGroups() QCOMPARE( def->valueAsString( groupsList, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( groupsList, context, ok ), QStringList( {"2", "6"} ) ); + QVERIFY( ok ); + QVERIFY( !def->dependsOnOtherParameters().isEmpty() ); QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) ); @@ -9624,6 +9680,9 @@ void TestQgsProcessing::parameterMeshDatasetTime() QCOMPARE( def->valueAsString( value, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( value, context, ok ), QStringList() ); + QVERIFY( !ok ); + QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "static" ) ); value[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" ); @@ -10109,6 +10168,9 @@ void TestQgsProcessing::parameterDxfLayers() QCOMPARE( def->valueAsString( layerList, context, ok ), QString() ); QVERIFY( !ok ); + QCOMPARE( def->valueAsStringList( layerList, context, ok ), QStringList() ); + QVERIFY( !ok ); + const QString pythonCode = def->asPythonString(); QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDxfLayers('dxf input layer', '')" ) ); From 0e241d5ca10558a527a7d030f624ffbee1470771 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 15:27:32 +1000 Subject: [PATCH 05/11] Add method to QgsProcessingAlgorithm to create equivalent qgis_process command --- .../processing/qgsprocessingalgorithm.sip.in | 14 +++++ .../processing/qgsprocessingalgorithm.cpp | 34 ++++++++++++ src/core/processing/qgsprocessingalgorithm.h | 14 +++++ tests/src/analysis/testqgsprocessing.cpp | 54 +++++++++++++++++++ 4 files changed, 116 insertions(+) diff --git a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in index 36cbdcd63844..27ddfe92520a 100644 --- a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in @@ -445,6 +445,20 @@ Algorithms which cannot be run from a Python command should return an empty string. %End + virtual QString asQgisProcessCommand( const QVariantMap ¶meters, QgsProcessingContext &context, bool &ok /Out/ ) const; +%Docstring +Returns a command string which will execut the algorithm using the specified ``parameters`` +via the command line qgis_process tool. + +Note that some combinations of parameter types and values cannot be represented as a qgis_process string. + +:param parameters: algorithm parameters +:param context: processing context + +:return: - equivalent qgis_process command + - ok: will be set to ``True`` if the command was successfully generated +%End + void setProvider( QgsProcessingProvider *provider ); %Docstring Associates this algorithm with its provider. No transfer of ownership is involved. diff --git a/src/core/processing/qgsprocessingalgorithm.cpp b/src/core/processing/qgsprocessingalgorithm.cpp index 489075e4c883..9f7420582bc0 100644 --- a/src/core/processing/qgsprocessingalgorithm.cpp +++ b/src/core/processing/qgsprocessingalgorithm.cpp @@ -306,6 +306,40 @@ QString QgsProcessingAlgorithm::asPythonCommand( const QVariantMap ¶meters, return s; } +QString QgsProcessingAlgorithm::asQgisProcessCommand( const QVariantMap ¶meters, QgsProcessingContext &context, bool &ok ) const +{ + ok = true; + QStringList parts; + parts.append( QStringLiteral( "qgis_process" ) ); + parts.append( QStringLiteral( "run" ) ); + parts.append( id() ); + + QgsProcessingContext::ProcessArgumentFlags argumentFlags; + // we only include the project path argument if a project is actually required by the algorithm + if ( flags() & FlagRequiresProject ) + argumentFlags |= QgsProcessingContext::ProcessArgumentFlag::IncludeProjectPath; + + parts.append( context.asQgisProcessArguments( argumentFlags ) ); + + for ( const QgsProcessingParameterDefinition *def : mParameters ) + { + if ( def->flags() & QgsProcessingParameterDefinition::FlagHidden ) + continue; + + if ( !parameters.contains( def->name() ) ) + continue; + + const QStringList partValues = def->valueAsStringList( parameters.value( def->name() ), context, ok ); + if ( !ok ) + return QString(); + + for ( const QString &partValue : partValues ) + parts << QStringLiteral( "--%1=%2" ).arg( def->name(), partValue ); + } + + return parts.join( ' ' ); +} + bool QgsProcessingAlgorithm::addParameter( QgsProcessingParameterDefinition *definition, bool createOutput ) { if ( !definition ) diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index b906fc99c4a4..61d460bbc819 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -461,6 +461,20 @@ class CORE_EXPORT QgsProcessingAlgorithm */ virtual QString asPythonCommand( const QVariantMap ¶meters, QgsProcessingContext &context ) const; + /** + * Returns a command string which will execut the algorithm using the specified \a parameters + * via the command line qgis_process tool. + * + * Note that some combinations of parameter types and values cannot be represented as a qgis_process string. + * + * \param parameters algorithm parameters + * \param context processing context + * \param ok will be set to TRUE if the command was successfully generated + * + * \returns equivalent qgis_process command + */ + virtual QString asQgisProcessCommand( const QVariantMap ¶meters, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + /** * Associates this algorithm with its provider. No transfer of ownership is involved. */ diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 2e9cf6a0f764..bf9ae0b3a6bf 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -345,6 +345,51 @@ class DummyAlgorithm : public QgsProcessingAlgorithm QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) ); } + void runAsQgisProcessCommandChecks() + { + addParameter( new QgsProcessingParameterString( "p1" ) ); + addParameter( new QgsProcessingParameterEnum( "p2", QString(), QStringList( {"a", "b"} ), true ) ); + QgsProcessingParameterString *hidden = new QgsProcessingParameterString( "p3" ); + hidden->setFlags( QgsProcessingParameterDefinition::FlagHidden ); + addParameter( hidden ); + + QVariantMap params; + QgsProcessingContext context; + + bool ok = false; + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test" ) ); + QVERIFY( ok ); + params.insert( QStringLiteral( "p1" ), "a" ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --p1=a" ) ); + QVERIFY( ok ); + params.insert( QStringLiteral( "p2" ), QVariant() ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --p1=a" ) ); + QVERIFY( ok ); + params.insert( "p2", "b" ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --p1=a --p2=b" ) ); + QVERIFY( ok ); + + params.insert( "p2", QStringList( {"b", "c"} ) ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --p1=a --p2=b --p2=c" ) ); + QVERIFY( ok ); + + // hidden, shouldn't be shown + params.insert( "p3", "b" ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --p1=a --p2=b --p2=c" ) ); + QVERIFY( ok ); + + // test inclusion of a context setting + context.setDistanceUnit( QgsUnitTypes::DistanceMeters ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --distance_units=meters --p1=a --p2=b --p2=c" ) ); + QVERIFY( ok ); + + // test non-convertible parameter value + params.insert( "p2", QVariant::fromValue( QRectF( 0, 1, 2, 3 ) ) ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QString() ); + QVERIFY( !ok ); + } + + void addDestParams() { QgsProcessingParameterFeatureSink *sinkParam1 = new QgsProcessingParameterFeatureSink( "supports" ); @@ -686,6 +731,7 @@ class TestQgsProcessing: public QObject void validateInputCrs(); void generateIteratingDestination(); void asPythonCommand(); + void asQgisProcessCommand(); void modelerAlgorithm(); void modelExecution(); void modelBranchPruning(); @@ -10828,6 +10874,14 @@ void TestQgsProcessing::asPythonCommand() alg.runAsPythonCommandChecks(); } +void TestQgsProcessing::asQgisProcessCommand() +{ + // test converting an algorithm to a qgis_process command + + DummyAlgorithm alg( "test" ); + alg.runAsQgisProcessCommandChecks(); +} + void TestQgsProcessing::modelerAlgorithm() { //static value source From 5b406be88416f3e52d8fc9f0b704dcb19bb44e96 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 15:30:15 +1000 Subject: [PATCH 06/11] [feature] Add action in advanced menu for processing algorithm to copy equivalent qgis_process command Allows for easy generation of qgis_process commands via the QGIS gui --- .../qgsprocessingalgorithmdialogbase.cpp | 38 ++++++++++++++++++- .../qgsprocessingalgorithmdialogbase.h | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp index 57e2a3a4f971..55bee4cd7ab7 100644 --- a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp +++ b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp @@ -158,15 +158,51 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par } } ); + mCopyAsQgisProcessCommand = new QAction( tr( "Copy as qgis_process Command" ), mAdvancedMenu ); + mCopyAsQgisProcessCommand->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionTerminal.svg" ) ) ); + + mAdvancedMenu->addAction( mCopyAsQgisProcessCommand ); + connect( mCopyAsQgisProcessCommand, &QAction::triggered, this, [this] + { + if ( const QgsProcessingAlgorithm *alg = algorithm() ) + { + QgsProcessingContext *context = processingContext(); + if ( !context ) + return; + + bool ok = false; + const QString command = alg->asQgisProcessCommand( createProcessingParameters(), *context, ok ); + if ( ! ok ) + { + mMessageBar->pushMessage( tr( "Current settings are not compatible with qgis_process" ), Qgis::MessageLevel::Warning ); + } + else + { + QMimeData *m = new QMimeData(); + m->setText( command ); + QClipboard *cb = QApplication::clipboard(); + +#ifdef Q_OS_LINUX + cb->setMimeData( m, QClipboard::Selection ); +#endif + cb->setMimeData( m, QClipboard::Clipboard ); + } + } + } ); + mButtonBox->addButton( mAdvancedButton, QDialogButtonBox::ResetRole ); break; } case DialogMode::Batch: break; - } + connect( mAdvancedMenu, &QMenu::aboutToShow, this, [ = ] + { + mCopyAsQgisProcessCommand->setEnabled( algorithm()&& !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool ) ); + } ); + connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm ); connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::showParameters ); connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::closeClicked ); diff --git a/src/gui/processing/qgsprocessingalgorithmdialogbase.h b/src/gui/processing/qgsprocessingalgorithmdialogbase.h index 437c8329e488..98ed9700fa87 100644 --- a/src/gui/processing/qgsprocessingalgorithmdialogbase.h +++ b/src/gui/processing/qgsprocessingalgorithmdialogbase.h @@ -421,6 +421,7 @@ class GUI_EXPORT QgsProcessingAlgorithmDialogBase : public QDialog, public QgsPr QgsMessageBar *mMessageBar = nullptr; QPushButton *mAdvancedButton = nullptr; QMenu *mAdvancedMenu = nullptr; + QAction *mCopyAsQgisProcessCommand = nullptr; bool mExecuted = false; bool mExecutedAnyResult = false; From 4393701985e63ee2c8552bd44623f330c443d61d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 15:41:58 +1000 Subject: [PATCH 07/11] Add method to copy QgsProcessingContext settings as a json map --- .../processing/qgsprocessingcontext.sip.in | 7 ++++ src/core/processing/qgsprocessingcontext.cpp | 15 +++++++ src/core/processing/qgsprocessingcontext.h | 7 ++++ tests/src/analysis/testqgsprocessing.cpp | 40 +++++++++++++++++++ 4 files changed, 69 insertions(+) diff --git a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in index 65a38f327fc6..8cdd1398c35f 100644 --- a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in @@ -585,6 +585,13 @@ Sets the logging ``level`` for algorithms to use when pushing feedback messages .. seealso:: :py:func:`logLevel` .. versionadded:: 3.20 +%End + + QVariantMap exportToMap() const; +%Docstring +Exports the context's settings to a variant map. + +.. versionadded:: 3.24 %End enum class ProcessArgumentFlag diff --git a/src/core/processing/qgsprocessingcontext.cpp b/src/core/processing/qgsprocessingcontext.cpp index 46891fc40cc4..daeec0adc0a7 100644 --- a/src/core/processing/qgsprocessingcontext.cpp +++ b/src/core/processing/qgsprocessingcontext.cpp @@ -139,6 +139,21 @@ void QgsProcessingContext::setLogLevel( LogLevel level ) mLogLevel = level; } +QVariantMap QgsProcessingContext::exportToMap() const +{ + QVariantMap res; + if ( mDistanceUnit != QgsUnitTypes::DistanceUnknownUnit ) + res.insert( QStringLiteral( "distance_units" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) ); + if ( mAreaUnit != QgsUnitTypes::AreaUnknownUnit ) + res.insert( QStringLiteral( "area_units" ), QgsUnitTypes::encodeUnit( mAreaUnit ) ); + if ( !mEllipsoid.isEmpty() ) + res.insert( QStringLiteral( "ellipsoid" ), mEllipsoid ); + if ( mProject ) + res.insert( QStringLiteral( "project_path" ), mProject->fileName() ); + + return res; +} + QStringList QgsProcessingContext::asQgisProcessArguments( QgsProcessingContext::ProcessArgumentFlags flags ) const { QStringList res; diff --git a/src/core/processing/qgsprocessingcontext.h b/src/core/processing/qgsprocessingcontext.h index ec7bc57a1c5a..99f7a620cbb8 100644 --- a/src/core/processing/qgsprocessingcontext.h +++ b/src/core/processing/qgsprocessingcontext.h @@ -657,6 +657,13 @@ class CORE_EXPORT QgsProcessingContext */ void setLogLevel( LogLevel level ); + /** + * Exports the context's settings to a variant map. + * + * \since QGIS 3.24 + */ + QVariantMap exportToMap() const; + /** * Flags controlling the results given by asQgisProcessArguments(). * diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index bf9ae0b3a6bf..571e0b4eedc5 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -654,6 +654,7 @@ class TestQgsProcessing: public QObject void normalizeLayerSource(); void context(); void contextToProcessArguments(); + void contextToMap(); void feedback(); void mapLayers(); void mapLayerFromStore(); @@ -1338,6 +1339,45 @@ void TestQgsProcessing::contextToProcessArguments() } ) ); } +void TestQgsProcessing::contextToMap() +{ + // test converting QgsProcessingContext settings to a json map + QgsProcessingContext context; + + QCOMPARE( context.exportToMap(), QVariantMap() ); + context.setDistanceUnit( QgsUnitTypes::DistanceKilometers ); + QCOMPARE( context.exportToMap(), QVariantMap( {{ + QStringLiteral( "distance_units" ), QStringLiteral( "km" ) + }} ) ); + + context.setAreaUnit( QgsUnitTypes::AreaHectares ); + QCOMPARE( context.exportToMap(), QVariantMap( + { + {QStringLiteral( "distance_units" ), QStringLiteral( "km" )}, + {QStringLiteral( "area_units" ), QStringLiteral( "ha" )} + } ) ); + + context.setEllipsoid( QStringLiteral( "EPSG:7019" ) ); + QCOMPARE( context.exportToMap(), QVariantMap( + { + {QStringLiteral( "distance_units" ), QStringLiteral( "km" )}, + {QStringLiteral( "area_units" ), QStringLiteral( "ha" )}, + {QStringLiteral( "ellipsoid" ), QStringLiteral( "EPSG:7019" )}, + } ) ); + + QgsProject p; + QgsProcessingContext context2; + QVERIFY( p.read( TEST_DATA_DIR + QStringLiteral( "/projects/custom_crs.qgs" ) ) ); + context2.setProject( &p ); + QCOMPARE( context2.exportToMap(), QVariantMap( + { + {QStringLiteral( "distance_units" ), QStringLiteral( "meters" )}, + {QStringLiteral( "area_units" ), QStringLiteral( "m2" )}, + {QStringLiteral( "ellipsoid" ), QStringLiteral( "NONE" )}, + {QStringLiteral( "project_path" ), QString( TEST_DATA_DIR + QStringLiteral( "/projects/custom_crs.qgs" ) )} + } ) ); +} + void TestQgsProcessing::feedback() { QgsProcessingFeedback f; From d2fb618a92369459c77c81eaeb910c90bc2f7d3b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 16:16:44 +1000 Subject: [PATCH 08/11] Add method to convert parameters for a processing algorithm to a JSON serializable map --- .../processing/qgsprocessingalgorithm.sip.in | 12 ++++- .../processing/qgsprocessingalgorithm.cpp | 24 ++++++++++ src/core/processing/qgsprocessingalgorithm.h | 11 ++++- tests/src/analysis/testqgsprocessing.cpp | 46 +++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in index 27ddfe92520a..1201617dd22c 100644 --- a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in @@ -447,7 +447,7 @@ string. virtual QString asQgisProcessCommand( const QVariantMap ¶meters, QgsProcessingContext &context, bool &ok /Out/ ) const; %Docstring -Returns a command string which will execut the algorithm using the specified ``parameters`` +Returns a command string which will execute the algorithm using the specified ``parameters`` via the command line qgis_process tool. Note that some combinations of parameter types and values cannot be represented as a qgis_process string. @@ -457,6 +457,16 @@ Note that some combinations of parameter types and values cannot be represented :return: - equivalent qgis_process command - ok: will be set to ``True`` if the command was successfully generated + + +.. versionadded:: 3.24 +%End + + virtual QVariantMap asMap( const QVariantMap ¶meters, QgsProcessingContext &context ) const; +%Docstring +Returns a JSON serializable variant map containing the specified ``parameters`` and ``context`` settings. + +.. versionadded:: 3.24 %End void setProvider( QgsProcessingProvider *provider ); diff --git a/src/core/processing/qgsprocessingalgorithm.cpp b/src/core/processing/qgsprocessingalgorithm.cpp index 9f7420582bc0..533e6cf97b61 100644 --- a/src/core/processing/qgsprocessingalgorithm.cpp +++ b/src/core/processing/qgsprocessingalgorithm.cpp @@ -340,6 +340,30 @@ QString QgsProcessingAlgorithm::asQgisProcessCommand( const QVariantMap ¶met return parts.join( ' ' ); } +QVariantMap QgsProcessingAlgorithm::asMap( const QVariantMap ¶meters, QgsProcessingContext &context ) const +{ + QVariantMap properties = context.exportToMap(); + + // we only include the project path argument if a project is actually required by the algorithm + if ( !( flags() & FlagRequiresProject ) ) + properties.remove( QStringLiteral( "project_path" ) ); + + QVariantMap paramValues; + for ( const QgsProcessingParameterDefinition *def : mParameters ) + { + if ( def->flags() & QgsProcessingParameterDefinition::FlagHidden ) + continue; + + if ( !parameters.contains( def->name() ) ) + continue; + + paramValues.insert( def->name(), def->valueAsJsonObject( parameters.value( def->name() ), context ) ); + } + + properties.insert( QStringLiteral( "inputs" ), paramValues ); + return properties; +} + bool QgsProcessingAlgorithm::addParameter( QgsProcessingParameterDefinition *definition, bool createOutput ) { if ( !definition ) diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index 61d460bbc819..a37508905c57 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -462,7 +462,7 @@ class CORE_EXPORT QgsProcessingAlgorithm virtual QString asPythonCommand( const QVariantMap ¶meters, QgsProcessingContext &context ) const; /** - * Returns a command string which will execut the algorithm using the specified \a parameters + * Returns a command string which will execute the algorithm using the specified \a parameters * via the command line qgis_process tool. * * Note that some combinations of parameter types and values cannot be represented as a qgis_process string. @@ -472,9 +472,18 @@ class CORE_EXPORT QgsProcessingAlgorithm * \param ok will be set to TRUE if the command was successfully generated * * \returns equivalent qgis_process command + * + * \since QGIS 3.24 */ virtual QString asQgisProcessCommand( const QVariantMap ¶meters, QgsProcessingContext &context, bool &ok SIP_OUT ) const; + /** + * Returns a JSON serializable variant map containing the specified \a parameters and \a context settings. + * + * \since QGIS 3.24 + */ + virtual QVariantMap asMap( const QVariantMap ¶meters, QgsProcessingContext &context ) const; + /** * Associates this algorithm with its provider. No transfer of ownership is involved. */ diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 571e0b4eedc5..19e5ccbff656 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -389,6 +389,43 @@ class DummyAlgorithm : public QgsProcessingAlgorithm QVERIFY( !ok ); } + void runAsAsJsonMapChecks() + { + addParameter( new QgsProcessingParameterString( "p1" ) ); + addParameter( new QgsProcessingParameterEnum( "p2", QString(), QStringList( {"a", "b"} ), true ) ); + QgsProcessingParameterString *hidden = new QgsProcessingParameterString( "p3" ); + hidden->setFlags( QgsProcessingParameterDefinition::FlagHidden ); + addParameter( hidden ); + + QVariantMap params; + QgsProcessingContext context; + + QString res; + QCOMPARE( asMap( params, context ), QVariantMap( { {QStringLiteral( "inputs" ), QVariantMap()}} ) ); + params.insert( QStringLiteral( "p1" ), "a" ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"inputs\":{\"p1\":\"a\"}}" ) ); + params.insert( QStringLiteral( "p2" ), QVariant() ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"inputs\":{\"p1\":\"a\",\"p2\":null}}" ) ); + params.insert( "p2", "b" ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"inputs\":{\"p1\":\"a\",\"p2\":\"b\"}}" ) ); + + params.insert( "p2", QStringList( {"b", "c"} ) ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"inputs\":{\"p1\":\"a\",\"p2\":[\"b\",\"c\"]}}" ) ); + + // hidden, shouldn't be shown + params.insert( "p3", "b" ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"inputs\":{\"p1\":\"a\",\"p2\":[\"b\",\"c\"]}}" ) ); + + // test inclusion of a context setting + context.setDistanceUnit( QgsUnitTypes::DistanceMeters ); + res = QString::fromStdString( QgsJsonUtils::jsonFromVariant( asMap( params, context ) ).dump() ); + QCOMPARE( res, QStringLiteral( "{\"distance_units\":\"meters\",\"inputs\":{\"p1\":\"a\",\"p2\":[\"b\",\"c\"]}}" ) ); + } void addDestParams() { @@ -733,6 +770,7 @@ class TestQgsProcessing: public QObject void generateIteratingDestination(); void asPythonCommand(); void asQgisProcessCommand(); + void asJsonMap(); void modelerAlgorithm(); void modelExecution(); void modelBranchPruning(); @@ -10922,6 +10960,14 @@ void TestQgsProcessing::asQgisProcessCommand() alg.runAsQgisProcessCommandChecks(); } +void TestQgsProcessing::asJsonMap() +{ + // test converting an algorithm to a json serializable map + + DummyAlgorithm alg( "test" ); + alg.runAsAsJsonMapChecks(); +} + void TestQgsProcessing::modelerAlgorithm() { //static value source From 4b6ec899af31810e18ce81358b8ac215dec670fb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 16:31:19 +1000 Subject: [PATCH 09/11] [feature] Add action to processing algorithm advanced menu to copy the current settings as JSON and paste json settings from clipboard The utility of this is two fold: 1. It provides a way for users to copy the settings defined in the dialog in a text format, so they can store these easily and then restore later by pasting the value 2. It provides an easy way for users to copy the settings into the json format consumed by qgis_process (following https://github.com/qgis/QGIS/pull/46497), so that it is easy for users to see the expected format even for complex parameters (like tin interpolation parameters) --- .../qgsprocessingalgorithmdialogbase.sip.in | 7 +++ .../qgsprocessingalgorithmdialogbase.cpp | 55 ++++++++++++++++++- .../qgsprocessingalgorithmdialogbase.h | 8 +++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/python/gui/auto_generated/processing/qgsprocessingalgorithmdialogbase.sip.in b/python/gui/auto_generated/processing/qgsprocessingalgorithmdialogbase.sip.in index 33eed64d2567..e14e69ba9177 100644 --- a/python/gui/auto_generated/processing/qgsprocessingalgorithmdialogbase.sip.in +++ b/python/gui/auto_generated/processing/qgsprocessingalgorithmdialogbase.sip.in @@ -131,6 +131,13 @@ Sets the logging ``level`` to use when running algorithms from the dialog. .. seealso:: :py:func:`logLevel` .. versionadded:: 3.20 +%End + + virtual void setParameters( const QVariantMap &values ); +%Docstring +Sets the parameter ``values`` to show in the dialog. + +.. versionadded:: 3.24 %End public slots: diff --git a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp index 55bee4cd7ab7..be7abf84cc4c 100644 --- a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp +++ b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp @@ -25,6 +25,7 @@ #include "qgsstringutils.h" #include "qgsapplication.h" #include "qgspanelwidget.h" +#include "qgsjsonutils.h" #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include ///@cond NOT_STABLE @@ -160,8 +162,8 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par mCopyAsQgisProcessCommand = new QAction( tr( "Copy as qgis_process Command" ), mAdvancedMenu ); mCopyAsQgisProcessCommand->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionTerminal.svg" ) ) ); - mAdvancedMenu->addAction( mCopyAsQgisProcessCommand ); + connect( mCopyAsQgisProcessCommand, &QAction::triggered, this, [this] { if ( const QgsProcessingAlgorithm *alg = algorithm() ) @@ -190,6 +192,51 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par } } ); + mAdvancedMenu->addSeparator(); + + QAction *copyAsJson = new QAction( tr( "Copy as JSON" ), mAdvancedMenu ); + copyAsJson->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionEditCopy.svg" ) ) ); + + mAdvancedMenu->addAction( copyAsJson ); + connect( copyAsJson, &QAction::triggered, this, [this] + { + if ( const QgsProcessingAlgorithm *alg = algorithm() ) + { + QgsProcessingContext *context = processingContext(); + if ( !context ) + return; + + const QVariantMap properties = alg->asMap( createProcessingParameters(), *context ); + const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( properties ).dump( 2 ) ); + + QMimeData *m = new QMimeData(); + m->setText( json ); + QClipboard *cb = QApplication::clipboard(); + +#ifdef Q_OS_LINUX + cb->setMimeData( m, QClipboard::Selection ); +#endif + cb->setMimeData( m, QClipboard::Clipboard ); + } + } ); + + mPasteJsonAction = new QAction( tr( "Paste Settings" ), mAdvancedMenu ); + mPasteJsonAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionEditPaste.svg" ) ) ); + + mAdvancedMenu->addAction( mPasteJsonAction ); + connect( mPasteJsonAction, &QAction::triggered, this, [this] + { + const QString text = QApplication::clipboard()->text(); + if ( text.isEmpty() ) + return; + + const QVariantMap parameterValues = QgsJsonUtils::parseJson( text ).toMap().value( QStringLiteral( "inputs" ) ).toMap(); + if ( parameterValues.isEmpty() ) + return; + + setParameters( parameterValues ); + } ); + mButtonBox->addButton( mAdvancedButton, QDialogButtonBox::ResetRole ); break; } @@ -200,7 +247,8 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par connect( mAdvancedMenu, &QMenu::aboutToShow, this, [ = ] { - mCopyAsQgisProcessCommand->setEnabled( algorithm()&& !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool ) ); + mCopyAsQgisProcessCommand->setEnabled( algorithm() && !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool ) ); + mPasteJsonAction->setEnabled( !QApplication::clipboard()->text().isEmpty() ); } ); connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm ); @@ -225,6 +273,9 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = default; +void QgsProcessingAlgorithmDialogBase::setParameters( const QVariantMap & ) +{} + void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm ) { mAlgorithm.reset( algorithm ); diff --git a/src/gui/processing/qgsprocessingalgorithmdialogbase.h b/src/gui/processing/qgsprocessingalgorithmdialogbase.h index 98ed9700fa87..13dafd80df73 100644 --- a/src/gui/processing/qgsprocessingalgorithmdialogbase.h +++ b/src/gui/processing/qgsprocessingalgorithmdialogbase.h @@ -191,6 +191,13 @@ class GUI_EXPORT QgsProcessingAlgorithmDialogBase : public QDialog, public QgsPr */ void setLogLevel( QgsProcessingContext::LogLevel level ); + /** + * Sets the parameter \a values to show in the dialog. + * + * \since QGIS 3.24 + */ + virtual void setParameters( const QVariantMap &values ); + public slots: /** @@ -422,6 +429,7 @@ class GUI_EXPORT QgsProcessingAlgorithmDialogBase : public QDialog, public QgsPr QPushButton *mAdvancedButton = nullptr; QMenu *mAdvancedMenu = nullptr; QAction *mCopyAsQgisProcessCommand = nullptr; + QAction *mPasteJsonAction = nullptr; bool mExecuted = false; bool mExecutedAnyResult = false; From 4248d820164a4f52f66e0376546f2fe358cd670f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 16 Dec 2021 16:43:59 +1000 Subject: [PATCH 10/11] Fix indentation dance --- src/gui/processing/qgsprocessingalgorithmdialogbase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp index be7abf84cc4c..d7fd5b9e736b 100644 --- a/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp +++ b/src/gui/processing/qgsprocessingalgorithmdialogbase.cpp @@ -247,7 +247,8 @@ QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *par connect( mAdvancedMenu, &QMenu::aboutToShow, this, [ = ] { - mCopyAsQgisProcessCommand->setEnabled( algorithm() && !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool ) ); + mCopyAsQgisProcessCommand->setEnabled( algorithm() + && !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool ) ); mPasteJsonAction->setEnabled( !QApplication::clipboard()->text().isEmpty() ); } ); From 8eb1d5a3d2fa088d7a6ea62bcc68baf23cfc6f0e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 17 Dec 2021 06:54:21 +1000 Subject: [PATCH 11/11] Fix typos --- .../auto_generated/processing/qgsprocessingparameters.sip.in | 2 +- src/core/processing/qgsprocessingparameters.cpp | 4 ++-- src/core/processing/qgsprocessingparameters.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index bd23d41acbac..ee1abda2c90a 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -526,7 +526,7 @@ parameter value when running an algorithm directly from a Python command. Returns a version of the parameter input ``value``, which is suitable for use in a JSON object. This method must return only simple values which can be losslessly encapsulated in a serialized -JSON map. For instance, and QGIS class values (such as :py:class:`QgsCoordinateReferenceSystem`) must be +JSON map. For instance, any QGIS class values (such as :py:class:`QgsCoordinateReferenceSystem`) must be converted to a simple string or numeric value equivalent. .. seealso:: :py:func:`valueAsPythonString` diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index d7c31922677d..fc24216ad6d3 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2480,7 +2480,7 @@ QVariant QgsProcessingParameterDefinition::valueAsJsonObject( const QVariant &va { switch ( value.userType() ) { - // simple types which can be directly represented in JSON -- not that strings are NOT handled here yet! + // simple types which can be directly represented in JSON -- note that strings are NOT handled here yet! case QMetaType::Bool: case QMetaType::Char: case QMetaType::Int: @@ -2658,7 +2658,7 @@ QString QgsProcessingParameterDefinition::valueAsString( const QVariant &value, switch ( value.userType() ) { - // simple types which can be directly represented in JSON -- not that strings are NOT handled here yet! + // simple types which can be directly represented in JSON -- note that strings are NOT handled here yet! case QMetaType::Bool: case QMetaType::Char: case QMetaType::Int: diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index c26a2f7fd2bd..6d2560a2f8c7 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -621,7 +621,7 @@ class CORE_EXPORT QgsProcessingParameterDefinition * Returns a version of the parameter input \a value, which is suitable for use in a JSON object. * * This method must return only simple values which can be losslessly encapsulated in a serialized - * JSON map. For instance, and QGIS class values (such as QgsCoordinateReferenceSystem) must be + * JSON map. For instance, any QGIS class values (such as QgsCoordinateReferenceSystem) must be * converted to a simple string or numeric value equivalent. * * \see valueAsPythonString()