QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsprocessingmodelchildalgorithm.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingmodelchildalgorithm.cpp
3 ------------------------------------
4 begin : June 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgsapplication.h"
22
24
25QgsProcessingModelChildAlgorithm::QgsProcessingModelChildAlgorithm( const QString &algorithmId )
26{
27 setAlgorithmId( algorithmId );
28}
29
30QgsProcessingModelChildAlgorithm::QgsProcessingModelChildAlgorithm( const QgsProcessingModelChildAlgorithm &other )
31 : QgsProcessingModelComponent( other )
32 , mId( other.mId )
33 , mConfiguration( other.mConfiguration )
34 , mParams( other.mParams )
35 , mModelOutputs( other.mModelOutputs )
36 , mActive( other.mActive )
37 , mDependencies( other.mDependencies )
38 , mComment( other.mComment )
39{
40 setAlgorithmId( other.algorithmId() );
41}
42
43QgsProcessingModelChildAlgorithm &QgsProcessingModelChildAlgorithm::operator=( const QgsProcessingModelChildAlgorithm &other )
44{
45 QgsProcessingModelComponent::operator =( other );
46 mId = other.mId;
47 mConfiguration = other.mConfiguration;
48 setAlgorithmId( other.algorithmId() );
49 mParams = other.mParams;
50 mModelOutputs = other.mModelOutputs;
51 mActive = other.mActive;
52 mDependencies = other.mDependencies;
53 mComment = other.mComment;
54 return *this;
55}
56
57QgsProcessingModelChildAlgorithm *QgsProcessingModelChildAlgorithm::clone() const
58{
59 return new QgsProcessingModelChildAlgorithm( *this );
60}
61
62void QgsProcessingModelChildAlgorithm::copyNonDefinitionPropertiesFromModel( QgsProcessingModelAlgorithm *model )
63{
64 const QgsProcessingModelChildAlgorithm existingChild = model->childAlgorithm( mId );
65 copyNonDefinitionProperties( existingChild );
66
67 int i = 0;
68 for ( auto it = mModelOutputs.begin(); it != mModelOutputs.end(); ++it )
69 {
70 const QMap<QString, QgsProcessingModelOutput> existingChildModelOutputs = existingChild.modelOutputs();
71 auto existingOutputIt = existingChildModelOutputs.find( it.key() );
72 if ( existingOutputIt == existingChildModelOutputs.end() )
73 continue;
74
75 if ( !existingOutputIt->position().isNull() )
76 {
77 it.value().setPosition( existingOutputIt->position() );
78 it.value().setSize( existingOutputIt->size() );
79 }
80 else
81 it.value().setPosition( position() + QPointF( size().width(), ( i + 1.5 ) * size().height() ) );
82
83 if ( QgsProcessingModelComment *comment = it.value().comment() )
84 {
85 if ( const QgsProcessingModelComment *existingComment = existingOutputIt->comment() )
86 {
87 comment->setDescription( existingComment->description() );
88 comment->setSize( existingComment->size() );
89 comment->setPosition( existingComment->position() );
90 comment->setColor( existingComment->color() );
91 }
92 }
93 i++;
94 }
95}
96
98{
99 return mAlgorithm.get();
100}
101
102void QgsProcessingModelChildAlgorithm::setModelOutputs( const QMap<QString, QgsProcessingModelOutput> &modelOutputs )
103{
104 mModelOutputs = modelOutputs;
105
106 QMap<QString, QgsProcessingModelOutput>::iterator outputIt = mModelOutputs.begin();
107 for ( ; outputIt != mModelOutputs.end(); ++outputIt )
108 {
109 // make sure values are consistent
110 outputIt->setName( outputIt.key() );
111 outputIt->setChildId( mId );
112 }
113}
114
115bool QgsProcessingModelChildAlgorithm::removeModelOutput( const QString &name )
116{
117 mModelOutputs.remove( name );
118 return true;
119}
120
121QVariant QgsProcessingModelChildAlgorithm::toVariant() const
122{
123 QVariantMap map;
124 map.insert( QStringLiteral( "id" ), mId );
125 map.insert( QStringLiteral( "alg_id" ), mAlgorithmId );
126 map.insert( QStringLiteral( "alg_config" ), mConfiguration );
127 map.insert( QStringLiteral( "active" ), mActive );
128
129 QVariantList dependencies;
130 for ( const QgsProcessingModelChildDependency &dependency : mDependencies )
131 {
132 dependencies << dependency.toVariant();
133 }
134 map.insert( QStringLiteral( "dependencies" ), dependencies );
135
136 saveCommonProperties( map );
137
138 QVariantMap paramMap;
139 QMap< QString, QgsProcessingModelChildParameterSources >::const_iterator paramIt = mParams.constBegin();
140 for ( ; paramIt != mParams.constEnd(); ++paramIt )
141 {
142 QVariantList sources;
143 const auto constValue = paramIt.value();
144 for ( const QgsProcessingModelChildParameterSource &source : constValue )
145 {
146 sources << source.toVariant();
147 }
148 paramMap.insert( paramIt.key(), sources );
149 }
150 map.insert( QStringLiteral( "params" ), paramMap );
151
152 QVariantMap outputMap;
153 QMap< QString, QgsProcessingModelOutput >::const_iterator outputIt = mModelOutputs.constBegin();
154 for ( ; outputIt != mModelOutputs.constEnd(); ++outputIt )
155 {
156 outputMap.insert( outputIt.key(), outputIt.value().toVariant() );
157 }
158 map.insert( QStringLiteral( "outputs" ), outputMap );
159
160 return map;
161}
162
163bool QgsProcessingModelChildAlgorithm::loadVariant( const QVariant &child )
164{
165 QVariantMap map = child.toMap();
166
167 mId = map.value( QStringLiteral( "id" ) ).toString();
168 if ( mId.isEmpty() )
169 return false;
170
171 mConfiguration = map.value( QStringLiteral( "alg_config" ) ).toMap();
172 setAlgorithmId( map.value( QStringLiteral( "alg_id" ) ).toString() );
173 if ( algorithmId().isEmpty() )
174 return false;
175 mActive = map.value( QStringLiteral( "active" ) ).toBool();
176
177 mDependencies.clear();
178 if ( map.value( QStringLiteral( "dependencies" ) ).type() == QVariant::StringList )
179 {
180 const QStringList dependencies = map.value( QStringLiteral( "dependencies" ) ).toStringList();
181 mDependencies.reserve( dependencies.size() );
182 for ( const QString &dependency : dependencies )
183 {
184 QgsProcessingModelChildDependency dep;
185 dep.childId = dependency;
186 mDependencies << dep;
187 }
188 }
189 else
190 {
191 const QVariantList dependencies = map.value( QStringLiteral( "dependencies" ) ).toList();
192 mDependencies.reserve( dependencies.size() );
193 for ( const QVariant &dependency : dependencies )
194 {
195 QgsProcessingModelChildDependency dep;
196 dep.loadVariant( dependency.toMap() );
197 mDependencies << dep;
198 }
199 }
200
201 restoreCommonProperties( map );
202
203 mParams.clear();
204 QVariantMap paramMap = map.value( QStringLiteral( "params" ) ).toMap();
205 QVariantMap::const_iterator paramIt = paramMap.constBegin();
206 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
207 {
208 QgsProcessingModelChildParameterSources sources;
209 const auto constToList = paramIt->toList();
210 sources.reserve( constToList.size() );
211 for ( const QVariant &sourceVar : constToList )
212 {
213 QgsProcessingModelChildParameterSource param;
214 if ( !param.loadVariant( sourceVar.toMap() ) )
215 return false;
216 sources << param;
217 }
218 mParams.insert( paramIt.key(), sources );
219 }
220
221 mModelOutputs.clear();
222 QVariantMap outputMap = map.value( QStringLiteral( "outputs" ) ).toMap();
223 QVariantMap::const_iterator outputIt = outputMap.constBegin();
224 for ( ; outputIt != outputMap.constEnd(); ++outputIt )
225 {
226 QgsProcessingModelOutput output;
227 if ( !output.loadVariant( outputIt.value().toMap() ) )
228 return false;
229
230 mModelOutputs.insert( outputIt.key(), output );
231 }
232
233 return true;
234}
235
236QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters,
237 int currentIndent, int indentSize, const QMap<QString, QString> &friendlyChildNames, const QMap<QString, QString> &friendlyOutputNames ) const
238{
239 QStringList lines;
240 const QString baseIndent = QString( ' ' ).repeated( currentIndent );
241 const QString lineIndent = QString( ' ' ).repeated( indentSize );
242
243 if ( !algorithm() )
244 return QStringList();
245
246 if ( !description().isEmpty() )
247 lines << baseIndent + QStringLiteral( "# %1" ).arg( description() );
248 if ( !mComment.description().isEmpty() )
249 {
250 const QStringList parts = mComment.description().split( QStringLiteral( "\n" ) );
251 for ( const QString &part : parts )
252 {
253 lines << baseIndent + QStringLiteral( "# %1" ).arg( part );
254 }
255 }
256
257 QStringList paramParts;
258 QStringList paramComments;
259 for ( auto paramIt = mParams.constBegin(); paramIt != mParams.constEnd(); ++paramIt )
260 {
261 QStringList sourceParts;
262 QStringList sourceComments;
263 const QgsProcessingParameterDefinition *def = algorithm() ? algorithm()->parameterDefinition( paramIt.key() ) : nullptr;
264 const auto parts = paramIt.value();
265 sourceParts.reserve( parts.size() );
266 sourceComments.reserve( parts.size() );
267 for ( const QgsProcessingModelChildParameterSource &source : parts )
268 {
269 QString part = source.asPythonCode( outputType, def, friendlyChildNames );
270 if ( !part.isEmpty() )
271 {
272 sourceParts << part;
273 sourceComments << source.asPythonComment( def );
274 }
275 }
276 if ( sourceParts.count() == 1 )
277 {
278 paramParts << QStringLiteral( "'%1': %2" ).arg( paramIt.key(), sourceParts.at( 0 ) );
279 paramComments << sourceComments.at( 0 );
280 }
281 else
282 {
283 paramParts << QStringLiteral( "'%1': [%2]" ).arg( paramIt.key(), sourceParts.join( ',' ) );
284 paramComments << QString();
285 }
286 }
287
288 lines << baseIndent + QStringLiteral( "alg_params = {" );
289 lines.reserve( lines.size() + paramParts.size() );
290 int i = 0;
291 for ( const QString &p : std::as_const( paramParts ) )
292 {
293 QString line = baseIndent + lineIndent + p + ',';
294 if ( !paramComments.value( i ).isEmpty() )
295 {
296 line += QStringLiteral( " # %1" ).arg( paramComments.value( i ) );
297 }
298 lines << line;
299 i++;
300 }
301 for ( auto it = extraParameters.constBegin(); it != extraParameters.constEnd(); ++it )
302 {
303 lines << baseIndent + lineIndent + QStringLiteral( "%1: %2," ).arg( QgsProcessingUtils::stringToPythonLiteral( it.key() ), it.value() );
304 }
305 if ( lines.constLast().endsWith( ',' ) )
306 {
307 lines[ lines.count() - 1 ].truncate( lines.constLast().length() - 1 );
308 }
309 lines << baseIndent + QStringLiteral( "}" );
310
311 lines << baseIndent + QStringLiteral( "outputs['%1'] = processing.run('%2', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ).arg( friendlyChildNames.value( mId, mId ), mAlgorithmId );
312
313 for ( auto outputIt = mModelOutputs.constBegin(); outputIt != mModelOutputs.constEnd(); ++outputIt )
314 {
315 QString outputName = QStringLiteral( "%1:%2" ).arg( mId, outputIt.key() );
316 outputName = friendlyOutputNames.value( outputName, outputName );
317 lines << baseIndent + QStringLiteral( "results['%1'] = outputs['%2']['%3']" ).arg( outputName, friendlyChildNames.value( mId, mId ), outputIt.value().childOutputName() );
318 }
319
320 return lines;
321}
322
323QVariantMap QgsProcessingModelChildAlgorithm::configuration() const
324{
325 return mConfiguration;
326}
327
328void QgsProcessingModelChildAlgorithm::setConfiguration( const QVariantMap &configuration )
329{
330 mConfiguration = configuration;
331 mAlgorithm.reset( QgsApplication::processingRegistry()->createAlgorithmById( mAlgorithmId, mConfiguration ) );
332}
333
334void QgsProcessingModelChildAlgorithm::generateChildId( const QgsProcessingModelAlgorithm &model )
335{
336 int i = 1;
337 QString id;
338 while ( true )
339 {
340 id = QStringLiteral( "%1_%2" ).arg( mAlgorithmId ).arg( i );
341 if ( !model.childAlgorithms().contains( id ) )
342 break;
343 i++;
344 }
345 mId = id;
346}
347
348bool QgsProcessingModelChildAlgorithm::setAlgorithmId( const QString &algorithmId )
349{
350 mAlgorithmId = algorithmId;
351 mAlgorithm.reset( QgsApplication::processingRegistry()->createAlgorithmById( mAlgorithmId, mConfiguration ) );
352 return static_cast< bool >( mAlgorithm.get() );
353}
354
355bool QgsProcessingModelChildAlgorithm::reattach() const
356{
357 return const_cast< QgsProcessingModelChildAlgorithm * >( this )->setAlgorithmId( mAlgorithmId );
358}
359
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
Abstract base class for processing algorithms.
const QgsProcessingParameterDefinition * parameterDefinition(const QString &name) const
Returns a matching parameter by name.
Base class for the definition of processing parameters.
static QString stringToPythonLiteral(const QString &string)
Converts a string to a Python string literal.
PythonOutputType
Available Python output types.
Definition: qgsprocessing.h:48
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
QMap< QString, QString > QgsStringMap
Definition: qgis.h:5737