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