QGIS API Documentation 4.3.0-Master (e17dae1dea8)
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"
21#include "qgsmodelgraphicitem.h"
26#include "qgsmodelviewtool.h"
36
37#include <QApplication>
38#include <QGraphicsSceneHoverEvent>
39#include <QMenu>
40#include <QMessageBox>
41#include <QPainter>
42#include <QPalette>
43#include <QPicture>
44#include <QString>
45#include <QSvgRenderer>
46
47#include "moc_qgsmodelcomponentgraphicitem.cpp"
48
49using namespace Qt::StringLiterals;
50
52
53QgsModelComponentGraphicItem::QgsModelComponentGraphicItem( QgsProcessingModelComponent *component, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
54 : QGraphicsObject( parent )
55 , mComponent( component )
56 , mModel( model )
57{
58 setAcceptHoverEvents( true );
59 setFlag( QGraphicsItem::ItemIsSelectable, true );
60 setFlag( QGraphicsItem::ItemSendsGeometryChanges, true );
61 setZValue( QgsModelGraphicsScene::ZValues::ModelComponent );
62
63 mFont.setPixelSize( 12 );
64
65 QSvgRenderer svg( QgsApplication::iconPath( u"mActionEditModelComponent.svg"_s ) );
66 QPicture editPicture;
67 QPainter painter( &editPicture );
68 svg.render( &painter );
69 painter.end();
70 mEditButton = new QgsModelDesignerFlatButtonGraphicItem( this, editPicture, QPointF( 0, 0 ) );
71 connect( mEditButton, &QgsModelDesignerFlatButtonGraphicItem::clicked, this, &QgsModelComponentGraphicItem::editComponent );
72
73 QSvgRenderer svg2( QgsApplication::iconPath( u"mActionDeleteModelComponent.svg"_s ) );
74 QPicture deletePicture;
75 painter.begin( &deletePicture );
76 svg2.render( &painter );
77 painter.end();
78 mDeleteButton = new QgsModelDesignerFlatButtonGraphicItem( this, deletePicture, QPointF( 0, 0 ) );
79 connect( mDeleteButton, &QgsModelDesignerFlatButtonGraphicItem::clicked, this, &QgsModelComponentGraphicItem::deleteComponent );
80
81 updateButtonPositions();
82}
83
84QgsModelComponentGraphicItem::Flags QgsModelComponentGraphicItem::flags() const
85{
86 return QgsModelComponentGraphicItem::Flags();
87}
88
89QgsModelComponentGraphicItem::~QgsModelComponentGraphicItem() = default;
90
91QgsProcessingModelComponent *QgsModelComponentGraphicItem::component()
92{
93 return mComponent.get();
94}
95
96const QgsProcessingModelComponent *QgsModelComponentGraphicItem::component() const
97{
98 return mComponent.get();
99}
100
101QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model()
102{
103 return mModel;
104}
105
106const QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model() const
107{
108 return mModel;
109}
110
111QgsModelGraphicsView *QgsModelComponentGraphicItem::view()
112{
113 if ( scene()->views().isEmpty() )
114 return nullptr;
115
116 return qobject_cast<QgsModelGraphicsView *>( scene()->views().first() );
117}
118
119QFont QgsModelComponentGraphicItem::font() const
120{
121 return mFont;
122}
123
124void QgsModelComponentGraphicItem::setFont( const QFont &font )
125{
126 mFont = font;
127 update();
128}
129
130void QgsModelComponentGraphicItem::moveComponentBy( qreal dx, qreal dy )
131{
132 setPos( mComponent->position().x() + dx, mComponent->position().y() + dy );
133 mComponent->setPosition( pos() );
134
135 emit aboutToChange( tr( "Move %1" ).arg( mComponent->description() ) );
136 updateStoredComponentPosition( pos(), mComponent->size() );
137 emit changed();
138
139 emit sizePositionChanged();
140 emit updateArrowPaths();
141}
142
143void QgsModelComponentGraphicItem::previewItemMove( qreal dx, qreal dy )
144{
145 setPos( mComponent->position().x() + dx, mComponent->position().y() + dy );
146 emit updateArrowPaths();
147}
148
149void QgsModelComponentGraphicItem::setItemRect( QRectF rect )
150{
151 rect = rect.normalized();
152
153 if ( rect.width() < MIN_COMPONENT_WIDTH )
154 rect.setWidth( MIN_COMPONENT_WIDTH );
155 if ( rect.height() < MIN_COMPONENT_HEIGHT )
156 rect.setHeight( MIN_COMPONENT_HEIGHT );
157
158 setPos( rect.center() );
159 prepareGeometryChange();
160
161 emit aboutToChange( tr( "Resize %1" ).arg( mComponent->description() ) );
162
163 mComponent->setPosition( pos() );
164 mComponent->setSize( rect.size() );
165 updateStoredComponentPosition( pos(), mComponent->size() );
166
167 updateButtonPositions();
168 emit changed();
169
170 emit updateArrowPaths();
171 emit sizePositionChanged();
172}
173
174QRectF QgsModelComponentGraphicItem::previewItemRectChange( QRectF rect )
175{
176 rect = rect.normalized();
177
178 if ( rect.width() < MIN_COMPONENT_WIDTH )
179 rect.setWidth( MIN_COMPONENT_WIDTH );
180 if ( rect.height() < MIN_COMPONENT_HEIGHT )
181 rect.setHeight( MIN_COMPONENT_HEIGHT );
182
183 setPos( rect.center() );
184 prepareGeometryChange();
185
186 mTempSize = rect.size();
187
188 updateButtonPositions();
189 emit updateArrowPaths();
190
191 return rect;
192}
193
194void QgsModelComponentGraphicItem::finalizePreviewedItemRectChange( QRectF )
195{
196 mComponent->setPosition( pos() );
197 prepareGeometryChange();
198 mComponent->setSize( mTempSize );
199 mTempSize = QSizeF();
200
201 emit aboutToChange( tr( "Resize %1" ).arg( mComponent->description() ) );
202 updateStoredComponentPosition( pos(), mComponent->size() );
203
204 updateButtonPositions();
205
206 emit changed();
207
208 emit sizePositionChanged();
209 emit updateArrowPaths();
210}
211
212void QgsModelComponentGraphicItem::modelHoverEnterEvent( QgsModelViewMouseEvent *event )
213{
214 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
215 updateToolTip( mapFromScene( event->modelPoint() ) );
216}
217
218void QgsModelComponentGraphicItem::modelHoverMoveEvent( QgsModelViewMouseEvent *event )
219{
220 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
221 updateToolTip( mapFromScene( event->modelPoint() ) );
222}
223
224void QgsModelComponentGraphicItem::modelHoverLeaveEvent( QgsModelViewMouseEvent * )
225{
226 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
227 {
228 setToolTip( QString() );
229 if ( mIsHovering )
230 {
231 mIsHovering = false;
232 update();
233 emit repaintArrows();
234 }
235 }
236}
237
238void QgsModelComponentGraphicItem::modelDoubleClickEvent( QgsModelViewMouseEvent * )
239{
240 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
241 editComponent();
242}
243
244void QgsModelComponentGraphicItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * )
245{
246 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
247 editComponent();
248}
249
250void QgsModelComponentGraphicItem::hoverEnterEvent( QGraphicsSceneHoverEvent *event )
251{
252 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
253 updateToolTip( event->pos() );
254}
255
256void QgsModelComponentGraphicItem::hoverMoveEvent( QGraphicsSceneHoverEvent *event )
257{
258 if ( view() && view()->tool() && view()->tool()->allowItemInteraction() )
259 updateToolTip( event->pos() );
260}
261
262void QgsModelComponentGraphicItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * )
263{
264 modelHoverLeaveEvent( nullptr );
265}
266
267QVariant QgsModelComponentGraphicItem::itemChange( QGraphicsItem::GraphicsItemChange change, const QVariant &value )
268{
269 switch ( change )
270 {
271 case QGraphicsItem::ItemSelectedChange:
272 {
273 emit repaintArrows();
274 break;
275 }
276
277 case QGraphicsItem::ItemSceneChange:
278 {
279 if ( !mInitialized )
280 {
281 // ideally would be in constructor, but cannot call virtual methods from that...
282 if ( linkPointCount( Qt::TopEdge ) )
283 {
284 mExpandTopButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::TopEdge ), QPointF( 0, 0 ) );
285 connect( mExpandTopButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [this]( bool folded ) { fold( Qt::TopEdge, folded ); } );
286
287 for ( int idx = 0; idx < linkPointCount( Qt::TopEdge ); ++idx )
288 {
289 mInSockets.append( new QgsModelDesignerSocketGraphicItem( this, mComponent.get(), idx, QPointF( 0, 0 ), Qt::TopEdge ) );
290 }
291 }
292 if ( linkPointCount( Qt::BottomEdge ) )
293 {
294 mExpandBottomButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::BottomEdge ), QPointF( 0, 0 ) );
295 connect( mExpandBottomButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [this]( bool folded ) { fold( Qt::BottomEdge, folded ); } );
296
297 for ( int idx = 0; idx < linkPointCount( Qt::BottomEdge ); ++idx )
298 {
299 mOutSockets.append( new QgsModelDesignerSocketGraphicItem( this, mComponent.get(), idx, QPointF( 0, 0 ), Qt::BottomEdge ) );
300 }
301 }
302 mInitialized = true;
303 updateButtonPositions();
304 }
305 break;
306 }
307
308 default:
309 break;
310 }
311
312 return QGraphicsObject::itemChange( change, value );
313}
314
315QRectF QgsModelComponentGraphicItem::boundingRect() const
316{
317 const QFontMetricsF fm( mFont );
318 const int linksAbove = linkPointCount( Qt::TopEdge );
319 const int linksBelow = linkPointCount( Qt::BottomEdge );
320
321 const double hUp = linksAbove == 0 ? 0 : fm.height() * 1.2 * ( ( mComponent->linksCollapsed( Qt::TopEdge ) ? 0 : linksAbove ) + 2 );
322 const double hDown = linksBelow == 0 ? 0 : fm.height() * 1.2 * ( ( mComponent->linksCollapsed( Qt::BottomEdge ) ? 0 : linksBelow ) + 2 );
323
324 double outlineSize = 0;
325 if ( outlineColor().isValid() )
326 {
327 outlineSize = RECT_OUTLINE_SIZE + 0.5; // 0.5 for antialiasing
328 }
329
330 return QRectF(
331 -( itemSize().width() ) / 2 - RECT_PEN_SIZE - outlineSize,
332 -( itemSize().height() ) / 2 - hUp - RECT_PEN_SIZE - outlineSize,
333 itemSize().width() + 2 * RECT_PEN_SIZE + outlineSize * 2,
334 itemSize().height() + hDown + hUp + 2 * RECT_PEN_SIZE + outlineSize * 2
335 );
336}
337
338bool QgsModelComponentGraphicItem::contains( const QPointF &point ) const
339{
340 const QRectF paintingBounds = boundingRect();
341 if ( point.x() < paintingBounds.left() + RECT_PEN_SIZE )
342 return false;
343 if ( point.x() > paintingBounds.right() - RECT_PEN_SIZE )
344 return false;
345 if ( point.y() < paintingBounds.top() + RECT_PEN_SIZE )
346 return false;
347 if ( point.y() > paintingBounds.bottom() - RECT_PEN_SIZE )
348 return false;
349
350 return true;
351}
352
353void QgsModelComponentGraphicItem::paintBackground( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
354{
355 const QRectF rect = itemRect();
356 QColor color;
357 QColor stroke;
358 if ( mComponent->color().isValid() )
359 {
360 color = mComponent->color();
361 switch ( state() )
362 {
363 case Selected:
364 color = color.darker( 110 );
365 break;
366 case Hover:
367 color = color.darker( 105 );
368 break;
369
370 case Normal:
371 break;
372 }
373 stroke = color.darker( 110 );
374 }
375 else
376 {
377 color = fillColor( state() );
378 stroke = strokeColor( state() );
379 }
380
381 paintOutline( painter, option, widget );
382
383 QPen strokePen = QPen( stroke, 0 ); // 0 width "cosmetic" pen
384 strokePen.setStyle( strokeStyle( state() ) );
385 painter->setPen( strokePen );
386 painter->setBrush( QBrush( color, Qt::SolidPattern ) );
387 painter->drawRect( rect );
388}
389
390void QgsModelComponentGraphicItem::paintOutline( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * )
391{
392 const QColor outline = outlineColor();
393 if ( outline.isValid() )
394 {
395 // outline may be sub-pixel sized, which looks ugly without antialiasing.
396 const bool wasAntiAliased = painter->testRenderHint( QPainter::RenderHint::Antialiasing );
397 painter->setRenderHint( QPainter::RenderHint::Antialiasing );
398 QPen strokePen = QPen( outline, RECT_OUTLINE_SIZE );
399 strokePen.setJoinStyle( Qt::MiterJoin );
400 strokePen.setStyle( strokeStyle( state() ) );
401 painter->setPen( strokePen );
402 painter->setBrush( Qt::NoBrush );
403 const QRectF rect = itemRect();
404 painter->drawRect( rect );
405 painter->setRenderHint( QPainter::RenderHint::Antialiasing, wasAntiAliased );
406 }
407}
408
409void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
410{
411 paintBackground( painter, option, widget );
412
413 const QRectF rect = itemRect();
414 QColor foreColor;
415 if ( mComponent->color().isValid() )
416 {
417 QColor color = mComponent->color();
418 switch ( state() )
419 {
420 case Selected:
421 color = color.darker( 110 );
422 break;
423 case Hover:
424 color = color.darker( 105 );
425 break;
426
427 case Normal:
428 break;
429 }
430 foreColor = color.lightness() > 150 ? QColor( 0, 0, 0 ) : QColor( 255, 255, 255 );
431 }
432 else
433 {
434 foreColor = textColor( state() );
435 }
436 painter->setFont( font() );
437 painter->setPen( QPen( foreColor ) );
438
439 QString text;
440
441 const QSizeF componentSize = itemSize();
442
443 const QFontMetricsF fm( font() );
444 double h = fm.ascent();
445 QPointF pt( -componentSize.width() / 2 + 25, componentSize.height() / 2.0 - h + 1 );
446
447 if ( iconPicture().isNull() && iconPixmap().isNull() )
448 {
449 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 );
450 text = label();
451 painter->drawText( labelRect, Qt::TextWordWrap | titleAlignment(), text );
452 }
453 else
454 {
455 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 );
456 text = label();
457 painter->drawText( labelRect, Qt::TextWordWrap | Qt::AlignVCenter, text );
458 }
459
460 painter->setPen( QPen( QApplication::palette().color( QPalette::Text ) ) );
461
462 if ( linkPointCount( Qt::TopEdge ) )
463 {
464 h = -( fm.height() * 1.2 );
465 h = h - componentSize.height() / 2.0 + 5;
466 pt = QPointF( -componentSize.width() / 2 + SOCKET_MARGIN, h );
467 painter->drawText( pt, QObject::tr( "In" ) );
468 int i = 1;
469 if ( !mComponent->linksCollapsed( Qt::TopEdge ) )
470 {
471 for ( int idx = 0; idx < linkPointCount( Qt::TopEdge ); ++idx )
472 {
473 text = linkPointText( Qt::TopEdge, idx );
474 h = -( fm.height() * 1.2 ) * ( i + 1 );
475 h = h - componentSize.height() / 2.0 + 5;
476 pt = QPointF( -componentSize.width() / 2 + SOCKET_MARGIN + 10, h );
477 painter->drawText( pt, text );
478 i += 1;
479 }
480 }
481 }
482 if ( linkPointCount( Qt::BottomEdge ) )
483 {
484 h = fm.height() * 1.1;
485 h = h + componentSize.height() / 2.0;
486 const double w = fm.boundingRect( "Out" ).width(); // Only to know the width ot the text
487 pt = QPointF( componentSize.width() / 2 - SOCKET_MARGIN - w, h );
488 painter->drawText( pt, QObject::tr( "Out" ) );
489 if ( !mComponent->linksCollapsed( Qt::BottomEdge ) )
490 {
491 for ( int idx = 0; idx < linkPointCount( Qt::BottomEdge ); ++idx )
492 {
493 text = linkPointText( Qt::BottomEdge, idx );
494 h = fm.height() * 1.2 * ( idx + 2 );
495 h = h + componentSize.height() / 2.0;
496 double w = fm.boundingRect( text ).width();
497
498 const double x = componentSize.width() / 2.0 - w - SOCKET_MARGIN - 10;
499
500 painter->drawText( QPointF( x, h ), text );
501 }
502 }
503 }
504
505 const QPixmap px = iconPixmap();
506 if ( !px.isNull() )
507 {
508 painter->drawPixmap( QPointF( -( componentSize.width() / 2.0 ) + 3, -8 ), px );
509 }
510 else
511 {
512 const QPicture pic = iconPicture();
513 if ( !pic.isNull() )
514 {
515 painter->drawPicture( QPointF( -( componentSize.width() / 2.0 ) + 3, -8 ), pic );
516 }
517 }
518}
519
520QRectF QgsModelComponentGraphicItem::itemRect( bool storedRect ) const
521{
522 if ( storedRect )
523 {
524 return QRectF( mComponent->position().x() - ( mComponent->size().width() ) / 2.0, mComponent->position().y() - ( mComponent->size().height() ) / 2.0, mComponent->size().width(), mComponent->size().height() );
525 }
526 else
527 return QRectF( -( itemSize().width() ) / 2.0, -( itemSize().height() ) / 2.0, itemSize().width(), itemSize().height() );
528}
529
530QString QgsModelComponentGraphicItem::truncatedTextForItem( const QString &text ) const
531{
532 const QFontMetricsF fm( mFont );
533 double width = fm.boundingRect( text ).width();
534 if ( width < itemSize().width() - SOCKET_MARGIN - mButtonSize.width() )
535 return text;
536
537 QString t = text;
538 t = t.left( t.length() - 3 ) + QChar( 0x2026 );
539 width = fm.boundingRect( t ).width();
540 while ( width > itemSize().width() - SOCKET_MARGIN - mButtonSize.width() )
541 {
542 if ( t.length() < 5 )
543 break;
544
545 t = t.left( t.length() - 4 ) + QChar( 0x2026 );
546 width = fm.boundingRect( t ).width();
547 }
548 return t;
549}
550
551Qt::PenStyle QgsModelComponentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
552{
553 return Qt::SolidLine;
554}
555
556Qt::Alignment QgsModelComponentGraphicItem::titleAlignment() const
557{
558 return Qt::AlignLeft;
559}
560
561QPicture QgsModelComponentGraphicItem::iconPicture() const
562{
563 return QPicture();
564}
565
566QPixmap QgsModelComponentGraphicItem::iconPixmap() const
567{
568 return QPixmap();
569}
570
571
572void QgsModelComponentGraphicItem::updateButtonPositions()
573{
574 mEditButton->setPosition( QPointF( itemSize().width() / 2.0 - mButtonSize.width() / 2.0 - BUTTON_MARGIN, itemSize().height() / 2.0 - mButtonSize.height() / 2.0 - BUTTON_MARGIN ) );
575 mDeleteButton->setPosition( QPointF( itemSize().width() / 2.0 - mButtonSize.width() / 2.0 - BUTTON_MARGIN, mButtonSize.height() / 2.0 - itemSize().height() / 2.0 + BUTTON_MARGIN ) );
576
577 if ( mExpandBottomButton )
578 {
579 const QPointF pt = linkPoint( Qt::BottomEdge, -1, false );
580 mExpandBottomButton->setPosition( QPointF( 0, pt.y() ) );
581
582 bool collapsed = mComponent->linksCollapsed( Qt::BottomEdge );
583 for ( QgsModelDesignerSocketGraphicItem *socket : std::as_const( mOutSockets ) )
584 {
585 const QPointF pt = linkPoint( Qt::BottomEdge, socket->index(), false );
586 socket->setPosition( pt );
587 socket->setVisible( !collapsed );
588 }
589 }
590
591
592 if ( mExpandTopButton )
593 {
594 const QPointF pt = linkPoint( Qt::TopEdge, -1, true );
595 mExpandTopButton->setPosition( QPointF( 0, pt.y() ) );
596
597 bool collapsed = mComponent->linksCollapsed( Qt::TopEdge );
598 for ( QgsModelDesignerSocketGraphicItem *socket : std::as_const( mInSockets ) )
599 {
600 const QPointF pt = linkPoint( Qt::TopEdge, socket->index(), true );
601 socket->setPosition( pt );
602 socket->setVisible( !collapsed );
603 }
604 }
605}
606
607
608QSizeF QgsModelComponentGraphicItem::itemSize() const
609{
610 return !mTempSize.isValid() ? mComponent->size() : mTempSize;
611}
612
613void QgsModelComponentGraphicItem::updateToolTip( const QPointF &pos )
614{
615 const bool prevHoverStatus = mIsHovering;
616 if ( itemRect().contains( pos ) )
617 {
618 setToolTip( mLabel );
619 mIsHovering = true;
620 }
621 else
622 {
623 setToolTip( QString() );
624 mIsHovering = false;
625 }
626 if ( mIsHovering != prevHoverStatus )
627 {
628 update();
629 emit repaintArrows();
630 }
631}
632
633void QgsModelComponentGraphicItem::fold( Qt::Edge edge, bool folded )
634{
635 emit aboutToChange( !folded ? tr( "Expand Item" ) : tr( "Collapse Item" ) );
636 mComponent->setLinksCollapsed( edge, folded );
637 // also need to update the model's stored component
638
639 // TODO - this is not so nice, consider moving this to model class
640 if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mComponent.get() ) )
641 {
642 mModel->childAlgorithm( child->childId() ).setLinksCollapsed( edge, folded );
643 }
644 else if ( QgsProcessingModelParameter *param = dynamic_cast<QgsProcessingModelParameter *>( mComponent.get() ) )
645 {
646 mModel->parameterComponent( param->parameterName() ).setLinksCollapsed( edge, folded );
647 }
648 else if ( QgsProcessingModelOutput *output = dynamic_cast<QgsProcessingModelOutput *>( mComponent.get() ) )
649 {
650 mModel->childAlgorithm( output->childId() ).modelOutput( output->name() ).setLinksCollapsed( edge, folded );
651 }
652
653 updateButtonPositions();
654 prepareGeometryChange();
655 emit updateArrowPaths();
656 emit changed();
657 update();
658}
659
660QString QgsModelComponentGraphicItem::label() const
661{
662 return mLabel;
663}
664
665void QgsModelComponentGraphicItem::setLabel( const QString &label )
666{
667 mLabel = label;
668 update();
669}
670
671QgsModelComponentGraphicItem::State QgsModelComponentGraphicItem::state() const
672{
673 if ( isSelected() )
674 return Selected;
675 else if ( mIsHovering )
676 return Hover;
677 else
678 return Normal;
679}
680
681int QgsModelComponentGraphicItem::linkPointCount( Qt::Edge ) const
682{
683 return 0;
684}
685
686QString QgsModelComponentGraphicItem::linkPointText( Qt::Edge, int ) const
687{
688 return QString();
689}
690
691QPointF QgsModelComponentGraphicItem::linkPoint( Qt::Edge edge, int index, bool incoming ) const
692{
693 switch ( edge )
694 {
695 case Qt::BottomEdge:
696 {
697 if ( linkPointCount( Qt::BottomEdge ) )
698 {
699 const int pointIndex = !mComponent->linksCollapsed( Qt::BottomEdge ) ? index : -1;
700 const QFontMetricsF fm( mFont );
701 const double h = fm.height() * 1.2 * ( pointIndex + 1 ) + fm.height() / 2.0;
702 const double y = h + itemSize().height() / 2.0 + 6.4;
703 double x;
704 if ( !mComponent->linksCollapsed( Qt::BottomEdge ) )
705 {
706 if ( !incoming )
707 {
708 x = itemSize().width() / 2 - SOCKET_MARGIN;
709 }
710 else
711 {
712 const QString text = truncatedTextForItem( linkPointText( Qt::BottomEdge, index ) );
713 const double w = fm.boundingRect( text ).width();
714 x = itemSize().width() / 2 - SOCKET_MARGIN - w - 14;
715 }
716 }
717 else // mComponent->linksCollapsed( Qt::BottomEdge )
718 {
719 if ( !incoming )
720 {
721 x = itemSize().width() / 2 - 17;
722 }
723 else
724 {
725 x = -14;
726 }
727 }
728
729 return QPointF( x, y );
730 }
731 break;
732 }
733
734 case Qt::TopEdge:
735 {
736 if ( linkPointCount( Qt::TopEdge ) )
737 {
738 double offsetX = SOCKET_MARGIN;
739 int paramIndex = index;
740 if ( mComponent->linksCollapsed( Qt::TopEdge ) )
741 {
742 paramIndex = -1;
743 offsetX = 17;
744 }
745 const QFontMetricsF fm( mFont );
746 const double h = -( fm.height() * 1.2 ) * ( paramIndex + 2 ) - fm.height() / 2.0 + 8;
747 const double y = h - itemSize().height() / 2.0;
748 const double x = -itemSize().width() / 2 + offsetX;
749 return QPointF( x, y );
750 }
751 break;
752 }
753 case Qt::LeftEdge:
754 case Qt::RightEdge:
755 break;
756 }
757
758 return QPointF();
759}
760
761QPointF QgsModelComponentGraphicItem::calculateAutomaticLinkPoint( QgsModelComponentGraphicItem *other, Qt::Edge &edge ) const
762{
763 // find closest edge to other item
764 const QgsRectangle otherRect( other->itemRect().translated( other->pos() ) );
765
766 const QPointF leftPoint = pos() + QPointF( -itemSize().width() / 2.0, 0 );
767 const double distLeft = otherRect.distance( QgsPointXY( leftPoint ) );
768
769 const QPointF rightPoint = pos() + QPointF( itemSize().width() / 2.0, 0 );
770 const double distRight = otherRect.distance( QgsPointXY( rightPoint ) );
771
772 const QPointF topPoint = pos() + QPointF( 0, -itemSize().height() / 2.0 );
773 const double distTop = otherRect.distance( QgsPointXY( topPoint ) );
774
775 const QPointF bottomPoint = pos() + QPointF( 0, itemSize().height() / 2.0 );
776 const double distBottom = otherRect.distance( QgsPointXY( bottomPoint ) );
777
778 if ( distLeft <= distRight && distLeft <= distTop && distLeft <= distBottom )
779 {
780 edge = Qt::LeftEdge;
781 return leftPoint;
782 }
783 else if ( distRight <= distTop && distRight <= distBottom )
784 {
785 edge = Qt::RightEdge;
786 return rightPoint;
787 }
788 else if ( distBottom <= distTop )
789 {
790 edge = Qt::BottomEdge;
791 return bottomPoint;
792 }
793 else
794 {
795 edge = Qt::TopEdge;
796 return topPoint;
797 }
798}
799
800QPointF QgsModelComponentGraphicItem::calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge ) const
801{
802 // find closest edge to other point
803 const QgsPointXY otherPt( point );
804 const QPointF leftPoint = pos() + QPointF( -itemSize().width() / 2.0, 0 );
805 const double distLeft = otherPt.distance( QgsPointXY( leftPoint ) );
806
807 const QPointF rightPoint = pos() + QPointF( itemSize().width() / 2.0, 0 );
808 const double distRight = otherPt.distance( QgsPointXY( rightPoint ) );
809
810 const QPointF topPoint = pos() + QPointF( 0, -itemSize().height() / 2.0 );
811 const double distTop = otherPt.distance( QgsPointXY( topPoint ) );
812
813 const QPointF bottomPoint = pos() + QPointF( 0, itemSize().height() / 2.0 );
814 const double distBottom = otherPt.distance( QgsPointXY( bottomPoint ) );
815
816 if ( distLeft <= distRight && distLeft <= distTop && distLeft <= distBottom )
817 {
818 edge = Qt::LeftEdge;
819 return leftPoint;
820 }
821 else if ( distRight <= distTop && distRight <= distBottom )
822 {
823 edge = Qt::RightEdge;
824 return rightPoint;
825 }
826 else if ( distBottom <= distTop )
827 {
828 edge = Qt::BottomEdge;
829 return bottomPoint;
830 }
831 else
832 {
833 edge = Qt::TopEdge;
834 return topPoint;
835 }
836}
837
838QgsModelDesignerSocketGraphicItem *QgsModelComponentGraphicItem::outSocketAt( int index ) const
839{
840 if ( index < 0 || index >= mOutSockets.size() )
841 {
842 return nullptr;
843 }
844 return mOutSockets.at( index );
845}
846
847QList<QgsModelArrowItem *> QgsModelComponentGraphicItem::incomingArrows()
848{
849 const QList<QGraphicsItem *> allItems = scene()->items();
850 QList<QgsModelArrowItem *> arrows;
851 for ( QGraphicsItem *item : allItems )
852 {
853 if ( auto arrowItem = dynamic_cast< QgsModelArrowItem * >( item ) )
854 {
855 if ( arrowItem->endItem() == this )
856 {
857 arrows << arrowItem;
858 }
859 }
860 }
861 return arrows;
862}
863
864QList<QgsModelArrowItem *> QgsModelComponentGraphicItem::outgoingArrows()
865{
866 const QList<QGraphicsItem *> allItems = scene()->items();
867 QList<QgsModelArrowItem *> arrows;
868 for ( QGraphicsItem *item : allItems )
869 {
870 if ( auto arrowItem = dynamic_cast< QgsModelArrowItem * >( item ) )
871 {
872 if ( arrowItem->startItem() == this )
873 {
874 arrows << arrowItem;
875 }
876 }
877 }
878 return arrows;
879}
880
881void QgsModelComponentGraphicItem::registerWidgetContextGenerator( QgsProcessingWidgetContextGenerator *generator )
882{
883 mWidgetContextGenerator = generator;
884}
885
886QgsProcessingParameterWidgetContext QgsModelComponentGraphicItem::createWidgetContext()
887{
888 if ( mWidgetContextGenerator )
889 {
890 return mWidgetContextGenerator->createWidgetContext();
891 }
893}
894
895QgsModelParameterGraphicItem::QgsModelParameterGraphicItem( QgsProcessingModelParameter *parameter, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
896 : QgsModelComponentGraphicItem( parameter, model, parent )
897{
898 QSvgRenderer svg( QgsApplication::iconPath( u"mIconModelInput.svg"_s ) );
899 QPainter painter( &mPicture );
900 svg.render( &painter );
901 painter.end();
902
903 if ( const QgsProcessingParameterDefinition *parameterDefinition = model->parameterDefinition( parameter->parameterName() ) )
904 setLabel( parameterDefinition->description() );
905 else
906 setLabel( QObject::tr( "Error (%1)" ).arg( parameter->parameterName() ) );
907}
908
909void QgsModelParameterGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
910{
911 QMenu *popupmenu = new QMenu( event->widget() );
912 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
913 connect( removeAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::deleteComponent );
914 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
915 connect( editAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComponent );
916 QAction *editCommentAction = popupmenu->addAction( component()->comment()->description().isEmpty() ? QObject::tr( "Add Comment…" ) : QObject::tr( "Edit Comment…" ) );
917 connect( editCommentAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComment );
918
919 popupmenu->exec( event->screenPos() );
920}
921
922QColor QgsModelParameterGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
923{
924 QColor c( 238, 242, 131 );
925 switch ( state )
926 {
927 case Selected:
928 c = c.darker( 110 );
929 break;
930 case Hover:
931 c = c.darker( 105 );
932 break;
933
934 case Normal:
935 break;
936 }
937 return c;
938}
939
940QColor QgsModelParameterGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
941{
942 switch ( state )
943 {
944 case Selected:
945 return QColor( 116, 113, 68 );
946 case Hover:
947 case Normal:
948 return QColor( 234, 226, 118 );
949 }
950 return QColor();
951}
952
953QColor QgsModelParameterGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
954{
955 return Qt::black;
956}
957
958QPicture QgsModelParameterGraphicItem::iconPicture() const
959{
960 return mPicture;
961}
962
963int QgsModelParameterGraphicItem::linkPointCount( Qt::Edge edge ) const
964{
965 switch ( edge )
966 {
967 case Qt::BottomEdge:
968 return 1;
969 case Qt::TopEdge:
970 case Qt::LeftEdge:
971 case Qt::RightEdge:
972 break;
973 }
974
975 return 0;
976}
977
978QString QgsModelParameterGraphicItem::linkPointText( Qt::Edge, int index ) const
979{
980 if ( index < 0 )
981 {
982 return QString();
983 }
984
985 if ( const QgsProcessingModelParameter *parameter = dynamic_cast< const QgsProcessingModelParameter * >( component() ) )
986 {
987 QString text = this->model()->parameterDefinition( parameter->parameterName() )->type();
988
989 // Getting the default value to append to the box name
990 if ( const QgsProcessingParameterDefinition *paramDef = this->model()->parameterDefinition( parameter->parameterName() ) )
991 {
992 const QVariant paramValue = paramDef->defaultValue();
993
994 if ( paramValue.isValid() )
995 {
996 text += ": " + paramDef->userFriendlyString( paramValue );
997 }
998 }
999 return truncatedTextForItem( text );
1000 }
1001
1002 return QString();
1003}
1004
1005QColor QgsModelParameterGraphicItem::linkColor( Qt::Edge /* unused in this implementation because parameters only have a bottom edge */, int index ) const
1006{
1007 if ( index < 0 )
1008 {
1009 return FALLBACK_COLOR;
1010 }
1011
1012 if ( const QgsProcessingModelParameter *parameter = dynamic_cast< const QgsProcessingModelParameter * >( component() ) )
1013 {
1014 if ( const QgsProcessingParameterDefinition *parameterDefinition = model()->parameterDefinition( parameter->parameterName() ) )
1015 {
1016 return parameterDefinition->modelColor();
1017 }
1018 }
1019
1020 return FALLBACK_COLOR;
1021}
1022
1023QString QgsModelParameterGraphicItem::applyEdit(
1024 std::unique_ptr< QgsProcessingParameterDefinition > newParameter, const QString &oldDescription, const QString &oldName, const QString &comment, const QColor &commentColor
1025)
1026{
1027 QgsProcessingModelParameter *paramComponent = dynamic_cast< QgsProcessingModelParameter * >( component() );
1028 if ( !paramComponent )
1029 return QString();
1030
1031 const QString undoCommandId = u"param:%1"_s.arg( paramComponent->parameterName() );
1032 emit aboutToChange( tr( "Edit %1" ).arg( newParameter->description() ), undoCommandId );
1033
1034 model()->removeModelParameter( paramComponent->parameterName() );
1035
1036 if ( newParameter->description() != oldDescription )
1037 {
1038 // only update name if user has changed the description -- we don't force this, as it may cause
1039 // unwanted name updates which could potentially break the model's API
1040 QString name = newParameter->name();
1041 const QString baseName = name;
1042 int i = 2;
1043 while ( model()->parameterDefinition( name ) )
1044 {
1045 name = u"%1%2"_s.arg( baseName ).arg( i );
1046 i++;
1047 }
1048
1049 newParameter->setName( name );
1050 model()->changeParameterName( oldName, newParameter->name() );
1051 }
1052
1053 paramComponent->setParameterName( newParameter->name() );
1054 paramComponent->setDescription( newParameter->name() );
1055 paramComponent->comment()->setDescription( comment );
1056 paramComponent->comment()->setColor( commentColor );
1057
1058 const QString description = newParameter->description();
1059 const QString name = newParameter->name();
1060 model()->addModelParameter( newParameter.release(), *paramComponent );
1061 setLabel( description );
1062 emit requestModelRepaint();
1063 emit changed();
1064
1065 return name;
1066}
1067
1068void QgsModelParameterGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1069{
1070 if ( QgsProcessingModelParameter *param = dynamic_cast<QgsProcessingModelParameter *>( component() ) )
1071 {
1072 model()->parameterComponent( param->parameterName() ).setPosition( pos );
1073 model()->parameterComponent( param->parameterName() ).setSize( size );
1074 }
1075}
1076
1077bool QgsModelParameterGraphicItem::canDeleteComponent()
1078{
1079 if ( const QgsProcessingModelParameter *param = dynamic_cast<const QgsProcessingModelParameter *>( component() ) )
1080 {
1081 if ( model()->childAlgorithmsDependOnParameter( param->parameterName() ) )
1082 {
1083 return false;
1084 }
1085 else if ( model()->otherParametersDependOnParameter( param->parameterName() ) )
1086 {
1087 return false;
1088 }
1089 else
1090 {
1091 return true;
1092 }
1093 }
1094 return false;
1095}
1096
1097void QgsModelParameterGraphicItem::editComponent()
1098{
1099 edit( false );
1100}
1101
1102void QgsModelParameterGraphicItem::editComment()
1103{
1104 edit( true );
1105}
1106
1107void QgsModelParameterGraphicItem::deleteComponent()
1108{
1109 if ( const QgsProcessingModelParameter *param = dynamic_cast<const QgsProcessingModelParameter *>( component() ) )
1110 {
1111 if ( model()->childAlgorithmsDependOnParameter( param->parameterName() ) )
1112 {
1113 QMessageBox::warning(
1114 nullptr,
1115 QObject::tr( "Could not remove input" ),
1116 QObject::tr(
1117 "Algorithms depend on the selected input.\n"
1118 "Remove them before trying to remove it."
1119 )
1120 );
1121 }
1122 else if ( model()->otherParametersDependOnParameter( param->parameterName() ) )
1123 {
1124 QMessageBox::warning(
1125 nullptr,
1126 QObject::tr( "Could not remove input" ),
1127 QObject::tr(
1128 "Other inputs depend on the selected input.\n"
1129 "Remove them before trying to remove it."
1130 )
1131 );
1132 }
1133 else
1134 {
1135 emit aboutToChange( tr( "Delete Input %1" ).arg( param->description() ) );
1136 model()->removeModelParameter( param->parameterName() );
1137 emit changed();
1138 emit requestModelRepaint();
1139 }
1140 }
1141}
1142
1143void QgsModelParameterGraphicItem::edit( bool editComment )
1144{
1145 const QgsProcessingModelParameter *paramComponent = dynamic_cast< const QgsProcessingModelParameter * >( component() );
1146 if ( !paramComponent )
1147 return;
1148
1149 const QgsProcessingParameterDefinition *existingParam = model()->parameterDefinition( paramComponent->parameterName() );
1150 if ( !existingParam )
1151 return;
1152
1153 const QString oldName = existingParam->name();
1154 const QString oldDescription = existingParam->description();
1155
1156 const QString comment = paramComponent->comment()->description();
1157 const QColor commentColor = paramComponent->comment()->color();
1158
1159 QgsProcessingParameterWidgetContext widgetContext = createWidgetContext();
1161
1162 QgsProcessingParameterDefinitionDialog dlg( existingParam->type(), *context, widgetContext, existingParam, model(), this->scene()->views().at( 0 ) );
1163
1164 dlg.setComments( comment );
1165 dlg.setCommentColor( commentColor );
1166 if ( widgetContext.processingContextGenerator() )
1167 {
1168 dlg.registerProcessingContextGenerator( widgetContext.processingContextGenerator() );
1169 }
1170
1171 if ( editComment )
1172 {
1173 dlg.switchToCommentTab();
1174 }
1175
1176 if ( dlg.exec() )
1177 {
1178 std::unique_ptr< QgsProcessingParameterDefinition > newParam( dlg.createParameter( existingParam->name() ) );
1179 if ( newParam )
1180 {
1181 const QString safeName = QgsProcessingModelAlgorithm::safeName( newParam->description() ).toLower();
1182 newParam->setName( safeName );
1183
1184 applyEdit( std::move( newParam ), oldDescription, oldName, dlg.comments(), dlg.commentColor() );
1185 }
1186 }
1187}
1188
1189
1190QgsModelChildAlgorithmGraphicItem::QgsModelChildAlgorithmGraphicItem( QgsProcessingModelChildAlgorithm *child, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1191 : QgsModelComponentGraphicItem( child, model, parent )
1192{
1193 if ( child->algorithm() && !child->algorithm()->svgIconPath().isEmpty() )
1194 {
1195 QSvgRenderer svg( child->algorithm()->svgIconPath() );
1196 const QSizeF size = svg.defaultSize();
1197 QPainter painter( &mPicture );
1198 painter.scale( 16.0 / size.width(), 16.0 / size.width() );
1199 svg.render( &painter );
1200 painter.end();
1201 }
1202 else if ( child->algorithm() )
1203 {
1204 mPixmap = child->algorithm()->icon().pixmap( 15, 15 );
1205 }
1206
1207 setLabel( child->description() );
1208
1209 QStringList issues;
1210 mIsValid = model->validateChildAlgorithm( child->childId(), issues );
1211}
1212
1213void QgsModelChildAlgorithmGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1214{
1215 QMenu *popupmenu = new QMenu( event->widget() );
1216
1217 if ( isSelected() )
1218 {
1219 QAction *runSelectedStepsAction = popupmenu->addAction( QObject::tr( "Run Selected Steps…" ) );
1220 runSelectedStepsAction->setIcon( QgsApplication::getThemeIcon( u"mActionRunSelected.svg"_s ) );
1221 connect( runSelectedStepsAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::runSelected );
1222 }
1223
1224 QAction *runFromHereAction = popupmenu->addAction( QObject::tr( "Run from Here…" ) );
1225 runFromHereAction->setIcon( QgsApplication::getThemeIcon( u"mActionStart.svg"_s ) );
1226 connect( runFromHereAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::runFromHere );
1227
1228 popupmenu->addSeparator();
1229
1230 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1231 connect( removeAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deleteComponent );
1232 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1233 connect( editAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::editComponent );
1234 QAction *editCommentAction = popupmenu->addAction( component()->comment()->description().isEmpty() ? QObject::tr( "Add Comment…" ) : QObject::tr( "Edit Comment…" ) );
1235 connect( editCommentAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::editComment );
1236 popupmenu->addSeparator();
1237
1238 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1239 {
1240 if ( !child->isActive() )
1241 {
1242 QAction *activateAction = popupmenu->addAction( QObject::tr( "Activate" ) );
1243 connect( activateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::activateAlgorithm );
1244 }
1245 else
1246 {
1247 QAction *deactivateAction = popupmenu->addAction( QObject::tr( "Deactivate" ) );
1248 connect( deactivateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm );
1249 }
1250
1251 // only show the "View Output Layers" action for algorithms which create layers
1252 if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
1253 {
1254 const QList<const QgsProcessingParameterDefinition *> outputParams = algorithm->destinationParameterDefinitions();
1255 if ( !outputParams.isEmpty() )
1256 {
1257 popupmenu->addSeparator();
1258 QAction *viewOutputLayersAction = popupmenu->addAction( QObject::tr( "View Output Layers" ) );
1259 viewOutputLayersAction->setIcon( QgsApplication::getThemeIcon( u"mActionShowSelectedLayers.svg"_s ) );
1260 connect( viewOutputLayersAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::showPreviousResults );
1261 // enable this action only when the child succeeded
1262 switch ( mResults.executionStatus() )
1263 {
1266 viewOutputLayersAction->setEnabled( false );
1267 break;
1268
1270 break;
1271 }
1272 }
1273 }
1274
1275 QAction *viewLogAction = popupmenu->addAction( QObject::tr( "View Log…" ) );
1276 connect( viewLogAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::showLog );
1277 // enable this action even when the child failed
1278 switch ( mResults.executionStatus() )
1279 {
1281 viewLogAction->setEnabled( false );
1282 break;
1283
1286 break;
1287 }
1288 }
1289
1290 popupmenu->exec( event->screenPos() );
1291}
1292
1293QColor QgsModelChildAlgorithmGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1294{
1295 QColor c;
1296
1297 if ( mIsValid )
1298 c = QColor( 255, 255, 255 );
1299 else
1300 c = QColor( 208, 0, 0 );
1301
1302 switch ( state )
1303 {
1304 case Selected:
1305 c = c.darker( 110 );
1306 break;
1307 case Hover:
1308 c = c.darker( 105 );
1309 break;
1310
1311 case Normal:
1312 break;
1313 }
1314 return c;
1315}
1316
1317QColor QgsModelChildAlgorithmGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1318{
1319 switch ( state )
1320 {
1321 case Selected:
1322 return mIsValid ? QColor( 50, 50, 50 ) : QColor( 80, 0, 0 );
1323 case Hover:
1324 case Normal:
1325 return mIsValid ? Qt::gray : QColor( 134, 0, 0 );
1326 }
1327 return QColor();
1328}
1329
1330QColor QgsModelChildAlgorithmGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1331{
1332 return mIsValid ? ( qgis::down_cast<const QgsProcessingModelChildAlgorithm *>( component() )->isActive() ? Qt::black : Qt::gray ) : QColor( 255, 255, 255 );
1333}
1334
1335QColor QgsModelChildAlgorithmGraphicItem::outlineColor() const
1336{
1337 if ( mOutdated )
1338 {
1339 return QColor( 150, 150, 0 );
1340 }
1341
1342 switch ( mResults.executionStatus() )
1343 {
1345 if ( mStarted )
1346 {
1347 return QColor( 150, 150, 150 );
1348 }
1349 return QColor();
1351 return QColor( 55, 160, 55 );
1353 return QColor( 208, 0, 0 );
1354 }
1356}
1357
1358QPixmap QgsModelChildAlgorithmGraphicItem::iconPixmap() const
1359{
1360 return mPixmap;
1361}
1362
1363QPicture QgsModelChildAlgorithmGraphicItem::iconPicture() const
1364{
1365 return mPicture;
1366}
1367
1368int QgsModelChildAlgorithmGraphicItem::linkPointCount( Qt::Edge edge ) const
1369{
1370 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1371 {
1372 if ( !child->algorithm() )
1373 return 0;
1374
1375 switch ( edge )
1376 {
1377 case Qt::BottomEdge:
1378 return child->algorithm()->outputDefinitions().size();
1379 case Qt::TopEdge:
1380 {
1381 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1382 params.erase(
1383 std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition *param ) { return param->flags() & Qgis::ProcessingParameterFlag::Hidden || param->isDestination(); } ),
1384 params.end()
1385 );
1386 return params.size();
1387 }
1388
1389 case Qt::LeftEdge:
1390 case Qt::RightEdge:
1391 break;
1392 }
1393 }
1394 return 0;
1395}
1396
1397QColor QgsModelComponentGraphicItem::linkColor( Qt::Edge edge, int index ) const
1398{
1399 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1400 {
1401 if ( !child->algorithm() )
1402 {
1403 return FALLBACK_COLOR;
1404 }
1405
1406 switch ( edge )
1407 {
1408 case Qt::BottomEdge:
1409 {
1410 if ( index <= child->algorithm()->outputDefinitions().size() - 1 )
1411 {
1412 return child->algorithm()->outputDefinitions().at( index )->modelColor();
1413 }
1414 return FALLBACK_COLOR;
1415 }
1416 case Qt::TopEdge:
1417 {
1418 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1419
1420 if ( index <= params.size() - 1 )
1421 {
1422 return params.at( index )->modelColor();
1423 }
1424
1425 return FALLBACK_COLOR;
1426 }
1427
1428 case Qt::LeftEdge:
1429 case Qt::RightEdge:
1430 break;
1431 }
1432 }
1433
1434 return FALLBACK_COLOR;
1435}
1436
1437QString QgsModelChildAlgorithmGraphicItem::linkPointText( Qt::Edge edge, int index ) const
1438{
1439 if ( index < 0 )
1440 return QString();
1441
1442 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1443 {
1444 if ( !child->algorithm() )
1445 return QString();
1446
1447 switch ( edge )
1448 {
1449 case Qt::BottomEdge:
1450 {
1451 if ( index >= child->algorithm()->outputDefinitions().length() )
1452 {
1453 // something goes wrong and tried to link to an not existing output
1454 QgsMessageLog::logMessage( tr( "Cannot link output for child: %1" ).arg( child->algorithm()->name() ), "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
1455 return QString();
1456 }
1457
1458 const QgsProcessingOutputDefinition *output = child->algorithm()->outputDefinitions().at( index );
1459 QString title = output->description();
1460 return truncatedTextForItem( title );
1461 }
1462
1463 case Qt::TopEdge:
1464 {
1465 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1466 params.erase(
1467 std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition *param ) { return param->flags() & Qgis::ProcessingParameterFlag::Hidden || param->isDestination(); } ),
1468 params.end()
1469 );
1470
1471 if ( index >= params.length() )
1472 {
1473 // something goes wrong and tried to link to an not existing source parameter
1474 QgsMessageLog::logMessage( tr( "Cannot link source for child: %1" ).arg( child->algorithm()->name() ), "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
1475 return QString();
1476 }
1477
1478 const QgsProcessingParameterDefinition *param = params.at( index );
1479 QString name = param->name();
1480 QString title = param->description();
1481 QgsProcessingModelChildParameterSources paramSources = child->parameterSources().value( name );
1482 QString parameterValueAsString;
1483
1484 if ( !paramSources.empty() )
1485 {
1486 QgsProcessingModelChildParameterSource firstParameterSource = paramSources[0];
1487
1488 switch ( firstParameterSource.source() )
1489 {
1491 parameterValueAsString = u": %1"_s.arg( firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) ) );
1492 break;
1493
1495 parameterValueAsString = u": %1"_s.arg( firstParameterSource.expression() );
1496 break;
1497
1500 parameterValueAsString = u": %1"_s.arg( firstParameterSource.expressionText() );
1502 break;
1503
1505 parameterValueAsString = u": <%1>"_s.arg( firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) ) );
1506 break;
1507
1509 {
1510 const QString friendlyName = firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) );
1511 parameterValueAsString = friendlyName.isEmpty() ? u":"_s : u": <%1>"_s.arg( friendlyName );
1512 break;
1513 }
1514
1516 const QVariant paramValue = paramSources[0].staticValue();
1517 parameterValueAsString = u": %1"_s.arg( param->userFriendlyString( paramValue ) );
1518 }
1519 title += parameterValueAsString;
1520 }
1521
1522 return truncatedTextForItem( title );
1523 }
1524
1525 case Qt::LeftEdge:
1526 case Qt::RightEdge:
1527 break;
1528 }
1529 }
1530 return QString();
1531}
1532
1533void QgsModelChildAlgorithmGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1534{
1535 if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast<QgsProcessingModelChildAlgorithm *>( component() ) )
1536 {
1537 model()->childAlgorithm( child->childId() ).setPosition( pos );
1538 model()->childAlgorithm( child->childId() ).setSize( size );
1539 }
1540}
1541
1542bool QgsModelChildAlgorithmGraphicItem::canDeleteComponent()
1543{
1544 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1545 {
1546 return model()->dependentChildAlgorithms( child->childId() ).empty();
1547 }
1548 return false;
1549}
1550
1551void QgsModelChildAlgorithmGraphicItem::setResults( const QgsProcessingModelChildAlgorithmResult &results )
1552{
1553 if ( mResults == results )
1554 return;
1555
1556 mOutdated = false;
1557 const QList< QgsModelArrowItem * > arrows = outgoingArrows();
1559 {
1560 for ( QgsModelArrowItem *arrow : arrows )
1561 {
1562 arrow->setShowBadge( false );
1563 }
1564 }
1565 else
1566 {
1567 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1568 {
1569 if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
1570 {
1571 const QVariantMap outputs = results.outputs();
1572 for ( auto it = outputs.constBegin(); it != outputs.constEnd(); ++it )
1573 {
1574 // don't show badges for output layers, these will just be the internal layer identifiers and we have logic elsewhere
1575 // to show actually useful information in the badges (feature counts)
1576 if ( const QgsProcessingOutputDefinition *outputDefinition = algorithm->outputDefinition( it.key() ); outputDefinition && outputDefinition->isMapLayer() )
1577 continue;
1578
1579 const int index = indexForOutput( it.key() );
1580 if ( index >= 0 )
1581 {
1582 for ( QgsModelArrowItem *arrow : arrows )
1583 {
1584 if ( arrow->startIndex() == index && arrow->startEdge() == Qt::BottomEdge )
1585 {
1586 arrow->setShowBadge( true );
1587 arrow->badgeItem()->setValue( it.value() );
1588 }
1589 }
1590 }
1591 }
1592 }
1593 }
1594 }
1595
1596 mResults = results;
1597 mStarted = false;
1598 update();
1599 emit updateArrowPaths();
1600}
1601
1602void QgsModelChildAlgorithmGraphicItem::setSourceFeatureCount( const QString &parameterName, long long featureCount )
1603{
1604 const int index = indexForInput( parameterName );
1605 if ( index < 0 )
1606 return;
1607
1608 const QList< QgsModelArrowItem * > arrows = incomingArrows();
1609 for ( QgsModelArrowItem *arrow : arrows )
1610 {
1611 if ( arrow->endIndex() == index && arrow->endEdge() == Qt::TopEdge )
1612 {
1613 arrow->setShowBadge( true );
1614 arrow->badgeItem()->setValue( featureCount );
1615 }
1616 }
1617}
1618
1619void QgsModelChildAlgorithmGraphicItem::setSinkFeatureCount( const QString &outputName, long long featureCount )
1620{
1621 const int index = indexForOutput( outputName );
1622 if ( index < 0 )
1623 return;
1624
1625 const QList< QgsModelArrowItem * > arrows = outgoingArrows();
1626 for ( QgsModelArrowItem *arrow : arrows )
1627 {
1628 if ( arrow->startIndex() == index && arrow->startEdge() == Qt::BottomEdge )
1629 {
1630 arrow->setShowBadge( true );
1631 arrow->badgeItem()->setValue( featureCount );
1632 }
1633 }
1634}
1635
1636void QgsModelChildAlgorithmGraphicItem::setProgress( double progress )
1637{
1638 if ( mProgress == progress )
1639 return;
1640
1641 mProgress = progress;
1642 update();
1643}
1644
1645void QgsModelChildAlgorithmGraphicItem::setStarted()
1646{
1647 mStarted = true;
1648 update();
1649}
1650
1651void QgsModelChildAlgorithmGraphicItem::setOutdated()
1652{
1653 mOutdated = true;
1654 update();
1655}
1656
1657int QgsModelChildAlgorithmGraphicItem::indexForInput( const QString &parameterName ) const
1658{
1659 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1660 {
1661 if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
1662 {
1664 }
1665 }
1666 return -1;
1667}
1668
1669int QgsModelChildAlgorithmGraphicItem::indexForOutput( const QString &output ) const
1670{
1671 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1672 {
1673 if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
1674 {
1676 }
1677 }
1678 return -1;
1679}
1680
1681void QgsModelChildAlgorithmGraphicItem::editComponent()
1682{
1683 edit( false );
1684}
1685
1686void QgsModelChildAlgorithmGraphicItem::editComment()
1687{
1688 edit( true );
1689}
1690
1691void QgsModelChildAlgorithmGraphicItem::applyEdit( const QgsProcessingModelChildAlgorithm &algorithm )
1692{
1693 const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() );
1694 if ( !child )
1695 return;
1696
1697 QgsProcessingModelChildAlgorithm newAlgorithm = algorithm;
1698 newAlgorithm.setChildId( child->childId() );
1699 newAlgorithm.copyNonDefinitionPropertiesFromModel( model() );
1700 if ( newAlgorithm.toVariant() == child->toVariant() )
1701 {
1702 // nothing changed, treat as cancel was pressed
1703 return;
1704 }
1705
1706 const QString undoCommandId = u"alg:%1"_s.arg( child->childId() );
1707 emit aboutToChange( tr( "Edit %1" ).arg( newAlgorithm.description() ), undoCommandId );
1708 model()->setChildAlgorithm( newAlgorithm );
1709 emit requestModelRepaint();
1710 emit changed();
1711}
1712
1713void QgsModelChildAlgorithmGraphicItem::paintBackground( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
1714{
1715 if ( mProgress < 0 )
1716 {
1717 QgsModelComponentGraphicItem::paintBackground( painter, option, widget );
1718 return;
1719 }
1720
1721 paintOutline( painter, option, widget );
1722
1723 const QRectF rect = itemRect();
1724 QColor color;
1725 QColor stroke;
1726 if ( component()->color().isValid() )
1727 {
1728 color = component()->color();
1729 switch ( state() )
1730 {
1731 case Selected:
1732 color = color.darker( 110 );
1733 break;
1734 case Hover:
1735 color = color.darker( 105 );
1736 break;
1737
1738 case Normal:
1739 break;
1740 }
1741 stroke = color.darker( 110 );
1742 }
1743 else
1744 {
1745 color = fillColor( state() );
1746 stroke = strokeColor( state() );
1747 }
1748
1749 QPen strokePen = QPen( stroke, 0 );
1750 strokePen.setStyle( strokeStyle( state() ) );
1751 painter->setPen( strokePen );
1752
1753 const QColor colorLeft = color.darker( 120 );
1754 QColor colorRight = color;
1755
1756 constexpr double fadeSize = 5;
1757 const double fadeSizeProportionRect = fadeSize / rect.width();
1758
1759 if ( mProgress < 98 )
1760 {
1761 QLinearGradient gradient( rect.topLeft(), rect.topRight() );
1762 gradient.setColorAt( 0.0, colorLeft );
1763 gradient.setColorAt( std::max( 0.0, mProgress / 100 - fadeSizeProportionRect / 2 ), colorLeft );
1764 gradient.setColorAt( std::min( 1.0, mProgress / 100 + fadeSizeProportionRect / 2 ), colorRight );
1765 gradient.setColorAt( 1.0, colorRight );
1766
1767 painter->setBrush( QBrush( gradient ) );
1768 }
1769 else
1770 {
1771 painter->setBrush( QBrush( colorLeft ) );
1772 }
1773 painter->drawRect( rect );
1774}
1775
1776void QgsModelChildAlgorithmGraphicItem::deleteComponent()
1777{
1778 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1779 {
1780 emit aboutToChange( tr( "Remove %1" ).arg( child->algorithm() ? child->algorithm()->displayName() : tr( "Algorithm" ) ) );
1781 if ( !model()->removeChildAlgorithm( child->childId() ) )
1782 {
1783 QMessageBox::warning(
1784 nullptr,
1785 QObject::tr( "Could not remove algorithm" ),
1786 QObject::tr(
1787 "Other algorithms depend on the selected one.\n"
1788 "Remove them before trying to remove it."
1789 )
1790 );
1791 }
1792 else
1793 {
1794 emit changed();
1795 emit requestModelRepaint();
1796 }
1797 }
1798}
1799
1800void QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm()
1801{
1802 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1803 {
1804 model()->deactivateChildAlgorithm( child->childId() );
1805 emit requestModelRepaint();
1806 }
1807}
1808
1809void QgsModelChildAlgorithmGraphicItem::activateAlgorithm()
1810{
1811 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1812 {
1813 if ( model()->activateChildAlgorithm( child->childId() ) )
1814 {
1815 emit requestModelRepaint();
1816 }
1817 else
1818 {
1819 QMessageBox::warning(
1820 nullptr,
1821 QObject::tr( "Could not activate algorithm" ),
1822 QObject::tr(
1823 "The selected algorithm depends on other currently non-active algorithms.\n"
1824 "Activate them them before trying to activate it.."
1825 )
1826 );
1827 }
1828 }
1829}
1830
1831void QgsModelChildAlgorithmGraphicItem::edit( bool editComment )
1832{
1833 const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() );
1834 if ( !child )
1835 return;
1836
1837 QgsProcessingParameterWidgetContext widgetContext = createWidgetContext();
1838 widgetContext.setModelChildAlgorithmId( child->childId() );
1840
1841 const QgsProcessingAlgorithm *algorithm = child->algorithm();
1842 QgsProcessingModelerParametersDialog dlg( algorithm, model(), *context, child->childId(), child->configuration(), this->scene()->views().at( 0 ) );
1843 dlg.setModal( true );
1844 dlg.setComments( child->comment()->description() );
1845 dlg.setCommentColor( child->comment()->color() );
1846 dlg.setWidgetContext( widgetContext );
1847 if ( editComment )
1848 {
1849 dlg.switchToCommentTab();
1850 }
1851
1852 if ( dlg.exec() )
1853 {
1854 std::unique_ptr< QgsProcessingModelChildAlgorithm > alg = dlg.createAlgorithm();
1855 if ( alg )
1856 {
1857 applyEdit( *alg );
1858 emit rebuildConfigurationDockWidget();
1859 }
1860 }
1861}
1862
1863
1864QgsModelOutputGraphicItem::QgsModelOutputGraphicItem( QgsProcessingModelOutput *output, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1865 : QgsModelComponentGraphicItem( output, model, parent )
1866{
1867 QSvgRenderer svg( QgsApplication::iconPath( u"mIconModelOutput.svg"_s ) );
1868 QPainter painter( &mPicture );
1869 svg.render( &painter );
1870 painter.end();
1871 setLabel( output->description() );
1872}
1873
1874QColor QgsModelOutputGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1875{
1876 QColor c( 172, 196, 114 );
1877 switch ( state )
1878 {
1879 case Selected:
1880 c = c.darker( 110 );
1881 break;
1882 case Hover:
1883 c = c.darker( 105 );
1884 break;
1885
1886 case Normal:
1887 break;
1888 }
1889 return c;
1890}
1891
1892QColor QgsModelOutputGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1893{
1894 switch ( state )
1895 {
1896 case Selected:
1897 return QColor( 42, 65, 42 );
1898 case Hover:
1899 case Normal:
1900 return QColor( 90, 140, 90 );
1901 }
1902 return QColor();
1903}
1904
1905QColor QgsModelOutputGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1906{
1907 return Qt::black;
1908}
1909
1910QPicture QgsModelOutputGraphicItem::iconPicture() const
1911{
1912 return mPicture;
1913}
1914
1915void QgsModelOutputGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1916{
1917 if ( QgsProcessingModelOutput *output = dynamic_cast<QgsProcessingModelOutput *>( component() ) )
1918 {
1919 model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos );
1920 model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setSize( size );
1921 }
1922}
1923
1924bool QgsModelOutputGraphicItem::canDeleteComponent()
1925{
1926 if ( dynamic_cast<const QgsProcessingModelOutput *>( component() ) )
1927 {
1928 return true;
1929 }
1930 return false;
1931}
1932
1933void QgsModelOutputGraphicItem::deleteComponent()
1934{
1935 if ( const QgsProcessingModelOutput *output = dynamic_cast<const QgsProcessingModelOutput *>( component() ) )
1936 {
1937 emit aboutToChange( tr( "Delete Output %1" ).arg( output->description() ) );
1938 model()->childAlgorithm( output->childId() ).removeModelOutput( output->name() );
1939 model()->updateDestinationParameters();
1940 emit changed();
1941 emit requestModelRepaint();
1942 }
1943}
1944
1945
1946//
1947// QgsModelGroupBoxGraphicItem
1948//
1949
1950QgsModelGroupBoxGraphicItem::QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1951 : QgsModelComponentGraphicItem( box, model, parent )
1952{
1953 setZValue( QgsModelGraphicsScene::ZValues::GroupBox );
1954 setLabel( box->description() );
1955
1956 QFont f = font();
1957 f.setBold( true );
1958 f.setPixelSize( 14 );
1959 setFont( f );
1960}
1961
1962void QgsModelGroupBoxGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1963{
1964 QMenu *popupmenu = new QMenu( event->widget() );
1965 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1966 connect( removeAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::deleteComponent );
1967 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1968 connect( editAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::editComponent );
1969 popupmenu->exec( event->screenPos() );
1970}
1971
1972QgsModelGroupBoxGraphicItem::~QgsModelGroupBoxGraphicItem() = default;
1973
1974QColor QgsModelGroupBoxGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1975{
1976 QColor c( 230, 230, 230 );
1977 switch ( state )
1978 {
1979 case Selected:
1980 c = c.darker( 110 );
1981 break;
1982 case Hover:
1983 c = c.darker( 105 );
1984 break;
1985
1986 case Normal:
1987 break;
1988 }
1989 return c;
1990}
1991
1992QColor QgsModelGroupBoxGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1993{
1994 switch ( state )
1995 {
1996 case Selected:
1997 return QColor( 50, 50, 50 );
1998 case Hover:
1999 case Normal:
2000 return QColor( 150, 150, 150 );
2001 }
2002 return QColor();
2003}
2004
2005QColor QgsModelGroupBoxGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
2006{
2007 return QColor( 100, 100, 100 );
2008}
2009
2010Qt::PenStyle QgsModelGroupBoxGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
2011{
2012 return Qt::DotLine;
2013}
2014
2015Qt::Alignment QgsModelGroupBoxGraphicItem::titleAlignment() const
2016{
2017 return Qt::AlignHCenter;
2018}
2019
2020void QgsModelGroupBoxGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
2021{
2022 if ( QgsProcessingModelGroupBox *box = dynamic_cast<QgsProcessingModelGroupBox *>( component() ) )
2023 {
2024 box->setPosition( pos );
2025 box->setSize( size );
2026 model()->addGroupBox( *box );
2027 }
2028}
2029
2030bool QgsModelGroupBoxGraphicItem::canDeleteComponent()
2031{
2032 if ( dynamic_cast<QgsProcessingModelGroupBox *>( component() ) )
2033 {
2034 return true;
2035 }
2036 return false;
2037}
2038
2039void QgsModelGroupBoxGraphicItem::applyEdit( const QgsProcessingModelGroupBox &groupBox )
2040{
2041 QgsProcessingModelGroupBox newGroupBox = groupBox;
2042 const QList<QgsProcessingModelGroupBox> existingGroupBoxes = model()->groupBoxes();
2043 for ( const QgsProcessingModelGroupBox &existingGroupBox : existingGroupBoxes )
2044 {
2045 if ( existingGroupBox.uuid() == newGroupBox.uuid() )
2046 {
2047 // copy position and size from existing group box
2048 newGroupBox.setPosition( existingGroupBox.position() );
2049 newGroupBox.setSize( existingGroupBox.size() );
2050 }
2051 }
2052
2053 const QString commandId = u"groupbox:%1"_s.arg( newGroupBox.uuid() );
2054 emit aboutToChange( tr( "Edit Group Box" ), commandId );
2055
2056 model()->addGroupBox( newGroupBox );
2057 emit changed();
2058 emit requestModelRepaint();
2059}
2060
2061void QgsModelGroupBoxGraphicItem::deleteComponent()
2062{
2063 if ( const QgsProcessingModelGroupBox *box = dynamic_cast<const QgsProcessingModelGroupBox *>( component() ) )
2064 {
2065 emit aboutToChange( tr( "Delete Group Box" ) );
2066 model()->removeGroupBox( box->uuid() );
2067 emit changed();
2068 emit requestModelRepaint();
2069 }
2070}
2071
2072void QgsModelGroupBoxGraphicItem::editComponent()
2073{
2074 if ( const QgsProcessingModelGroupBox *box = dynamic_cast<const QgsProcessingModelGroupBox *>( component() ) )
2075 {
2076 QgsModelGroupBoxDefinitionDialog dlg( *box, this->scene()->views().at( 0 ) );
2077
2078 if ( dlg.exec() )
2079 {
2080 applyEdit( dlg.groupBox() );
2081 }
2082 }
2083}
2084
2085//
2086// QgsModelCommentGraphicItem
2087//
2088
2089QgsModelCommentGraphicItem::QgsModelCommentGraphicItem( QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
2090 : QgsModelComponentGraphicItem( comment, model, parent )
2091 , mParentComponent( parentItem->component()->clone() )
2092 , mParentItem( parentItem )
2093{
2094 setLabel( comment->description() );
2095
2096 QFont f = font();
2097 f.setPixelSize( 9 );
2098 setFont( f );
2099}
2100
2101void QgsModelCommentGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
2102{
2103 QMenu *popupmenu = new QMenu( event->widget() );
2104 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
2105 connect( removeAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::deleteComponent );
2106 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
2107 connect( editAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::editComponent );
2108 popupmenu->exec( event->screenPos() );
2109}
2110
2111QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
2112
2113QColor QgsModelCommentGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
2114{
2115 QColor c( 230, 230, 230 );
2116 switch ( state )
2117 {
2118 case Selected:
2119 c = c.darker( 110 );
2120 break;
2121 case Hover:
2122 c = c.darker( 105 );
2123 break;
2124
2125 case Normal:
2126 break;
2127 }
2128 return c;
2129}
2130
2131QColor QgsModelCommentGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
2132{
2133 switch ( state )
2134 {
2135 case Selected:
2136 return QColor( 50, 50, 50 );
2137 case Hover:
2138 case Normal:
2139 return QColor( 150, 150, 150 );
2140 }
2141 return QColor();
2142}
2143
2144QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
2145{
2146 return QColor( 100, 100, 100 );
2147}
2148
2149Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
2150{
2151 return Qt::DotLine;
2152}
2153
2154void QgsModelCommentGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
2155{
2156 if ( QgsProcessingModelComment *comment = modelComponent() )
2157 {
2158 comment->setPosition( pos );
2159 comment->setSize( size );
2160 }
2161}
2162
2163bool QgsModelCommentGraphicItem::canDeleteComponent()
2164{
2165 if ( modelComponent() )
2166 {
2167 return true;
2168 }
2169 return false;
2170}
2171
2172void QgsModelCommentGraphicItem::deleteComponent()
2173{
2174 if ( QgsProcessingModelComment *comment = modelComponent() )
2175 {
2176 emit aboutToChange( tr( "Delete Comment" ) );
2177 comment->setDescription( QString() );
2178 emit changed();
2179 emit requestModelRepaint();
2180 }
2181}
2182
2183void QgsModelCommentGraphicItem::editComponent()
2184{
2185 if ( mParentItem )
2186 {
2187 mParentItem->editComment();
2188 }
2189}
2190
2191QgsProcessingModelComment *QgsModelCommentGraphicItem::modelComponent()
2192{
2193 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( mParentComponent.get() ) )
2194 {
2195 return model()->childAlgorithm( child->childId() ).comment();
2196 }
2197 else if ( const QgsProcessingModelParameter *param = dynamic_cast<const QgsProcessingModelParameter *>( mParentComponent.get() ) )
2198 {
2199 return model()->parameterComponent( param->parameterName() ).comment();
2200 }
2201 else if ( const QgsProcessingModelOutput *output = dynamic_cast<const QgsProcessingModelOutput *>( mParentComponent.get() ) )
2202 {
2203 return model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).comment();
2204 }
2205 return nullptr;
2206}
2207
2208QgsModelComponentGraphicItem *QgsModelCommentGraphicItem::parentComponentItem() const
2209{
2210 return mParentItem;
2211}
2212
2213
@ Success
Child was successfully executed.
Definition qgis.h:4084
@ NotExecuted
Child has not been executed.
Definition qgis.h:4083
@ Failed
Child encountered an error while executing.
Definition qgis.h:4085
@ Warning
Warning message.
Definition qgis.h:162
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
Definition qgis.h:4071
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:4072
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:4068
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:4067
@ StaticValue
Parameter value is a static value.
Definition qgis.h:4069
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:4070
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A dialog 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:62
Abstract base class for processing algorithms.
virtual QgsProcessingContext * processingContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return a Proc...
Contains information about the context in which a processing algorithm is executed.
Encapsulates the results of running a child algorithm within a model.
QVariantMap outputs() const
Returns the outputs generated by the child algorithm.
Qgis::ProcessingModelChildAlgorithmExecutionStatus executionStatus() const
Returns the status of executing the child algorithm.
A dialog for configuring parameter settings and comments for a child algorithm in a Processing model.
Base class for the definition of processing outputs.
QString description() const
Returns the description for the output.
A dialog which allows users to specify the properties of a Processing parameter.
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.
virtual QString type() const =0
Unique parameter type name.
QString name() const
Returns the name of the parameter.
Contains settings which reflect the context in which a Processing parameter widget is shown.
void setModelChildAlgorithmId(const QString &id)
Sets the child algorithm id within the model which the parameter widget is associated with.
QgsProcessingContextGenerator * processingContextGenerator() const
Returns the Processing context generator class that will be used to retrieve a Processing context for...
static int parameterDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the parameter with matching name for a specified algorithm.
static int outputDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the output matching name for a specified algorithm.
An interface for objects which can create Processing widget contexts.
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
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8155
#define BUILTIN_UNREACHABLE
Definition qgis.h:8191
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8154
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.