QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsmodelgraphicsscene.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmodelgraphicsscene.cpp
3  ----------------------------------
4  Date : March 2020
5  Copyright : (C) 2020 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 
16 #include "qgsmodelgraphicsscene.h"
17 #include "qgsprocessingmodelchildparametersource.h"
18 #include "qgsprocessingmodelalgorithm.h"
20 #include "qgsmodelarrowitem.h"
21 #include "qgsprocessingmodelgroupbox.h"
22 #include "qgsmessagebar.h"
23 #include "qgsmessagebaritem.h"
24 #include "qgsmessageviewer.h"
25 #include <QGraphicsSceneMouseEvent>
26 #include <QPushButton>
27 
29 
30 QgsModelGraphicsScene::QgsModelGraphicsScene( QObject *parent )
31  : QGraphicsScene( parent )
32 {
33  setItemIndexMethod( QGraphicsScene::NoIndex );
34 }
35 
36 QgsProcessingModelAlgorithm *QgsModelGraphicsScene::model()
37 {
38  return mModel;
39 }
40 
41 void QgsModelGraphicsScene::setModel( QgsProcessingModelAlgorithm *model )
42 {
43  mModel = model;
44 }
45 
46 void QgsModelGraphicsScene::setFlag( QgsModelGraphicsScene::Flag flag, bool on )
47 {
48  if ( on )
49  mFlags |= flag;
50  else
51  mFlags &= ~flag;
52 }
53 
54 void QgsModelGraphicsScene::mousePressEvent( QGraphicsSceneMouseEvent *event )
55 {
56  if ( event->button() != Qt::LeftButton )
57  return;
58  QGraphicsScene::mousePressEvent( event );
59 }
60 
61 QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const
62 {
63  return new QgsModelParameterGraphicItem( param, model, nullptr );
64 }
65 
66 QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const
67 {
68  return new QgsModelChildAlgorithmGraphicItem( child, model, nullptr );
69 }
70 
71 QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const
72 {
73  return new QgsModelOutputGraphicItem( output, model, nullptr );
74 }
75 
76 QgsModelComponentGraphicItem *QgsModelGraphicsScene::createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem ) const
77 {
78  return new QgsModelCommentGraphicItem( comment, parentItem, model, nullptr );
79 }
80 
81 QgsModelComponentGraphicItem *QgsModelGraphicsScene::createGroupBoxGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelGroupBox *box ) const
82 {
83  return new QgsModelGroupBoxGraphicItem( box, model, nullptr );
84 }
85 
86 void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context )
87 {
88  // model group boxes
89  const QList<QgsProcessingModelGroupBox> boxes = model->groupBoxes();
90  mGroupBoxItems.clear();
91  for ( const QgsProcessingModelGroupBox &box : boxes )
92  {
93  QgsModelComponentGraphicItem *item = createGroupBoxGraphicItem( model, box.clone() );
94  addItem( item );
95  item->setPos( box.position().x(), box.position().y() );
96  mGroupBoxItems.insert( box.uuid(), item );
97  connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
98  connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
99  connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
100  }
101 
102  // model input parameters
103  const QMap<QString, QgsProcessingModelParameter> params = model->parameterComponents();
104  for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
105  {
106  QgsModelComponentGraphicItem *item = createParameterGraphicItem( model, it.value().clone() );
107  addItem( item );
108  item->setPos( it.value().position().x(), it.value().position().y() );
109  mParameterItems.insert( it.value().parameterName(), item );
110  connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
111  connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
112  connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
113 
114  addCommentItemForComponent( model, it.value(), item );
115  }
116 
117  // input dependency arrows
118  for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
119  {
120  const QgsProcessingParameterDefinition *parameterDef = model->parameterDefinition( it.key() );
121  const QStringList parameterLinks = parameterDef->dependsOnOtherParameters();
122  for ( const QString &otherName : parameterLinks )
123  {
124  if ( mParameterItems.contains( it.key() ) && mParameterItems.contains( otherName ) )
125  {
126  std::unique_ptr< QgsModelArrowItem > arrow = qgis::make_unique< QgsModelArrowItem >( mParameterItems.value( otherName ), QgsModelArrowItem::Marker::Circle, mParameterItems.value( it.key() ), QgsModelArrowItem::Marker::ArrowHead );
127  arrow->setPenStyle( Qt::DotLine );
128  addItem( arrow.release() );
129  }
130  }
131  }
132 
133  // child algorithms
134  const QMap<QString, QgsProcessingModelChildAlgorithm> childAlgs = model->childAlgorithms();
135  for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
136  {
137  QgsModelChildAlgorithmGraphicItem *item = createChildAlgGraphicItem( model, it.value().clone() );
138  addItem( item );
139  item->setPos( it.value().position().x(), it.value().position().y() );
140  item->setResults( mChildResults.value( it.value().childId() ).toMap() );
141  item->setInputs( mChildInputs.value( it.value().childId() ).toMap() );
142  mChildAlgorithmItems.insert( it.value().childId(), item );
143  connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
144  connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
145  connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
146 
147  addCommentItemForComponent( model, it.value(), item );
148  }
149 
150  // arrows linking child algorithms
151  for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
152  {
153  int topIdx = 0;
154  int bottomIdx = 0;
155  if ( !it.value().algorithm() )
156  continue;
157 
158  const QgsProcessingParameterDefinitions parameters = it.value().algorithm()->parameterDefinitions();
159  for ( const QgsProcessingParameterDefinition *parameter : parameters )
160  {
161  if ( !( parameter->flags() & QgsProcessingParameterDefinition::FlagHidden ) )
162  {
163  QList< QgsProcessingModelChildParameterSource > sources;
164  if ( it.value().parameterSources().contains( parameter->name() ) )
165  sources = it.value().parameterSources()[parameter->name()];
166  for ( const QgsProcessingModelChildParameterSource &source : sources )
167  {
168  const QList< LinkSource > sourceItems = linkSourcesForParameterValue( model, QVariant::fromValue( source ), it.value().childId(), context );
169  for ( const LinkSource &link : sourceItems )
170  {
171  if ( !link.item )
172  continue;
173  QgsModelArrowItem *arrow = nullptr;
174  if ( link.linkIndex == -1 )
175  arrow = new QgsModelArrowItem( link.item, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge, parameter->isDestination() ? bottomIdx : topIdx, QgsModelArrowItem::Marker::Circle );
176  else
177  arrow = new QgsModelArrowItem( link.item, link.edge, link.linkIndex, true, QgsModelArrowItem::Marker::Circle,
178  mChildAlgorithmItems.value( it.value().childId() ),
179  parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge,
180  parameter->isDestination() ? bottomIdx : topIdx,
181  true,
182  QgsModelArrowItem::Marker::Circle );
183  addItem( arrow );
184  }
185  }
186  }
187  if ( parameter->isDestination() )
188  bottomIdx++;
189  else
190  topIdx++;
191  }
192  const QList< QgsProcessingModelChildDependency > dependencies = it.value().dependencies();
193  for ( const QgsProcessingModelChildDependency &depend : dependencies )
194  {
195  if ( depend.conditionalBranch.isEmpty() || !model->childAlgorithm( depend.childId ).algorithm() )
196  {
197  addItem( new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead ) );
198  }
199  else
200  {
201  // find branch link point
202  const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( depend.childId ).algorithm()->outputDefinitions();
203  int i = 0;
204  for ( const QgsProcessingOutputDefinition *output : outputs )
205  {
206  if ( output->name() == depend.conditionalBranch )
207  break;
208  i++;
209  }
210  addItem( new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), Qt::BottomEdge, i, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead ) );
211  }
212  }
213  }
214 
215  // and finally the model outputs
216  for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
217  {
218  const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
219  QMap< QString, QgsModelComponentGraphicItem * > outputItems;
220 
221  for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
222  {
223  QgsModelComponentGraphicItem *item = createOutputGraphicItem( model, outputIt.value().clone() );
224  addItem( item );
225  connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
226  connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
227  connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
228 
229  QPointF pos = outputIt.value().position();
230  int idx = -1;
231  int i = 0;
232  // find the actual index of the linked output from the child algorithm it comes from
233  if ( it.value().algorithm() )
234  {
235  const QgsProcessingOutputDefinitions sourceChildAlgOutputs = it.value().algorithm()->outputDefinitions();
236  for ( const QgsProcessingOutputDefinition *childAlgOutput : sourceChildAlgOutputs )
237  {
238  if ( childAlgOutput->name() == outputIt.value().childOutputName() )
239  {
240  idx = i;
241  break;
242  }
243  i++;
244  }
245  }
246 
247  item->setPos( pos );
248  outputItems.insert( outputIt.key(), item );
249  addItem( new QgsModelArrowItem( mChildAlgorithmItems[it.value().childId()], Qt::BottomEdge, idx, QgsModelArrowItem::Marker::Circle, item, QgsModelArrowItem::Marker::Circle ) );
250 
251  addCommentItemForComponent( model, outputIt.value(), item );
252  }
253  mOutputItems.insert( it.value().childId(), outputItems );
254  }
255 }
256 
257 QList<QgsModelComponentGraphicItem *> QgsModelGraphicsScene::selectedComponentItems()
258 {
259  QList<QgsModelComponentGraphicItem *> componentItemList;
260 
261  const QList<QGraphicsItem *> graphicsItemList = selectedItems();
262  for ( QGraphicsItem *item : graphicsItemList )
263  {
264  if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
265  {
266  componentItemList.push_back( componentItem );
267  }
268  }
269 
270  return componentItemList;
271 }
272 
273 QgsModelComponentGraphicItem *QgsModelGraphicsScene::componentItemAt( QPointF position ) const
274 {
275  //get a list of items which intersect the specified position, in descending z order
276  const QList<QGraphicsItem *> itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
277 
278  for ( QGraphicsItem *graphicsItem : itemList )
279  {
280  if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
281  {
282  return componentItem;
283  }
284  }
285  return nullptr;
286 }
287 
288 QgsModelComponentGraphicItem *QgsModelGraphicsScene::groupBoxItem( const QString &uuid )
289 {
290  return mGroupBoxItems.value( uuid );
291 }
292 
293 void QgsModelGraphicsScene::selectAll()
294 {
295  //select all items in scene
296  QgsModelComponentGraphicItem *focusedItem = nullptr;
297  const QList<QGraphicsItem *> itemList = items();
298  for ( QGraphicsItem *graphicsItem : itemList )
299  {
300  if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
301  {
302  componentItem->setSelected( true );
303  if ( !focusedItem )
304  focusedItem = componentItem;
305  }
306  }
307  emit selectedItemChanged( focusedItem );
308 }
309 
310 void QgsModelGraphicsScene::deselectAll()
311 {
312  //we can't use QGraphicsScene::clearSelection, as that emits no signals
313  //and we don't know which items are being deselected
314  //instead, do the clear selection manually...
315  const QList<QGraphicsItem *> selectedItemList = selectedItems();
316  for ( QGraphicsItem *item : selectedItemList )
317  {
318  if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
319  {
320  componentItem->setSelected( false );
321  }
322  }
323  emit selectedItemChanged( nullptr );
324 }
325 
326 void QgsModelGraphicsScene::setSelectedItem( QgsModelComponentGraphicItem *item )
327 {
328  whileBlocking( this )->deselectAll();
329  if ( item )
330  {
331  item->setSelected( true );
332  }
333  emit selectedItemChanged( item );
334 }
335 
336 void QgsModelGraphicsScene::setChildAlgorithmResults( const QVariantMap &results )
337 {
338  mChildResults = results;
339 
340  for ( auto it = mChildResults.constBegin(); it != mChildResults.constEnd(); ++it )
341  {
342  if ( QgsModelChildAlgorithmGraphicItem *item = mChildAlgorithmItems.value( it.key() ) )
343  {
344  item->setResults( it.value().toMap() );
345  }
346  }
347 }
348 
349 void QgsModelGraphicsScene::setChildAlgorithmInputs( const QVariantMap &inputs )
350 {
351  mChildInputs = inputs;
352 
353  for ( auto it = mChildInputs.constBegin(); it != mChildInputs.constEnd(); ++it )
354  {
355  if ( QgsModelChildAlgorithmGraphicItem *item = mChildAlgorithmItems.value( it.key() ) )
356  {
357  item->setInputs( it.value().toMap() );
358  }
359  }
360 }
361 
362 QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForParameterValue( QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context ) const
363 {
364  QList<QgsModelGraphicsScene::LinkSource> res;
365  if ( value.type() == QVariant::List )
366  {
367  const QVariantList list = value.toList();
368  for ( const QVariant &v : list )
369  res.append( linkSourcesForParameterValue( model, v, childId, context ) );
370  }
371  else if ( value.type() == QVariant::StringList )
372  {
373  const QStringList list = value.toStringList();
374  for ( const QString &v : list )
375  res.append( linkSourcesForParameterValue( model, v, childId, context ) );
376  }
377  else if ( value.canConvert< QgsProcessingModelChildParameterSource >() )
378  {
379  const QgsProcessingModelChildParameterSource source = value.value< QgsProcessingModelChildParameterSource >();
380  switch ( source.source() )
381  {
382  case QgsProcessingModelChildParameterSource::ModelParameter:
383  {
384  LinkSource l;
385  l.item = mParameterItems.value( source.parameterName() );
386  res.append( l );
387  break;
388  }
389  case QgsProcessingModelChildParameterSource::ChildOutput:
390  {
391  if ( !model->childAlgorithm( source.outputChildId() ).algorithm() )
392  break;
393 
394  const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions();
395  int i = 0;
396  for ( const QgsProcessingOutputDefinition *output : outputs )
397  {
398  if ( output->name() == source.outputName() )
399  break;
400  i++;
401  }
402  if ( mChildAlgorithmItems.contains( source.outputChildId() ) )
403  {
404  LinkSource l;
405  l.item = mChildAlgorithmItems.value( source.outputChildId() );
406  l.edge = Qt::BottomEdge;
407  l.linkIndex = i;
408  res.append( l );
409  }
410 
411  break;
412  }
413 
414  case QgsProcessingModelChildParameterSource::Expression:
415  {
416  const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = model->variablesForChildAlgorithm( childId, context );
417  QgsExpression exp( source.expression() );
418  const QSet<QString> vars = exp.referencedVariables();
419  for ( const QString &v : vars )
420  {
421  if ( variables.contains( v ) )
422  {
423  res.append( linkSourcesForParameterValue( model, QVariant::fromValue( variables.value( v ).source ), childId, context ) );
424  }
425  }
426  break;
427  }
428 
429  case QgsProcessingModelChildParameterSource::StaticValue:
430  case QgsProcessingModelChildParameterSource::ExpressionText:
431  case QgsProcessingModelChildParameterSource::ModelOutput:
432  break;
433  }
434  }
435  return res;
436 }
437 
438 void QgsModelGraphicsScene::addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem )
439 {
440  if ( mFlags & FlagHideComments || !component.comment() || component.comment()->description().isEmpty() )
441  return;
442 
443  QgsModelComponentGraphicItem *commentItem = createCommentGraphicItem( model, component.comment()->clone(), parentItem );
444  commentItem->setPos( component.comment()->position().x(), component.comment()->position().y() );
445  addItem( commentItem );
446  connect( commentItem, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
447  connect( commentItem, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
448  connect( commentItem, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
449 
450  std::unique_ptr< QgsModelArrowItem > arrow = qgis::make_unique< QgsModelArrowItem >( parentItem, QgsModelArrowItem::Circle, commentItem, QgsModelArrowItem::Circle );
451  arrow->setPenStyle( Qt::DotLine );
452  addItem( arrow.release() );
453 }
454 
455 QgsMessageBar *QgsModelGraphicsScene::messageBar() const
456 {
457  return mMessageBar;
458 }
459 
460 void QgsModelGraphicsScene::setMessageBar( QgsMessageBar *messageBar )
461 {
462  mMessageBar = messageBar;
463 }
464 
465 void QgsModelGraphicsScene::showWarning( const QString &shortMessage, const QString &title, const QString &longMessage, Qgis::MessageLevel level )
466 {
467  QgsMessageBarItem *messageWidget = mMessageBar->createMessage( QString(), shortMessage );
468  QPushButton *detailsButton = new QPushButton( tr( "Details" ) );
469  connect( detailsButton, &QPushButton::clicked, detailsButton, [ = ]
470  {
471  QgsMessageViewer *dialog = new QgsMessageViewer( detailsButton );
472  dialog->setTitle( title );
473  dialog->setMessage( longMessage, QgsMessageOutput::MessageHtml );
474  dialog->showMessage();
475  } );
476  messageWidget->layout()->addWidget( detailsButton );
477  mMessageBar->clearWidgets();
478  mMessageBar->pushWidget( messageWidget, level, 0 );
479 }
480 
482 
qgsmessagebaritem.h
algorithm
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
QgsProcessingParameterDefinition
Definition: qgsprocessingparameters.h:330
QgsProcessingParameterDefinitions
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Definition: qgsprocessingparameters.h:732
qgsmodelarrowitem.h
QgsProcessingAlgorithm::outputDefinitions
QgsProcessingOutputDefinitions outputDefinitions() const
Returns an ordered list of output definitions utilized by the algorithm.
Definition: qgsprocessingalgorithm.h:304
QgsProcessingOutputDefinition
Definition: qgsprocessingoutputs.h:41
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
QgsProcessingContext
Definition: qgsprocessingcontext.h:43
QgsMessageBarItem
Definition: qgsmessagebaritem.h:38
qgsmessageviewer.h
QgsMessageBar
Definition: qgsmessagebar.h:60
qgsmessagebar.h
qgsmodelcomponentgraphicitem.h
qgsmodelgraphicsscene.h
Qgis::MessageLevel
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:101
QgsProcessingParameterDefinition::FlagHidden
@ FlagHidden
Parameter is hidden and should not be shown to users.
Definition: qgsprocessingparameters.h:420
QgsMessageOutput::MessageHtml
@ MessageHtml
Definition: qgsmessageoutput.h:47
QgsProcessingOutputDefinitions
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
Definition: qgsprocessingoutputs.h:143
QgsExpression
Definition: qgsexpression.h:113
QgsProcessingParameterDefinition::dependsOnOtherParameters
virtual QStringList dependsOnOtherParameters() const
Returns a list of other parameter names on which this parameter is dependent (e.g.
Definition: qgsprocessingparameters.h:576