QGIS API Documentation 3.29.0-Master (006c3c0232)
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
23#include "qgsapplication.h"
24#include "qgsmodelgraphicitem.h"
27#include "qgsmodelviewtool.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
42QgsModelComponentGraphicItem::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
73QgsModelComponentGraphicItem::Flags QgsModelComponentGraphicItem::flags() const
74{
75 return QgsModelComponentGraphicItem::Flags();
76}
77
78QgsModelComponentGraphicItem::~QgsModelComponentGraphicItem() = default;
79
80QgsProcessingModelComponent *QgsModelComponentGraphicItem::component()
81{
82 return mComponent.get();
83}
84
85const QgsProcessingModelComponent *QgsModelComponentGraphicItem::component() const
86{
87 return mComponent.get();
88}
89
90QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model()
91{
92 return mModel;
93}
94
95QgsModelGraphicsView *QgsModelComponentGraphicItem::view()
96{
97 if ( scene()->views().isEmpty() )
98 return nullptr;
99
100 return qobject_cast< QgsModelGraphicsView * >( scene()->views().first() );
101}
102
103QFont QgsModelComponentGraphicItem::font() const
104{
105 return mFont;
106}
107
108void QgsModelComponentGraphicItem::setFont( const QFont &font )
109{
110 mFont = font;
111 update();
112}
113
114void 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
127void QgsModelComponentGraphicItem::previewItemMove( qreal dx, qreal dy )
128{
129 setPos( mComponent->position().x() + dx, mComponent->position().y() + dy );
130 emit updateArrowPaths();
131}
132
133void 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
158QRectF 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
178void 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
196void QgsModelComponentGraphicItem::modelHoverEnterEvent( QgsModelViewMouseEvent *event )
197{
198 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
199 updateToolTip( mapFromScene( event->modelPoint() ) );
200}
201
202void QgsModelComponentGraphicItem::modelHoverMoveEvent( QgsModelViewMouseEvent *event )
203{
204 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
205 updateToolTip( mapFromScene( event->modelPoint() ) );
206}
207
208void 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
222void QgsModelComponentGraphicItem::modelDoubleClickEvent( QgsModelViewMouseEvent * )
223{
224 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
225 editComponent();
226}
227
228void QgsModelComponentGraphicItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * )
229{
230 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
231 editComponent();
232}
233
234void QgsModelComponentGraphicItem::hoverEnterEvent( QGraphicsSceneHoverEvent *event )
235{
236 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
237 updateToolTip( event->pos() );
238}
239
240void QgsModelComponentGraphicItem::hoverMoveEvent( QGraphicsSceneHoverEvent *event )
241{
242 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
243 updateToolTip( event->pos() );
244}
245
246void QgsModelComponentGraphicItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * )
247{
248 modelHoverLeaveEvent( nullptr );
249}
250
251QVariant 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
289QRectF 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
305bool 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
320void 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
435QRectF 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
451QString 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
472Qt::PenStyle QgsModelComponentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
473{
474 return Qt::SolidLine;
475}
476
477Qt::Alignment QgsModelComponentGraphicItem::titleAlignment() const
478{
479 return Qt::AlignLeft;
480}
481
482QPicture QgsModelComponentGraphicItem::iconPicture() const
483{
484 return QPicture();
485}
486
487QPixmap QgsModelComponentGraphicItem::iconPixmap() const
488{
489 return QPixmap();
490}
491
492void 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
511QSizeF QgsModelComponentGraphicItem::itemSize() const
512{
513 return !mTempSize.isValid() ? mComponent->size() : mTempSize;
514}
515
516void 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
536void 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
556QString QgsModelComponentGraphicItem::label() const
557{
558 return mLabel;
559}
560
561void QgsModelComponentGraphicItem::setLabel( const QString &label )
562{
563 mLabel = label;
564 update();
565}
566
567QgsModelComponentGraphicItem::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
577int QgsModelComponentGraphicItem::linkPointCount( Qt::Edge ) const
578{
579 return 0;
580}
581
582QString QgsModelComponentGraphicItem::linkPointText( Qt::Edge, int ) const
583{
584 return QString();
585}
586
587QPointF 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
644QPointF 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
683QPointF 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
721QgsModelParameterGraphicItem::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
735void 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
748QColor 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
766QColor 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
779QColor QgsModelParameterGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
780{
781 return Qt::black;
782}
783
784QPicture QgsModelParameterGraphicItem::iconPicture() const
785{
786 return mPicture;
787}
788
789void 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
798bool 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
818void 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
846QgsModelChildAlgorithmGraphicItem::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
869void 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
897QColor 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
921QColor 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
934QColor 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
939QPixmap QgsModelChildAlgorithmGraphicItem::iconPixmap() const
940{
941 return mPixmap;
942}
943
944QPicture QgsModelChildAlgorithmGraphicItem::iconPicture() const
945{
946 return mPicture;
947}
948
949int 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
978QString 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
1041void 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
1050bool 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
1059void QgsModelChildAlgorithmGraphicItem::setResults( const QVariantMap &results )
1060{
1061 if ( mResults == results )
1062 return;
1063
1064 mResults = results;
1065 update();
1066 emit updateArrowPaths();
1067}
1068
1069void QgsModelChildAlgorithmGraphicItem::setInputs( const QVariantMap &inputs )
1070{
1071 if ( mInputs == inputs )
1072 return;
1073
1074 mInputs = inputs;
1075 update();
1076 emit updateArrowPaths();
1077}
1078
1079void 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
1098void 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
1107void 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
1125QgsModelOutputGraphicItem::QgsModelOutputGraphicItem( QgsProcessingModelOutput *output, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1126 : QgsModelComponentGraphicItem( output, model, parent )
1127{
1128 QSvgRenderer svg( QgsApplication::iconPath( QStringLiteral( "mIconModelOutput.svg" ) ) );
1129 QPainter painter( &mPicture );
1130 svg.render( &painter );
1131 painter.end();
1132 setLabel( output->description() );
1133}
1134
1135QColor 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
1153QColor 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
1166QColor QgsModelOutputGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1167{
1168 return Qt::black;
1169}
1170
1171QPicture QgsModelOutputGraphicItem::iconPicture() const
1172{
1173 return mPicture;
1174}
1175
1176void 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
1185bool QgsModelOutputGraphicItem::canDeleteComponent()
1186{
1187 if ( dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
1188 {
1189 return true;
1190 }
1191 return false;
1192}
1193
1194void 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
1211QgsModelGroupBoxGraphicItem::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
1223void 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
1233QgsModelGroupBoxGraphicItem::~QgsModelGroupBoxGraphicItem() = default;
1234
1235QColor 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
1253QColor 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
1266QColor QgsModelGroupBoxGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1267{
1268 return QColor( 100, 100, 100 );
1269}
1270
1271Qt::PenStyle QgsModelGroupBoxGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1272{
1273 return Qt::DotLine;
1274}
1275
1276Qt::Alignment QgsModelGroupBoxGraphicItem::titleAlignment() const
1277{
1278 return Qt::AlignHCenter;
1279}
1280
1281void 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
1291bool QgsModelGroupBoxGraphicItem::canDeleteComponent()
1292{
1293 if ( dynamic_cast< QgsProcessingModelGroupBox * >( component() ) )
1294 {
1295 return true;
1296 }
1297 return false;
1298}
1299
1300void 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
1311void 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
1331QgsModelCommentGraphicItem::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
1343void 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
1353QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
1354
1355QColor 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
1373QColor 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
1386QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1387{
1388 return QColor( 100, 100, 100 );
1389}
1390
1391Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1392{
1393 return Qt::DotLine;
1394}
1395
1396void 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
1405bool QgsModelCommentGraphicItem::canDeleteComponent()
1406{
1407 if ( modelComponent() )
1408 {
1409 return true;
1410 }
1411 return false;
1412}
1413
1414void 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
1425void QgsModelCommentGraphicItem::editComponent()
1426{
1427 if ( mParentItem )
1428 {
1429 mParentItem->editComment();
1430 }
1431}
1432
1433QgsProcessingModelComment *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
1450QgsModelComponentGraphicItem *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.