QGIS API Documentation  3.21.0-Master (909859188c)
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  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->name() );
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 
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
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).
A widget which allow users to specify the properties of a model group box.
A QgsModelViewMouseEvent is the result of a user interaction with the mouse on a QgsModelGraphicsView...
QPointF modelPoint() const
Returns the event point location in model coordinates.
A class to represent a 2D point.
Definition: qgspointxy.h:59
Base class for the definition of processing outputs.
QString name() const
Returns the name of the output.
QString description() const
Returns the description for the output.
Base class for the definition of processing parameters.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
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
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.