QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
17 #include "qgsprocessingmodelcomponent.h"
18 #include "qgsprocessingmodelparameter.h"
19 #include "qgsprocessingmodelchildalgorithm.h"
20 #include "qgsprocessingmodeloutput.h"
21 #include "qgsprocessingmodelgroupbox.h"
22 #include "qgsmodelgraphicsscene.h"
23 #include "qgsapplication.h"
24 #include "qgsmodelgraphicitem.h"
25 #include "qgsprocessingmodelalgorithm.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  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  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  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  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  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  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  QPointF pt = linkPoint( Qt::TopEdge, -1, true );
502  mExpandTopButton->setPosition( QPointF( 0, pt.y() ) );
503  }
504  if ( mExpandBottomButton )
505  {
506  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  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  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 ? ( dynamic_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 0;
987 
988  switch ( edge )
989  {
990  case Qt::BottomEdge:
991  {
992  const QgsProcessingOutputDefinition *output = child->algorithm()->outputDefinitions().at( index );
993  QString title = output->description();
994  if ( mResults.contains( output->name() ) )
995  {
996  title += QStringLiteral( ": %1" ).arg( mResults.value( output->name() ).toString() );
997  }
998  return truncatedTextForItem( title );
999  }
1000 
1001  case Qt::TopEdge:
1002  {
1003  QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1004  params.erase( std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition * param )
1005  {
1006  return param->flags() & QgsProcessingParameterDefinition::FlagHidden || param->isDestination();
1007  } ), params.end() );
1008 
1009 
1010  QString title = params.at( index )->description();
1011  if ( !mInputs.value( params.at( index )->name() ).toString().isEmpty() )
1012  title += QStringLiteral( ": %1" ).arg( mInputs.value( params.at( index )->name() ).toString() );
1013  return truncatedTextForItem( title );
1014  }
1015 
1016  case Qt::LeftEdge:
1017  case Qt::RightEdge:
1018  break;
1019  }
1020  }
1021  return QString();
1022 }
1023 
1024 void QgsModelChildAlgorithmGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1025 {
1026  if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast< QgsProcessingModelChildAlgorithm * >( component() ) )
1027  {
1028  model()->childAlgorithm( child->childId() ).setPosition( pos );
1029  model()->childAlgorithm( child->childId() ).setSize( size );
1030  }
1031 }
1032 
1033 bool QgsModelChildAlgorithmGraphicItem::canDeleteComponent()
1034 {
1035  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1036  {
1037  return model()->dependentChildAlgorithms( child->childId() ).empty();
1038  }
1039  return false;
1040 }
1041 
1042 void QgsModelChildAlgorithmGraphicItem::setResults( const QVariantMap &results )
1043 {
1044  if ( mResults == results )
1045  return;
1046 
1047  mResults = results;
1048  update();
1049  emit updateArrowPaths();
1050 }
1051 
1052 void QgsModelChildAlgorithmGraphicItem::setInputs( const QVariantMap &inputs )
1053 {
1054  if ( mInputs == inputs )
1055  return;
1056 
1057  mInputs = inputs;
1058  update();
1059  emit updateArrowPaths();
1060 }
1061 
1062 void QgsModelChildAlgorithmGraphicItem::deleteComponent()
1063 {
1064  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1065  {
1066  emit aboutToChange( tr( "Remove %1" ).arg( child->algorithm() ? child->algorithm()->displayName() : tr( "Algorithm" ) ) );
1067  if ( !model()->removeChildAlgorithm( child->childId() ) )
1068  {
1069  QMessageBox::warning( nullptr, QObject::tr( "Could not remove algorithm" ),
1070  QObject::tr( "Other algorithms depend on the selected one.\n"
1071  "Remove them before trying to remove it." ) );
1072  }
1073  else
1074  {
1075  emit changed();
1076  emit requestModelRepaint();
1077  }
1078  }
1079 }
1080 
1081 void QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm()
1082 {
1083  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1084  {
1085  model()->deactivateChildAlgorithm( child->childId() );
1086  emit requestModelRepaint();
1087  }
1088 }
1089 
1090 void QgsModelChildAlgorithmGraphicItem::activateAlgorithm()
1091 {
1092  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
1093  {
1094  if ( model()->activateChildAlgorithm( child->childId() ) )
1095  {
1096  emit requestModelRepaint();
1097  }
1098  else
1099  {
1100  QMessageBox::warning( nullptr, QObject::tr( "Could not activate algorithm" ),
1101  QObject::tr( "The selected algorithm depends on other currently non-active algorithms.\n"
1102  "Activate them them before trying to activate it.." ) );
1103  }
1104  }
1105 }
1106 
1107 
1108 QgsModelOutputGraphicItem::QgsModelOutputGraphicItem( QgsProcessingModelOutput *output, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1109  : QgsModelComponentGraphicItem( output, model, parent )
1110 {
1111  QSvgRenderer svg( QgsApplication::iconPath( QStringLiteral( "mIconModelOutput.svg" ) ) );
1112  QPainter painter( &mPicture );
1113  svg.render( &painter );
1114  painter.end();
1115  setLabel( output->name() );
1116 }
1117 
1118 QColor QgsModelOutputGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1119 {
1120  QColor c( 172, 196, 114 );
1121  switch ( state )
1122  {
1123  case Selected:
1124  c = c.darker( 110 );
1125  break;
1126  case Hover:
1127  c = c.darker( 105 );
1128  break;
1129 
1130  case Normal:
1131  break;
1132  }
1133  return c;
1134 }
1135 
1136 QColor QgsModelOutputGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1137 {
1138  switch ( state )
1139  {
1140  case Selected:
1141  return QColor( 42, 65, 42 );
1142  case Hover:
1143  case Normal:
1144  return QColor( 90, 140, 90 );
1145  }
1146  return QColor();
1147 }
1148 
1149 QColor QgsModelOutputGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1150 {
1151  return Qt::black;
1152 }
1153 
1154 QPicture QgsModelOutputGraphicItem::iconPicture() const
1155 {
1156  return mPicture;
1157 }
1158 
1159 void QgsModelOutputGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1160 {
1161  if ( QgsProcessingModelOutput *output = dynamic_cast< QgsProcessingModelOutput * >( component() ) )
1162  {
1163  model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos );
1164  model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setSize( size );
1165  }
1166 }
1167 
1168 bool QgsModelOutputGraphicItem::canDeleteComponent()
1169 {
1170  if ( dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
1171  {
1172  return true;
1173  }
1174  return false;
1175 }
1176 
1177 void QgsModelOutputGraphicItem::deleteComponent()
1178 {
1179  if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
1180  {
1181  emit aboutToChange( tr( "Delete Output %1" ).arg( output->description() ) );
1182  model()->childAlgorithm( output->childId() ).removeModelOutput( output->name() );
1183  model()->updateDestinationParameters();
1184  emit changed();
1185  emit requestModelRepaint();
1186  }
1187 }
1188 
1189 
1190 //
1191 // QgsModelGroupBoxGraphicItem
1192 //
1193 
1194 QgsModelGroupBoxGraphicItem::QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1195  : QgsModelComponentGraphicItem( box, model, parent )
1196 {
1197  setZValue( QgsModelGraphicsScene::ZValues::GroupBox );
1198  setLabel( box->description() );
1199 
1200  QFont f = font();
1201  f.setBold( true );
1202  f.setPixelSize( 14 );
1203  setFont( f );
1204 }
1205 
1206 void QgsModelGroupBoxGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1207 {
1208  QMenu *popupmenu = new QMenu( event->widget() );
1209  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1210  connect( removeAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::deleteComponent );
1211  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1212  connect( editAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::editComponent );
1213  popupmenu->exec( event->screenPos() );
1214 }
1215 
1216 QgsModelGroupBoxGraphicItem::~QgsModelGroupBoxGraphicItem() = default;
1217 
1218 QColor QgsModelGroupBoxGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1219 {
1220  QColor c( 230, 230, 230 );
1221  switch ( state )
1222  {
1223  case Selected:
1224  c = c.darker( 110 );
1225  break;
1226  case Hover:
1227  c = c.darker( 105 );
1228  break;
1229 
1230  case Normal:
1231  break;
1232  }
1233  return c;
1234 }
1235 
1236 QColor QgsModelGroupBoxGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1237 {
1238  switch ( state )
1239  {
1240  case Selected:
1241  return QColor( 50, 50, 50 );
1242  case Hover:
1243  case Normal:
1244  return QColor( 150, 150, 150 );
1245  }
1246  return QColor();
1247 }
1248 
1249 QColor QgsModelGroupBoxGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1250 {
1251  return QColor( 100, 100, 100 );
1252 }
1253 
1254 Qt::PenStyle QgsModelGroupBoxGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1255 {
1256  return Qt::DotLine;
1257 }
1258 
1259 Qt::Alignment QgsModelGroupBoxGraphicItem::titleAlignment() const
1260 {
1261  return Qt::AlignHCenter;
1262 }
1263 
1264 void QgsModelGroupBoxGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1265 {
1266  if ( QgsProcessingModelGroupBox *box = dynamic_cast< QgsProcessingModelGroupBox * >( component() ) )
1267  {
1268  box->setPosition( pos );
1269  box->setSize( size );
1270  model()->addGroupBox( *box );
1271  }
1272 }
1273 
1274 bool QgsModelGroupBoxGraphicItem::canDeleteComponent()
1275 {
1276  if ( dynamic_cast< QgsProcessingModelGroupBox * >( component() ) )
1277  {
1278  return true;
1279  }
1280  return false;
1281 }
1282 
1283 void QgsModelGroupBoxGraphicItem::deleteComponent()
1284 {
1285  if ( const QgsProcessingModelGroupBox *box = dynamic_cast< const QgsProcessingModelGroupBox * >( component() ) )
1286  {
1287  emit aboutToChange( tr( "Delete Group Box" ) );
1288  model()->removeGroupBox( box->uuid() );
1289  emit changed();
1290  emit requestModelRepaint();
1291  }
1292 }
1293 
1294 void QgsModelGroupBoxGraphicItem::editComponent()
1295 {
1296  if ( const QgsProcessingModelGroupBox *box = dynamic_cast< const QgsProcessingModelGroupBox * >( component() ) )
1297  {
1298  QgsModelGroupBoxDefinitionDialog dlg( *box, this->scene()->views().at( 0 ) );
1299 
1300  if ( dlg.exec() )
1301  {
1302  emit aboutToChange( tr( "Edit Group Box" ) );
1303  model()->addGroupBox( dlg.groupBox() );
1304  emit changed();
1305  emit requestModelRepaint();
1306  }
1307  }
1308 }
1309 
1310 //
1311 // QgsModelCommentGraphicItem
1312 //
1313 
1314 QgsModelCommentGraphicItem::QgsModelCommentGraphicItem( QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1315  : QgsModelComponentGraphicItem( comment, model, parent )
1316  , mParentComponent( parentItem->component()->clone() )
1317  , mParentItem( parentItem )
1318 {
1319  setLabel( comment->description() );
1320 
1321  QFont f = font();
1322  f.setPixelSize( 9 );
1323  setFont( f );
1324 }
1325 
1326 void QgsModelCommentGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1327 {
1328  QMenu *popupmenu = new QMenu( event->widget() );
1329  QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1330  connect( removeAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::deleteComponent );
1331  QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1332  connect( editAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::editComponent );
1333  popupmenu->exec( event->screenPos() );
1334 }
1335 
1336 QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
1337 
1338 QColor QgsModelCommentGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1339 {
1340  QColor c( 230, 230, 230 );
1341  switch ( state )
1342  {
1343  case Selected:
1344  c = c.darker( 110 );
1345  break;
1346  case Hover:
1347  c = c.darker( 105 );
1348  break;
1349 
1350  case Normal:
1351  break;
1352  }
1353  return c;
1354 }
1355 
1356 QColor QgsModelCommentGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1357 {
1358  switch ( state )
1359  {
1360  case Selected:
1361  return QColor( 50, 50, 50 );
1362  case Hover:
1363  case Normal:
1364  return QColor( 150, 150, 150 );
1365  }
1366  return QColor();
1367 }
1368 
1369 QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1370 {
1371  return QColor( 100, 100, 100 );
1372 }
1373 
1374 Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1375 {
1376  return Qt::DotLine;
1377 }
1378 
1379 void QgsModelCommentGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1380 {
1381  if ( QgsProcessingModelComment *comment = modelComponent() )
1382  {
1383  comment->setPosition( pos );
1384  comment->setSize( size );
1385  }
1386 }
1387 
1388 bool QgsModelCommentGraphicItem::canDeleteComponent()
1389 {
1390  if ( modelComponent() )
1391  {
1392  return true;
1393  }
1394  return false;
1395 }
1396 
1397 void QgsModelCommentGraphicItem::deleteComponent()
1398 {
1399  if ( QgsProcessingModelComment *comment = modelComponent() )
1400  {
1401  emit aboutToChange( tr( "Delete Comment" ) );
1402  comment->setDescription( QString() );
1403  emit changed();
1404  emit requestModelRepaint();
1405  }
1406 }
1407 
1408 void QgsModelCommentGraphicItem::editComponent()
1409 {
1410  if ( mParentItem )
1411  {
1412  mParentItem->editComment();
1413  }
1414 }
1415 
1416 QgsProcessingModelComment *QgsModelCommentGraphicItem::modelComponent()
1417 {
1418  if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( mParentComponent.get() ) )
1419  {
1420  return model()->childAlgorithm( child->childId() ).comment();
1421  }
1422  else if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( mParentComponent.get() ) )
1423  {
1424  return model()->parameterComponent( param->parameterName() ).comment();
1425  }
1426  else if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( mParentComponent.get() ) )
1427  {
1428  return model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).comment();
1429  }
1430  return nullptr;
1431 }
1432 
1433 QgsModelComponentGraphicItem *QgsModelCommentGraphicItem::parentComponentItem() const
1434 {
1435  return mParentItem;
1436 }
1437 
1438 
QgsModelViewMouseEvent
A QgsModelViewMouseEvent is the result of a user interaction with the mouse on a QgsModelGraphicsView...
Definition: qgsmodelviewmouseevent.h:37
qgsmodelgroupboxdefinitionwidget.h
QgsProcessingParameterDefinition
Base class for the definition of processing parameters.
Definition: qgsprocessingparameters.h:331
QgsApplication::iconPath
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Definition: qgsapplication.cpp:615
qgsmodelviewmouseevent.h
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsProcessingParameterDefinitions
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Definition: qgsprocessingparameters.h:761
qgsapplication.h
QgsProcessingOutputDefinition
Base class for the definition of processing outputs.
Definition: qgsprocessingoutputs.h:42
qgsmodelgraphicsview.h
QgsModelGroupBoxDefinitionDialog
A widget which allow users to specify the properties of a model group box.
Definition: qgsmodelgroupboxdefinitionwidget.h:45
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:44
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:106
QgsProcessingOutputDefinition::name
QString name() const
Returns the name of the output.
Definition: qgsprocessingoutputs.h:92
qgsmodelviewtool.h