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