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