QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsprocessingbatchalgorithmdialogbase.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprocessingbatchalgorithmdialogbase.cpp
3  ------------------------------------
4  Date : March 2022
5  Copyright : (C) 2022 Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 #include "qgsprocessingbatch.h"
18 #include "qgsproxyprogresstask.h"
19 #include "qgsprocessingalgorithm.h"
20 #include "qgsjsonutils.h"
22 #include "qgsapplication.h"
23 #include <nlohmann/json.hpp>
24 
26 QgsProcessingBatchAlgorithmDialogBase::QgsProcessingBatchAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
27  : QgsProcessingAlgorithmDialogBase( parent, flags, QgsProcessingAlgorithmDialogBase::DialogMode::Batch )
28 {
29  mButtonRunSingle = new QPushButton( tr( "Run as Single Process…" ) );
30  connect( mButtonRunSingle, &QPushButton::clicked, this, &QgsProcessingBatchAlgorithmDialogBase::runAsSingle );
31  buttonBox()->addButton( mButtonRunSingle, QDialogButtonBox::ResetRole ); // reset role to ensure left alignment
32 
33  connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingBatchAlgorithmDialogBase::taskTriggered );
34 
35  updateRunButtonVisibility();
36 }
37 
38 QgsProcessingBatchAlgorithmDialogBase::~QgsProcessingBatchAlgorithmDialogBase() = default;
39 
40 void QgsProcessingBatchAlgorithmDialogBase::resetAdditionalGui()
41 {
42  mButtonRunSingle->setEnabled( true );
43 }
44 
45 void QgsProcessingBatchAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
46 {
47  mButtonRunSingle->setEnabled( false );
48 }
49 
50 void QgsProcessingBatchAlgorithmDialogBase::execute( const QList<QVariantMap> &parameters )
51 {
52  mQueuedParameters = parameters;
53  mCurrentStep = 0;
54  mTotalSteps = mQueuedParameters.size();
55  mResults.clear();
56  mErrors.clear();
57 
58  mFeedback.reset( createFeedback() );
59 
60  mBatchFeedback = std::make_unique< QgsProcessingBatchFeedback >( mTotalSteps, mFeedback.get() );
61 
62  mProxyTask = new QgsProxyProgressTask( tr( "Batch Processing - %1" ).arg( algorithm()->displayName() ), true );
63  connect( mProxyTask, &QgsProxyProgressTask::canceled, mBatchFeedback.get(), &QgsFeedback::cancel );
64  connect( mFeedback.get(), &QgsFeedback::progressChanged, mProxyTask, &QgsProxyProgressTask::setProxyProgress );
65  QgsApplication::taskManager()->addTask( mProxyTask );
66 
67  blockControlsWhileRunning();
68  setExecutedAnyResult( true );
69  cancelButton()->setEnabled( true );
70 
71  // Make sure the Log tab is visible before executing the algorithm
72  showLog();
73  repaint();
74 
75  mTotalTimer.restart();
76  if ( mTotalSteps > 0 )
77  executeNext();
78 }
79 
80 bool QgsProcessingBatchAlgorithmDialogBase::isFinalized()
81 {
82  return mQueuedParameters.empty();
83 }
84 
85 void QgsProcessingBatchAlgorithmDialogBase::executeNext()
86 {
87  if ( mQueuedParameters.empty() || mFeedback->isCanceled() )
88  {
89  allTasksComplete( false );
90  return;
91  }
92 
93  mBatchFeedback->setCurrentStep( mCurrentStep++ );
94  setProgressText( QStringLiteral( "\n" ) + tr( "Processing algorithm %1/%2…" ).arg( mCurrentStep ).arg( mTotalSteps ) );
95  setInfo( tr( "<b>Algorithm %1 starting&hellip;</b>" ).arg( algorithm()->displayName() ), false, false );
96 
97  pushInfo( tr( "Input parameters:" ) );
98 
99  // important - we create a new context for each iteration
100  // this avoids holding onto resources and layers from earlier iterations,
101  // and allows batch processing of many more items then is possible
102  // if we hold on to these layers
103  mTaskContext.reset( createContext( mBatchFeedback.get() ) );
104 
105  const QVariantMap paramsJson = algorithm()->asMap( mQueuedParameters.constFirst(), *mTaskContext ).value( QStringLiteral( "inputs" ) ).toMap();
106  pushCommandInfo( QString::fromStdString( QgsJsonUtils::jsonFromVariant( paramsJson ).dump() ) );
107  pushInfo( QString() );
108 
109  mCurrentParameters = algorithm()->preprocessParameters( mQueuedParameters.constFirst() );
110  mQueuedParameters.pop_front();
111 
112  mCurrentStepTimer.restart();
113  if ( !( algorithm()->flags() & QgsProcessingAlgorithm::FlagNoThreading ) )
114  {
115  QgsProcessingAlgRunnerTask *task = new QgsProcessingAlgRunnerTask( algorithm(), mCurrentParameters, *mTaskContext, mBatchFeedback.get(), QgsTask::CanCancel | QgsTask::Hidden );
116  if ( task->algorithmCanceled() )
117  onTaskComplete( false, {} );
118  else
119  {
120  setCurrentTask( task );
121  }
122  }
123  else
124  {
125  // have to execute in main thread, no tasks allowed
126  bool ok = false;
127  const QVariantMap results = algorithm()->run( mCurrentParameters, *mTaskContext, mBatchFeedback.get(), &ok );
128  onTaskComplete( ok, results );
129  }
130 }
131 
132 void QgsProcessingBatchAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap &results )
133 {
134  // parent class cleanup first!
135  QgsProcessingAlgorithmDialogBase::algExecuted( successful, results );
136  onTaskComplete( successful, results );
137 }
138 
139 void QgsProcessingBatchAlgorithmDialogBase::onTaskComplete( bool ok, const QVariantMap &results )
140 {
141  if ( ok )
142  {
143  setInfo( tr( "Algorithm %1 correctly executed…" ).arg( algorithm()->displayName() ), false, false );
144  pushInfo( tr( "Execution completed in %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ) );
145  pushInfo( tr( "Results:" ) );
146 
147  pushCommandInfo( QString::fromStdString( QgsJsonUtils::jsonFromVariant( results ).dump() ) );
148  pushInfo( QString() );
149 
150  mResults.append( QVariantMap(
151  {
152  { QStringLiteral( "parameters" ), mCurrentParameters },
153  { QStringLiteral( "results" ), results }
154  } ) );
155 
156  handleAlgorithmResults( algorithm(), *mTaskContext, mBatchFeedback.get(), mCurrentParameters );
157  executeNext();
158  }
159  else if ( mBatchFeedback->isCanceled() )
160  {
161  setInfo( tr( "Algorithm %1 canceled…" ).arg( algorithm()->displayName() ), false, false );
162  pushInfo( tr( "Execution canceled after %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ) );
163  allTasksComplete( true );
164  }
165  else
166  {
167  const QStringList taskErrors = mBatchFeedback->popErrors();
168  setInfo( tr( "Algorithm %1 failed…" ).arg( algorithm()->displayName() ), false, false );
169  reportError( tr( "Execution failed after %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ), false );
170 
171  mErrors.append( QVariantMap(
172  {
173  { QStringLiteral( "parameters" ), mCurrentParameters },
174  { QStringLiteral( "errors" ), taskErrors }
175  } ) );
176  executeNext();
177  }
178 }
179 
180 void QgsProcessingBatchAlgorithmDialogBase::taskTriggered( QgsTask *task )
181 {
182  if ( task == mProxyTask )
183  {
184  show();
185  raise();
186  setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
187  activateWindow();
188  showLog();
189  }
190 }
191 
192 void QgsProcessingBatchAlgorithmDialogBase::allTasksComplete( bool canceled )
193 {
194  mBatchFeedback.reset();
195  mFeedback.reset();
196  mTaskContext.reset();
197  mQueuedParameters.clear();
198  if ( mProxyTask )
199  {
200  mProxyTask->finalize( true );
201  mProxyTask = nullptr;
202  }
203 
204  if ( !canceled )
205  {
206  pushInfo( tr( "Batch execution completed in %1 seconds" ).arg( mTotalTimer.elapsed() / 1000.0, 2 ) );
207  if ( !mErrors.empty() )
208  {
209  reportError( tr( "%1 executions failed. See log for further details." ).arg( mErrors.size() ), true );
210  }
211 
212  for ( int i = 0; i < mResults.size(); ++i )
213  {
214  loadHtmlResults( mResults.at( i ).value( QStringLiteral( "results" ) ).toMap(), i );
215  }
216 
217  createSummaryTable( mResults, mErrors );
218  }
219 
220  resetGui();
221  cancelButton()->setEnabled( false );
222 }
223 
224 
QgsProcessingAlgRunnerTask
QgsTask task which runs a QgsProcessingAlgorithm in a background task.
Definition: qgsprocessingalgrunnertask.h:35
qgsproxyprogresstask.h
QgsProcessingAlgRunnerTask::algorithmCanceled
bool algorithmCanceled()
Returns true if the algorithm was canceled.
Definition: qgsprocessingalgrunnertask.h:60
algorithm
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
QgsProxyProgressTask
A QgsTask shell which proxies progress reports.
Definition: qgsproxyprogresstask.h:37
qgsprocessingbatchalgorithmdialogbase.h
QgsProcessingAlgorithm::asMap
virtual QVariantMap asMap(const QVariantMap &parameters, QgsProcessingContext &context) const
Returns a JSON serializable variant map containing the specified parameters and context settings.
Definition: qgsprocessingalgorithm.cpp:362
QgsProcessingAlgorithm::run
QVariantMap run(const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok=nullptr, const QVariantMap &configuration=QVariantMap(), bool catchExceptions=true) const SIP_THROW(QgsProcessingException)
Executes the algorithm using the specified parameters.
Definition: qgsprocessingalgorithm.cpp:526
QgsFeedback::cancel
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:121
QgsTaskManager::addTask
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Definition: qgstaskmanager.cpp:420
qgsapplication.h
QgsTask::CanCancel
@ CanCancel
Task can be canceled.
Definition: qgstaskmanager.h:74
qgsprocessingalgorithm.h
QgsTask::Hidden
@ Hidden
Hide task from GUI (since QGIS 3.26)
Definition: qgstaskmanager.h:76
QgsApplication::taskManager
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
Definition: qgsapplication.cpp:2300
QgsProxyProgressTask::canceled
void canceled()
Emitted when the task is canceled.
QgsFeedback::progressChanged
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
qgsprocessingalgrunnertask.h
QgsProcessingAlgorithm::FlagNoThreading
@ FlagNoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
Definition: qgsprocessingalgorithm.h:76
qgsprocessingbatch.h
QgsProcessingAlgorithm::preprocessParameters
virtual QVariantMap preprocessParameters(const QVariantMap &parameters)
Pre-processes a set of parameters, allowing the algorithm to clean their values.
Definition: qgsprocessingalgorithm.cpp:125
qgsjsonutils.h
QgsTaskManager::taskTriggered
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
QgsJsonUtils::jsonFromVariant
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
Definition: qgsjsonutils.cpp:401
QgsProxyProgressTask::setProxyProgress
void setProxyProgress(double progress)
Sets the progress (from 0 to 100) for the proxied operation.
Definition: qgsproxyprogresstask.cpp:47
QgsTask
Abstract base class for long running background tasks. Tasks can be controlled directly,...
Definition: qgstaskmanager.h:54