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