Skip to content

Commit

Permalink
[processing] When converting models to python, correctly use a
Browse files Browse the repository at this point in the history
multi-step feedback object to give scripts accurate progress reports

And also add in checks for cancelation between child algorithm
execution to allow generated scripts to early exit
  • Loading branch information
nyalldawson committed Feb 13, 2019
1 parent 1e431f2 commit fc8fd1e
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 10 deletions.
33 changes: 24 additions & 9 deletions src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Expand Up @@ -373,12 +373,21 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth


const QString algorithmClassName = safeName( name() ); const QString algorithmClassName = safeName( name() );


QSet< QString > toExecute;
for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
{
if ( childIt->isActive() && childIt->algorithm() )
toExecute.insert( childIt->childId() );
}
const int totalSteps = toExecute.count();

switch ( outputType ) switch ( outputType )
{ {
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass: case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{ {
lines << QStringLiteral( "from qgis.core import QgsProcessing" ); lines << QStringLiteral( "from qgis.core import QgsProcessing" );
lines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" ); lines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
lines << QStringLiteral( "from qgis.core import QgsProcessingMultiStepFeedback" );
// add specific parameter type imports // add specific parameter type imports
const auto params = parameterDefinitions(); const auto params = parameterDefinitions();
QStringList importLines; // not a set - we need regular ordering QStringList importLines; // not a set - we need regular ordering
Expand All @@ -405,8 +414,12 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
} }


lines << QString(); lines << QString();
lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, feedback):" ); lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, model_feedback):" );
currentIndent = indent + indent; currentIndent = indent + indent;

lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
lines << currentIndent + QStringLiteral( "# overall progress through the model" );
lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
break; break;
} }
#if 0 #if 0
Expand Down Expand Up @@ -450,15 +463,9 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
lines << currentIndent + QStringLiteral( "results = {}" ); lines << currentIndent + QStringLiteral( "results = {}" );
lines << currentIndent + QStringLiteral( "outputs = {}" ); lines << currentIndent + QStringLiteral( "outputs = {}" );


QSet< QString > toExecute;
for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
{
if ( childIt->isActive() && childIt->algorithm() )
toExecute.insert( childIt->childId() );
}

QSet< QString > executed; QSet< QString > executed;
bool executedAlg = true; bool executedAlg = true;
int currentStep = 0;
while ( executedAlg && executed.count() < toExecute.count() ) while ( executedAlg && executed.count() < toExecute.count() )
{ {
executedAlg = false; executedAlg = false;
Expand Down Expand Up @@ -530,7 +537,15 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
} }


lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize ); lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize );

currentStep++;
if ( currentStep < totalSteps )
{
lines << QString();
lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
lines << currentIndent + indent + QStringLiteral( "return {}" );
lines << QString();
}
executed.insert( childId ); executed.insert( childId );
} }
} }
Expand Down
16 changes: 15 additions & 1 deletion tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -6986,6 +6986,7 @@ void TestQgsProcessing::modelExecution()
QgsDebugMsg( actualParts.join( '\n' ) ); QgsDebugMsg( actualParts.join( '\n' ) );
QStringList expectedParts = QStringLiteral( "from qgis.core import QgsProcessing\n" QStringList expectedParts = QStringLiteral( "from qgis.core import QgsProcessing\n"
"from qgis.core import QgsProcessingAlgorithm\n" "from qgis.core import QgsProcessingAlgorithm\n"
"from qgis.core import QgsProcessingMultiStepFeedback\n"
"from qgis.core import QgsProcessingParameterFeatureSource\n" "from qgis.core import QgsProcessingParameterFeatureSource\n"
"from qgis.core import QgsProcessingParameterNumber\n" "from qgis.core import QgsProcessingParameterNumber\n"
"from qgis.core import QgsProcessingParameterFeatureSink\n" "from qgis.core import QgsProcessingParameterFeatureSink\n"
Expand All @@ -7000,7 +7001,10 @@ void TestQgsProcessing::modelExecution()
" self.addParameter(QgsProcessingParameterFeatureSink('cx1:MODEL_OUT_LAYER', '', type=QgsProcessing.TypeVectorPolygon, createByDefault=True, defaultValue=None))\n" " self.addParameter(QgsProcessingParameterFeatureSink('cx1:MODEL_OUT_LAYER', '', type=QgsProcessing.TypeVectorPolygon, createByDefault=True, defaultValue=None))\n"
" self.addParameter(QgsProcessingParameterFeatureSink('cx3:MY_OUT', '', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))\n" " self.addParameter(QgsProcessingParameterFeatureSink('cx3:MY_OUT', '', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))\n"
"\n" "\n"
" def processAlgorithm(self, parameters, context, feedback):\n" " def processAlgorithm(self, parameters, context, model_feedback):\n"
" # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the\n"
" # overall progress through the model\n"
" feedback = QgsProcessingMultiStepFeedback(3, model_feedback)\n"
" results = {}\n" " results = {}\n"
" outputs = {}\n" " outputs = {}\n"
" alg_params = {\n" " alg_params = {\n"
Expand All @@ -7014,11 +7018,21 @@ void TestQgsProcessing::modelExecution()
" }\n" " }\n"
" outputs['cx1'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n" " outputs['cx1'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
" results['cx1:MODEL_OUT_LAYER'] = outputs['cx1']['OUTPUT']\n" " results['cx1:MODEL_OUT_LAYER'] = outputs['cx1']['OUTPUT']\n"
"\n"
" feedback.setCurrentStep(1)\n"
" if feedback.isCanceled():\n"
" return {}\n"
"\n"
" alg_params = {\n" " alg_params = {\n"
" 'INPUT': outputs['cx1']['OUTPUT'],\n" " 'INPUT': outputs['cx1']['OUTPUT'],\n"
" 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT\n" " 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT\n"
" }\n" " }\n"
" outputs['cx2'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n" " outputs['cx2'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
"\n"
" feedback.setCurrentStep(2)\n"
" if feedback.isCanceled():\n"
" return {}\n"
"\n"
" alg_params = {\n" " alg_params = {\n"
" 'EXPRESSION': 'true',\n" " 'EXPRESSION': 'true',\n"
" 'INPUT': outputs['cx1']['OUTPUT'],\n" " 'INPUT': outputs['cx1']['OUTPUT'],\n"
Expand Down

0 comments on commit fc8fd1e

Please sign in to comment.