QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmodelcomponentgraphicitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmodelcomponentgraphicitem.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 
22 #include "qgsmodelgraphicsscene.h"
23 #include "qgsapplication.h"
24 #include "qgsmodelgraphicitem.h"
26 #include "qgsmodelgraphicsview.h"
27 #include "qgsmodelviewtool.h"
28 #include "qgsmodelviewmouseevent.h"
30 
31 #include <QSvgRenderer>
32 #include <QPicture>
33 #include <QPainter>
34 #include <QGraphicsSceneHoverEvent>
35 #include <QApplication>
36 #include <QPalette>
37 #include <QMessageBox>
38 #include <QMenu>
39 
41 
42 QgsModelComponentGraphicItem::QgsModelComponentGraphicItem( QgsProcessingModelComponent *component, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
43  : QGraphicsObject( parent )
44  , mComponent( component )
45  , mModel( model )
46 {
47  setAcceptHoverEvents( true );
48  setFlag( QGraphicsItem::ItemIsSelectable, true );
49  setFlag( QGraphicsItem::ItemSendsGeometryChanges, true );
50  setZValue( QgsModelGraphicsScene::ZValues::ModelComponent );
51 
52  mFont.setPixelSize( 12 );
53 
54  QSvgRenderer svg( QgsApplication::iconPath( QStringLiteral( "mActionEditModelComponent.svg" ) ) );
55  QPicture editPicture;
56  QPainter painter( &editPicture );
57  svg.render( &painter );
58  painter.end();
59  mEditButton = new QgsModelDesignerFlatButtonGraphicItem( this, editPicture, QPointF( 0, 0 ) );
60  connect( mEditButton, &QgsModelDesignerFlatButtonGraphicItem::clicked, this, &QgsModelComponentGraphicItem::editComponent );
61 
62  QSvgRenderer svg2( QgsApplication::iconPath( QStringLiteral( "mActionDeleteModelComponent.svg" ) ) );
63  QPicture deletePicture;
64  painter.begin( &deletePicture );
65  svg2.render( &painter );
66  painter.end();
67  mDeleteButton = new QgsModelDesignerFlatButtonGraphicItem( this, deletePicture, QPointF( 0, 0 ) );
68  connect( mDeleteButton, &QgsModelDesignerFlatButtonGraphicItem::clicked, this, &QgsModelComponentGraphicItem::deleteComponent );
69 
70  updateButtonPositions();
71 }
72 
73 QgsModelComponentGraphicItem::Flags QgsModelComponentGraphicItem::flags() const
74 {
75  return QgsModelComponentGraphicItem::Flags();
76 }
77 
78 QgsModelComponentGraphicItem::~QgsModelComponentGraphicItem() = default;
79 
80 QgsProcessingModelComponent *QgsModelComponentGraphicItem::component()
81 {
82  return mComponent.get();
83 }
84 
85 const QgsProcessingModelComponent *QgsModelComponentGraphicItem::component() const
86 {
87  return mComponent.get();
88 }
89 
90 QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model()
91 {
92  return mModel;
93 }
94 
95 QgsModelGraphicsView *QgsModelComponentGraphicItem::view()
96 {
97  if ( scene()->views().isEmpty() )
98  return nullptr;
99 
100  return qobject_cast< QgsModelGraphicsView * >( scene()->views().first() );
101 }
102 
103 QFont QgsModelComponentGraphicItem::font() const
104 {
105  return mFont;
106 }
107 
108 void QgsModelComponentGraphicItem::setFont( const QFont &font )
109 {
110  mFont = font;
111  update();
112 }
113 
114 void QgsModelComponentGraphicItem::moveComponentBy( qreal dx, qreal dy )
115 {
116  setPos( mComponent->position().x() + dx, mComponent->position().y() + dy );
117  mComponent->setPosition( pos() );
118 
119  emit aboutToChange( tr( "Move %1" ).arg( mComponent->description() ) );
120  updateStoredComponentPosition( pos(), mComponent->size() );
121  emit changed();
122 
123  emit sizePositionChanged();
124  emit updateArrowPaths();
125 }
126 
127 void QgsModelComponentGraphicItem::previewItemMove( qreal dx, qreal dy )
128 {
129  setPos( mComponent->position().x() + dx, mComponent->position().y() + dy );
130  emit updateArrowPaths();
131 }
132 
133 void QgsModelComponentGraphicItem::setItemRect( QRectF rect )
134 {
135  rect = rect.normalized();
136 
137  if ( rect.width() < MIN_COMPONENT_WIDTH )
138  rect.setWidth( MIN_COMPONENT_WIDTH );
139  if ( rect.height() < MIN_COMPONENT_HEIGHT )
140  rect.setHeight( MIN_COMPONENT_HEIGHT );
141 
142  setPos( rect.center() );
143  prepareGeometryChange();
144 
145  emit aboutToChange( tr( "Resize %1" ).arg( mComponent->description() ) );
146 
147  mComponent->setPosition( pos() );
148  mComponent->setSize( rect.size() );
149  updateStoredComponentPosition( pos(), mComponent->size() );
150 
151  updateButtonPositions();
152  emit changed();
153 
154  emit updateArrowPaths();
155  emit sizePositionChanged();
156 }
157 
158 QRectF QgsModelComponentGraphicItem::previewItemRectChange( QRectF rect )
159 {
160  rect = rect.normalized();
161 
162  if ( rect.width() < MIN_COMPONENT_WIDTH )
163  rect.setWidth( MIN_COMPONENT_WIDTH );
164  if ( rect.height() < MIN_COMPONENT_HEIGHT )
165  rect.setHeight( MIN_COMPONENT_HEIGHT );
166 
167  setPos( rect.center() );
168  prepareGeometryChange();
169 
170  mTempSize = rect.size();
171 
172  updateButtonPositions();
173  emit updateArrowPaths();
174 
175  return rect;
176 }
177 
178 void QgsModelComponentGraphicItem::finalizePreviewedItemRectChange( QRectF )
179 {
180  mComponent->setPosition( pos() );
181  prepareGeometryChange();
182  mComponent->setSize( mTempSize );
183  mTempSize = QSizeF();
184 
185  emit aboutToChange( tr( "Resize %1" ).arg( mComponent->description() ) );
186  updateStoredComponentPosition( pos(), mComponent->size() );
187 
188  updateButtonPositions();
189 
190  emit changed();
191 
192  emit sizePositionChanged();
193  emit updateArrowPaths();
194 }
195 
196 void QgsModelComponentGraphicItem::modelHoverEnterEvent( QgsModelViewMouseEvent *event )
197 {
198  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
199  updateToolTip( mapFromScene( event->modelPoint() ) );
200 }
201 
202 void QgsModelComponentGraphicItem::modelHoverMoveEvent( QgsModelViewMouseEvent *event )
203 {
204  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
205  updateToolTip( mapFromScene( event->modelPoint() ) );
206 }
207 
208 void QgsModelComponentGraphicItem::modelHoverLeaveEvent( QgsModelViewMouseEvent * )
209 {
210  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
211  {
212  setToolTip( QString() );
213  if ( mIsHovering )
214  {
215  mIsHovering = false;
216  update();
217  emit repaintArrows();
218  }
219  }
220 }
221 
222 void QgsModelComponentGraphicItem::modelDoubleClickEvent( QgsModelViewMouseEvent * )
223 {
224  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
225  editComponent();
226 }
227 
228 void QgsModelComponentGraphicItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * )
229 {
230  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
231  editComponent();
232 }
233 
234 void QgsModelComponentGraphicItem::hoverEnterEvent( QGraphicsSceneHoverEvent *event )
235 {
236  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
237  updateToolTip( event->pos() );
238 }
239 
240 void QgsModelComponentGraphicItem::hoverMoveEvent( QGraphicsSceneHoverEvent *event )
241 {
242  if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
243  updateToolTip( event->pos() );
244 }
245 
246 void QgsModelComponentGraphicItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * )
247 {
248  modelHoverLeaveEvent( nullptr );
249 }
250 
251 QVariant QgsModelComponentGraphicItem::itemChange( QGraphicsItem::GraphicsItemChange change, const QVariant &value )
252 {
253  switch ( change )
254  {
255  case QGraphicsItem::ItemSelectedChange:
256  {
257  emit repaintArrows();
258  break;
259  }
260 
261  case QGraphicsItem::ItemSceneChange:
262  {
263  if ( !mInitialized )
264  {
265  // ideally would be in constructor, but cannot call virtual methods from that...
266  if ( linkPointCount( Qt::TopEdge ) )
267  {
268  mExpandTopButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::TopEdge ), QPointF( 0, 0 ) );
269  connect( mExpandTopButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [ = ]( bool folded ) { fold( Qt::TopEdge, folded ); } );
270  }
271  if ( linkPointCount( Qt::BottomEdge ) )
272  {
273  mExpandBottomButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::BottomEdge ), QPointF( 0, 0 ) );
274  connect( mExpandBottomButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [ = ]( bool folded ) { fold( Qt::BottomEdge, folded ); } );
275  }
276  mInitialized = true;
277  updateButtonPositions();
278  }
279  break;
280  }
281 
282  default:
283  break;
284  }
285 
286  return QGraphicsObject::itemChange( change, value );
287 }
288 
289 QRectF QgsModelComponentGraphicItem::boundingRect() const
290 {
291  const QFontMetricsF fm( mFont );
292  const int linksAbove = linkPointCount( Qt::TopEdge );
293  const int linksBelow = linkPointCount( Qt::BottomEdge );
294 
295  const double hUp = linksAbove == 0 ? 0 :
296  fm.height() * 1.2 * ( ( mComponent->linksCollapsed( Qt::TopEdge ) ? 0 : linksAbove ) + 2 );
297  const double hDown = linksBelow == 0 ? 0 :
298  fm.height() * 1.2 * ( ( mComponent->linksCollapsed( Qt::BottomEdge ) ? 0 : linksBelow ) + 2 );
299  return QRectF( -( itemSize().width() ) / 2 - RECT_PEN_SIZE,
300  -( itemSize().height() ) / 2 - hUp - RECT_PEN_SIZE,
301  itemSize().width() + 2 * RECT_PEN_SIZE,
302  itemSize().height() + hDown + hUp + 2 * RECT_PEN_SIZE );
303 }
304 
305 bool QgsModelComponentGraphicItem::contains( const QPointF &point ) const
306 {
307  const QRectF paintingBounds = boundingRect();
308  if ( point.x() < paintingBounds.left() + RECT_PEN_SIZE )
309  return false;
310  if ( point.x() > paintingBounds.right() - RECT_PEN_SIZE )
311  return false;
312  if ( point.y() < paintingBounds.top() + RECT_PEN_SIZE )
313  return false;
314  if ( point.y() > paintingBounds.bottom() - RECT_PEN_SIZE )
315  return false;
316 
317  return true;
318 }
319 
320 void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * )
321 {
322  const QRectF rect = itemRect();
323  QColor color;
324  QColor stroke;
325  QColor foreColor;
326  if ( mComponent->color().isValid() )
327  {
328  color = mComponent->color();
329  switch ( state() )
330  {
331  case Selected:
332  color = color.darker( 110 );
333  break;
334  case Hover:
335  color = color.darker( 105 );
336  break;
337 
338  case Normal:
339  break;
340  }
341  stroke = color.darker( 110 );
342  foreColor = color.lightness() > 150 ? QColor( 0, 0, 0 ) : QColor( 255, 255, 255 );
343  }
344  else
345  {
346  color = fillColor( state() );
347  stroke = strokeColor( state() );
348  foreColor = textColor( state() );
349  }
350 
351  QPen strokePen = QPen( stroke, 0 ) ; // 0 width "cosmetic" pen
352  strokePen.setStyle( strokeStyle( state() ) );
353  painter->setPen( strokePen );
354  painter->setBrush( QBrush( color, Qt::SolidPattern ) );
355  painter->drawRect( rect );
356  painter->setFont( font() );
357  painter->setPen( QPen( foreColor ) );
358 
359  QString text;
360 
361  const QSizeF componentSize = itemSize();
362 
363  const QFontMetricsF fm( font() );
364  double h = fm.ascent();
365  QPointF pt( -componentSize.width() / 2 + 25, componentSize.height() / 2.0 - h + 1 );
366 
367  if ( iconPicture().isNull() && iconPixmap().isNull() )
368  {
369  const QRectF labelRect = QRectF( rect.left() + TEXT_MARGIN, rect.top() + TEXT_MARGIN, rect.width() - 2 * TEXT_MARGIN - mButtonSize.width() - BUTTON_MARGIN, rect.height() - 2 * TEXT_MARGIN );
370  text = label();
371  painter->drawText( labelRect, Qt::TextWordWrap | titleAlignment(), text );
372  }
373  else
374  {
375  const QRectF labelRect = QRectF( rect.left() + 21 + TEXT_MARGIN, rect.top() + TEXT_MARGIN,
376  rect.width() - 2 * TEXT_MARGIN - mButtonSize.width() - BUTTON_MARGIN - 21, rect.height() - 2 * TEXT_MARGIN );
377  text = label();
378  painter->drawText( labelRect, Qt::TextWordWrap | Qt::AlignVCenter, text );
379  }
380 
381  painter->setPen( QPen( QApplication::palette().color( QPalette::WindowText ) ) );
382 
383  if ( linkPointCount( Qt::TopEdge ) || linkPointCount( Qt::BottomEdge ) )
384  {
385  h = -( fm.height() * 1.2 );
386  h = h - componentSize.height() / 2.0 + 5;
387  pt = QPointF( -componentSize.width() / 2 + 25, h );
388  painter->drawText( pt, QObject::tr( "In" ) );
389  int i = 1;
390  if ( !mComponent->linksCollapsed( Qt::TopEdge ) )
391  {
392  for ( int idx = 0; idx < linkPointCount( Qt::TopEdge ); ++idx )
393  {
394  text = linkPointText( Qt::TopEdge, idx );
395  h = -( fm.height() * 1.2 ) * ( i + 1 );
396  h = h - componentSize.height() / 2.0 + 5;
397  pt = QPointF( -componentSize.width() / 2 + 33, h );
398  painter->drawText( pt, text );
399  i += 1;
400  }
401  }
402 
403  h = fm.height() * 1.1;
404  h = h + componentSize.height() / 2.0;
405  pt = QPointF( -componentSize.width() / 2 + 25, h );
406  painter->drawText( pt, QObject::tr( "Out" ) );
407  if ( !mComponent->linksCollapsed( Qt::BottomEdge ) )
408  {
409  for ( int idx = 0; idx < linkPointCount( Qt::BottomEdge ); ++idx )
410  {
411  text = linkPointText( Qt::BottomEdge, idx );
412  h = fm.height() * 1.2 * ( idx + 2 );
413  h = h + componentSize.height() / 2.0;
414  pt = QPointF( -componentSize.width() / 2 + 33, h );
415  painter->drawText( pt, text );
416  }
417  }
418  }
419 
420  const QPixmap px = iconPixmap();
421  if ( !px.isNull() )
422  {
423  painter->drawPixmap( QPointF( -( componentSize.width() / 2.0 ) + 3, -8 ), px );
424  }
425  else
426  {
427  const QPicture pic = iconPicture();
428  if ( !pic.isNull() )
429  {
430  painter->drawPicture( QPointF( -( componentSize.width() / 2.0 ) + 3, -8 ), pic );
431  }
432  }
433 }
434 
435 QRectF QgsModelComponentGraphicItem::itemRect( bool storedRect ) const
436 {
437  if ( storedRect )
438  {
439  return QRectF( mComponent->position().x() - ( mComponent->size().width() ) / 2.0,
440  mComponent->position().y() - ( mComponent->size().height() ) / 2.0,
441  mComponent->size().width(),
442  mComponent->size().height() );
443  }
444  else
445  return QRectF( -( itemSize().width() ) / 2.0,
446  -( itemSize().height() ) / 2.0,
447  itemSize().width(),
448  itemSize().height() );
449 }
450 
451 QString QgsModelComponentGraphicItem::truncatedTextForItem( const QString &text ) const
452 {
453  const QFontMetricsF fm( mFont );
454  double width = fm.boundingRect( text ).width();
455  if ( width < itemSize().width() - 25 - mButtonSize.width() )
456  return text;
457 
458  QString t = text;
459  t = t.left( t.length() - 3 ) + QChar( 0x2026 );
460  width = fm.boundingRect( t ).width();
461  while ( width > itemSize().width() - 25 - mButtonSize.width() )
462  {
463  if ( t.length() < 5 )
464  break;
465 
466  t = t.left( t.length() - 4 ) + QChar( 0x2026 );
467  width = fm.boundingRect( t ).width();
468  }
469  return t;
470 }
471 
472 Qt::PenStyle QgsModelComponentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
473 {
474  return Qt::SolidLine;
475 }
476 
477 Qt::Alignment QgsModelComponentGraphicItem::titleAlignment() const
478 {
479  return Qt::AlignLeft;
480 }
481 
482 QPicture QgsModelComponentGraphicItem::iconPicture() const
483 {
484  return QPicture();
485 }
486 
487 QPixmap QgsModelComponentGraphicItem::iconPixmap() const
488 {
489  return QPixmap();
490 }
491 
492 void QgsModelComponentGraphicItem::updateButtonPositions()
493 {
494  mEditButton->setPosition( QPointF( itemSize().width() / 2.0 - mButtonSize.width() / 2.0 - BUTTON_MARGIN,
495  itemSize().height() / 2.0 - mButtonSize.height() / 2.0 - BUTTON_MARGIN ) );
496  mDeleteButton->setPosition( QPointF( itemSize().width() / 2.0 - mButtonSize.width() / 2.0 - BUTTON_MARGIN,
497  mButtonSize.height() / 2.0 - itemSize().height() / 2.0 + BUTTON_MARGIN ) );
498 
499  if ( mExpandTopButton )
500  {
501  const QPointF pt = linkPoint( Qt::TopEdge, -1, true );
502  mExpandTopButton->setPosition( QPointF( 0, pt.y() ) );
503  }
504  if ( mExpandBottomButton )
505  {
506  const QPointF pt = linkPoint( Qt::BottomEdge, -1, false );
507  mExpandBottomButton->setPosition( QPointF( 0, pt.y() ) );
508  }
509 }
510 
511 QSizeF QgsModelComponentGraphicItem::itemSize() const
512 {
513  return !mTempSize.isValid() ? mComponent->size() : mTempSize;
514 }
515 
516 void QgsModelComponentGraphicItem::updateToolTip( const QPointF &pos )
517 {
518  const bool prevHoverStatus = mIsHovering;
519  if ( itemRect().contains( pos ) )
520  {
521  setToolTip( mLabel );
522  mIsHovering = true;
523  }
524  else
525  {
526  setToolTip( QString() );
527  mIsHovering = false;
528  }
529  if ( mIsHovering != prevHoverStatus )
530  {
531  update();
532  emit repaintArrows();
533  }
534 }
535 
536 void QgsModelComponentGraphicItem::fold( Qt::Edge edge, bool folded )
537 {
538  emit aboutToChange( !folded ? tr( "Expand Item" ) : tr( "Collapse Item" ) );
539  mComponent->setLinksCollapsed( edge, folded );
540  // also need to update the model's stored component
541 
542  // TODO - this is not so nice, consider moving this to model class
543  if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast< QgsProcessingModelChildAlgorithm * >( mComponent.get() ) )
544  mModel->childAlgorithm( child->childId() ).setLinksCollapsed( edge, folded );
545  else if ( QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( mComponent.get() ) )
546  mModel->parameterComponent( param->parameterName() ).setLinksCollapsed( edge, folded );
547  else if ( QgsProcessingModelOutput *output = dynamic_cast< QgsProcessingModelOutput * >( mComponent.get() ) )
548  mModel->childAlgorithm( output->childId() ).modelOutput( output->name() ).setLinksCollapsed( edge, folded );
549 
550  prepareGeometryChange();
551  emit updateArrowPaths();
552  emit changed();
553  update();
554 }
555 
556 QString QgsModelComponentGraphicItem::label() const
557 {
558  return mLabel;
559 }
560 
561 void QgsModelComponentGraphicItem::setLabel( const QString &label )
562 {
563  mLabel = label;
564  update();
565 }
566 
567 QgsModelComponentGraphicItem::State QgsModelComponentGraphicItem::state() const
568 {
569  if ( isSelected() )
570  return Selected;
571  else if ( mIsHovering )
572  return Hover;
573  else
574  return Normal;
575 }
576 
577 int QgsModelComponentGraphicItem::linkPointCount( Qt::Edge ) const
578 {
579  return 0;
580 }
581 
582 QString QgsModelComponentGraphicItem::linkPointText( Qt::Edge, int ) const
583 {
584  return QString();
585 }
586 
587 QPointF QgsModelComponentGraphicItem::linkPoint( Qt::Edge edge, int index, bool incoming ) const
588 {
589  switch ( edge )
590  {
591  case Qt::BottomEdge:
592  {
593  if ( linkPointCount( Qt::BottomEdge ) )
594  {
595  double offsetX = 25;
596  if ( mComponent->linksCollapsed( Qt::BottomEdge ) )
597  {
598  offsetX = 17;
599  }
600  const int pointIndex = !mComponent->linksCollapsed( Qt::BottomEdge ) ? index : -1;
601  const QString text = truncatedTextForItem( linkPointText( Qt::BottomEdge, index ) );
602  const QFontMetricsF fm( mFont );
603  const double w = fm.boundingRect( text ).width();
604  const double h = fm.height() * 1.2 * ( pointIndex + 1 ) + fm.height() / 2.0;
605  const double y = h + itemSize().height() / 2.0 + 5;
606  const double x = !mComponent->linksCollapsed( Qt::BottomEdge ) ? ( -itemSize().width() / 2 + 33 + w + 5 ) : 10;
607  return QPointF( incoming ? -itemSize().width() / 2 + offsetX
608  : x,
609  y );
610  }
611  break;
612  }
613 
614  case Qt::TopEdge:
615  {
616  if ( linkPointCount( Qt::TopEdge ) )
617  {
618  double offsetX = 25;
619  int paramIndex = index;
620  if ( mComponent->linksCollapsed( Qt::TopEdge ) )
621  {
622  paramIndex = -1;
623  offsetX = 17;
624  }
625  const QFontMetricsF fm( mFont );
626  const QString text = truncatedTextForItem( linkPointText( Qt::TopEdge, index ) );
627  const double w = fm.boundingRect( text ).width();
628  double h = -( fm.height() * 1.2 ) * ( paramIndex + 2 ) - fm.height() / 2.0 + 8;
629  h = h - itemSize().height() / 2.0;
630  return QPointF( incoming ? -itemSize().width() / 2 + offsetX
631  : ( !mComponent->linksCollapsed( Qt::TopEdge ) ? ( -itemSize().width() / 2 + 33 + w + 5 ) : 10 ),
632  h );
633  }
634  break;
635  }
636  case Qt::LeftEdge:
637  case Qt::RightEdge:
638  break;
639  }
640 
641  return QPointF();
642 }
643 
644 QPointF QgsModelComponentGraphicItem::calculateAutomaticLinkPoint( QgsModelComponentGraphicItem *other, Qt::Edge &edge ) const
645 {
646  // find closest edge to other item
647  const QgsRectangle otherRect( other->itemRect().translated( other->pos() ) );
648 
649  const QPointF leftPoint = pos() + QPointF( -itemSize().width() / 2.0, 0 );
650  const double distLeft = otherRect.distance( QgsPointXY( leftPoint ) );
651 
652  const QPointF rightPoint = pos() + QPointF( itemSize().width() / 2.0, 0 );
653  const double distRight = otherRect.distance( QgsPointXY( rightPoint ) );
654 
655  const QPointF topPoint = pos() + QPointF( 0, -itemSize().height() / 2.0 );
656  const double distTop = otherRect.distance( QgsPointXY( topPoint ) );
657 
658  const QPointF bottomPoint = pos() + QPointF( 0, itemSize().height() / 2.0 );
659  const double distBottom = otherRect.distance( QgsPointXY( bottomPoint ) );
660 
661  if ( distLeft <= distRight && distLeft <= distTop && distLeft <= distBottom )
662  {
663  edge = Qt::LeftEdge;
664  return leftPoint;
665  }
666  else if ( distRight <= distTop && distRight <= distBottom )
667  {
668  edge = Qt::RightEdge;
669  return rightPoint;
670  }
671  else if ( distBottom <= distTop )
672  {
673  edge = Qt::BottomEdge;
674  return bottomPoint;
675  }
676  else
677  {
678  edge = Qt::TopEdge;
679  return topPoint;
680  }
681 }
682 
683 QPointF QgsModelComponentGraphicItem::calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge ) const
684 {
685  // find closest edge to other point
686  const QgsPointXY otherPt( point );
687  const QPointF leftPoint = pos() + QPointF( -itemSize().width() / 2.0, 0 );
688  const double distLeft = otherPt.distance( QgsPointXY( leftPoint ) );
689 
690  const QPointF rightPoint = pos() + QPointF( itemSize().width() / 2.0, 0 );
691  const double distRight = otherPt.distance( QgsPointXY( rightPoint ) );
692 
693  const QPointF topPoint = pos() + QPointF( 0, -itemSize().height() / 2.0 );
694  const double distTop = otherPt.distance( QgsPointXY( topPoint ) );
695 
696  const QPointF bottomPoint = pos() + QPointF( 0, itemSize().height() / 2.0 );
697  const double distBottom = otherPt.distance( QgsPointXY( bottomPoint ) );
698 
699  if ( distLeft <= distRight && distLeft <= distTop && distLeft <= distBottom )
700  {
701  edge = Qt::LeftEdge;
702  return leftPoint;
703  }
704  else if ( distRight <= distTop && distRight <= distBottom )
705  {
706  edge = Qt::RightEdge;
707  return rightPoint;
708  }
709  else if ( distBottom <= distTop )
710  {
711  edge = Qt::BottomEdge;
712  return bottomPoint;
713  }
714  else
715  {
716  edge = Qt::TopEdge;
717  return topPoint;
718  }
719 }
720 
721 QgsModelParameterGraphicItem::QgsModelParameterGraphicItem( QgsProcessingModelParameter *parameter, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
722  : QgsModelComponentGraphicItem( parameter, model, parent )
723 {
724  QSvgRenderer svg( QgsApplication::iconPath( QStringLiteral( "mIconModelInput.svg" ) ) );
725  QPainter painter( &mPicture );
726  svg.render( &painter );
727  painter.end();
728 
729  if ( const QgsProcessingParameterDefinition *paramDef = model->parameterDefinition( parameter->parameterName() ) )
730  setLabel( paramDef->description() );
731  else
732  setLabel( QObject::tr( "Error (%1)" ).arg( parameter->parameterName() ) );
733 }
734 
735 void QgsModelParameterGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
736 {
737  QMenu *popupmenu = new QMenu( event->widget() );
738  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
739  connect( removeAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::deleteComponent );
740  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
741  connect( editAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComponent );
742  QAction *editCommentAction = popupmenu->addAction( component()->comment()->description().isEmpty() ? QObject::tr( "Add Comment…" ) : QObject::tr( "Edit Comment…" ) );
743  connect( editCommentAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComment );
744 
745  popupmenu->exec( event->screenPos() );
746 }
747 
748 QColor QgsModelParameterGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
749 {
750  QColor c( 238, 242, 131 );
751  switch ( state )
752  {
753  case Selected:
754  c = c.darker( 110 );
755  break;
756  case Hover:
757  c = c.darker( 105 );
758  break;
759 
760  case Normal:
761  break;
762  }
763  return c;
764 }
765 
766 QColor QgsModelParameterGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
767 {
768  switch ( state )
769  {
770  case Selected:
771  return QColor( 116, 113, 68 );
772  case Hover:
773  case Normal:
774  return QColor( 234, 226, 118 );
775  }
776  return QColor();
777 }
778 
779 QColor QgsModelParameterGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
780 {
781  return Qt::black;
782 }
783 
784 QPicture QgsModelParameterGraphicItem::iconPicture() const
785 {
786  return mPicture;
787 }
788 
789 void QgsModelParameterGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
790 {
791  if ( QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( component() ) )
792  {
793  model()->parameterComponent( param->parameterName() ).setPosition( pos );
794  model()->parameterComponent( param->parameterName() ).setSize( size );
795  }
796 }
797 
798 bool QgsModelParameterGraphicItem::canDeleteComponent()
799 {
800  if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( component() ) )
801  {
802  if ( model()->childAlgorithmsDependOnParameter( param->parameterName() ) )
803  {
804  return false;
805  }
806  else if ( model()->otherParametersDependOnParameter( param->parameterName() ) )
807  {
808  return false;
809  }
810  else
811  {
812  return true;
813  }
814  }
815  return false;
816 }
817 
818 void QgsModelParameterGraphicItem::deleteComponent()
819 {
820  if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( component() ) )
821  {
822  if ( model()->childAlgorithmsDependOnParameter( param->parameterName() ) )
823  {
824  QMessageBox::warning( nullptr, QObject::tr( "Could not remove input" ),
825  QObject::tr( "Algorithms depend on the selected input.\n"
826  "Remove them before trying to remove it." ) );
827  }
828  else if ( model()->otherParametersDependOnParameter( param->parameterName() ) )
829  {
830  QMessageBox::warning( nullptr, QObject::tr( "Could not remove input" ),
831  QObject::tr( "Other inputs depend on the selected input.\n"
832  "Remove them before trying to remove it." ) );
833  }
834  else
835  {
836  emit aboutToChange( tr( "Delete Input %1" ).arg( param->description() ) );
837  model()->removeModelParameter( param->parameterName() );
838  emit changed();
839  emit requestModelRepaint();
840  }
841  }
842 }
843 
844 
845 
846 QgsModelChildAlgorithmGraphicItem::QgsModelChildAlgorithmGraphicItem( QgsProcessingModelChildAlgorithm *child, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
847  : QgsModelComponentGraphicItem( child, model, parent )
848 {
849  if ( child->algorithm() && !child->algorithm()->svgIconPath().isEmpty() )
850  {
851  QSvgRenderer svg( child->algorithm()->svgIconPath() );
852  const QSizeF size = svg.defaultSize();
853  QPainter painter( &mPicture );
854  painter.scale( 16.0 / size.width(), 16.0 / size.width() );
855  svg.render( &painter );
856  painter.end();
857  }
858  else if ( child->algorithm() )
859  {
860  mPixmap = child->algorithm()->icon().pixmap( 15, 15 );
861  }
862 
863  setLabel( child->description() );
864 
865  QStringList issues;
866  mIsValid = model->validateChildAlgorithm( child->childId(), issues );
867 }
868 
869 void QgsModelChildAlgorithmGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
870 {
871  QMenu *popupmenu = new QMenu( event->widget() );
872  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
873  connect( removeAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deleteComponent );
874  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
875  connect( editAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::editComponent );
876  QAction *editCommentAction = popupmenu->addAction( component()->comment()->description().isEmpty() ? QObject::tr( "Add Comment…" ) : QObject::tr( "Edit Comment…" ) );
877  connect( editCommentAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComment );
878  popupmenu->addSeparator();
879 
880  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
881  {
882  if ( !child->isActive() )
883  {
884  QAction *activateAction = popupmenu->addAction( QObject::tr( "Activate" ) );
885  connect( activateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::activateAlgorithm );
886  }
887  else
888  {
889  QAction *deactivateAction = popupmenu->addAction( QObject::tr( "Deactivate" ) );
890  connect( deactivateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm );
891  }
892  }
893 
894  popupmenu->exec( event->screenPos() );
895 }
896 
897 QColor QgsModelChildAlgorithmGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
898 {
899  QColor c;
900 
901  if ( mIsValid )
902  c = QColor( 255, 255, 255 );
903  else
904  c = QColor( 208, 0, 0 );
905 
906  switch ( state )
907  {
908  case Selected:
909  c = c.darker( 110 );
910  break;
911  case Hover:
912  c = c.darker( 105 );
913  break;
914 
915  case Normal:
916  break;
917  }
918  return c;
919 }
920 
921 QColor QgsModelChildAlgorithmGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
922 {
923  switch ( state )
924  {
925  case Selected:
926  return mIsValid ? QColor( 50, 50, 50 ) : QColor( 80, 0, 0 );
927  case Hover:
928  case Normal:
929  return mIsValid ? Qt::gray : QColor( 134, 0, 0 );
930  }
931  return QColor();
932 }
933 
934 QColor QgsModelChildAlgorithmGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
935 {
936  return mIsValid ? ( qgis::down_cast< const QgsProcessingModelChildAlgorithm * >( component() )->isActive() ? Qt::black : Qt::gray ) : QColor( 255, 255, 255 );
937 }
938 
939 QPixmap QgsModelChildAlgorithmGraphicItem::iconPixmap() const
940 {
941  return mPixmap;
942 }
943 
944 QPicture QgsModelChildAlgorithmGraphicItem::iconPicture() const
945 {
946  return mPicture;
947 }
948 
949 int QgsModelChildAlgorithmGraphicItem::linkPointCount( Qt::Edge edge ) const
950 {
951  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
952  {
953  if ( !child->algorithm() )
954  return 0;
955 
956  switch ( edge )
957  {
958  case Qt::BottomEdge:
959  return child->algorithm()->outputDefinitions().size();
960  case Qt::TopEdge:
961  {
962  QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
963  params.erase( std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition * param )
964  {
965  return param->flags() & QgsProcessingParameterDefinition::FlagHidden || param->isDestination();
966  } ), params.end() );
967  return params.size();
968  }
969 
970  case Qt::LeftEdge:
971  case Qt::RightEdge:
972  break;
973  }
974  }
975  return 0;
976 }
977 
978 QString QgsModelChildAlgorithmGraphicItem::linkPointText( Qt::Edge edge, int index ) const
979 {
980  if ( index < 0 )
981  return QString();
982 
983  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
984  {
985  if ( !child->algorithm() )
986  return QString();
987 
988  switch ( edge )
989  {
990  case Qt::BottomEdge:
991  {
992  if ( index >= child->algorithm()->outputDefinitions().length() )
993  {
994  // something goes wrong and tried to link to an not existing output
996  tr( "Cannot link output for child: %1" ).arg( child->algorithm()->name() ),
997  "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
998  return QString();
999  }
1000 
1001  const QgsProcessingOutputDefinition *output = child->algorithm()->outputDefinitions().at( index );
1002  QString title = output->description();
1003  if ( mResults.contains( output->name() ) )
1004  {
1005  title += QStringLiteral( ": %1" ).arg( mResults.value( output->name() ).toString() );
1006  }
1007  return truncatedTextForItem( title );
1008  }
1009 
1010  case Qt::TopEdge:
1011  {
1012  QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1013  params.erase( std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition * param )
1014  {
1015  return param->flags() & QgsProcessingParameterDefinition::FlagHidden || param->isDestination();
1016  } ), params.end() );
1017 
1018  if ( index >= params.length() )
1019  {
1020  // something goes wrong and tried to link to an not existing source parameter
1022  tr( "Cannot link source for child: %1" ).arg( child->algorithm()->name() ),
1023  "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
1024  return QString();
1025  }
1026 
1027  QString title = params.at( index )->description();
1028  if ( !mInputs.value( params.at( index )->name() ).toString().isEmpty() )
1029  title += QStringLiteral( ": %1" ).arg( mInputs.value( params.at( index )->name() ).toString() );
1030  return truncatedTextForItem( title );
1031  }
1032 
1033  case Qt::LeftEdge:
1034  case Qt::RightEdge:
1035  break;
1036  }
1037  }
1038  return QString();
1039 }
1040 
1041 void QgsModelChildAlgorithmGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1042 {
1043  if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast< QgsProcessingModelChildAlgorithm * >( component() ) )
1044  {
1045  model()->childAlgorithm( child->childId() ).setPosition( pos );
1046  model()->childAlgorithm( child->childId() ).setSize( size );
1047  }
1048 }
1049 
1050 bool QgsModelChildAlgorithmGraphicItem::canDeleteComponent()
1051 {
1052  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1053  {
1054  return model()->dependentChildAlgorithms( child->childId() ).empty();
1055  }
1056  return false;
1057 }
1058 
1059 void QgsModelChildAlgorithmGraphicItem::setResults( const QVariantMap &results )
1060 {
1061  if ( mResults == results )
1062  return;
1063 
1064  mResults = results;
1065  update();
1066  emit updateArrowPaths();
1067 }
1068 
1069 void QgsModelChildAlgorithmGraphicItem::setInputs( const QVariantMap &inputs )
1070 {
1071  if ( mInputs == inputs )
1072  return;
1073 
1074  mInputs = inputs;
1075  update();
1076  emit updateArrowPaths();
1077 }
1078 
1079 void QgsModelChildAlgorithmGraphicItem::deleteComponent()
1080 {
1081  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1082  {
1083  emit aboutToChange( tr( "Remove %1" ).arg( child->algorithm() ? child->algorithm()->displayName() : tr( "Algorithm" ) ) );
1084  if ( !model()->removeChildAlgorithm( child->childId() ) )
1085  {
1086  QMessageBox::warning( nullptr, QObject::tr( "Could not remove algorithm" ),
1087  QObject::tr( "Other algorithms depend on the selected one.\n"
1088  "Remove them before trying to remove it." ) );
1089  }
1090  else
1091  {
1092  emit changed();
1093  emit requestModelRepaint();
1094  }
1095  }
1096 }
1097 
1098 void QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm()
1099 {
1100  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1101  {
1102  model()->deactivateChildAlgorithm( child->childId() );
1103  emit requestModelRepaint();
1104  }
1105 }
1106 
1107 void QgsModelChildAlgorithmGraphicItem::activateAlgorithm()
1108 {
1109  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1110  {
1111  if ( model()->activateChildAlgorithm( child->childId() ) )
1112  {
1113  emit requestModelRepaint();
1114  }
1115  else
1116  {
1117  QMessageBox::warning( nullptr, QObject::tr( "Could not activate algorithm" ),
1118  QObject::tr( "The selected algorithm depends on other currently non-active algorithms.\n"
1119  "Activate them them before trying to activate it.." ) );
1120  }
1121  }
1122 }
1123 
1124 
1125 QgsModelOutputGraphicItem::QgsModelOutputGraphicItem( QgsProcessingModelOutput *output, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1126  : QgsModelComponentGraphicItem( output, model, parent )
1127 {
1128  QSvgRenderer svg( QgsApplication::iconPath( QStringLiteral( "mIconModelOutput.svg" ) ) );
1129  QPainter painter( &mPicture );
1130  svg.render( &painter );
1131  painter.end();
1132  setLabel( output->description() );
1133 }
1134 
1135 QColor QgsModelOutputGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1136 {
1137  QColor c( 172, 196, 114 );
1138  switch ( state )
1139  {
1140  case Selected:
1141  c = c.darker( 110 );
1142  break;
1143  case Hover:
1144  c = c.darker( 105 );
1145  break;
1146 
1147  case Normal:
1148  break;
1149  }
1150  return c;
1151 }
1152 
1153 QColor QgsModelOutputGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1154 {
1155  switch ( state )
1156  {
1157  case Selected:
1158  return QColor( 42, 65, 42 );
1159  case Hover:
1160  case Normal:
1161  return QColor( 90, 140, 90 );
1162  }
1163  return QColor();
1164 }
1165 
1166 QColor QgsModelOutputGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1167 {
1168  return Qt::black;
1169 }
1170 
1171 QPicture QgsModelOutputGraphicItem::iconPicture() const
1172 {
1173  return mPicture;
1174 }
1175 
1176 void QgsModelOutputGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1177 {
1178  if ( QgsProcessingModelOutput *output = dynamic_cast< QgsProcessingModelOutput * >( component() ) )
1179  {
1180  model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos );
1181  model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setSize( size );
1182  }
1183 }
1184 
1185 bool QgsModelOutputGraphicItem::canDeleteComponent()
1186 {
1187  if ( dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
1188  {
1189  return true;
1190  }
1191  return false;
1192 }
1193 
1194 void QgsModelOutputGraphicItem::deleteComponent()
1195 {
1196  if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
1197  {
1198  emit aboutToChange( tr( "Delete Output %1" ).arg( output->description() ) );
1199  model()->childAlgorithm( output->childId() ).removeModelOutput( output->name() );
1200  model()->updateDestinationParameters();
1201  emit changed();
1202  emit requestModelRepaint();
1203  }
1204 }
1205 
1206 
1207 //
1208 // QgsModelGroupBoxGraphicItem
1209 //
1210 
1211 QgsModelGroupBoxGraphicItem::QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1212  : QgsModelComponentGraphicItem( box, model, parent )
1213 {
1214  setZValue( QgsModelGraphicsScene::ZValues::GroupBox );
1215  setLabel( box->description() );
1216 
1217  QFont f = font();
1218  f.setBold( true );
1219  f.setPixelSize( 14 );
1220  setFont( f );
1221 }
1222 
1223 void QgsModelGroupBoxGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1224 {
1225  QMenu *popupmenu = new QMenu( event->widget() );
1226  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1227  connect( removeAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::deleteComponent );
1228  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1229  connect( editAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::editComponent );
1230  popupmenu->exec( event->screenPos() );
1231 }
1232 
1233 QgsModelGroupBoxGraphicItem::~QgsModelGroupBoxGraphicItem() = default;
1234 
1235 QColor QgsModelGroupBoxGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1236 {
1237  QColor c( 230, 230, 230 );
1238  switch ( state )
1239  {
1240  case Selected:
1241  c = c.darker( 110 );
1242  break;
1243  case Hover:
1244  c = c.darker( 105 );
1245  break;
1246 
1247  case Normal:
1248  break;
1249  }
1250  return c;
1251 }
1252 
1253 QColor QgsModelGroupBoxGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1254 {
1255  switch ( state )
1256  {
1257  case Selected:
1258  return QColor( 50, 50, 50 );
1259  case Hover:
1260  case Normal:
1261  return QColor( 150, 150, 150 );
1262  }
1263  return QColor();
1264 }
1265 
1266 QColor QgsModelGroupBoxGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1267 {
1268  return QColor( 100, 100, 100 );
1269 }
1270 
1271 Qt::PenStyle QgsModelGroupBoxGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1272 {
1273  return Qt::DotLine;
1274 }
1275 
1276 Qt::Alignment QgsModelGroupBoxGraphicItem::titleAlignment() const
1277 {
1278  return Qt::AlignHCenter;
1279 }
1280 
1281 void QgsModelGroupBoxGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1282 {
1283  if ( QgsProcessingModelGroupBox *box = dynamic_cast< QgsProcessingModelGroupBox * >( component() ) )
1284  {
1285  box->setPosition( pos );
1286  box->setSize( size );
1287  model()->addGroupBox( *box );
1288  }
1289 }
1290 
1291 bool QgsModelGroupBoxGraphicItem::canDeleteComponent()
1292 {
1293  if ( dynamic_cast< QgsProcessingModelGroupBox * >( component() ) )
1294  {
1295  return true;
1296  }
1297  return false;
1298 }
1299 
1300 void QgsModelGroupBoxGraphicItem::deleteComponent()
1301 {
1302  if ( const QgsProcessingModelGroupBox *box = dynamic_cast< const QgsProcessingModelGroupBox * >( component() ) )
1303  {
1304  emit aboutToChange( tr( "Delete Group Box" ) );
1305  model()->removeGroupBox( box->uuid() );
1306  emit changed();
1307  emit requestModelRepaint();
1308  }
1309 }
1310 
1311 void QgsModelGroupBoxGraphicItem::editComponent()
1312 {
1313  if ( const QgsProcessingModelGroupBox *box = dynamic_cast< const QgsProcessingModelGroupBox * >( component() ) )
1314  {
1315  QgsModelGroupBoxDefinitionDialog dlg( *box, this->scene()->views().at( 0 ) );
1316 
1317  if ( dlg.exec() )
1318  {
1319  emit aboutToChange( tr( "Edit Group Box" ) );
1320  model()->addGroupBox( dlg.groupBox() );
1321  emit changed();
1322  emit requestModelRepaint();
1323  }
1324  }
1325 }
1326 
1327 //
1328 // QgsModelCommentGraphicItem
1329 //
1330 
1331 QgsModelCommentGraphicItem::QgsModelCommentGraphicItem( QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1332  : QgsModelComponentGraphicItem( comment, model, parent )
1333  , mParentComponent( parentItem->component()->clone() )
1334  , mParentItem( parentItem )
1335 {
1336  setLabel( comment->description() );
1337 
1338  QFont f = font();
1339  f.setPixelSize( 9 );
1340  setFont( f );
1341 }
1342 
1343 void QgsModelCommentGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1344 {
1345  QMenu *popupmenu = new QMenu( event->widget() );
1346  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1347  connect( removeAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::deleteComponent );
1348  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1349  connect( editAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::editComponent );
1350  popupmenu->exec( event->screenPos() );
1351 }
1352 
1353 QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
1354 
1355 QColor QgsModelCommentGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1356 {
1357  QColor c( 230, 230, 230 );
1358  switch ( state )
1359  {
1360  case Selected:
1361  c = c.darker( 110 );
1362  break;
1363  case Hover:
1364  c = c.darker( 105 );
1365  break;
1366 
1367  case Normal:
1368  break;
1369  }
1370  return c;
1371 }
1372 
1373 QColor QgsModelCommentGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1374 {
1375  switch ( state )
1376  {
1377  case Selected:
1378  return QColor( 50, 50, 50 );
1379  case Hover:
1380  case Normal:
1381  return QColor( 150, 150, 150 );
1382  }
1383  return QColor();
1384 }
1385 
1386 QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1387 {
1388  return QColor( 100, 100, 100 );
1389 }
1390 
1391 Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1392 {
1393  return Qt::DotLine;
1394 }
1395 
1396 void QgsModelCommentGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1397 {
1398  if ( QgsProcessingModelComment *comment = modelComponent() )
1399  {
1400  comment->setPosition( pos );
1401  comment->setSize( size );
1402  }
1403 }
1404 
1405 bool QgsModelCommentGraphicItem::canDeleteComponent()
1406 {
1407  if ( modelComponent() )
1408  {
1409  return true;
1410  }
1411  return false;
1412 }
1413 
1414 void QgsModelCommentGraphicItem::deleteComponent()
1415 {
1416  if ( QgsProcessingModelComment *comment = modelComponent() )
1417  {
1418  emit aboutToChange( tr( "Delete Comment" ) );
1419  comment->setDescription( QString() );
1420  emit changed();
1421  emit requestModelRepaint();
1422  }
1423 }
1424 
1425 void QgsModelCommentGraphicItem::editComponent()
1426 {
1427  if ( mParentItem )
1428  {
1429  mParentItem->editComment();
1430  }
1431 }
1432 
1433 QgsProcessingModelComment *QgsModelCommentGraphicItem::modelComponent()
1434 {
1435  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( mParentComponent.get() ) )
1436  {
1437  return model()->childAlgorithm( child->childId() ).comment();
1438  }
1439  else if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( mParentComponent.get() ) )
1440  {
1441  return model()->parameterComponent( param->parameterName() ).comment();
1442  }
1443  else if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( mParentComponent.get() ) )
1444  {
1445  return model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).comment();
1446  }
1447  return nullptr;
1448 }
1449 
1450 QgsModelComponentGraphicItem *QgsModelCommentGraphicItem::parentComponentItem() const
1451 {
1452  return mParentItem;
1453 }
1454 
1455 
qgsprocessingmodeloutput.h
QgsModelViewMouseEvent
A QgsModelViewMouseEvent is the result of a user interaction with the mouse on a QgsModelGraphicsView...
Definition: qgsmodelviewmouseevent.h:36
qgsmodelgroupboxdefinitionwidget.h
qgsprocessingmodelalgorithm.h
qgsprocessingmodelgroupbox.h
QgsProcessingParameterDefinition
Base class for the definition of processing parameters.
Definition: qgsprocessingparameters.h:334
QgsApplication::iconPath
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Definition: qgsapplication.cpp:682
qgsmodelviewmouseevent.h
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsProcessingParameterDefinitions
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Definition: qgsprocessingparameters.h:889
qgsapplication.h
QgsProcessingOutputDefinition
Base class for the definition of processing outputs.
Definition: qgsprocessingoutputs.h:41
qgsmodelgraphicsview.h
qgsprocessingmodelcomponent.h
qgsprocessingmodelchildalgorithm.h
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsModelGroupBoxDefinitionDialog
A widget which allow users to specify the properties of a model group box.
Definition: qgsmodelgroupboxdefinitionwidget.h:44
qgsprocessingmodelparameter.h
QgsModelViewMouseEvent::modelPoint
QPointF modelPoint() const
Returns the event point location in model coordinates.
Definition: qgsmodelviewmouseevent.cpp:44
qgsmodelcomponentgraphicitem.h
qgsmodelgraphicsscene.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
c
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 c
Definition: porting_processing.dox:1
qgsmodelgraphicitem.h
QgsProcessingOutputDefinition::description
QString description() const
Returns the description for the output.
Definition: qgsprocessingoutputs.h:108
QgsProcessingOutputDefinition::name
QString name() const
Returns the name of the output.
Definition: qgsprocessingoutputs.h:94
qgsmodelviewtool.h