Skip to content
Permalink
Browse files
[processing] When exporting a model to a python script, include
helpful comments next to all static enum parameter values used
in child algorithms with the corresponding text.

I.e. instead of

  alg_params = {
    'END_CAP_STYLE': 2,
  }

we now export

  alg_params = {
    'END_CAP_STYLE': 2,  # Flat
  }

Much more readable!
  • Loading branch information
nyalldawson committed Apr 9, 2021
1 parent 3cf8a98 commit d3870937b2fcd0f257e486ae4ae9b2e9b8755b69
@@ -248,11 +248,18 @@ Loads this source from a QVariantMap.
.. seealso:: :py:func:`toVariant`
%End

QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const;
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlyChildNames ) const;
%Docstring
Attempts to convert the source to executable Python code.

The ``friendlyChildNames`` argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
%End

QString asPythonComment( const QgsProcessingParameterDefinition *definition ) const;
%Docstring
Returns an explanatory Python comment for the source, or an empty string if no comment is available.

.. versionadded:: 3.20
%End

QString friendlyIdentifier( QgsProcessingModelAlgorithm *model ) const;
@@ -507,6 +507,13 @@ 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.
%End

virtual QString valueAsPythonComment( const QVariant &value, QgsProcessingContext &context ) const;
%Docstring
Returns a Python comment explaining a parameter ``value``, or an empty string if no comment is required.

.. versionadded:: 3.20
%End

virtual QString asScriptCode() const;
@@ -2435,6 +2442,8 @@ Returns the type name for the parameter class.

virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;

virtual QString valueAsPythonComment( const QVariant &value, QgsProcessingContext &context ) const;

virtual QString asScriptCode() const;

virtual QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const;
@@ -241,28 +241,48 @@ QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing:
lines << baseIndent + QStringLiteral( "# %1" ).arg( mComment.description() );

QStringList paramParts;
QStringList paramComments;
for ( auto paramIt = mParams.constBegin(); paramIt != mParams.constEnd(); ++paramIt )
{
QStringList sourceParts;
QStringList sourceComments;
const QgsProcessingParameterDefinition *def = algorithm() ? algorithm()->parameterDefinition( paramIt.key() ) : nullptr;
const auto parts = paramIt.value();
sourceParts.reserve( parts.size() );
sourceComments.reserve( parts.size() );
for ( const QgsProcessingModelChildParameterSource &source : parts )
{
QString part = source.asPythonCode( outputType, def, friendlyChildNames );
if ( !part.isEmpty() )
{
sourceParts << part;
sourceComments << source.asPythonComment( def );
}
}
if ( sourceParts.count() == 1 )
{
paramParts << QStringLiteral( "'%1': %2" ).arg( paramIt.key(), sourceParts.at( 0 ) );
paramComments << sourceComments.at( 0 );
}
else
{
paramParts << QStringLiteral( "'%1': [%2]" ).arg( paramIt.key(), sourceParts.join( ',' ) );
paramComments << QString();
}
}

