Skip to content

Commit

Permalink
[processing] Proper progress reports during model execution
Browse files Browse the repository at this point in the history
Instead of showing the progress reports for each child algorithm
individually, which leads to repeated 0->100% progress for every
step of a model, we proxy the progress reports and account for the
overall progress through a model as well. This means that
the progress accounts for both the progress within the current
model step AND the total number of steps left to execute.
  • Loading branch information
nyalldawson committed Sep 22, 2017
1 parent 9caa722 commit 6afe25e
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 16 deletions.
2 changes: 2 additions & 0 deletions python/core/processing/models/qgsprocessingmodelalgorithm.sip
Expand Up @@ -374,6 +374,8 @@ Translated description of variable
};




/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
12 changes: 7 additions & 5 deletions python/core/qgsfeedback.sip
Expand Up @@ -38,11 +38,6 @@ class QgsFeedback : QObject
QgsFeedback( QObject *parent /TransferThis/ = 0 );
%Docstring
Construct a feedback object
%End

void cancel();
%Docstring
Tells the internal routines that the current operation should be canceled. This should be run by the main thread
%End

bool isCanceled() const;
Expand Down Expand Up @@ -71,6 +66,13 @@ Tells whether the operation has been canceled already
:rtype: float
%End

public slots:

void cancel();
%Docstring
Tells the internal routines that the current operation should be canceled. This should be run by the main thread
%End

signals:
void canceled();
%Docstring
Expand Down
2 changes: 1 addition & 1 deletion src/core/CMakeLists.txt
Expand Up @@ -682,6 +682,7 @@ SET(QGIS_CORE_MOC_HDRS
processing/qgsprocessingfeedback.h
processing/qgsprocessingprovider.h
processing/qgsprocessingregistry.h
processing/models/qgsprocessingmodelalgorithm.h

providers/memory/qgsmemoryprovider.h

Expand Down Expand Up @@ -978,7 +979,6 @@ SET(QGIS_CORE_HDRS
processing/qgsprocessingoutputs.h
processing/qgsprocessingparameters.h
processing/qgsprocessingutils.h
processing/models/qgsprocessingmodelalgorithm.h
processing/models/qgsprocessingmodelchildalgorithm.h
processing/models/qgsprocessingmodelchildparametersource.h
processing/models/qgsprocessingmodelcomponent.h
Expand Down
71 changes: 70 additions & 1 deletion src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Expand Up @@ -214,6 +214,7 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
QTime totalTime;
totalTime.start();

QgsProcessingModelFeedback modelFeedback( toExecute.count(), feedback );
QgsExpressionContext baseContext = createExpressionContext( parameters, context );

QVariantMap childResults;
Expand All @@ -225,6 +226,9 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
executedAlg = false;
Q_FOREACH ( const QString &childId, toExecute )
{
if ( feedback && feedback->isCanceled() )
break;

if ( executed.contains( childId ) )
continue;

Expand Down Expand Up @@ -260,7 +264,7 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa

bool ok = false;
std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
QVariantMap results = childAlg->run( childParams, context, feedback, &ok );
QVariantMap results = childAlg->run( childParams, context, &modelFeedback, &ok );
childAlg.reset( nullptr );
if ( !ok )
{
Expand All @@ -280,8 +284,12 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
}

executed.insert( childId );
modelFeedback.setCurrentStep( executed.count() );
feedback->pushDebugInfo( QObject::tr( "OK. Execution took %1 s (%2 outputs)." ).arg( childTime.elapsed() / 1000.0 ).arg( results.count() ) );
}

if ( feedback && feedback->isCanceled() )
break;
}
feedback->pushDebugInfo( QObject::tr( "Model processed OK. Executed %1 algorithms total in %2 s." ).arg( executed.count() ).arg( totalTime.elapsed() / 1000.0 ) );

Expand Down Expand Up @@ -1123,4 +1131,65 @@ QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
return alg;
}




//
// QgsProcessingModelFeedback
//

