diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 9d57fef7033b..bf4b26f74482 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2692,12 +2692,10 @@ QString QgsProcessingParameterDefinition::valueAsStringPrivate( const QVariant & return QString(); case QgsProperty::StaticProperty: return valueAsString( prop.staticValue(), context, ok ); - - // these are not supported for serialization case QgsProperty::FieldBasedProperty: + return QStringLiteral( "field:%1" ).arg( prop.field() ); case QgsProperty::ExpressionBasedProperty: - QgsDebugMsg( QStringLiteral( "could not convert expression/field based property to string" ) ); - return QString(); + return QStringLiteral( "expression:%1" ).arg( prop.expressionString() ); } } diff --git a/src/core/processing/qgsprocessingparametertypeimpl.h b/src/core/processing/qgsprocessingparametertypeimpl.h index 863367261a3d..28597fb434c5 100644 --- a/src/core/processing/qgsprocessingparametertypeimpl.h +++ b/src/core/processing/qgsprocessingparametertypeimpl.h @@ -292,7 +292,9 @@ class CORE_EXPORT QgsProcessingParameterTypeBoolean : public QgsProcessingParame QStringList acceptedStringValues() const override { return QStringList() << QObject::tr( "1 for true/yes" ) - << QObject::tr( "0 for false/no" ); + << QObject::tr( "0 for false/no" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1124,7 +1126,9 @@ class CORE_EXPORT QgsProcessingParameterTypeString : public QgsProcessingParamet QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "String value" ); + return QStringList() << QObject::tr( "String value" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1330,7 +1334,9 @@ class CORE_EXPORT QgsProcessingParameterTypeNumber : public QgsProcessingParamet QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "A numeric value" ); + return QStringList() << QObject::tr( "A numeric value" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1382,7 +1388,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDistance : public QgsProcessingParam QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "A numeric value" ); + return QStringList() << QObject::tr( "A numeric value" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1434,7 +1442,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDuration : public QgsProcessingParam QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "A numeric value (unit type set by algorithms)" ); + return QStringList() << QObject::tr( "A numeric value (unit type set by algorithms)" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1753,7 +1763,9 @@ class CORE_EXPORT QgsProcessingParameterTypeColor : public QgsProcessingParamete QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "String representation of color, e.g #ff0000 or rgba(200,100,50,0.8)" ); + return QStringList() << QObject::tr( "String representation of color, e.g #ff0000 or rgba(200,100,50,0.8)" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; @@ -1908,7 +1920,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDateTime : public QgsProcessingParam QStringList acceptedStringValues() const override { - return QStringList() << QObject::tr( "A datetime value in ISO format" ); + return QStringList() << QObject::tr( "A datetime value in ISO format" ) + << QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" ) + << QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" ); } }; diff --git a/src/core/processing/qgsprocessingutils.cpp b/src/core/processing/qgsprocessingutils.cpp index 34f0f6c69597..201ac3e26aa7 100644 --- a/src/core/processing/qgsprocessingutils.cpp +++ b/src/core/processing/qgsprocessingutils.cpp @@ -1411,6 +1411,23 @@ QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantM output.insert( it.key(), it.value() ); } } + else if ( it.value().type() == QVariant::String ) + { + const QString stringValue = it.value().toString(); + + if ( stringValue.startsWith( QLatin1String( "field:" ) ) ) + { + output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) ); + } + else if ( stringValue.startsWith( QLatin1String( "expression:" ) ) ) + { + output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) ); + } + else + { + output.insert( it.key(), it.value() ); + } + } else { output.insert( it.key(), it.value() ); diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index c97aa73aee9c..6858074aa9a4 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -396,6 +396,14 @@ class DummyAlgorithm : public QgsProcessingAlgorithm params.insert( "p2", QStringLiteral( "thisisa'test" ) ); QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --distance_units=meters --p1=a --p2='thisisa'\\''test'" ) ); QVERIFY( ok ); + + addParameter( new QgsProcessingParameterString( "dd_field", QString(), true ) ); + addParameter( new QgsProcessingParameterString( "dd_expression", QString(), true ) ); + + params.insert( "dd_field", QgsProperty::fromField( QStringLiteral( "my field" ) ) ); + params.insert( "dd_expression", QgsProperty::fromExpression( QStringLiteral( "\"some field\" * 200" ) ) ); + QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --distance_units=meters --p1=a --p2='thisisa'\\''test' --dd_field='field:my field' --dd_expression='expression:\"some field\" * 200'" ) ); + QVERIFY( ok ); } void runAsAsJsonMapChecks() @@ -11679,6 +11687,8 @@ void TestQgsProcessing::preprocessParameters() inputs.insert( QStringLiteral( "string" ), QStringLiteral( "a string" ) ); inputs.insert( QStringLiteral( "data defined field" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}, {QStringLiteral( "field" ), QStringLiteral( "DEPTH_FIELD" ) }} ) ); inputs.insert( QStringLiteral( "data defined expression" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}, {QStringLiteral( "expression" ), QStringLiteral( "A_FIELD * 200" ) }} ) ); + inputs.insert( QStringLiteral( "data defined field using string" ), QStringLiteral( "field:MY FIELD" ) ); + inputs.insert( QStringLiteral( "data defined expression using string" ), QStringLiteral( "expression:SOME_FIELD * 2" ) ); inputs.insert( QStringLiteral( "invalid" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}} ) ); bool ok = false; @@ -11697,8 +11707,12 @@ void TestQgsProcessing::preprocessParameters() QCOMPARE( outputs.value( QStringLiteral( "string" ) ).toString(), QStringLiteral( "a string" ) ); QCOMPARE( outputs.value( QStringLiteral( "data defined field" ) ).value< QgsProperty >().propertyType(), QgsProperty::FieldBasedProperty ); QCOMPARE( outputs.value( QStringLiteral( "data defined field" ) ).value< QgsProperty >().field(), QStringLiteral( "DEPTH_FIELD" ) ); + QCOMPARE( outputs.value( QStringLiteral( "data defined field using string" ) ).value< QgsProperty >().propertyType(), QgsProperty::FieldBasedProperty ); + QCOMPARE( outputs.value( QStringLiteral( "data defined field using string" ) ).value< QgsProperty >().field(), QStringLiteral( "MY FIELD" ) ); QCOMPARE( outputs.value( QStringLiteral( "data defined expression" ) ).value< QgsProperty >().propertyType(), QgsProperty::ExpressionBasedProperty ); QCOMPARE( outputs.value( QStringLiteral( "data defined expression" ) ).value< QgsProperty >().expressionString(), QStringLiteral( "A_FIELD * 200" ) ); + QCOMPARE( outputs.value( QStringLiteral( "data defined expression using string" ) ).value< QgsProperty >().propertyType(), QgsProperty::ExpressionBasedProperty ); + QCOMPARE( outputs.value( QStringLiteral( "data defined expression using string" ) ).value< QgsProperty >().expressionString(), QStringLiteral( "SOME_FIELD * 2" ) ); // test round trip of data defined parameters const QgsProcessingAlgorithm *bufferAlg = QgsApplication::processingRegistry()->algorithmById( "native:buffer" ); diff --git a/tests/src/python/test_qgsprocessexecutable_pt2.py b/tests/src/python/test_qgsprocessexecutable_pt2.py index f679d4808494..b0d400120b2d 100644 --- a/tests/src/python/test_qgsprocessexecutable_pt2.py +++ b/tests/src/python/test_qgsprocessexecutable_pt2.py @@ -306,6 +306,19 @@ def testLoadLayer(self): self.assertEqual(rc, 1) def testDynamicParameters(self): + output_file = self.TMP_DIR + '/dynamic_out2.shp' + + rc, output, err = self.run_process( + ['run', 'native:buffer', '--INPUT=' + TEST_DATA_DIR + '/points.shp', '--OUTPUT=' + output_file, '--DISTANCE=field:fid', '--json']) + self.assertFalse(self._strip_ignorable_errors(err)) + + self.assertEqual(rc, 0) + + res = json.loads(output) + self.assertEqual(res['algorithm_details']['id'], 'native:buffer') + self.assertEqual(res['inputs']['DISTANCE'], 'field:fid') + + def testDynamicParametersJson(self): output_file = self.TMP_DIR + '/dynamic_out.shp' params = {