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