QgsProcessingModelFeedback::QgsProcessingModelFeedback( int childAlgorithmCount, QgsProcessingFeedback *feedback )
: mChildSteps( childAlgorithmCount )
, mFeedback( feedback )
{
connect( mFeedback, &QgsFeedback::canceled, this, &QgsFeedback::cancel, Qt::DirectConnection );
connect( this, &QgsFeedback::progressChanged, this, &QgsProcessingModelFeedback::updateOverallProgress );
}

void QgsProcessingModelFeedback::setCurrentStep( int step )
{
mCurrentStep = step;
mFeedback->setProgress( 100.0 * static_cast< double >( mCurrentStep ) / mChildSteps );
}

void QgsProcessingModelFeedback::setProgressText( const QString &text )
{
mFeedback->setProgressText( text );
}

void QgsProcessingModelFeedback::reportError( const QString &error )
{
mFeedback->reportError( error );
}

void QgsProcessingModelFeedback::pushInfo( const QString &info )
{
mFeedback->pushInfo( info );
}

void QgsProcessingModelFeedback::pushCommandInfo( const QString &info )
{
mFeedback->pushCommandInfo( info );
}

void QgsProcessingModelFeedback::pushDebugInfo( const QString &info )
{
mFeedback->pushDebugInfo( info );
}

void QgsProcessingModelFeedback::pushConsoleInfo( const QString &info )
{
mFeedback->pushConsoleInfo( info );
}

void QgsProcessingModelFeedback::updateOverallProgress( double progress )
{
double baseProgress = 100.0 * static_cast< double >( mCurrentStep ) / mChildSteps;
double currentAlgorithmProgress = progress / mChildSteps;
mFeedback->setProgress( baseProgress + currentAlgorithmProgress );
}



///@endcond

47 changes: 47 additions & 0 deletions src/core/processing/models/qgsprocessingmodelalgorithm.h
Expand Up @@ -403,6 +403,53 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
friend class TestQgsProcessing;
};


#ifndef SIP_RUN

/**
* Model algorithm feedback which proxies its calls to an underlying
* feedback object, but scales overall progress reports to account
* for the number of child steps in a model.
*/
class QgsProcessingModelFeedback : public QgsProcessingFeedback
{
Q_OBJECT

public:

/**
* Constructor for QgsProcessingModelFeedback, for a model with the specified
* number of active child algorithms. This feedback object will proxy calls
* to the specified \a feedback object.
*/
QgsProcessingModelFeedback( int childAlgorithmCount, QgsProcessingFeedback *feedback );

/**
* Sets the current child algorithm \a step which is being executed. This is used
* to scale the overall progress to account for progress through the overall model.
*/
void setCurrentStep( int step );

void setProgressText( const QString &text ) override;
void reportError( const QString &error ) override;
void pushInfo( const QString &info ) override;
void pushCommandInfo( const QString &info ) override;
void pushDebugInfo( const QString &info ) override;
void pushConsoleInfo( const QString &info ) override;

private slots:

void updateOverallProgress( double progress );

private:

int mChildSteps = 0;
int mCurrentStep = 0;
QgsProcessingFeedback *mFeedback = nullptr;
};

#endif

///@endcond

#endif // QGSPROCESSINGMODELALGORITHM_H
20 changes: 11 additions & 9 deletions src/core/qgsfeedback.h
Expand Up @@ -50,15 +50,6 @@ class CORE_EXPORT QgsFeedback : public QObject
, mCanceled( false )
{}

//! Tells the internal routines that the current operation should be canceled. This should be run by the main thread
void cancel()
{
if ( mCanceled )
return; // only emit the signal once
mCanceled = true;
emit canceled();
}

//! Tells whether the operation has been canceled already
bool isCanceled() const { return mCanceled; }

Expand Down Expand Up @@ -88,6 +79,17 @@ class CORE_EXPORT QgsFeedback : public QObject
*/
double progress() const { return mProgress; }

public slots:

//! Tells the internal routines that the current operation should be canceled. This should be run by the main thread
void cancel()
{
if ( mCanceled )
return; // only emit the signal once
mCanceled = true;
emit canceled();
}

signals:
//! Internal routines can connect to this signal if they use event loop
void canceled();
Expand Down

0 comments on commit 6afe25e

Please sign in to comment.