QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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(
928 nullptr,
929 QObject::tr( "Could not remove input" ),
930 QObject::tr(
931 "Algorithms depend on the selected input.\n"
932 "Remove them before trying to remove it."
933 )
934 );
935 }
936 else if ( model()->otherParametersDependOnParameter( param->parameterName() ) )
937 {
938 QMessageBox::warning(
939 nullptr,
940 QObject::tr( "Could not remove input" ),
941 QObject::tr(
942 "Other inputs depend on the selected input.\n"
943 "Remove them before trying to remove it."
944 )
945 );
946 }
947 else
948 {
949 emit aboutToChange( tr( "Delete Input %1" ).arg( param->description() ) );
950 model()->removeModelParameter( param->parameterName() );
951 emit changed();
952 emit requestModelRepaint();
953 }
954 }
955}
956
957
958QgsModelChildAlgorithmGraphicItem::QgsModelChildAlgorithmGraphicItem( QgsProcessingModelChildAlgorithm *child, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
959 : QgsModelComponentGraphicItem( child, model, parent )
960{
961 if ( child->algorithm() && !child->algorithm()->svgIconPath().isEmpty() )
962 {
963 QSvgRenderer svg( child->algorithm()->svgIconPath() );
964 const QSizeF size = svg.defaultSize();
965 QPainter painter( &mPicture );
966 painter.scale( 16.0 / size.width(), 16.0 / size.width() );
967 svg.render( &painter );
968 painter.end();
969 }
970 else if ( child->algorithm() )
971 {
972 mPixmap = child->algorithm()->icon().pixmap( 15, 15 );
973 }
974
975 setLabel( child->description() );
976
977 QStringList issues;
978 mIsValid = model->validateChildAlgorithm( child->childId(), issues );
979}
980
981void QgsModelChildAlgorithmGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
982{
983 QMenu *popupmenu = new QMenu( event->widget() );
984
985 if ( isSelected() )
986 {
987 QAction *runSelectedStepsAction = popupmenu->addAction( QObject::tr( "Run Selected Steps…" ) );
988 runSelectedStepsAction->setIcon( QgsApplication::getThemeIcon( u"mActionRunSelected.svg"_s ) );
989 connect( runSelectedStepsAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::runSelected );
990 }
991
992 QAction *runFromHereAction = popupmenu->addAction( QObject::tr( "Run from Here…" ) );
993 runFromHereAction->setIcon( QgsApplication::getThemeIcon( u"mActionStart.svg"_s ) );
994 connect( runFromHereAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::runFromHere );
995
996 popupmenu->addSeparator();
997
998 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
999 connect( removeAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deleteComponent );
1000 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1001 connect( editAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::editComponent );
1002 QAction *editCommentAction = popupmenu->addAction( component()->comment()->description().isEmpty() ? QObject::tr( "Add Comment…" ) : QObject::tr( "Edit Comment…" ) );
1003 connect( editCommentAction, &QAction::triggered, this, &QgsModelParameterGraphicItem::editComment );
1004 popupmenu->addSeparator();
1005
1006 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1007 {
1008 if ( !child->isActive() )
1009 {
1010 QAction *activateAction = popupmenu->addAction( QObject::tr( "Activate" ) );
1011 connect( activateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::activateAlgorithm );
1012 }
1013 else
1014 {
1015 QAction *deactivateAction = popupmenu->addAction( QObject::tr( "Deactivate" ) );
1016 connect( deactivateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm );
1017 }
1018
1019 // only show the "View Output Layers" action for algorithms which create layers
1020 if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
1021 {
1022 const QList<const QgsProcessingParameterDefinition *> outputParams = algorithm->destinationParameterDefinitions();
1023 if ( !outputParams.isEmpty() )
1024 {
1025 popupmenu->addSeparator();
1026 QAction *viewOutputLayersAction = popupmenu->addAction( QObject::tr( "View Output Layers" ) );
1027 viewOutputLayersAction->setIcon( QgsApplication::getThemeIcon( u"mActionShowSelectedLayers.svg"_s ) );
1028 connect( viewOutputLayersAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::showPreviousResults );
1029 // enable this action only when the child succeeded
1030 switch ( mResults.executionStatus() )
1031 {
1034 viewOutputLayersAction->setEnabled( false );
1035 break;
1036
1038 break;
1039 }
1040 }
1041 }
1042
1043 QAction *viewLogAction = popupmenu->addAction( QObject::tr( "View Log…" ) );
1044 connect( viewLogAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::showLog );
1045 // enable this action even when the child failed
1046 switch ( mResults.executionStatus() )
1047 {
1049 viewLogAction->setEnabled( false );
1050 break;
1051
1054 break;
1055 }
1056 }
1057
1058 popupmenu->exec( event->screenPos() );
1059}
1060
1061QColor QgsModelChildAlgorithmGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1062{
1063 QColor c;
1064
1065 if ( mIsValid )
1066 c = QColor( 255, 255, 255 );
1067 else
1068 c = QColor( 208, 0, 0 );
1069
1070 switch ( state )
1071 {
1072 case Selected:
1073 c = c.darker( 110 );
1074 break;
1075 case Hover:
1076 c = c.darker( 105 );
1077 break;
1078
1079 case Normal:
1080 break;
1081 }
1082 return c;
1083}
1084
1085QColor QgsModelChildAlgorithmGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1086{
1087 switch ( state )
1088 {
1089 case Selected:
1090 return mIsValid ? QColor( 50, 50, 50 ) : QColor( 80, 0, 0 );
1091 case Hover:
1092 case Normal:
1093 return mIsValid ? Qt::gray : QColor( 134, 0, 0 );
1094 }
1095 return QColor();
1096}
1097
1098QColor QgsModelChildAlgorithmGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1099{
1100 return mIsValid ? ( qgis::down_cast<const QgsProcessingModelChildAlgorithm *>( component() )->isActive() ? Qt::black : Qt::gray ) : QColor( 255, 255, 255 );
1101}
1102
1103QPixmap QgsModelChildAlgorithmGraphicItem::iconPixmap() const
1104{
1105 return mPixmap;
1106}
1107
1108QPicture QgsModelChildAlgorithmGraphicItem::iconPicture() const
1109{
1110 return mPicture;
1111}
1112
1113int QgsModelChildAlgorithmGraphicItem::linkPointCount( Qt::Edge edge ) const
1114{
1115 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1116 {
1117 if ( !child->algorithm() )
1118 return 0;
1119
1120 switch ( edge )
1121 {
1122 case Qt::BottomEdge:
1123 return child->algorithm()->outputDefinitions().size();
1124 case Qt::TopEdge:
1125 {
1126 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1127 params.erase(
1128 std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition *param ) { return param->flags() & Qgis::ProcessingParameterFlag::Hidden || param->isDestination(); } ),
1129 params.end()
1130 );
1131 return params.size();
1132 }
1133
1134 case Qt::LeftEdge:
1135 case Qt::RightEdge:
1136 break;
1137 }
1138 }
1139 return 0;
1140}
1141
1142QColor QgsModelComponentGraphicItem::linkColor( Qt::Edge edge, int index ) const
1143{
1144 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1145 {
1146 if ( !child->algorithm() )
1147 {
1148 return FALLBACK_COLOR;
1149 }
1150
1151 switch ( edge )
1152 {
1153 case Qt::BottomEdge:
1154 {
1155 if ( index <= child->algorithm()->outputDefinitions().size() - 1 )
1156 {
1157 return child->algorithm()->outputDefinitions().at( index )->modelColor();
1158 }
1159 return FALLBACK_COLOR;
1160 }
1161 case Qt::TopEdge:
1162 {
1163 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1164
1165 if ( index <= params.size() - 1 )
1166 {
1167 return params.at( index )->modelColor();
1168 }
1169
1170 return FALLBACK_COLOR;
1171 }
1172
1173 case Qt::LeftEdge:
1174 case Qt::RightEdge:
1175 break;
1176 }
1177 }
1178
1179 return FALLBACK_COLOR;
1180}
1181
1182QString QgsModelChildAlgorithmGraphicItem::linkPointText( Qt::Edge edge, int index ) const
1183{
1184 if ( index < 0 )
1185 return QString();
1186
1187 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1188 {
1189 if ( !child->algorithm() )
1190 return QString();
1191
1192 switch ( edge )
1193 {
1194 case Qt::BottomEdge:
1195 {
1196 if ( index >= child->algorithm()->outputDefinitions().length() )
1197 {
1198 // something goes wrong and tried to link to an not existing output
1199 QgsMessageLog::logMessage( tr( "Cannot link output for child: %1" ).arg( child->algorithm()->name() ), "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
1200 return QString();
1201 }
1202
1203 const QgsProcessingOutputDefinition *output = child->algorithm()->outputDefinitions().at( index );
1204 QString title = output->description();
1205 return truncatedTextForItem( title );
1206 }
1207
1208 case Qt::TopEdge:
1209 {
1210 QgsProcessingParameterDefinitions params = child->algorithm()->parameterDefinitions();
1211 params.erase(
1212 std::remove_if( params.begin(), params.end(), []( const QgsProcessingParameterDefinition *param ) { return param->flags() & Qgis::ProcessingParameterFlag::Hidden || param->isDestination(); } ),
1213 params.end()
1214 );
1215
1216 if ( index >= params.length() )
1217 {
1218 // something goes wrong and tried to link to an not existing source parameter
1219 QgsMessageLog::logMessage( tr( "Cannot link source for child: %1" ).arg( child->algorithm()->name() ), "QgsModelChildAlgorithmGraphicItem", Qgis::MessageLevel::Warning, true );
1220 return QString();
1221 }
1222
1223 const QgsProcessingParameterDefinition *param = params.at( index );
1224 QString name = param->name();
1225 QString title = param->description();
1226 QgsProcessingModelChildParameterSources paramSources = child->parameterSources().value( name );
1227 QString parameterValueAsString;
1228
1229 if ( !paramSources.empty() )
1230 {
1231 QgsProcessingModelChildParameterSource firstParameterSource = paramSources[0];
1232
1233 switch ( firstParameterSource.source() )
1234 {
1236 parameterValueAsString = u": %1"_s.arg( firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) ) );
1237 break;
1238
1240 parameterValueAsString = u": %1"_s.arg( firstParameterSource.expression() );
1241 break;
1242
1244 parameterValueAsString = u": %1"_s.arg( firstParameterSource.expressionText() );
1245 break;
1246
1248 parameterValueAsString = u": <%1>"_s.arg( firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) ) );
1249 break;
1250
1252 {
1253 const QString friendlyName = firstParameterSource.friendlyIdentifier( const_cast<QgsProcessingModelAlgorithm *>( model() ) );
1254 parameterValueAsString = friendlyName.isEmpty() ? u":"_s : u": <%1>"_s.arg( friendlyName );
1255 break;
1256 }
1257
1259 const QVariant paramValue = paramSources[0].staticValue();
1260 parameterValueAsString = u": %1"_s.arg( param->userFriendlyString( paramValue ) );
1261 }
1262 title += parameterValueAsString;
1263 }
1264
1265 return truncatedTextForItem( title );
1266 }
1267
1268 case Qt::LeftEdge:
1269 case Qt::RightEdge:
1270 break;
1271 }
1272 }
1273 return QString();
1274}
1275
1276void QgsModelChildAlgorithmGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1277{
1278 if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast<QgsProcessingModelChildAlgorithm *>( component() ) )
1279 {
1280 model()->childAlgorithm( child->childId() ).setPosition( pos );
1281 model()->childAlgorithm( child->childId() ).setSize( size );
1282 }
1283}
1284
1285bool QgsModelChildAlgorithmGraphicItem::canDeleteComponent()
1286{
1287 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1288 {
1289 return model()->dependentChildAlgorithms( child->childId() ).empty();
1290 }
1291 return false;
1292}
1293
1294void QgsModelChildAlgorithmGraphicItem::setResults( const QgsProcessingModelChildAlgorithmResult &results )
1295{
1296 if ( mResults == results )
1297 return;
1298
1299 mResults = results;
1300 update();
1301 emit updateArrowPaths();
1302}
1303
1304void QgsModelChildAlgorithmGraphicItem::deleteComponent()
1305{
1306 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1307 {
1308 emit aboutToChange( tr( "Remove %1" ).arg( child->algorithm() ? child->algorithm()->displayName() : tr( "Algorithm" ) ) );
1309 if ( !model()->removeChildAlgorithm( child->childId() ) )
1310 {
1311 QMessageBox::warning(
1312 nullptr,
1313 QObject::tr( "Could not remove algorithm" ),
1314 QObject::tr(
1315 "Other algorithms depend on the selected one.\n"
1316 "Remove them before trying to remove it."
1317 )
1318 );
1319 }
1320 else
1321 {
1322 emit changed();
1323 emit requestModelRepaint();
1324 }
1325 }
1326}
1327
1328void QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm()
1329{
1330 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1331 {
1332 model()->deactivateChildAlgorithm( child->childId() );
1333 emit requestModelRepaint();
1334 }
1335}
1336
1337void QgsModelChildAlgorithmGraphicItem::activateAlgorithm()
1338{
1339 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( component() ) )
1340 {
1341 if ( model()->activateChildAlgorithm( child->childId() ) )
1342 {
1343 emit requestModelRepaint();
1344 }
1345 else
1346 {
1347 QMessageBox::warning(
1348 nullptr,
1349 QObject::tr( "Could not activate algorithm" ),
1350 QObject::tr(
1351 "The selected algorithm depends on other currently non-active algorithms.\n"
1352 "Activate them them before trying to activate it.."
1353 )
1354 );
1355 }
1356 }
1357}
1358
1359
1360QgsModelOutputGraphicItem::QgsModelOutputGraphicItem( QgsProcessingModelOutput *output, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1361 : QgsModelComponentGraphicItem( output, model, parent )
1362{
1363 QSvgRenderer svg( QgsApplication::iconPath( u"mIconModelOutput.svg"_s ) );
1364 QPainter painter( &mPicture );
1365 svg.render( &painter );
1366 painter.end();
1367 setLabel( output->description() );
1368}
1369
1370QColor QgsModelOutputGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1371{
1372 QColor c( 172, 196, 114 );
1373 switch ( state )
1374 {
1375 case Selected:
1376 c = c.darker( 110 );
1377 break;
1378 case Hover:
1379 c = c.darker( 105 );
1380 break;
1381
1382 case Normal:
1383 break;
1384 }
1385 return c;
1386}
1387
1388QColor QgsModelOutputGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1389{
1390 switch ( state )
1391 {
1392 case Selected:
1393 return QColor( 42, 65, 42 );
1394 case Hover:
1395 case Normal:
1396 return QColor( 90, 140, 90 );
1397 }
1398 return QColor();
1399}
1400
1401QColor QgsModelOutputGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1402{
1403 return Qt::black;
1404}
1405
1406QPicture QgsModelOutputGraphicItem::iconPicture() const
1407{
1408 return mPicture;
1409}
1410
1411void QgsModelOutputGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1412{
1413 if ( QgsProcessingModelOutput *output = dynamic_cast<QgsProcessingModelOutput *>( component() ) )
1414 {
1415 model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos );
1416 model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setSize( size );
1417 }
1418}
1419
1420bool QgsModelOutputGraphicItem::canDeleteComponent()
1421{
1422 if ( dynamic_cast<const QgsProcessingModelOutput *>( component() ) )
1423 {
1424 return true;
1425 }
1426 return false;
1427}
1428
1429void QgsModelOutputGraphicItem::deleteComponent()
1430{
1431 if ( const QgsProcessingModelOutput *output = dynamic_cast<const QgsProcessingModelOutput *>( component() ) )
1432 {
1433 emit aboutToChange( tr( "Delete Output %1" ).arg( output->description() ) );
1434 model()->childAlgorithm( output->childId() ).removeModelOutput( output->name() );
1435 model()->updateDestinationParameters();
1436 emit changed();
1437 emit requestModelRepaint();
1438 }
1439}
1440
1441
1442//
1443// QgsModelGroupBoxGraphicItem
1444//
1445
1446QgsModelGroupBoxGraphicItem::QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1447 : QgsModelComponentGraphicItem( box, model, parent )
1448{
1449 setZValue( QgsModelGraphicsScene::ZValues::GroupBox );
1450 setLabel( box->description() );
1451
1452 QFont f = font();
1453 f.setBold( true );
1454 f.setPixelSize( 14 );
1455 setFont( f );
1456}
1457
1458void QgsModelGroupBoxGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1459{
1460 QMenu *popupmenu = new QMenu( event->widget() );
1461 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1462 connect( removeAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::deleteComponent );
1463 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1464 connect( editAction, &QAction::triggered, this, &QgsModelGroupBoxGraphicItem::editComponent );
1465 popupmenu->exec( event->screenPos() );
1466}
1467
1468QgsModelGroupBoxGraphicItem::~QgsModelGroupBoxGraphicItem() = default;
1469
1470QColor QgsModelGroupBoxGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1471{
1472 QColor c( 230, 230, 230 );
1473 switch ( state )
1474 {
1475 case Selected:
1476 c = c.darker( 110 );
1477 break;
1478 case Hover:
1479 c = c.darker( 105 );
1480 break;
1481
1482 case Normal:
1483 break;
1484 }
1485 return c;
1486}
1487
1488QColor QgsModelGroupBoxGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1489{
1490 switch ( state )
1491 {
1492 case Selected:
1493 return QColor( 50, 50, 50 );
1494 case Hover:
1495 case Normal:
1496 return QColor( 150, 150, 150 );
1497 }
1498 return QColor();
1499}
1500
1501QColor QgsModelGroupBoxGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1502{
1503 return QColor( 100, 100, 100 );
1504}
1505
1506Qt::PenStyle QgsModelGroupBoxGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1507{
1508 return Qt::DotLine;
1509}
1510
1511Qt::Alignment QgsModelGroupBoxGraphicItem::titleAlignment() const
1512{
1513 return Qt::AlignHCenter;
1514}
1515
1516void QgsModelGroupBoxGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1517{
1518 if ( QgsProcessingModelGroupBox *box = dynamic_cast<QgsProcessingModelGroupBox *>( component() ) )
1519 {
1520 box->setPosition( pos );
1521 box->setSize( size );
1522 model()->addGroupBox( *box );
1523 }
1524}
1525
1526bool QgsModelGroupBoxGraphicItem::canDeleteComponent()
1527{
1528 if ( dynamic_cast<QgsProcessingModelGroupBox *>( component() ) )
1529 {
1530 return true;
1531 }
1532 return false;
1533}
1534
1535void QgsModelGroupBoxGraphicItem::applyEdit( const QgsProcessingModelGroupBox &groupBox )
1536{
1537 const QString commandId = u"groupbox:%1"_s.arg( groupBox.uuid() );
1538 emit aboutToChange( tr( "Edit Group Box" ), commandId );
1539 model()->addGroupBox( groupBox );
1540 emit changed();
1541 emit requestModelRepaint();
1542}
1543
1544void QgsModelGroupBoxGraphicItem::deleteComponent()
1545{
1546 if ( const QgsProcessingModelGroupBox *box = dynamic_cast<const QgsProcessingModelGroupBox *>( component() ) )
1547 {
1548 emit aboutToChange( tr( "Delete Group Box" ) );
1549 model()->removeGroupBox( box->uuid() );
1550 emit changed();
1551 emit requestModelRepaint();
1552 }
1553}
1554
1555void QgsModelGroupBoxGraphicItem::editComponent()
1556{
1557 if ( const QgsProcessingModelGroupBox *box = dynamic_cast<const QgsProcessingModelGroupBox *>( component() ) )
1558 {
1559 QgsModelGroupBoxDefinitionDialog dlg( *box, this->scene()->views().at( 0 ) );
1560
1561 if ( dlg.exec() )
1562 {
1563 applyEdit( dlg.groupBox() );
1564 }
1565 }
1566}
1567
1568//
1569// QgsModelCommentGraphicItem
1570//
1571
1572QgsModelCommentGraphicItem::QgsModelCommentGraphicItem( QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
1573 : QgsModelComponentGraphicItem( comment, model, parent )
1574 , mParentComponent( parentItem->component()->clone() )
1575 , mParentItem( parentItem )
1576{
1577 setLabel( comment->description() );
1578
1579 QFont f = font();
1580 f.setPixelSize( 9 );
1581 setFont( f );
1582}
1583
1584void QgsModelCommentGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
1585{
1586 QMenu *popupmenu = new QMenu( event->widget() );
1587 QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
1588 connect( removeAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::deleteComponent );
1589 QAction *editAction = popupmenu->addAction( QObject::tr( "Edit…" ) );
1590 connect( editAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::editComponent );
1591 popupmenu->exec( event->screenPos() );
1592}
1593
1594QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
1595
1596QColor QgsModelCommentGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
1597{
1598 QColor c( 230, 230, 230 );
1599 switch ( state )
1600 {
1601 case Selected:
1602 c = c.darker( 110 );
1603 break;
1604 case Hover:
1605 c = c.darker( 105 );
1606 break;
1607
1608 case Normal:
1609 break;
1610 }
1611 return c;
1612}
1613
1614QColor QgsModelCommentGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
1615{
1616 switch ( state )
1617 {
1618 case Selected:
1619 return QColor( 50, 50, 50 );
1620 case Hover:
1621 case Normal:
1622 return QColor( 150, 150, 150 );
1623 }
1624 return QColor();
1625}
1626
1627QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
1628{
1629 return QColor( 100, 100, 100 );
1630}
1631
1632Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
1633{
1634 return Qt::DotLine;
1635}
1636
1637void QgsModelCommentGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size )
1638{
1639 if ( QgsProcessingModelComment *comment = modelComponent() )
1640 {
1641 comment->setPosition( pos );
1642 comment->setSize( size );
1643 }
1644}
1645
1646bool QgsModelCommentGraphicItem::canDeleteComponent()
1647{
1648 if ( modelComponent() )
1649 {
1650 return true;
1651 }
1652 return false;
1653}
1654
1655void QgsModelCommentGraphicItem::deleteComponent()
1656{
1657 if ( QgsProcessingModelComment *comment = modelComponent() )
1658 {
1659 emit aboutToChange( tr( "Delete Comment" ) );
1660 comment->setDescription( QString() );
1661 emit changed();
1662 emit requestModelRepaint();
1663 }
1664}
1665
1666void QgsModelCommentGraphicItem::editComponent()
1667{
1668 if ( mParentItem )
1669 {
1670 mParentItem->editComment();
1671 }
1672}
1673
1674QgsProcessingModelComment *QgsModelCommentGraphicItem::modelComponent()
1675{
1676 if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast<const QgsProcessingModelChildAlgorithm *>( mParentComponent.get() ) )
1677 {
1678 return model()->childAlgorithm( child->childId() ).comment();
1679 }
1680 else if ( const QgsProcessingModelParameter *param = dynamic_cast<const QgsProcessingModelParameter *>( mParentComponent.get() ) )
1681 {
1682 return model()->parameterComponent( param->parameterName() ).comment();
1683 }
1684 else if ( const QgsProcessingModelOutput *output = dynamic_cast<const QgsProcessingModelOutput *>( mParentComponent.get() ) )
1685 {
1686 return model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).comment();
1687 }
1688 return nullptr;
1689}
1690
1691QgsModelComponentGraphicItem *QgsModelCommentGraphicItem::parentComponentItem() const
1692{
1693 return mParentItem;
1694}
1695
1696
@ Success
Child was successfully executed.
Definition qgis.h:3982
@ NotExecuted
Child has not been executed.
Definition qgis.h:3981
@ Failed
Child encountered an error while executing.
Definition qgis.h:3983
@ 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:3969
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:3970
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:3966
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:3965
@ StaticValue
Parameter value is a static value.
Definition qgis.h:3967
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:3968
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.
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.