QGIS API Documentation 4.1.0-Master (31622b25bb0)
Loading...
Searching...
No Matches
qgsmodelgraphicsscene.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmodelgraphicsscene.cpp
3 ----------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsmessagebar.h"
19#include "qgsmessagebaritem.h"
20#include "qgsmessagelog.h"
21#include "qgsmessageviewer.h"
22#include "qgsmodelarrowitem.h"
24#include "qgsmodelgraphicitem.h"
29#include "qgsvectorlayer.h"
30
31#include <QGraphicsSceneMouseEvent>
32#include <QGraphicsTextItem>
33#include <QPushButton>
34#include <QString>
35
36#include "moc_qgsmodelgraphicsscene.cpp"
37
38using namespace Qt::StringLiterals;
39
41
42QgsModelGraphicsScene::QgsModelGraphicsScene( QObject *parent )
43 : QGraphicsScene( parent )
44{
45 setItemIndexMethod( QGraphicsScene::NoIndex );
46
47 connect( this, &QgsModelGraphicsScene::componentChanged, this, &QgsModelGraphicsScene::updateBounds );
48}
49
50QgsProcessingModelAlgorithm *QgsModelGraphicsScene::model()
51{
52 return mModel;
53}
54
55void QgsModelGraphicsScene::setModel( QgsProcessingModelAlgorithm *model )
56{
57 mModel = model;
58}
59
60void QgsModelGraphicsScene::setFlag( QgsModelGraphicsScene::Flag flag, bool on )
61{
62 if ( on )
63 mFlags |= flag;
64 else
65 mFlags &= ~flag;
66}
67
68void QgsModelGraphicsScene::mousePressEvent( QGraphicsSceneMouseEvent *event )
69{
70 if ( event->button() != Qt::LeftButton )
71 return;
72 QGraphicsScene::mousePressEvent( event );
73}
74
75void QgsModelGraphicsScene::updateBounds()
76{
77 //start with an empty rectangle
78 QRectF bounds;
79
80 //add all items
81 const QList<QGraphicsItem *> constItems = items();
82 for ( QGraphicsItem *item : constItems )
83 {
84 QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item );
85 if ( componentItem )
86 bounds = bounds.united( componentItem->sceneBoundingRect() );
87 }
88
89 if ( bounds.isValid() )
90 {
91 // Add a margin and ensure bounds are rounded to integer-like values
92 bounds.adjust( -SCENE_COMPONENT_MARGIN, -SCENE_COMPONENT_MARGIN, SCENE_COMPONENT_MARGIN, SCENE_COMPONENT_MARGIN );
93 bounds.setLeft( std::floor( bounds.left() ) );
94 bounds.setTop( std::floor( bounds.top() ) );
95 bounds.setRight( std::ceil( bounds.right() ) );
96 bounds.setBottom( std::ceil( bounds.bottom() ) );
97 }
98
99 setSceneRect( bounds );
100}
101
102void QgsModelGraphicsScene::setupFeedbackConnections( QgsProcessingModelFeedback *feedback )
103{
104 connect( feedback, &QgsProcessingModelFeedback::childProgressChanged, this, [this]( const QString &childId, double progress ) {
105 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
106 {
107 item->setProgress( progress );
108 }
109 } );
110
111 connect( feedback, &QgsProcessingModelFeedback::childStarted, this, [this]( const QString &childId ) {
112 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
113 {
114 item->setStarted();
115 }
116 } );
117
118 connect( feedback, &QgsProcessingModelFeedback::childResultReported, this, [this]( const QString &childId, const QgsProcessingModelChildAlgorithmResult &result ) {
119 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
120 {
121 item->setResults( result );
122 }
123 } );
124
125 connect( feedback, &QgsProcessingModelFeedback::childSinkFeatureCountChanged, this, [this]( const QString &childId, const QString &outputName, long long featureCount ) {
126 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
127 {
128 item->setSinkFeatureCount( outputName, featureCount );
129 }
130 } );
131
132 connect( feedback, &QgsProcessingModelFeedback::childSourceLoaded, this, [this]( const QString &childId, const QString &parameterName, long long featureCount ) {
133 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
134 {
135 item->setSourceFeatureCount( parameterName, featureCount );
136 }
137 } );
138}
139
140void QgsModelGraphicsScene::flagChildrenAsOutdated( const QSet<QString> &children )
141{
142 for ( const QString &child : children )
143 {
144 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( child ) )
145 {
146 item->setOutdated();
147 }
148 }
149}
150
151QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const
152{
153 return new QgsModelParameterGraphicItem( param, model, nullptr );
154}
155
156QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const
157{
158 return new QgsModelChildAlgorithmGraphicItem( child, model, nullptr );
159}
160
161QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const
162{
163 return new QgsModelOutputGraphicItem( output, model, nullptr );
164}
165
166QgsModelComponentGraphicItem *QgsModelGraphicsScene::createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem ) const
167{
168 return new QgsModelCommentGraphicItem( comment, parentItem, model, nullptr );
169}
170
171QgsModelComponentGraphicItem *QgsModelGraphicsScene::createGroupBoxGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelGroupBox *box ) const
172{
173 return new QgsModelGroupBoxGraphicItem( box, model, nullptr );
174}
175
176void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context )
177{
178 // model group boxes
179 const QList<QgsProcessingModelGroupBox> boxes = model->groupBoxes();
180 mGroupBoxItems.clear();
181 for ( const QgsProcessingModelGroupBox &box : boxes )
182 {
183 QgsModelComponentGraphicItem *item = createGroupBoxGraphicItem( model, box.clone() );
184 addItem( item );
185 item->setPos( box.position().x(), box.position().y() );
186 mGroupBoxItems.insert( box.uuid(), item );
187 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
188 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
189 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
190 }
191
192 // model input parameters
193 const QMap<QString, QgsProcessingModelParameter> params = model->parameterComponents();
194 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
195 {
196 QgsModelComponentGraphicItem *item = createParameterGraphicItem( model, it.value().clone() );
197 addItem( item );
198 item->setPos( it.value().position().x(), it.value().position().y() );
199 mParameterItems.insert( it.value().parameterName(), item );
200 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
201 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
202 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
203
204 addCommentItemForComponent( model, it.value(), item );
205 }
206
207 // input dependency arrows
208 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
209 {
210 const QgsProcessingParameterDefinition *parameterDef = model->parameterDefinition( it.key() );
211 const QStringList parameterLinks = parameterDef->dependsOnOtherParameters();
212 for ( const QString &otherName : parameterLinks )
213 {
214 if ( mParameterItems.contains( it.key() ) && mParameterItems.contains( otherName ) )
215 {
216 auto arrow = std::make_unique<QgsModelArrowItem>( mParameterItems.value( otherName ), QgsModelArrowItem::Marker::Circle, mParameterItems.value( it.key() ), QgsModelArrowItem::Marker::ArrowHead );
217 arrow->setPenStyle( Qt::DotLine );
218 addItem( arrow.release() );
219 }
220 }
221 }
222
223 // child algorithms
224 const QMap<QString, QgsProcessingModelChildAlgorithm> childAlgs = model->childAlgorithms();
225 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
226 {
227 QgsModelChildAlgorithmGraphicItem *item = createChildAlgGraphicItem( model, it.value().clone() );
228 addItem( item );
229 item->setPos( it.value().position().x(), it.value().position().y() );
230
231 const QString childId = it.value().childId();
232 mChildAlgorithmItems.insert( childId, item );
233 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
234 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
235 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
236 connect( item, &QgsModelChildAlgorithmGraphicItem::runFromHere, this, [this, childId] { emit runFromChild( childId ); } );
237 connect( item, &QgsModelChildAlgorithmGraphicItem::runSelected, this, &QgsModelGraphicsScene::runSelected );
238 connect( item, &QgsModelChildAlgorithmGraphicItem::showPreviousResults, this, [this, childId] { emit showChildAlgorithmOutputs( childId ); } );
239 connect( item, &QgsModelChildAlgorithmGraphicItem::showLog, this, [this, childId] { emit showChildAlgorithmLog( childId ); } );
240
241 addCommentItemForComponent( model, it.value(), item );
242 }
243
244 // arrows linking child algorithms
245 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
246 {
247 int topIdx = 0;
248 int bottomIdx = 0;
249 if ( !it.value().algorithm() )
250 continue;
251
252 const QgsProcessingParameterDefinitions parameters = it.value().algorithm()->parameterDefinitions();
253 for ( const QgsProcessingParameterDefinition *parameter : parameters )
254 {
255 if ( !( parameter->flags() & Qgis::ProcessingParameterFlag::Hidden ) )
256 {
257 QList<QgsProcessingModelChildParameterSource> sources;
258 if ( it.value().parameterSources().contains( parameter->name() ) )
259 sources = it.value().parameterSources()[parameter->name()];
260 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
261 {
262 const QList<LinkSource> sourceItems = linkSourcesForParameterValue( model, QVariant::fromValue( source ), it.value().childId(), context );
263 for ( const LinkSource &link : sourceItems )
264 {
265 if ( !link.item )
266 continue;
267 QgsModelArrowItem *arrow = nullptr;
268 if ( link.linkIndex == -1 )
269 {
270 arrow = new QgsModelArrowItem(
271 link.item,
272 QgsModelArrowItem::Marker::NoMarker,
273 mChildAlgorithmItems.value( it.value().childId() ),
274 parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge,
275 parameter->isDestination() ? bottomIdx : topIdx,
276 QgsModelArrowItem::Marker::Circle
277 );
278 }
279 else
280 {
281 arrow = new QgsModelArrowItem(
282 link.item,
283 link.edge,
284 link.linkIndex,
285 true,
286 QgsModelArrowItem::Marker::NoMarker,
287 mChildAlgorithmItems.value( it.value().childId() ),
288 parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge,
289 parameter->isDestination() ? bottomIdx : topIdx,
290 true,
291 QgsModelArrowItem::Marker::NoMarker
292 );
293 }
294 addItem( arrow );
295
296 const QString layerId = mLastResult.childResults().value( it.value().childId() ).inputs().value( parameter->name() ).toString();
297 addFeatureCountItemForArrow( arrow, layerId );
298 }
299 }
300 if ( parameter->isDestination() )
301 bottomIdx++;
302 else
303 topIdx++;
304 }
305 }
306 const QList<QgsProcessingModelChildDependency> dependencies = it.value().dependencies();
307 for ( const QgsProcessingModelChildDependency &depend : dependencies )
308 {
309 if ( depend.conditionalBranch.isEmpty() || !model->childAlgorithm( depend.childId ).algorithm() )
310 {
311 addItem(
312 new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead )
313 );
314 }
315 else
316 {
317 // find branch link point
318 const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( depend.childId ).algorithm()->outputDefinitions();
319 int i = 0;
320 bool found = false;
321 for ( const QgsProcessingOutputDefinition *output : outputs )
322 {
323 if ( output->name() == depend.conditionalBranch )
324 {
325 found = true;
326 break;
327 }
328 i++;
329 }
330 if ( found )
331 addItem(
332 new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), Qt::BottomEdge, i, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead )
333 );
334 }
335 }
336 }
337
338 // and finally the model outputs
339 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
340 {
341 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
342 QMap<QString, QgsModelComponentGraphicItem *> outputItems;
343
344 // offsets from algorithm item needed to correctly place output items
345 // which does not have valid position assigned (https://github.com/qgis/QGIS/issues/48132)
346 QgsProcessingModelComponent *algItem = mChildAlgorithmItems[it.value().childId()]->component();
347 const double outputOffsetX = algItem->size().width();
348 double outputOffsetY = 1.5 * algItem->size().height();
349
350 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
351 {
352 QgsModelComponentGraphicItem *item = createOutputGraphicItem( model, outputIt.value().clone() );
353 addItem( item );
354 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
355 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
356 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
357
358 // if output added not at the same time as algorithm then it does not have
359 // valid position and will be placed at (0,0). We need to calculate better position.
360 // See https://github.com/qgis/QGIS/issues/48132.
361 QPointF pos = outputIt.value().position();
362 if ( pos.isNull() )
363 {
364 pos = algItem->position() + QPointF( outputOffsetX, outputOffsetY );
365 outputOffsetY += 1.5 * outputIt.value().size().height();
366 }
367 int idx = -1;
368 int i = 0;
369 // find the actual index of the linked output from the child algorithm it comes from
370 if ( it.value().algorithm() )
371 {
372 const QgsProcessingOutputDefinitions sourceChildAlgOutputs = it.value().algorithm()->outputDefinitions();
373 for ( const QgsProcessingOutputDefinition *childAlgOutput : sourceChildAlgOutputs )
374 {
375 if ( childAlgOutput->name() == outputIt.value().childOutputName() )
376 {
377 idx = i;
378 break;
379 }
380 i++;
381 }
382 }
383
384 item->setPos( pos );
385 item->component()->setPosition( pos );
386 outputItems.insert( outputIt.value().childOutputName(), item );
387 QgsModelArrowItem *arrow = new QgsModelArrowItem( mChildAlgorithmItems[it.value().childId()], Qt::BottomEdge, idx, QgsModelArrowItem::Marker::Circle, item, QgsModelArrowItem::Marker::Circle );
388 addItem( arrow );
389
390 QString layerId = mLastResult.childResults().value( it.value().childId() ).outputs().value( outputIt.value().childOutputName() ).toString();
391 addFeatureCountItemForArrow( arrow, layerId );
392
393 addCommentItemForComponent( model, outputIt.value(), item );
394 }
395 mOutputItems.insert( it.value().childId(), outputItems );
396 }
397
398 // update last results -- this MUST happen after all arrows have been created
399 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
400 {
401 const QString childId = it.value().childId();
402 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( it.key() ) )
403 {
404 item->setResults( mLastResult.childResults().value( childId ) );
405 }
406 }
407}
408
409QList<QgsModelComponentGraphicItem *> QgsModelGraphicsScene::selectedComponentItems()
410{
411 QList<QgsModelComponentGraphicItem *> componentItemList;
412
413 const QList<QGraphicsItem *> graphicsItemList = selectedItems();
414 for ( QGraphicsItem *item : graphicsItemList )
415 {
416 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
417 {
418 componentItemList.push_back( componentItem );
419 }
420 }
421
422 return componentItemList;
423}
424
425QgsModelComponentGraphicItem *QgsModelGraphicsScene::componentItemAt( QPointF position ) const
426{
427 //get a list of items which intersect the specified position, in descending z order
428 const QList<QGraphicsItem *> itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
429
430 for ( QGraphicsItem *graphicsItem : itemList )
431 {
432 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
433 {
434 return componentItem;
435 }
436 }
437 return nullptr;
438}
439
440QgsModelComponentGraphicItem *QgsModelGraphicsScene::groupBoxItem( const QString &uuid )
441{
442 return mGroupBoxItems.value( uuid );
443}
444
445QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::childAlgorithmItem( const QString &childId )
446{
447 return mChildAlgorithmItems.value( childId );
448}
449
450void QgsModelGraphicsScene::resetChildAlgorithmItems( const QSet<QString> &childAlgorithmSubset )
451{
452 if ( !childAlgorithmSubset.isEmpty() )
453 {
454 for ( const QString &childId : childAlgorithmSubset )
455 {
456 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
457 {
458 item->setResults( QgsProcessingModelChildAlgorithmResult() );
459 }
460 }
461 }
462 else
463 {
464 for ( auto it = mChildAlgorithmItems.constBegin(); it != mChildAlgorithmItems.constEnd(); ++it )
465 {
466 it.value()->setResults( QgsProcessingModelChildAlgorithmResult() );
467 }
468 }
469}
470
471QgsModelComponentGraphicItem *QgsModelGraphicsScene::parameterItem( const QString &name )
472{
473 return mParameterItems.value( name );
474}
475
476QgsModelComponentGraphicItem *QgsModelGraphicsScene::outputItem( const QString &childId, const QString &childOutputName )
477{
478 auto it = mOutputItems.constFind( childId );
479 if ( it == mOutputItems.constEnd() )
480 return nullptr;
481
482 auto outputIt = it->constFind( childOutputName );
483 if ( outputIt == it->constEnd() )
484 return nullptr;
485
486 return outputIt.value();
487}
488
489void QgsModelGraphicsScene::selectAll()
490{
491 //select all items in scene
492 QgsModelComponentGraphicItem *focusedItem = nullptr;
493 const QList<QGraphicsItem *> itemList = items();
494 for ( QGraphicsItem *graphicsItem : itemList )
495 {
496 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
497 {
498 componentItem->setSelected( true );
499 if ( !focusedItem )
500 focusedItem = componentItem;
501 }
502 }
503 emit selectedItemChanged( focusedItem );
504}
505
506void QgsModelGraphicsScene::deselectAll()
507{
508 //we can't use QGraphicsScene::clearSelection, as that emits no signals
509 //and we don't know which items are being deselected
510 //instead, do the clear selection manually...
511 const QList<QGraphicsItem *> selectedItemList = selectedItems();
512 for ( QGraphicsItem *item : selectedItemList )
513 {
514 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
515 {
516 componentItem->setSelected( false );
517 }
518 }
519 emit selectedItemChanged( nullptr );
520}
521
522void QgsModelGraphicsScene::setSelectedItem( QgsModelComponentGraphicItem *item )
523{
524 whileBlocking( this )->deselectAll();
525 if ( item )
526 {
527 item->setSelected( true );
528 }
529 emit selectedItemChanged( item );
530}
531
532void QgsModelGraphicsScene::setLastRunResult( const QgsProcessingModelResult &result, QgsProcessingContext &context )
533{
534 mLastResult = result;
535
536 const auto childResults = mLastResult.childResults();
537 for ( auto it = childResults.constBegin(); it != childResults.constEnd(); ++it )
538 {
539 if ( QgsModelChildAlgorithmGraphicItem *item = mChildAlgorithmItems.value( it.key() ) )
540 {
541 item->setResults( it.value() );
542 }
543 }
544
545 mLastResultCount.clear();
546 // Match inputs and outputs to corresponding layer and get feature counts if possible
547 for ( auto it = childResults.constBegin(); it != childResults.constEnd(); ++it )
548 {
549 QVariantMap inputs = childResults.value( it.key() ).inputs();
550 for ( auto inputIt = inputs.constBegin(); inputIt != inputs.constEnd(); inputIt++ )
551 {
552 if ( QgsMapLayer *resultMapLayer = QgsProcessingUtils::mapLayerFromString( inputs.value( inputIt.key() ).toString(), context, false ) )
553 {
554 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( resultMapLayer );
555 if ( vl && vl->featureCount() >= 0 )
556 {
557 mLastResultCount.insert( inputs.value( inputIt.key() ).toString(), vl->featureCount() );
558 }
559 }
560 }
561
562 QVariantMap outputs = childResults.value( it.key() ).outputs();
563 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); outputIt++ )
564 {
565 if ( QgsMapLayer *resultMapLayer = QgsProcessingUtils::mapLayerFromString( outputs.value( outputIt.key() ).toString(), context, false ) )
566 {
567 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( resultMapLayer );
568 if ( vl && vl->featureCount() >= 0 )
569 {
570 mLastResultCount.insert( outputs.value( outputIt.key() ).toString(), vl->featureCount() );
571 }
572 }
573 }
574 }
575
576 emit requestRebuildRequired();
577}
578
579QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForParameterValue(
580 QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context
581) const
582{
583 QList<QgsModelGraphicsScene::LinkSource> res;
584 if ( value.userType() == QMetaType::Type::QVariantList )
585 {
586 const QVariantList list = value.toList();
587 for ( const QVariant &v : list )
588 res.append( linkSourcesForParameterValue( model, v, childId, context ) );
589 }
590 else if ( value.userType() == QMetaType::Type::QStringList )
591 {
592 const QStringList list = value.toStringList();
593 for ( const QString &v : list )
594 res.append( linkSourcesForParameterValue( model, v, childId, context ) );
595 }
596 else if ( value.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
597 {
598 const QgsProcessingModelChildParameterSource source = value.value<QgsProcessingModelChildParameterSource>();
599 switch ( source.source() )
600 {
602 {
603 LinkSource l;
604 l.item = mParameterItems.value( source.parameterName() );
605 l.edge = Qt::BottomEdge;
606 l.linkIndex = 0;
607
608 res.append( l );
609 break;
610 }
612 {
613 if ( !model->childAlgorithm( source.outputChildId() ).algorithm() )
614 break;
615
616 const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions();
617 int i = 0;
618 for ( const QgsProcessingOutputDefinition *output : outputs )
619 {
620 if ( output->name() == source.outputName() )
621 break;
622 i++;
623 }
624 if ( mChildAlgorithmItems.contains( source.outputChildId() ) )
625 {
626 LinkSource l;
627 l.item = mChildAlgorithmItems.value( source.outputChildId() );
628 l.edge = Qt::BottomEdge;
629
630 // do sanity check of linked index
631 if ( i >= model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions().length() )
632 {
633 QString short_message = tr( "Check output links for alg: %1" ).arg( model->childAlgorithm( source.outputChildId() ).algorithm()->name() );
634 QString long_message = tr( "Cannot link output for alg: %1" ).arg( model->childAlgorithm( source.outputChildId() ).algorithm()->name() );
635 QString title( tr( "Algorithm link error" ) );
636 if ( messageBar() )
637 showWarning( const_cast<QString &>( short_message ), const_cast<QString &>( title ), const_cast<QString &>( long_message ) );
638 else
639 QgsMessageLog::logMessage( long_message, "QgsModelGraphicsScene", Qgis::MessageLevel::Warning, true );
640 break;
641 }
642
643 l.linkIndex = i;
644 res.append( l );
645 }
646
647 break;
648 }
649
651 {
652 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = model->variablesForChildAlgorithm( childId, &context );
653 const QgsExpression exp( source.expression() );
654 const QSet<QString> vars = exp.referencedVariables();
655 for ( const QString &v : vars )
656 {
657 if ( variables.contains( v ) )
658 {
659 res.append( linkSourcesForParameterValue( model, QVariant::fromValue( variables.value( v ).source ), childId, context ) );
660 }
661 }
662 break;
663 }
664
668 break;
669 }
670 }
671 return res;
672}
673
674void QgsModelGraphicsScene::addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem )
675{
676 if ( mFlags & FlagHideComments || !component.comment() || component.comment()->description().isEmpty() )
677 return;
678
679 QgsModelComponentGraphicItem *commentItem = createCommentGraphicItem( model, component.comment()->clone(), parentItem );
680 commentItem->setPos( component.comment()->position().x(), component.comment()->position().y() );
681 addItem( commentItem );
682 connect( commentItem, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
683 connect( commentItem, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
684 connect( commentItem, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
685
686 auto arrow = std::make_unique<QgsModelArrowItem>( parentItem, QgsModelArrowItem::Circle, commentItem, QgsModelArrowItem::Circle );
687 arrow->setPenStyle( Qt::DotLine );
688 addItem( arrow.release() );
689}
690
691
692void QgsModelGraphicsScene::addFeatureCountItemForArrow( QgsModelArrowItem *arrow, const QString &layerId )
693{
694 if ( mFlags & FlagHideFeatureCount )
695 {
696 arrow->setShowBadge( false );
697 return;
698 }
699
700 if ( !mLastResultCount.contains( layerId ) )
701 {
702 arrow->setShowBadge( false );
703 return;
704 }
705
706 arrow->setShowBadge( true );
707 if ( QgsModelDesignerArrowBadgeItem *badge = arrow->badgeItem() )
708 {
709 badge->setValue( mLastResultCount.value( layerId ) );
710 }
711}
712
713
714QgsMessageBar *QgsModelGraphicsScene::messageBar() const
715{
716 return mMessageBar;
717}
718
719void QgsModelGraphicsScene::setMessageBar( QgsMessageBar *messageBar )
720{
721 mMessageBar = messageBar;
722}
723
724void QgsModelGraphicsScene::showWarning( const QString &shortMessage, const QString &title, const QString &longMessage, Qgis::MessageLevel level ) const
725{
726 QgsMessageBarItem *messageWidget = QgsMessageBar::createMessage( QString(), shortMessage );
727 QPushButton *detailsButton = new QPushButton( tr( "Details" ) );
728 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, title, longMessage] {
729 QgsMessageViewer *dialog = new QgsMessageViewer( detailsButton );
730 dialog->setTitle( title );
731 dialog->setMessage( longMessage, Qgis::StringFormat::Html );
732 dialog->showMessage();
733 } );
734 messageWidget->layout()->addWidget( detailsButton );
735 mMessageBar->clearWidgets();
736 mMessageBar->pushWidget( messageWidget, level, 0 );
737}
738
739void QgsModelGraphicsScene::requestRebuildRequired()
740{
741 emit rebuildRequired();
742}
743
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition qgis.h:160
@ Warning
Warning message.
Definition qgis.h:162
@ Html
HTML message.
Definition qgis.h:177
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
Definition qgis.h:4036
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:4037
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:4033
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:4032
@ StaticValue
Parameter value is a static value.
Definition qgis.h:4034
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:4035
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3948
Handles parsing and evaluation of expressions (formerly called "search strings").
Base class for all map layer types.
Definition qgsmaplayer.h:83
Represents an item shown within a QgsMessageBar widget.
A bar for displaying non-blocking messages to the user.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A generic message view for displaying QGIS messages.
void setMessage(const QString &message, Qgis::StringFormat format) override
Sets message, it won't be displayed until.
void setTitle(const QString &title) override
Sets title for the messages.
void showMessage(bool blocking=true) override
display the message to the user and deletes itself
Contains information about the context in which a processing algorithm is executed.
Encapsulates the results of running a child algorithm within a model.
A Processing feedback class with extra signals and properties specific to feedback from Processing mo...
void childStarted(const QString &childId, const QVariantMap &childParameters)
Emitted when a child algorithm has started executing.
void childResultReported(const QString &childId, const QgsProcessingModelChildAlgorithmResult &result)
Emitted when the result of a child algorithm has been reported.
void childSourceLoaded(const QString &childId, const QString &parameterName, long long featureCount)
Emitted when a feature source was retrieved for the specified child algorithm input parameter.
void childProgressChanged(const QString &childId, double progress)
Emitted when a child algorithm changes progress.
void childSinkFeatureCountChanged(const QString &childId, const QString &childOutput, long long featureCount)
Emitted when the count of features pushed to a child's sink has changed.
Encapsulates the results of running a Processing model.
QMap< QString, QgsProcessingModelChildAlgorithmResult > childResults() const
Returns the map of child algorithm results.
Base class for the definition of processing outputs.
Base class for the definition of processing parameters.
virtual QStringList dependsOnOtherParameters() const
Returns a list of other parameter names on which this parameter is dependent (e.g.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7127
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.