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