QGIS API Documentation 3.41.0-Master (cea29feecf2)
Loading...
Searching...
No Matches
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 "moc_qgsprocessingbatchalgorithmdialogbase.cpp"
18#include "qgsprocessingbatch.h"
21#include "qgsjsonutils.h"
23#include "qgsapplication.h"
24#include <nlohmann/json.hpp>
25
27QgsProcessingBatchAlgorithmDialogBase::QgsProcessingBatchAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
28 : QgsProcessingAlgorithmDialogBase( parent, flags, QgsProcessingAlgorithmDialogBase::DialogMode::Batch )
29{
30 mButtonRunSingle = new QPushButton( tr( "Run as Single Process…" ) );
31 connect( mButtonRunSingle, &QPushButton::clicked, this, &QgsProcessingBatchAlgorithmDialogBase::runAsSingle );
32 buttonBox()->addButton( mButtonRunSingle, QDialogButtonBox::ResetRole ); // reset role to ensure left alignment
33
34 connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingBatchAlgorithmDialogBase::taskTriggered );
35
36 updateRunButtonVisibility();
37}
38
39QgsProcessingBatchAlgorithmDialogBase::~QgsProcessingBatchAlgorithmDialogBase() = default;
40
41void QgsProcessingBatchAlgorithmDialogBase::resetAdditionalGui()
42{
43 mButtonRunSingle->setEnabled( true );
44}
45
46void QgsProcessingBatchAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
47{
48 mButtonRunSingle->setEnabled( false );
49}
50
51void QgsProcessingBatchAlgorithmDialogBase::execute( const QList<QVariantMap> &parameters )
52{
53 mQueuedParameters = parameters;
54 mCurrentStep = 0;
55 mTotalSteps = mQueuedParameters.size();
56 mResults.clear();
57 mErrors.clear();
58
59 mFeedback.reset( createFeedback() );
60
61 mBatchFeedback = std::make_unique<QgsProcessingBatchFeedback>( mTotalSteps, mFeedback.get() );
62
63 mProxyTask = new QgsProxyProgressTask( tr( "Batch Processing - %1" ).arg( algorithm()->displayName() ), true );
64 connect( mProxyTask, &QgsProxyProgressTask::canceled, mBatchFeedback.get(), &QgsFeedback::cancel );
65 connect( mFeedback.get(), &QgsFeedback::progressChanged, mProxyTask, &QgsProxyProgressTask::setProxyProgress );
66 QgsApplication::taskManager()->addTask( mProxyTask );
67
68 blockControlsWhileRunning();
69 setExecutedAnyResult( true );
70 cancelButton()->setEnabled( true );
71
72 // Make sure the Log tab is visible before executing the algorithm
73 showLog();
74 repaint();
75
76 mTotalTimer.restart();
77 if ( mTotalSteps > 0 )
78 executeNext();
79}
80
81bool QgsProcessingBatchAlgorithmDialogBase::isFinalized()
82{
83 return mQueuedParameters.empty();
84}
85
86void QgsProcessingBatchAlgorithmDialogBase::executeNext()
87{
88 if ( mQueuedParameters.empty() || mFeedback->isCanceled() )
89 {
90 allTasksComplete( false );
91 return;
92 }
93
94 mBatchFeedback->setCurrentStep( mCurrentStep++ );
95 setProgressText( QStringLiteral( "\n" ) + tr( "Processing algorithm %1/%2…" ).arg( mCurrentStep ).arg( mTotalSteps ) );
96 setInfo( tr( "<b>Algorithm %1 starting&hellip;</b>" ).arg( algorithm()->displayName() ), false, false );
97
98 pushInfo( tr( "Input parameters:" ) );
99
100 // important - we create a new context for each iteration
101 // this avoids holding onto resources and layers from earlier iterations,
102 // and allows batch processing of many more items then is possible
103 // if we hold on to these layers
104 mTaskContext.reset( createContext( mBatchFeedback.get() ) );
105
106 const QVariantMap paramsJson = algorithm()->asMap( mQueuedParameters.constFirst(), *mTaskContext ).value( QStringLiteral( "inputs" ) ).toMap();
107 pushCommandInfo( QString::fromStdString( QgsJsonUtils::jsonFromVariant( paramsJson ).dump() ) );
108 pushInfo( QString() );
109
110 mCurrentParameters = algorithm()->preprocessParameters( mQueuedParameters.constFirst() );
111 mQueuedParameters.pop_front();
112
113 mCurrentStepTimer.restart();
115 {
116 QgsProcessingAlgRunnerTask *task = new QgsProcessingAlgRunnerTask( algorithm(), mCurrentParameters, *mTaskContext, mBatchFeedback.get(), QgsTask::CanCancel | QgsTask::Hidden );
117 if ( task->algorithmCanceled() )
118 onTaskComplete( false, {} );
119 else
120 {
121 setCurrentTask( task );
122 }
123 }
124 else
125 {
126 // have to execute in main thread, no tasks allowed
127 bool ok = false;
128 const QVariantMap results = algorithm()->run( mCurrentParameters, *mTaskContext, mBatchFeedback.get(), &ok );
129 onTaskComplete( ok, results );
130 }
131}
132
133void QgsProcessingBatchAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap &results )
134{
135 // parent class cleanup first!
136 QgsProcessingAlgorithmDialogBase::algExecuted( successful, results );
137 onTaskComplete( successful, results );
138}
139
140void QgsProcessingBatchAlgorithmDialogBase::onTaskComplete( bool ok, const QVariantMap &results )
141{
142 if ( ok )
143 {
144 setInfo( tr( "Algorithm %1 correctly executed…" ).arg( algorithm()->displayName() ), false, false );
145 pushInfo( tr( "Execution completed in %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ) );
146 pushInfo( tr( "Results:" ) );
147
148 pushCommandInfo( QString::fromStdString( QgsJsonUtils::jsonFromVariant( results ).dump() ) );
149 pushInfo( QString() );
150
151 mResults.append( QVariantMap(
152 { { QStringLiteral( "parameters" ), mCurrentParameters },
153 { QStringLiteral( "results" ), results }
154 }
155 ) );
156
157 handleAlgorithmResults( algorithm(), *mTaskContext, mBatchFeedback.get(), mCurrentParameters );
158 executeNext();
159 }
160 else if ( mBatchFeedback->isCanceled() )
161 {
162 setInfo( tr( "Algorithm %1 canceled…" ).arg( algorithm()->displayName() ), false, false );
163 pushInfo( tr( "Execution canceled after %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ) );
164 allTasksComplete( true );
165 }
166 else
167 {
168 const QStringList taskErrors = mBatchFeedback->popErrors();
169 setInfo( tr( "Algorithm %1 failed…" ).arg( algorithm()->displayName() ), false, false );
170 reportError( tr( "Execution failed after %1 seconds" ).arg( mCurrentStepTimer.elapsed() / 1000.0, 2 ), false );
171
172 mErrors.append( QVariantMap(
173 { { QStringLiteral( "parameters" ), mCurrentParameters },
174 { QStringLiteral( "errors" ), taskErrors }
175 }
176 ) );
177 executeNext();
178 }
179}
180
181void QgsProcessingBatchAlgorithmDialogBase::taskTriggered( QgsTask *task )
182{
183 if ( task == mProxyTask )
184 {
185 show();
186 raise();
187 setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
188 activateWindow();
189 showLog();
190 }
191}
192
193void QgsProcessingBatchAlgorithmDialogBase::allTasksComplete( bool canceled )
194{
195 mBatchFeedback.reset();
196 mFeedback.reset();
197 mTaskContext.reset();
198 mQueuedParameters.clear();
199 if ( mProxyTask )
200 {
201 mProxyTask->finalize( true );
202 mProxyTask = nullptr;
203 }
204
205 if ( !canceled )
206 {
207 pushInfo( tr( "Batch execution completed in %1 seconds" ).arg( mTotalTimer.elapsed() / 1000.0, 2 ) );
208 if ( !mErrors.empty() )
209 {
210 reportError( tr( "%1 executions failed. See log for further details." ).arg( mErrors.size() ), true );
211 }
212
213 for ( int i = 0; i < mResults.size(); ++i )
214 {
215 loadHtmlResults( mResults.at( i ).value( QStringLiteral( "results" ) ).toMap(), i );
216 }
217
218 createSummaryTable( mResults, mErrors );
219 }
220
221 resetGui();
222 cancelButton()->setEnabled( false );
223}
224
225
@ NoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
QgsTask task which runs a QgsProcessingAlgorithm in a background task.
bool algorithmCanceled()
Returns true if the algorithm was canceled.
virtual QVariantMap preprocessParameters(const QVariantMap &parameters)
Pre-processes a set of parameters, allowing the algorithm to clean their values.
QVariantMap run(const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok=nullptr, const QVariantMap &configuration=QVariantMap(), bool catchExceptions=true) const
Executes the algorithm using the specified parameters.
virtual QVariantMap asMap(const QVariantMap &parameters, QgsProcessingContext &context) const
Returns a JSON serializable variant map containing the specified parameters and context settings.
A QgsTask shell which proxies progress reports.
void setProxyProgress(double progress)
Sets the progress (from 0 to 100) for the proxied operation.
void canceled()
Emitted when the task is canceled.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
Abstract base class for long running background tasks.
@ Hidden
Hide task from GUI.
@ CanCancel
Task can be canceled.
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