QGIS API Documentation 4.3.0-Master (550e9e0643d)
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
151void QgsModelGraphicsScene::registerWidgetContextGenerator( QgsProcessingWidgetContextGenerator *generator )
152{
153 mWidgetContextGenerator = generator;
154}
155
156QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const
157{
158 return new QgsModelParameterGraphicItem( param, model, nullptr );
159}
160
161QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const
162{
163 return new QgsModelChildAlgorithmGraphicItem( child, model, nullptr );
164}
165
166QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const
167{
168 return new QgsModelOutputGraphicItem( output, model, nullptr );
169}
170
171QgsModelComponentGraphicItem *QgsModelGraphicsScene::createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem ) const
172{
173 return new QgsModelCommentGraphicItem( comment, parentItem, model, nullptr );
174}
175
176QgsModelComponentGraphicItem *QgsModelGraphicsScene::createGroupBoxGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelGroupBox *box ) const
177{
178 return new QgsModelGroupBoxGraphicItem( box, model, nullptr );
179}
180
181void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context )
182{
183 // model group boxes
184 const QList<QgsProcessingModelGroupBox> boxes = model->groupBoxes();
185 mGroupBoxItems.clear();
186 for ( const QgsProcessingModelGroupBox &box : boxes )
187 {
188 QgsModelComponentGraphicItem *item = createGroupBoxGraphicItem( model, box.clone() );
189 item->registerWidgetContextGenerator( mWidgetContextGenerator );
190 addItem( item );
191 item->setPos( box.position().x(), box.position().y() );
192 mGroupBoxItems.insert( box.uuid(), item );
193 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
194 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
195 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
196 }
197
198 // model input parameters
199 const QMap<QString, QgsProcessingModelParameter> params = model->parameterComponents();
200 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
201 {
202 QgsModelComponentGraphicItem *item = createParameterGraphicItem( model, it.value().clone() );
203 item->registerWidgetContextGenerator( mWidgetContextGenerator );
204 addItem( item );
205 item->setPos( it.value().position().x(), it.value().position().y() );
206 mParameterItems.insert( it.value().parameterName(), item );
207 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
208 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
209 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
210
211 addCommentItemForComponent( model, it.value(), item );
212 }
213
214 // input dependency arrows
215 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
216 {
217 const QgsProcessingParameterDefinition *parameterDef = model->parameterDefinition( it.key() );
218 const QStringList parameterLinks = parameterDef->dependsOnOtherParameters();
219 for ( const QString &otherName : parameterLinks )
220 {
221 if ( mParameterItems.contains( it.key() ) && mParameterItems.contains( otherName ) )
222 {
223 auto arrow = std::make_unique<QgsModelArrowItem>( mParameterItems.value( otherName ), QgsModelArrowItem::Marker::Circle, mParameterItems.value( it.key() ), QgsModelArrowItem::Marker::ArrowHead );
224 arrow->setPenStyle( Qt::DotLine );
225 addItem( arrow.release() );
226 }
227 }
228 }
229
230 // child algorithms
231 const QMap<QString, QgsProcessingModelChildAlgorithm> childAlgs = model->childAlgorithms();
232 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
233 {
234 QgsModelChildAlgorithmGraphicItem *item = createChildAlgGraphicItem( model, it.value().clone() );
235 item->registerWidgetContextGenerator( mWidgetContextGenerator );
236 addItem( item );
237 item->setPos( it.value().position().x(), it.value().position().y() );
238
239 const QString childId = it.value().childId();
240 mChildAlgorithmItems.insert( childId, item );
241 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
242 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
243 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
244 connect( item, &QgsModelChildAlgorithmGraphicItem::runFromHere, this, [this, childId] { emit runFromChild( childId ); } );
245 connect( item, &QgsModelChildAlgorithmGraphicItem::runSelected, this, &QgsModelGraphicsScene::runSelected );
246 connect( item, &QgsModelChildAlgorithmGraphicItem::showPreviousResults, this, [this, childId] { emit showChildAlgorithmOutputs( childId ); } );
247 connect( item, &QgsModelChildAlgorithmGraphicItem::showLog, this, [this, childId] { emit showChildAlgorithmLog( childId ); } );
248
249 addCommentItemForComponent( model, it.value(), item );
250 }
251
252 // arrows linking child algorithms
253 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
254 {
255 int topIdx = 0;
256 int bottomIdx = 0;
257 if ( !it.value().algorithm() )
258 continue;
259
260 const QgsProcessingParameterDefinitions parameters = it.value().algorithm()->parameterDefinitions();
261 for ( const QgsProcessingParameterDefinition *parameter : parameters )
262 {
263 if ( !( parameter->flags() & Qgis::ProcessingParameterFlag::Hidden ) )
264 {
265 QList<QgsProcessingModelChildParameterSource> sources;
266 if ( it.value().parameterSources().contains( parameter->name() ) )
267 sources = it.value().parameterSources()[parameter->name()];
268 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
269 {
270 const QList<LinkSource> sourceItems = linkSourcesForParameterValue( model, QVariant::fromValue( source ), it.value().childId(), context );
271 for ( const LinkSource &link : sourceItems )
272 {
273 if ( !link.item )
274 continue;
275 QgsModelArrowItem *arrow = nullptr;
276 if ( link.linkIndex == -1 )
277 {
278 arrow = new QgsModelArrowItem(
279 link.item,
280 QgsModelArrowItem::Marker::NoMarker,
281 mChildAlgorithmItems.value( it.value().childId() ),
282 parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge,
283 parameter->isDestination() ? bottomIdx : topIdx,
284 QgsModelArrowItem::Marker::Circle
285 );
286 }
287 else
288 {
289 arrow = new QgsModelArrowItem(
290 link.item,
291 link.edge,
292 link.linkIndex,
293 true,
294 QgsModelArrowItem::Marker::NoMarker,
295 mChildAlgorithmItems.value( it.value().childId() ),
296 parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge,
297 parameter->isDestination() ? bottomIdx : topIdx,
298 true,
299 QgsModelArrowItem::Marker::NoMarker
300 );
301 }
302 addItem( arrow );
303
304 const QString layerId = mLastResult.childResults().value( it.value().childId() ).inputs().value( parameter->name() ).toString();
305 addFeatureCountItemForArrow( arrow, layerId );
306 }
307 }
308 if ( parameter->isDestination() )
309 bottomIdx++;
310 else
311 topIdx++;
312 }
313 }
314 const QList<QgsProcessingModelChildDependency> dependencies = it.value().dependencies();
315 for ( const QgsProcessingModelChildDependency &depend : dependencies )
316 {
317 if ( depend.conditionalBranch.isEmpty() || !model->childAlgorithm( depend.childId ).algorithm() )
318 {
319 addItem(
320 new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead )
321 );
322 }
323 else
324 {
325 // find branch link point
326 const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( depend.childId ).algorithm()->outputDefinitions();
327 int i = 0;
328 bool found = false;
329 for ( const QgsProcessingOutputDefinition *output : outputs )
330 {
331 if ( output->name() == depend.conditionalBranch )
332 {
333 found = true;
334 break;
335 }
336 i++;
337 }
338 if ( found )
339 addItem(
340 new QgsModelArrowItem( mChildAlgorithmItems.value( depend.childId ), Qt::BottomEdge, i, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), QgsModelArrowItem::Marker::ArrowHead )
341 );
342 }
343 }
344 }
345
346 // and finally the model outputs
347 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
348 {
349 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
350 QMap<QString, QgsModelComponentGraphicItem *> outputItems;
351
352 // offsets from algorithm item needed to correctly place output items
353 // which does not have valid position assigned (https://github.com/qgis/QGIS/issues/48132)
354 QgsProcessingModelComponent *algItem = mChildAlgorithmItems[it.value().childId()]->component();
355 const double outputOffsetX = algItem->size().width();
356 double outputOffsetY = 1.5 * algItem->size().height();
357
358 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
359 {
360 QgsModelComponentGraphicItem *item = createOutputGraphicItem( model, outputIt.value().clone() );
361 item->registerWidgetContextGenerator( mWidgetContextGenerator );
362 addItem( item );
363 connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
364 connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
365 connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
366
367 // if output added not at the same time as algorithm then it does not have
368 // valid position and will be placed at (0,0). We need to calculate better position.
369 // See https://github.com/qgis/QGIS/issues/48132.
370 QPointF pos = outputIt.value().position();
371 if ( pos.isNull() )
372 {
373 pos = algItem->position() + QPointF( outputOffsetX, outputOffsetY );
374 outputOffsetY += 1.5 * outputIt.value().size().height();
375 }
376 int idx = -1;
377 int i = 0;
378 // find the actual index of the linked output from the child algorithm it comes from
379 if ( it.value().algorithm() )
380 {
381 const QgsProcessingOutputDefinitions sourceChildAlgOutputs = it.value().algorithm()->outputDefinitions();
382 for ( const QgsProcessingOutputDefinition *childAlgOutput : sourceChildAlgOutputs )
383 {
384 if ( childAlgOutput->name() == outputIt.value().childOutputName() )
385 {
386 idx = i;
387 break;
388 }
389 i++;
390 }
391 }
392
393 item->setPos( pos );
394 item->component()->setPosition( pos );
395 outputItems.insert( outputIt.value().childOutputName(), item );
396 QgsModelArrowItem *arrow = new QgsModelArrowItem( mChildAlgorithmItems[it.value().childId()], Qt::BottomEdge, idx, QgsModelArrowItem::Marker::Circle, item, QgsModelArrowItem::Marker::Circle );
397 addItem( arrow );
398
399 QString layerId = mLastResult.childResults().value( it.value().childId() ).outputs().value( outputIt.value().childOutputName() ).toString();
400 addFeatureCountItemForArrow( arrow, layerId );
401
402 addCommentItemForComponent( model, outputIt.value(), item );
403 }
404 mOutputItems.insert( it.value().childId(), outputItems );
405 }
406
407 // update last results -- this MUST happen after all arrows have been created
408 for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
409 {
410 const QString childId = it.value().childId();
411 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( it.key() ) )
412 {
413 item->setResults( mLastResult.childResults().value( childId ) );
414 }
415 }
416}
417
418QList<QgsModelComponentGraphicItem *> QgsModelGraphicsScene::selectedComponentItems()
419{
420 QList<QgsModelComponentGraphicItem *> componentItemList;
421
422 const QList<QGraphicsItem *> graphicsItemList = selectedItems();
423 for ( QGraphicsItem *item : graphicsItemList )
424 {
425 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
426 {
427 componentItemList.push_back( componentItem );
428 }
429 }
430
431 return componentItemList;
432}
433
434QgsModelComponentGraphicItem *QgsModelGraphicsScene::componentItemAt( QPointF position ) const
435{
436 //get a list of items which intersect the specified position, in descending z order
437 const QList<QGraphicsItem *> itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
438
439 for ( QGraphicsItem *graphicsItem : itemList )
440 {
441 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
442 {
443 return componentItem;
444 }
445 }
446 return nullptr;
447}
448
449QgsModelComponentGraphicItem *QgsModelGraphicsScene::groupBoxItem( const QString &uuid )
450{
451 return mGroupBoxItems.value( uuid );
452}
453
454QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::childAlgorithmItem( const QString &childId )
455{
456 return mChildAlgorithmItems.value( childId );
457}
458
459void QgsModelGraphicsScene::resetChildAlgorithmItems( const QSet<QString> &childAlgorithmSubset )
460{
461 if ( !childAlgorithmSubset.isEmpty() )
462 {
463 for ( const QString &childId : childAlgorithmSubset )
464 {
465 if ( QgsModelChildAlgorithmGraphicItem *item = childAlgorithmItem( childId ) )
466 {
467 item->setResults( QgsProcessingModelChildAlgorithmResult() );
468 }
469 }
470 }
471 else
472 {
473 for ( auto it = mChildAlgorithmItems.constBegin(); it != mChildAlgorithmItems.constEnd(); ++it )
474 {
475 it.value()->setResults( QgsProcessingModelChildAlgorithmResult() );
476 }
477 }
478}
479
480QgsModelComponentGraphicItem *QgsModelGraphicsScene::parameterItem( const QString &name )
481{
482 return mParameterItems.value( name );
483}
484
485QgsModelComponentGraphicItem *QgsModelGraphicsScene::outputItem( const QString &childId, const QString &childOutputName )
486{
487 auto it = mOutputItems.constFind( childId );
488 if ( it == mOutputItems.constEnd() )
489 return nullptr;
490
491 auto outputIt = it->constFind( childOutputName );
492 if ( outputIt == it->constEnd() )
493 return nullptr;
494
495 return outputIt.value();
496}
497
498void QgsModelGraphicsScene::selectAll()
499{
500 //select all items in scene
501 QgsModelComponentGraphicItem *focusedItem = nullptr;
502 const QList<QGraphicsItem *> itemList = items();
503 for ( QGraphicsItem *graphicsItem : itemList )
504 {
505 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( graphicsItem ) )
506 {
507 componentItem->setSelected( true );
508 if ( !focusedItem )
509 focusedItem = componentItem;
510 }
511 }
512 emit selectedItemChanged( focusedItem );
513}
514
515void QgsModelGraphicsScene::deselectAll()
516{
517 //we can't use QGraphicsScene::clearSelection, as that emits no signals
518 //and we don't know which items are being deselected
519 //instead, do the clear selection manually...
520 const QList<QGraphicsItem *> selectedItemList = selectedItems();
521 for ( QGraphicsItem *item : selectedItemList )
522 {
523 if ( QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item ) )
524 {
525 componentItem->setSelected( false );
526 }
527 }
528 emit selectedItemChanged( nullptr );
529}
530
531void QgsModelGraphicsScene::setSelectedItem( QgsModelComponentGraphicItem *item )
532{
533 whileBlocking( this )->deselectAll();
534 if ( item )
535 {
536 item->setSelected( true );
537 }
538 emit selectedItemChanged( item );
539}
540
541void QgsModelGraphicsScene::setLastRunResult( const QgsProcessingModelResult &result, QgsProcessingContext &context )
542{
543 mLastResult = result;
544
545 const auto childResults = mLastResult.childResults();
546 for ( auto it = childResults.constBegin(); it != childResults.constEnd(); ++it )
547 {
548 if ( QgsModelChildAlgorithmGraphicItem *item = mChildAlgorithmItems.value( it.key() ) )
549 {
550 item->setResults( it.value() );
551 }
552 }
553
554 mLastResultCount.clear();
555 // Match inputs and outputs to corresponding layer and get feature counts if possible
556 for ( auto it = childResults.constBegin(); it != childResults.constEnd(); ++it )
557 {
558 QVariantMap inputs = childResults.value( it.key() ).inputs();
559 for ( auto inputIt = inputs.constBegin(); inputIt != inputs.constEnd(); inputIt++ )
560 {
561 if ( QgsMapLayer *resultMapLayer = QgsProcessingUtils::mapLayerFromString( inputs.value( inputIt.key() ).toString(), context, false ) )
562 {
563 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( resultMapLayer );
564 if ( vl && vl->featureCount() >= 0 )
565 {
566 mLastResultCount.insert( inputs.value( inputIt.key() ).toString(), vl->featureCount() );
567 }
568 }
569 }
570
571 QVariantMap outputs = childResults.value( it.key() ).outputs();
572 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); outputIt++ )
573 {
574 if ( QgsMapLayer *resultMapLayer = QgsProcessingUtils::mapLayerFromString( outputs.value( outputIt.key() ).toString(), context, false ) )
575 {
576 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( resultMapLayer );
577 if ( vl && vl->featureCount() >= 0 )
578 {
579 mLastResultCount.insert( outputs.value( outputIt.key() ).toString(), vl->featureCount() );
580 }
581 }
582 }
583 }
584
585 emit requestRebuildRequired();
586}
587
588QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForParameterValue(
589 QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context
590) const
591{
592 QList<QgsModelGraphicsScene::LinkSource> res;
593 if ( value.userType() == QMetaType::Type::QVariantList )
594 {
595 const QVariantList list = value.toList();
596 for ( const QVariant &v : list )
597 res.append( linkSourcesForParameterValue( model, v, childId, context ) );
598 }
599 else if ( value.userType() == QMetaType::Type::QStringList )
600 {
601 const QStringList list = value.toStringList();
602 for ( const QString &v : list )
603 res.append( linkSourcesForParameterValue( model, v, childId, context ) );
604 }
605 else if ( value.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
606 {
607 const QgsProcessingModelChildParameterSource source = value.value<QgsProcessingModelChildParameterSource>();
608 switch ( source.source() )
609 {
611 {
612 LinkSource l;
613 l.item = mParameterItems.value( source.parameterName() );
614 l.edge = Qt::BottomEdge;
615 l.linkIndex = 0;
616
617 res.append( l );
618 break;
619 }
621 {
622 if ( !model->childAlgorithm( source.outputChildId() ).algorithm() )
623 break;
624
625 const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions();
626 int i = 0;
627 for ( const QgsProcessingOutputDefinition *output : outputs )
628 {
629 if ( output->name() == source.outputName() )
630 break;
631 i++;
632 }
633 if ( mChildAlgorithmItems.contains( source.outputChildId() ) )
634 {
635 LinkSource l;
636 l.item = mChildAlgorithmItems.value( source.outputChildId() );
637 l.edge = Qt::BottomEdge;
638
639 // do sanity check of linked index
640 if ( i >= model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions().length() )
641 {
642 QString short_message = tr( "Check output links for alg: %1" ).arg( model->childAlgorithm( source.outputChildId() ).algorithm()->name() );
643 QString long_message = tr( "Cannot link output for alg: %1" ).arg( model->childAlgorithm( source.outputChildId() ).algorithm()->name() );
644 QString title( tr( "Algorithm link error" ) );
645 if ( messageBar() )
646 showWarning( const_cast<QString &>( short_message ), const_cast<QString &>( title ), const_cast<QString &>( long_message ) );
647 else
648 QgsMessageLog::logMessage( long_message, "QgsModelGraphicsScene", Qgis::MessageLevel::Warning, true );
649 break;
650 }
651
652 l.linkIndex = i;
653 res.append( l );
654 }
655
656 break;
657 }
658
660 {
661 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = model->variablesForChildAlgorithm( childId, &context );
662 const QgsExpression exp( source.expression() );
663 const QSet<QString> vars = exp.referencedVariables();
664 for ( const QString &v : vars )
665 {
666 if ( variables.contains( v ) )
667 {
668 res.append( linkSourcesForParameterValue( model, QVariant::fromValue( variables.value( v ).source ), childId, context ) );
669 }
670 }
671 break;
672 }
673
677 break;
678 }
679 }
680 return res;
681}
682
683void QgsModelGraphicsScene::addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem )
684{
685 if ( mFlags & FlagHideComments || !component.comment() || component.comment()->description().isEmpty() )
686 return;
687
688 QgsModelComponentGraphicItem *commentItem = createCommentGraphicItem( model, component.comment()->clone(), parentItem );
689 commentItem->registerWidgetContextGenerator( mWidgetContextGenerator );
690 commentItem->setPos( component.comment()->position().x(), component.comment()->position().y() );
691 addItem( commentItem );
692 connect( commentItem, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
693 connect( commentItem, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
694 connect( commentItem, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
695
696 auto arrow = std::make_unique<QgsModelArrowItem>( parentItem, QgsModelArrowItem::Circle, commentItem, QgsModelArrowItem::Circle );
697 arrow->setPenStyle( Qt::DotLine );
698 addItem( arrow.release() );
699}
700
701
702void QgsModelGraphicsScene::addFeatureCountItemForArrow( QgsModelArrowItem *arrow, const QString &layerId )
703{
704 if ( mFlags & FlagHideFeatureCount )
705 {
706 arrow->setShowBadge( false );
707 return;
708 }
709
710 if ( !mLastResultCount.contains( layerId ) )
711 {
712 arrow->setShowBadge( false );
713 return;
714 }
715
716 arrow->setShowBadge( true );
717 if ( QgsModelDesignerArrowBadgeItem *badge = arrow->badgeItem() )
718 {
719 badge->setValue( mLastResultCount.value( layerId ) );
720 }
721}
722
723
724QgsMessageBar *QgsModelGraphicsScene::messageBar() const
725{
726 return mMessageBar;
727}
728
729void QgsModelGraphicsScene::setMessageBar( QgsMessageBar *messageBar )
730{
731 mMessageBar = messageBar;
732}
733
734void QgsModelGraphicsScene::showWarning( const QString &shortMessage, const QString &title, const QString &longMessage, Qgis::MessageLevel level ) const
735{
736 QgsMessageBarItem *messageWidget = QgsMessageBar::createMessage( QString(), shortMessage );
737 QPushButton *detailsButton = new QPushButton( tr( "Details" ) );
738 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, title, longMessage] {
739 QgsMessageViewer *dialog = new QgsMessageViewer( detailsButton );
740 dialog->setTitle( title );
741 dialog->setMessage( longMessage, Qgis::StringFormat::Html );
742 dialog->showMessage();
743 } );
744 messageWidget->layout()->addWidget( detailsButton );
745 mMessageBar->clearWidgets();
746 mMessageBar->pushWidget( messageWidget, level, 0 );
747}
748
749void QgsModelGraphicsScene::requestRebuildRequired()
750{
751 emit rebuildRequired();
752}
753
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:4071
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:4072
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:4068
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:4067
@ StaticValue
Parameter value is a static value.
Definition qgis.h:4069
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:4070
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3983
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.
An interface for objects which can create Processing widget contexts.
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:7423
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.