lines << baseIndent + QStringLiteral( "alg_params = {" );
lines.reserve( lines.size() + paramParts.size() );
int i = 0;
for ( const QString &p : std::as_const( paramParts ) )
{
lines << baseIndent + lineIndent + p + ',';
QString line = baseIndent + lineIndent + p + ',';
if ( !paramComments.value( i ).isEmpty() )
{
line += QStringLiteral( " # %1" ).arg( paramComments.value( i ) );
}
lines << line;
i++;
}
for ( auto it = extraParameters.constBegin(); it != extraParameters.constEnd(); ++it )
{
@@ -161,15 +161,15 @@ bool QgsProcessingModelChildParameterSource::loadVariant( const QVariantMap &map
return true;
}

QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessing::PythonOutputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const
QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessing::PythonOutputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlyChildNames ) const
{
switch ( mSource )
{
case ModelParameter:
return QStringLiteral( "parameters['%1']" ).arg( mParameterName );

case ChildOutput:
return QStringLiteral( "outputs['%1']['%2']" ).arg( friendlydChildNames.value( mChildId, mChildId ), mOutputName );
return QStringLiteral( "outputs['%1']['%2']" ).arg( friendlyChildNames.value( mChildId, mChildId ), mOutputName );

case StaticValue:
if ( definition )
@@ -194,6 +194,31 @@ QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessin
return QString();
}

QString QgsProcessingModelChildParameterSource::asPythonComment( const QgsProcessingParameterDefinition *definition ) const
{
switch ( mSource )
{
case ModelParameter:
case ChildOutput:
case Expression:
case ExpressionText:
case ModelOutput:
return QString();

case StaticValue:
if ( definition )
{
QgsProcessingContext c;
return definition->valueAsPythonComment( mStaticValue, c );
}
else
{
return QString();
}
}
return QString();
}

QString QgsProcessingModelChildParameterSource::friendlyIdentifier( QgsProcessingModelAlgorithm *model ) const
{
switch ( mSource )
@@ -227,7 +227,14 @@ class CORE_EXPORT QgsProcessingModelChildParameterSource
*
* The \a friendlyChildNames argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
*/
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const;
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlyChildNames ) const;

/**
* Returns an explanatory Python comment for the source, or an empty string if no comment is available.
*
* \since QGIS 3.20
*/
QString asPythonComment( const QgsProcessingParameterDefinition *definition ) const;

/**
* Returns a user-friendly identifier for this source, given the context of the specified \a model.
@@ -2407,6 +2407,11 @@ QString QgsProcessingParameterDefinition::valueAsPythonString( const QVariant &v
return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
}

QString QgsProcessingParameterDefinition::valueAsPythonComment( const QVariant &, QgsProcessingContext & ) const
{
return QString();
}

QString QgsProcessingParameterDefinition::asScriptCode() const
{
QString code = QStringLiteral( "##%1=" ).arg( mName );
@@ -4439,6 +4444,52 @@ QString QgsProcessingParameterEnum::valueAsPythonString( const QVariant &value,
}
}

QString QgsProcessingParameterEnum::valueAsPythonComment( const QVariant &value, QgsProcessingContext & ) const
{
if ( !value.isValid() )
return QString();

if ( value.canConvert<QgsProperty>() )
return QString();

if ( mUsesStaticStrings )
{
return QString();
}
else
{
if ( value.type() == QVariant::List )
{
QStringList parts;
const QVariantList toList = value.toList();
parts.reserve( toList.size() );
for ( const QVariant &val : toList )
{
parts << mOptions.value( static_cast< int >( val.toDouble() ) );
}
return parts.join( ',' );
}
else if ( value.type() == QVariant::String )
{
const QStringList parts = value.toString().split( ',' );
QStringList comments;
if ( parts.count() > 1 )
{
for ( const QString &part : parts )
{
bool ok = false;
int val = part.toInt( &ok );
if ( ok )
comments << mOptions.value( val );
}
return comments.join( ',' );
}
}

return mOptions.value( static_cast< int >( value.toDouble() ) );
}
}

QString QgsProcessingParameterEnum::asScriptCode() const
{
QString code = QStringLiteral( "##%1=" ).arg( mName );
@@ -602,6 +602,13 @@ class CORE_EXPORT QgsProcessingParameterDefinition
*/
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;

/**
* Returns a Python comment explaining a parameter \a value, or an empty string if no comment is required.
*
* \since QGIS 3.20
*/
virtual QString valueAsPythonComment( const QVariant &value, QgsProcessingContext &context ) const;

/**
* Returns the parameter definition encoded in a string which can be used within a
* Processing script.
@@ -2377,6 +2384,7 @@ class CORE_EXPORT QgsProcessingParameterEnum : public QgsProcessingParameterDefi
QString type() const override { return typeName(); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
QString valueAsPythonComment( const QVariant &value, QgsProcessingContext &context ) const override;
QString asScriptCode() const override;
QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const override;

@@ -4940,6 +4940,11 @@ 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->valueAsPythonComment( QVariant(), context ), QString() );
QCOMPARE( def->valueAsPythonComment( 2, context ), QStringLiteral( "C" ) );
QCOMPARE( def->valueAsPythonComment( QVariantList() << 1 << 2, context ), QStringLiteral( "B,C" ) );
QCOMPARE( def->valueAsPythonComment( QStringLiteral( "1,2" ), context ), QStringLiteral( "B,C" ) );

pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=True, usesStaticStrings=False, defaultValue=5)" ) );

" alg_params = {\n"
" 'DISSOLVE': False,\n"
" 'DISTANCE': parameters['DIST'],\n"
" 'END_CAP_STYLE': 1,\n"
" 'END_CAP_STYLE': 1, # Flat\n"
" 'INPUT': parameters['SOURCE_LAYER'],\n"
" 'JOIN_STYLE': 2,\n"
" 'JOIN_STYLE': 2, # Bevel\n"
" 'SEGMENTS': QgsExpression('@myvar*2').evaluate(),\n"
" 'OUTPUT': parameters['MyModelOutput']\n"
" }\n"

0 comments on commit d387093

Please sign in to comment.