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