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