QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
20 #include "qgsprocessingregistry.h"
22 
24 
25 QgsProcessingModelChildAlgorithm::QgsProcessingModelChildAlgorithm( const QString &algorithmId )
26 {
27  setAlgorithmId( algorithmId );
28 }
29 
30 QgsProcessingModelChildAlgorithm::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 
43 QgsProcessingModelChildAlgorithm &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 
57 QgsProcessingModelChildAlgorithm *QgsProcessingModelChildAlgorithm::clone() const
58 {
59  return new QgsProcessingModelChildAlgorithm( *this );
60 }
61 
62 void 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  if ( !existingChild.modelOutputs().value( it.key() ).position().isNull() )
71  {
72  it.value().setPosition( existingChild.modelOutputs().value( it.key() ).position() );
73  it.value().setSize( existingChild.modelOutputs().value( it.key() ).size() );
74  }
75  else
76  it.value().setPosition( position() + QPointF( size().width(), ( i + 1.5 ) * size().height() ) );
77 
78  if ( QgsProcessingModelComment *comment = it.value().comment() )
79  {
80  if ( const QgsProcessingModelComment *existingComment = existingChild.modelOutputs().value( it.key() ).comment() )
81  {
82  comment->setDescription( existingComment->description() );
83  comment->setSize( existingComment->size() );
84  comment->setPosition( existingComment->position() );
85  comment->setColor( existingComment->color() );
86  }
87  }
88  i++;
89  }
90 }
91 
93 {
94  return mAlgorithm.get();
95 }
96 
97 void QgsProcessingModelChildAlgorithm::setModelOutputs( const QMap<QString, QgsProcessingModelOutput> &modelOutputs )
98 {
99  mModelOutputs = modelOutputs;
100 
101  QMap<QString, QgsProcessingModelOutput>::iterator outputIt = mModelOutputs.begin();
102  for ( ; outputIt != mModelOutputs.end(); ++outputIt )
103  {
104  // make sure values are consistent
105  outputIt->setName( outputIt.key() );
106  outputIt->setChildId( mId );
107  }
108 }
109 
110 bool QgsProcessingModelChildAlgorithm::removeModelOutput( const QString &name )
111 {
112  mModelOutputs.remove( name );
113  return true;
114 }
115 
116 QVariant QgsProcessingModelChildAlgorithm::toVariant() const
117 {
118  QVariantMap map;
119  map.insert( QStringLiteral( "id" ), mId );
120  map.insert( QStringLiteral( "alg_id" ), mAlgorithmId );
121  map.insert( QStringLiteral( "alg_config" ), mConfiguration );
122  map.insert( QStringLiteral( "active" ), mActive );
123 
124  QVariantList dependencies;
125  for ( const QgsProcessingModelChildDependency &dependency : mDependencies )
126  {
127  dependencies << dependency.toVariant();
128  }
129  map.insert( QStringLiteral( "dependencies" ), dependencies );
130 
131  saveCommonProperties( map );
132 
133  QVariantMap paramMap;
134  QMap< QString, QgsProcessingModelChildParameterSources >::const_iterator paramIt = mParams.constBegin();
135  for ( ; paramIt != mParams.constEnd(); ++paramIt )
136  {
137  QVariantList sources;
138  const auto constValue = paramIt.value();
139  for ( const QgsProcessingModelChildParameterSource &source : constValue )
140  {
141  sources << source.toVariant();
142  }
143  paramMap.insert( paramIt.key(), sources );
144  }
145  map.insert( QStringLiteral( "params" ), paramMap );
146 
147  QVariantMap outputMap;
148  QMap< QString, QgsProcessingModelOutput >::const_iterator outputIt = mModelOutputs.constBegin();
149  for ( ; outputIt != mModelOutputs.constEnd(); ++outputIt )
150  {
151  outputMap.insert( outputIt.key(), outputIt.value().toVariant() );
152  }
153  map.insert( QStringLiteral( "outputs" ), outputMap );
154 
155  return map;
156 }
157 
158 bool QgsProcessingModelChildAlgorithm::loadVariant( const QVariant &child )
159 {
160  QVariantMap map = child.toMap();
161 
162  mId = map.value( QStringLiteral( "id" ) ).toString();
163  if ( mId.isEmpty() )
164  return false;
165 
166  mConfiguration = map.value( QStringLiteral( "alg_config" ) ).toMap();
167  setAlgorithmId( map.value( QStringLiteral( "alg_id" ) ).toString() );
168  if ( algorithmId().isEmpty() )
169  return false;
170  mActive = map.value( QStringLiteral( "active" ) ).toBool();
171 
172  mDependencies.clear();
173  if ( map.value( QStringLiteral( "dependencies" ) ).type() == QVariant::StringList )
174  {
175  const QStringList dependencies = map.value( QStringLiteral( "dependencies" ) ).toStringList();
176  for ( const QString &dependency : dependencies )
177  {
178  QgsProcessingModelChildDependency dep;
179  dep.childId = dependency;
180  mDependencies << dep;
181  }
182  }
183  else
184  {
185  const QVariantList dependencies = map.value( QStringLiteral( "dependencies" ) ).toList();
186  for ( const QVariant &dependency : dependencies )
187  {
188  QgsProcessingModelChildDependency dep;
189  dep.loadVariant( dependency.toMap() );
190  mDependencies << dep;
191  }
192  }
193 
194  restoreCommonProperties( map );
195 
196  mParams.clear();
197  QVariantMap paramMap = map.value( QStringLiteral( "params" ) ).toMap();
198  QVariantMap::const_iterator paramIt = paramMap.constBegin();
199  for ( ; paramIt != paramMap.constEnd(); ++paramIt )
200  {
201  QgsProcessingModelChildParameterSources sources;
202  const auto constToList = paramIt->toList();
203  for ( const QVariant &sourceVar : constToList )
204  {
205  QgsProcessingModelChildParameterSource param;
206  if ( !param.loadVariant( sourceVar.toMap() ) )
207  return false;
208  sources << param;
209  }
210  mParams.insert( paramIt.key(), sources );
211  }
212 
213  mModelOutputs.clear();
214  QVariantMap outputMap = map.value( QStringLiteral( "outputs" ) ).toMap();
215  QVariantMap::const_iterator outputIt = outputMap.constBegin();
216  for ( ; outputIt != outputMap.constEnd(); ++outputIt )
217  {
218  QgsProcessingModelOutput output;
219  if ( !output.loadVariant( outputIt.value().toMap() ) )
220  return false;
221 
222  mModelOutputs.insert( outputIt.key(), output );
223  }
224 
225  return true;
226 }
227 
228 QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters,
229  int currentIndent, int indentSize, const QMap<QString, QString> &friendlyChildNames, const QMap<QString, QString> &friendlyOutputNames ) const
230 {
231  QStringList lines;
232  const QString baseIndent = QString( ' ' ).repeated( currentIndent );
233  const QString lineIndent = QString( ' ' ).repeated( indentSize );
234 
235  if ( !algorithm() )
236  return QStringList();
237 
238  if ( !description().isEmpty() )
239  lines << baseIndent + QStringLiteral( "# %1" ).arg( description() );
240  if ( !mComment.description().isEmpty() )
241  lines << baseIndent + QStringLiteral( "# %1" ).arg( mComment.description() );
242 
243  QStringList paramParts;
244  QStringList paramComments;
245  for ( auto paramIt = mParams.constBegin(); paramIt != mParams.constEnd(); ++paramIt )
246  {
247  QStringList sourceParts;
248  QStringList sourceComments;
249  const QgsProcessingParameterDefinition *def = algorithm() ? algorithm()->parameterDefinition( paramIt.key() ) : nullptr;
250  const auto parts = paramIt.value();
251  sourceParts.reserve( parts.size() );
252  sourceComments.reserve( parts.size() );
253  for ( const QgsProcessingModelChildParameterSource &source : parts )
254  {
255  QString part = source.asPythonCode( outputType, def, friendlyChildNames );
256  if ( !part.isEmpty() )
257  {
258  sourceParts << part;
259  sourceComments << source.asPythonComment( def );
260  }
261  }
262  if ( sourceParts.count() == 1 )
263  {
264  paramParts << QStringLiteral( "'%1': %2" ).arg( paramIt.key(), sourceParts.at( 0 ) );
265  paramComments << sourceComments.at( 0 );
266  }
267  else
268  {
269  paramParts << QStringLiteral( "'%1': [%2]" ).arg( paramIt.key(), sourceParts.join( ',' ) );
270  paramComments << QString();
271  }
272  }
273 
274  lines << baseIndent + QStringLiteral( "alg_params = {" );
275  lines.reserve( lines.size() + paramParts.size() );
276  int i = 0;
277  for ( const QString &p : std::as_const( paramParts ) )
278  {
279  QString line = baseIndent + lineIndent + p + ',';
280  if ( !paramComments.value( i ).isEmpty() )
281  {
282  line += QStringLiteral( " # %1" ).arg( paramComments.value( i ) );
283  }
284  lines << line;
285  i++;
286  }
287  for ( auto it = extraParameters.constBegin(); it != extraParameters.constEnd(); ++it )
288  {
289  lines << baseIndent + lineIndent + QStringLiteral( "%1: %2," ).arg( QgsProcessingUtils::stringToPythonLiteral( it.key() ), it.value() );
290  }
291  if ( lines.constLast().endsWith( ',' ) )
292  {
293  lines[ lines.count() - 1 ].truncate( lines.constLast().length() - 1 );
294  }
295  lines << baseIndent + QStringLiteral( "}" );
296 
297  lines << baseIndent + QStringLiteral( "outputs['%1'] = processing.run('%2', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ).arg( friendlyChildNames.value( mId, mId ), mAlgorithmId );
298 
299  for ( auto outputIt = mModelOutputs.constBegin(); outputIt != mModelOutputs.constEnd(); ++outputIt )
300  {
301  QString outputName = QStringLiteral( "%1:%2" ).arg( mId, outputIt.key() );
302  outputName = friendlyOutputNames.value( outputName, outputName );
303  lines << baseIndent + QStringLiteral( "results['%1'] = outputs['%2']['%3']" ).arg( outputName, friendlyChildNames.value( mId, mId ), outputIt.value().childOutputName() );
304  }
305 
306  return lines;
307 }
308 
309 QVariantMap QgsProcessingModelChildAlgorithm::configuration() const
310 {
311  return mConfiguration;
312 }
313 
314 void QgsProcessingModelChildAlgorithm::setConfiguration( const QVariantMap &configuration )
315 {
316  mConfiguration = configuration;
317  mAlgorithm.reset( QgsApplication::processingRegistry()->createAlgorithmById( mAlgorithmId, mConfiguration ) );
318 }
319 
320 void QgsProcessingModelChildAlgorithm::generateChildId( const QgsProcessingModelAlgorithm &model )
321 {
322  int i = 1;
323  QString id;
324  while ( true )
325  {
326  id = QStringLiteral( "%1_%2" ).arg( mAlgorithmId ).arg( i );
327  if ( !model.childAlgorithms().contains( id ) )
328  break;
329  i++;
330  }
331  mId = id;
332 }
333 
334 bool QgsProcessingModelChildAlgorithm::setAlgorithmId( const QString &algorithmId )
335 {
336  mAlgorithmId = algorithmId;
337  mAlgorithm.reset( QgsApplication::processingRegistry()->createAlgorithmById( mAlgorithmId, mConfiguration ) );
338  return static_cast< bool >( mAlgorithm.get() );
339 }
340 
341 bool QgsProcessingModelChildAlgorithm::reattach() const
342 {
343  return const_cast< QgsProcessingModelChildAlgorithm * >( this )->setAlgorithmId( mAlgorithmId );
344 }
345 
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:63
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:1703