QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsprocessingtoolboxmodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingtoolboxmodel.cpp
3 -------------------------------
4 begin : May 2018
5 copyright : (C) 2018 by 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 <functional>
19
20#include "qgsapplication.h"
24#include "qgsvectorlayer.h"
25
26#include <QMimeData>
27#include <QPalette>
28
29#include "moc_qgsprocessingtoolboxmodel.cpp"
30
31#ifdef ENABLE_MODELTEST
32#include "modeltest.h"
33#endif
34
36
37//
38// QgsProcessingToolboxModelNode
39//
40
41QgsProcessingToolboxModelNode::~QgsProcessingToolboxModelNode()
42{
43 deleteChildren();
44}
45
46QgsProcessingToolboxModelNode *QgsProcessingToolboxModelNode::takeChild( QgsProcessingToolboxModelNode *node )
47{
48 return mChildren.takeAt( mChildren.indexOf( node ) );
49}
50
51QgsProcessingToolboxModelGroupNode *QgsProcessingToolboxModelNode::getChildGroupNode( const QString &groupId )
52{
53 for ( QgsProcessingToolboxModelNode *node : std::as_const( mChildren ) )
54 {
55 if ( node->nodeType() == NodeType::Group )
56 {
57 QgsProcessingToolboxModelGroupNode *groupNode = qobject_cast<QgsProcessingToolboxModelGroupNode *>( node );
58 if ( groupNode && groupNode->id() == groupId )
59 return groupNode;
60 }
61 }
62 return nullptr;
63}
64
65void QgsProcessingToolboxModelNode::addChildNode( QgsProcessingToolboxModelNode *node )
66{
67 if ( !node )
68 return;
69
70 Q_ASSERT( !node->mParent );
71 node->mParent = this;
72
73 mChildren.append( node );
74}
75
76void QgsProcessingToolboxModelNode::deleteChildren()
77{
78 qDeleteAll( mChildren );
79 mChildren.clear();
80}
81
82//
83// QgsProcessingToolboxModelProviderNode
84//
85
86QgsProcessingToolboxModelProviderNode::QgsProcessingToolboxModelProviderNode( QgsProcessingProvider *provider )
87 : mProviderId( provider->id() )
88 , mProvider( provider )
89{}
90
91QgsProcessingProvider *QgsProcessingToolboxModelProviderNode::provider()
92{
93 return mProvider;
94}
95
96//
97// QgsProcessingToolboxModelGroupNode
98//
99
100QgsProcessingToolboxModelGroupNode::QgsProcessingToolboxModelGroupNode( const QString &id, const QString &name )
101 : mId( id )
102 , mName( name )
103{}
104
105//
106// QgsProcessingToolboxModelAlgorithmNode
107//
108
109QgsProcessingToolboxModelAlgorithmNode::QgsProcessingToolboxModelAlgorithmNode( const QgsProcessingAlgorithm *algorithm )
110 : mAlgorithm( algorithm )
111{}
112
113const QgsProcessingAlgorithm *QgsProcessingToolboxModelAlgorithmNode::algorithm() const
114{
115 return mAlgorithm;
116}
117
118QgsProcessingToolboxModelParameterNode::QgsProcessingToolboxModelParameterNode( const QgsProcessingParameterType *paramType )
119 : mParamType( paramType )
120{}
121
122const QgsProcessingParameterType *QgsProcessingToolboxModelParameterNode::parameterType() const
123{
124 return mParamType;
125}
126
128
129//
130// QgsProcessingToolboxModel
131//
132
133QgsProcessingToolboxModel::QgsProcessingToolboxModel( QObject *parent, QgsProcessingRegistry *registry, QgsProcessingRecentAlgorithmLog *recentLog, QgsProcessingFavoriteAlgorithmManager *favoriteManager )
134 : QAbstractItemModel( parent )
135 , mRegistry( registry ? registry : QgsApplication::processingRegistry() )
136 , mRecentLog( recentLog )
137 , mFavoriteManager( favoriteManager )
138 , mRootNode( std::make_unique<QgsProcessingToolboxModelGroupNode>( QString(), QString() ) )
139{
140 rebuild();
141
142 if ( mRecentLog )
143 connect( mRecentLog, &QgsProcessingRecentAlgorithmLog::changed, this, [this] { repopulateRecentAlgorithms(); } );
144
145 if ( mFavoriteManager )
146 connect( mFavoriteManager, &QgsProcessingFavoriteAlgorithmManager::changed, this, [this] { repopulateFavoriteAlgorithms(); } );
147
148 connect( mRegistry, &QgsProcessingRegistry::providerAdded, this, &QgsProcessingToolboxModel::rebuild );
149 connect( mRegistry, &QgsProcessingRegistry::providerRemoved, this, &QgsProcessingToolboxModel::rebuild );
150
151 connect( mRegistry, &QgsProcessingRegistry::parameterTypeAdded, this, &QgsProcessingToolboxModel::rebuild );
152 connect( mRegistry, &QgsProcessingRegistry::parameterTypeRemoved, this, &QgsProcessingToolboxModel::rebuild );
153}
154
155void QgsProcessingToolboxModel::rebuild()
156{
157 beginResetModel();
158
159 mRootNode->deleteChildren();
160 mRecentNode = nullptr;
161 mFavoriteNode = nullptr;
162
163 if ( mRecentLog )
164 {
165 auto recentNode = std::make_unique<QgsProcessingToolboxModelRecentNode>();
166 // cppcheck-suppress danglingLifetime
167 mRecentNode = recentNode.get();
168 mRootNode->addChildNode( recentNode.release() );
169 repopulateRecentAlgorithms( true );
170 }
171
172 if ( mFavoriteManager )
173 {
174 auto favoriteNode = std::make_unique<QgsProcessingToolboxModelFavoriteNode>();
175 // cppcheck-suppress danglingLifetime
176 mFavoriteNode = favoriteNode.get();
177 mRootNode->addChildNode( favoriteNode.release() );
178 repopulateFavoriteAlgorithms( true );
179 }
180
181
182 if ( mRegistry )
183 {
184 auto groupNode = std::make_unique<QgsProcessingToolboxModelParameterGroupNode>();
185
186 QList<QgsProcessingParameterType *> available = QgsApplication::processingRegistry()->parameterTypes();
187
188 std::sort( available.begin(), available.end(), []( const QgsProcessingParameterType *a, const QgsProcessingParameterType *b ) -> bool {
189 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
190 } );
191 for ( QgsProcessingParameterType *param : std::as_const( available ) )
192 {
194 {
195 auto paramNode = std::make_unique<QgsProcessingToolboxModelParameterNode>( param );
196 groupNode->addChildNode( paramNode.release() );
197 }
198 }
199 mRootNode->addChildNode( groupNode.release() );
200 }
201
202
203 if ( mRegistry )
204 {
205 const QList<QgsProcessingProvider *> providers = mRegistry->providers();
206 for ( QgsProcessingProvider *provider : providers )
207 {
208 addProvider( provider );
209 }
210 }
211 endResetModel();
212}
213
214void QgsProcessingToolboxModel::repopulateRecentAlgorithms( bool resetting )
215{
216 if ( !mRecentNode || !mRecentLog )
217 return;
218
219 QModelIndex recentIndex = index( 0, 0 );
220 const int prevCount = rowCount( recentIndex );
221 if ( !resetting && prevCount > 0 )
222 {
223 beginRemoveRows( recentIndex, 0, prevCount - 1 );
224 mRecentNode->deleteChildren();
225 endRemoveRows();
226 }
227
228 if ( !mRegistry )
229 {
230 if ( !resetting )
232 return;
233 }
234
235 const QStringList recentAlgIds = mRecentLog->recentAlgorithmIds();
236 QList<const QgsProcessingAlgorithm *> recentAlgorithms;
237 recentAlgorithms.reserve( recentAlgIds.count() );
238 for ( const QString &id : recentAlgIds )
239 {
240 const QgsProcessingAlgorithm *algorithm = mRegistry->algorithmById( id );
241 if ( algorithm )
242 recentAlgorithms << algorithm;
243 }
244
245 if ( recentAlgorithms.empty() )
246 {
247 if ( !resetting )
249 return;
250 }
251
252 if ( !resetting )
253 {
254 beginInsertRows( recentIndex, 0, recentAlgorithms.count() - 1 );
255 }
256
257 for ( const QgsProcessingAlgorithm *algorithm : std::as_const( recentAlgorithms ) )
258 {
259 auto algorithmNode = std::make_unique<QgsProcessingToolboxModelAlgorithmNode>( algorithm );
260 mRecentNode->addChildNode( algorithmNode.release() );
261 }
262
263 if ( !resetting )
264 {
265 endInsertRows();
267 }
268}
269
270void QgsProcessingToolboxModel::repopulateFavoriteAlgorithms( bool resetting )
271{
272 if ( !mFavoriteNode || !mFavoriteManager )
273 return;
274
275 // favorite node should be under the Recent node if it is present or
276 // the first top-level item in the toolbox if Recent node is not present
277 int idx = ( mRecentNode && mRecentLog ) ? 1 : 0;
278
279 QModelIndex favoriteIndex = index( idx, 0 );
280 const int prevCount = rowCount( favoriteIndex );
281 if ( !resetting && prevCount > 0 )
282 {
283 beginRemoveRows( favoriteIndex, 0, prevCount - 1 );
284 mFavoriteNode->deleteChildren();
285 endRemoveRows();
286 }
287
288 if ( !mRegistry )
289 {
290 if ( !resetting )
292 return;
293 }
294
295 const QStringList favoriteAlgIds = mFavoriteManager->favoriteAlgorithmIds();
296 QList<const QgsProcessingAlgorithm *> favoriteAlgorithms;
297 favoriteAlgorithms.reserve( favoriteAlgIds.count() );
298 for ( const QString &id : favoriteAlgIds )
299 {
300 const QgsProcessingAlgorithm *algorithm = mRegistry->algorithmById( id );
301 if ( algorithm )
302 favoriteAlgorithms << algorithm;
303 }
304
305 if ( favoriteAlgorithms.empty() )
306 {
307 if ( !resetting )
309 return;
310 }
311
312 if ( !resetting )
313 {
314 beginInsertRows( favoriteIndex, 0, favoriteAlgorithms.count() - 1 );
315 }
316
317 for ( const QgsProcessingAlgorithm *algorithm : std::as_const( favoriteAlgorithms ) )
318 {
319 auto algorithmNode = std::make_unique<QgsProcessingToolboxModelAlgorithmNode>( algorithm );
320 mFavoriteNode->addChildNode( algorithmNode.release() );
321 }
322
323 if ( !resetting )
324 {
325 endInsertRows();
327 }
328}
329
330QgsProcessingToolboxModelNode *QgsProcessingToolboxModel::index2node( const QModelIndex &index ) const
331{
332 if ( !index.isValid() )
333 return mRootNode.get();
334
335 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
336 return qobject_cast<QgsProcessingToolboxModelNode *>( obj );
337}
338
339QModelIndex QgsProcessingToolboxModel::node2index( QgsProcessingToolboxModelNode *node ) const
340{
341 if ( !node || !node->parent() )
342 return QModelIndex(); // this is the only root item -> invalid index
343
344 QModelIndex parentIndex = node2index( node->parent() );
345
346 int row = node->parent()->children().indexOf( node );
347 Q_ASSERT( row >= 0 );
348 return index( row, 0, parentIndex );
349}
350
351void QgsProcessingToolboxModel::addProvider( QgsProcessingProvider *provider )
352{
353 if ( !provider )
354 return;
355
356 connect( provider, &QgsProcessingProvider::algorithmsLoaded, this, &QgsProcessingToolboxModel::rebuild, Qt::UniqueConnection );
357
358 QgsProcessingToolboxModelNode *parentNode = nullptr;
359 if ( !isTopLevelProvider( provider->id() ) )
360 {
361 auto node = std::make_unique<QgsProcessingToolboxModelProviderNode>( provider );
362 parentNode = node.get();
363 mRootNode->addChildNode( node.release() );
364 }
365 else
366 {
367 parentNode = mRootNode.get();
368 }
369
370 const QList<const QgsProcessingAlgorithm *> algorithms = provider->algorithms();
371 for ( const QgsProcessingAlgorithm *algorithm : algorithms )
372 {
373 auto algorithmNode = std::make_unique<QgsProcessingToolboxModelAlgorithmNode>( algorithm );
374
375 const QString groupId = algorithm->groupId();
376 if ( !groupId.isEmpty() )
377 {
378 // cppcheck-suppress invalidLifetime
379 QgsProcessingToolboxModelGroupNode *groupNode = parentNode->getChildGroupNode( groupId );
380 if ( !groupNode )
381 {
382 groupNode = new QgsProcessingToolboxModelGroupNode( algorithm->groupId(), algorithm->group() );
383 // cppcheck-suppress invalidLifetime
384 parentNode->addChildNode( groupNode );
385 }
386 groupNode->addChildNode( algorithmNode.release() );
387 }
388 else
389 {
390 // "top level" algorithm - no group
391 // cppcheck-suppress invalidLifetime
392 parentNode->addChildNode( algorithmNode.release() );
393 }
394 }
395}
396
397bool QgsProcessingToolboxModel::isTopLevelProvider( const QString &providerId )
398{
399 return providerId == QLatin1String( "qgis" ) || providerId == QLatin1String( "native" ) || providerId == QLatin1String( "3d" ) || providerId == QLatin1String( "pdal" );
400}
401
402QString QgsProcessingToolboxModel::toolTipForAlgorithm( const QgsProcessingAlgorithm *algorithm )
403{
404 return QStringLiteral( "<p><b>%1</b></p>%2<p>%3</p>%4" ).arg( algorithm->displayName(), !algorithm->shortDescription().isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( algorithm->shortDescription() ) : QString(), QObject::tr( "Algorithm ID: ‘%1’" ).arg( QStringLiteral( "<i>%1</i>" ).arg( algorithm->id() ) ), ( algorithm->flags() & Qgis::ProcessingAlgorithmFlag::KnownIssues ) ? QStringLiteral( "<b style=\"color:red\">%1</b>" ).arg( QObject::tr( "Warning: Algorithm has known issues" ) ) : QString() );
405}
406
407Qt::ItemFlags QgsProcessingToolboxModel::flags( const QModelIndex &index ) const
408{
409 if ( !index.isValid() )
410 return Qt::ItemFlags();
411
412 return QAbstractItemModel::flags( index );
413}
414
415QVariant QgsProcessingToolboxModel::data( const QModelIndex &index, int role ) const
416{
417 if ( !index.isValid() )
418 return QVariant();
419
420 if ( role == static_cast<int>( CustomRole::NodeType ) )
421 {
422 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
423 return static_cast<int>( node->nodeType() );
424 else
425 return QVariant();
426 }
427
428 bool isRecentNode = false;
429 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
430 isRecentNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Recent;
431
432 bool isFavoriteNode = false;
433 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
434 isFavoriteNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Favorite;
435
436 bool isParameterGroupNode = false;
437 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
438 isParameterGroupNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::ParameterGroup;
439
441 QgsProcessingToolboxModelGroupNode *groupNode = qobject_cast<QgsProcessingToolboxModelGroupNode *>( index2node( index ) );
444
445 switch ( role )
446 {
447 case Qt::DisplayRole:
448 {
449 switch ( index.column() )
450 {
451 case 0:
452 if ( provider )
453 return provider->name();
454 else if ( algorithm )
455 return algorithm->displayName();
456 else if ( paramType )
457 return paramType->name();
458 else if ( groupNode )
459 return groupNode->name();
460 else if ( isRecentNode )
461 return tr( "Recently used" );
462 else if ( isFavoriteNode )
463 return tr( "Favorites" );
464 else if ( isParameterGroupNode )
465 return tr( "Input parameters" );
466 else
467 return QVariant();
468
469 default:
470 return QVariant();
471 }
472 break;
473 }
474
475 case Qt::ToolTipRole:
476 {
477 if ( provider )
478 return provider->longName();
479 else if ( algorithm )
480 return toolTipForAlgorithm( algorithm );
481 else if ( paramType )
482 return paramType->description();
483 else if ( groupNode )
484 return groupNode->name();
485 else if ( isParameterGroupNode )
486 return tr( "Input parameters used in the modeler" );
487 else
488 return QVariant();
489 }
490
491 case Qt::ForegroundRole:
492 {
494 return QBrush( QColor( Qt::red ) );
495 else
496 return QVariant();
497 }
498
499 case Qt::DecorationRole:
500 {
501 switch ( index.column() )
502 {
503 case 0:
504 {
505 if ( provider )
506 return provider->icon();
507 else if ( algorithm )
508 {
510 return QgsApplication::getThemeIcon( QStringLiteral( "mIconWarning.svg" ) );
511 return algorithm->icon();
512 }
513 else if ( paramType )
514 return QgsApplication::getThemeIcon( QStringLiteral( "mIconModelInput.svg" ) );
515 else if ( isRecentNode )
516 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconHistory.svg" ) );
517 else if ( isFavoriteNode )
518 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFavorites.svg" ) );
519 else if ( isParameterGroupNode )
520 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconModelInput.svg" ) );
521 else if ( !index.parent().isValid() )
522 // top level groups get the QGIS icon
523 return QgsApplication::getThemeIcon( QStringLiteral( "/providerQgis.svg" ) );
524 else
525 return QVariant();
526 }
527
528 default:
529 return QVariant();
530 }
531 break;
532 }
533
534 case static_cast<int>( CustomRole::AlgorithmFlags ):
535 switch ( index.column() )
536 {
537 case 0:
538 {
539 if ( algorithm )
540 return static_cast<int>( algorithm->flags() );
541 else
542 return QVariant();
543 }
544
545 default:
546 return QVariant();
547 }
548 break;
549
550 case static_cast<int>( CustomRole::ProviderFlags ):
551 switch ( index.column() )
552 {
553 case 0:
554 {
555 if ( provider )
556 return static_cast<int>( provider->flags() );
557 else if ( algorithm && algorithm->provider() )
558 return static_cast<int>( algorithm->provider()->flags() );
559 else if ( index.parent().data( static_cast<int>( CustomRole::ProviderFlags ) ).isValid() ) // group node
560 return static_cast<int>( index.parent().data( static_cast<int>( CustomRole::ProviderFlags ) ).toInt() );
561 else
562 return QVariant();
563 }
564
565 default:
566 return QVariant();
567 }
568 break;
569
570 case static_cast<int>( CustomRole::AlgorithmId ):
571 switch ( index.column() )
572 {
573 case 0:
574 {
575 if ( algorithm )
576 return algorithm->id();
577 else
578 return QVariant();
579 }
580
581 default:
582 return QVariant();
583 }
584 break;
585
586 case static_cast<int>( CustomRole::AlgorithmName ):
587 switch ( index.column() )
588 {
589 case 0:
590 {
591 if ( algorithm )
592 return algorithm->name();
593 else
594 return QVariant();
595 }
596
597 default:
598 return QVariant();
599 }
600 break;
601
602 case static_cast<int>( CustomRole::AlgorithmTags ):
603 switch ( index.column() )
604 {
605 case 0:
606 {
607 if ( algorithm )
608 return algorithm->tags();
609 else
610 return QVariant();
611 }
612
613 default:
614 return QVariant();
615 }
616 break;
617
618 case static_cast<int>( CustomRole::AlgorithmShortDescription ):
619 switch ( index.column() )
620 {
621 case 0:
622 {
623 if ( algorithm )
624 return algorithm->shortDescription();
625 else
626 return QVariant();
627 }
628
629 default:
630 return QVariant();
631 }
632 break;
633 case static_cast<int>( CustomRole::ParameterTypeId ):
634 switch ( index.column() )
635 {
636 case 0:
637 {
638 if ( paramType )
639 return paramType->id();
640 else
641 return QVariant();
642 }
643
644 default:
645 return QVariant();
646 }
647 break;
648 default:
649 return QVariant();
650 }
651#ifndef _MSC_VER // avoid warning
652 return QVariant();
653#endif
654}
655
656int QgsProcessingToolboxModel::rowCount( const QModelIndex &parent ) const
657{
658 QgsProcessingToolboxModelNode *n = index2node( parent );
659 if ( !n )
660 return 0;
661
662 return n->children().count();
663}
664
665int QgsProcessingToolboxModel::columnCount( const QModelIndex & ) const
666{
667 return 1;
668}
669
670QModelIndex QgsProcessingToolboxModel::index( int row, int column, const QModelIndex &parent ) const
671{
672 if ( !hasIndex( row, column, parent ) )
673 return QModelIndex();
674
675 QgsProcessingToolboxModelNode *n = index2node( parent );
676 if ( !n )
677 return QModelIndex(); // have no children
678
679 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
680}
681
682QModelIndex QgsProcessingToolboxModel::parent( const QModelIndex &child ) const
683{
684 if ( !child.isValid() )
685 return QModelIndex();
686
687 if ( QgsProcessingToolboxModelNode *n = index2node( child ) )
688 {
689 return indexOfParentTreeNode( n->parent() ); // must not be null
690 }
691 else
692 {
693 Q_ASSERT( false ); // no other node types!
694 return QModelIndex();
695 }
696}
697
698QMimeData *QgsProcessingToolboxModel::mimeData( const QModelIndexList &indexes ) const
699{
700 if ( indexes.isEmpty() )
701 {
702 return nullptr;
703 }
704 if ( isAlgorithm( indexes.at( 0 ) ) )
705 {
706 QByteArray encodedData;
707 QDataStream stream( &encodedData, QIODevice::WriteOnly | QIODevice::Truncate );
708
709 auto mimeData = std::make_unique<QMimeData>();
710 const QgsProcessingAlgorithm *algorithm = algorithmForIndex( indexes.at( 0 ) );
711 if ( algorithm )
712 {
713 stream << algorithm->id();
714 }
715 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ), encodedData );
716 return mimeData.release();
717 }
718 if ( isParameter( indexes.at( 0 ) ) )
719 {
720 QByteArray encodedData;
721 QDataStream stream( &encodedData, QIODevice::WriteOnly | QIODevice::Truncate );
722
723 auto mimeData = std::make_unique<QMimeData>();
724 const QgsProcessingParameterType *paramType = parameterTypeForIndex( indexes.at( 0 ) );
725 if ( paramType )
726 {
727 stream << paramType->id();
728 }
729 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.parametertypeid" ), encodedData );
730 return mimeData.release();
731 }
732 return nullptr;
733}
734
736{
737 QgsProcessingToolboxModelNode *n = index2node( index );
738 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
739 return nullptr;
740
741 return qobject_cast<QgsProcessingToolboxModelProviderNode *>( n )->provider();
742}
743
744QString QgsProcessingToolboxModel::providerIdForIndex( const QModelIndex &index ) const
745{
746 QgsProcessingToolboxModelNode *n = index2node( index );
747 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
748 return nullptr;
749
750 return qobject_cast<QgsProcessingToolboxModelProviderNode *>( n )->providerId();
751}
752
754{
755 QgsProcessingToolboxModelNode *n = index2node( index );
756 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Algorithm )
757 return nullptr;
758
759 return qobject_cast<QgsProcessingToolboxModelAlgorithmNode *>( n )->algorithm();
760}
761
762bool QgsProcessingToolboxModel::isAlgorithm( const QModelIndex &index ) const
763{
764 QgsProcessingToolboxModelNode *n = index2node( index );
765 return ( n && n->nodeType() == QgsProcessingToolboxModelNode::NodeType::Algorithm );
766}
767
768bool QgsProcessingToolboxModel::isParameter( const QModelIndex &index ) const
769{
770 QgsProcessingToolboxModelNode *n = index2node( index );
771 return ( n && n->nodeType() == QgsProcessingToolboxModelNode::NodeType::Parameter );
772}
773
775{
776 QgsProcessingToolboxModelNode *n = index2node( index );
777 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Parameter )
778 return nullptr;
779
780 return qobject_cast<QgsProcessingToolboxModelParameterNode *>( n )->parameterType();
781}
782
783QModelIndex QgsProcessingToolboxModel::indexForProvider( const QString &providerId ) const
784{
785 std::function<QModelIndex( const QModelIndex &parent, const QString &providerId )> findIndex = [&]( const QModelIndex &parent, const QString &providerId ) -> QModelIndex {
786 for ( int row = 0; row < rowCount( parent ); ++row )
787 {
788 QModelIndex current = index( row, 0, parent );
789 const QString currentProviderId = providerIdForIndex( current );
790 if ( !currentProviderId.isEmpty() && currentProviderId == providerId )
791 return current;
792
793 QModelIndex checkChildren = findIndex( current, providerId );
794 if ( checkChildren.isValid() )
795 return checkChildren;
796 }
797 return QModelIndex();
798 };
799
800 return findIndex( QModelIndex(), providerId );
801}
802
803QModelIndex QgsProcessingToolboxModel::indexOfParentTreeNode( QgsProcessingToolboxModelNode *parentNode ) const
804{
805 Q_ASSERT( parentNode );
806
807 QgsProcessingToolboxModelNode *grandParentNode = parentNode->parent();
808 if ( !grandParentNode )
809 return QModelIndex(); // root node -> invalid index
810
811 int row = grandParentNode->children().indexOf( parentNode );
812 Q_ASSERT( row >= 0 );
813
814 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
815}
816
817//
818// QgsProcessingToolboxProxyModel
819//
820
821QgsProcessingToolboxProxyModel::QgsProcessingToolboxProxyModel( QObject *parent, QgsProcessingRegistry *registry, QgsProcessingRecentAlgorithmLog *recentLog, QgsProcessingFavoriteAlgorithmManager *favoriteManager )
822 : QSortFilterProxyModel( parent )
823 , mModel( new QgsProcessingToolboxModel( this, registry, recentLog, favoriteManager ) )
824{
825 setSourceModel( mModel );
826 setDynamicSortFilter( true );
827 setSortLocaleAware( true );
828 setFilterCaseSensitivity( Qt::CaseInsensitive );
829 sort( 0 );
830
831 connect( mModel, &QgsProcessingToolboxModel::recentAlgorithmAdded, this, [this] { invalidateFilter(); } );
832 connect( mModel, &QgsProcessingToolboxModel::favoriteAlgorithmAdded, this, [this] { invalidateFilter(); } );
833}
834
839
844
850
852{
853 mInPlaceLayer = layer;
854 invalidateFilter();
855}
856
857
859{
860 mFilterString = filter;
861 invalidateFilter();
862}
863
864bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
865{
866 QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
867 if ( mModel->isAlgorithm( sourceIndex ) )
868 {
869 const bool hasKnownIssues = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast<int>( Qgis::ProcessingAlgorithmFlag::KnownIssues );
870 if ( hasKnownIssues && !( mFilters & Filter::ShowKnownIssues ) )
871 return false;
872
873 if ( !mFilterString.trimmed().isEmpty() )
874 {
875 const QString algId = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmId ) ).toString();
876 const QString algName = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmName ) ).toString();
877 const QStringList algTags = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmTags ) ).toStringList();
878 const QString shortDesc = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmShortDescription ) ).toString();
879
880 QStringList parentText;
881 QModelIndex parent = sourceIndex.parent();
882 while ( parent.isValid() )
883 {
884 const QStringList parentParts = sourceModel()->data( parent, Qt::DisplayRole ).toString().split( ' ' );
885 if ( !parentParts.empty() )
886 parentText.append( parentParts );
887 parent = parent.parent();
888 }
889
890 QStringList partsToSearch;
891 partsToSearch << sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
892 partsToSearch << algId << algName;
893 partsToSearch.append( algTags );
894 if ( !shortDesc.isEmpty() )
895 partsToSearch.append( shortDesc.split( ' ' ) );
896 partsToSearch.append( parentText );
897
898 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
899 for ( const QString &part : partsToMatch )
900 {
901 bool found = false;
902 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
903 {
904 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
905 {
906 found = true;
907 break;
908 }
909 }
910 if ( !found )
911 return false; // couldn't find a match for this word, so hide algorithm
912 }
913 }
914
915 if ( mFilters & Filter::InPlace )
916 {
917 const bool supportsInPlace = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast<int>( Qgis::ProcessingAlgorithmFlag::SupportsInPlaceEdits );
918 if ( !supportsInPlace )
919 return false;
920
921 const QgsProcessingAlgorithm *alg = mModel->algorithmForIndex( sourceIndex );
922 if ( !( mInPlaceLayer && alg && alg->supportInPlaceEdit( mInPlaceLayer ) ) )
923 {
924 return false;
925 }
926 }
927 if ( mFilters & Filter::Modeler )
928 {
929 bool isHiddenFromModeler = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast<int>( Qgis::ProcessingAlgorithmFlag::HideFromModeler );
930 return !isHiddenFromModeler;
931 }
932 if ( mFilters & Filter::Toolbox )
933 {
934 bool isHiddenFromToolbox = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast<int>( Qgis::ProcessingAlgorithmFlag::HideFromToolbox );
935 return !isHiddenFromToolbox;
936 }
937 return true;
938 }
939 if ( mModel->isParameter( sourceIndex ) )
940 {
941 if ( mFilters & Filter::Toolbox )
942 {
943 /* Always hide in the toolbox */
944 return false;
945 }
946
947 if ( !mFilterString.trimmed().isEmpty() )
948 {
949 QStringList partsToSearch;
950 partsToSearch << sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
951 partsToSearch << sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::ParameterTypeId ) ).toString();
952
953 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
954 for ( const QString &part : partsToMatch )
955 {
956 bool found = false;
957 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
958 {
959 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
960 {
961 found = true;
962 break;
963 }
964 }
965 if ( !found )
966 return false; // couldn't find a match for this word, so hide algorithm
967 }
968 }
969
970 return true;
971 }
972 bool hasChildren = false;
973 // groups/providers are shown only if they have visible children
974 int count = sourceModel()->rowCount( sourceIndex );
975 for ( int i = 0; i < count; ++i )
976 {
977 if ( filterAcceptsRow( i, sourceIndex ) )
978 {
979 hasChildren = true;
980 break;
981 }
982 }
983
984 return hasChildren;
985}
986
987bool QgsProcessingToolboxProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
988{
989 QgsProcessingToolboxModelNode::NodeType leftType = static_cast<QgsProcessingToolboxModelNode::NodeType>( sourceModel()->data( left, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() );
990 QgsProcessingToolboxModelNode::NodeType rightType = static_cast<QgsProcessingToolboxModelNode::NodeType>( sourceModel()->data( right, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() );
991
992 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Recent )
993 return true;
994 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Recent )
995 return false;
996 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Favorite )
997 return true;
998 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Favorite )
999 return false;
1000 else if ( leftType != rightType )
1001 {
1002 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Provider )
1003 return false;
1004 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Provider )
1005 return true;
1006 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Group )
1007 return false;
1008 else
1009 return true;
1010 }
1011
1012 // if node represents a recent algorithm, it's not sorted at all
1013 bool isRecentNode = false;
1014 QModelIndex parent = left.parent();
1015 while ( parent.isValid() )
1016 {
1017 if ( mModel->data( parent, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() == static_cast<int>( QgsProcessingToolboxModelNode::NodeType::Recent ) )
1018 {
1019 isRecentNode = true;
1020 break;
1021 }
1022 parent = parent.parent();
1023 }
1024 if ( isRecentNode )
1025 {
1026 return left.row() < right.row();
1027 }
1028
1029 // default mode is alphabetical order
1030 QString leftStr = sourceModel()->data( left ).toString();
1031 QString rightStr = sourceModel()->data( right ).toString();
1032 return QString::localeAwareCompare( leftStr, rightStr ) < 0;
1033}
1034
1035QVariant QgsProcessingToolboxProxyModel::data( const QModelIndex &index, int role ) const
1036{
1037 if ( role == Qt::ForegroundRole && !mFilterString.isEmpty() )
1038 {
1039 QModelIndex sourceIndex = mapToSource( index );
1040 const QVariant flags = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::ProviderFlags ) );
1041 if ( flags.isValid() && flags.toInt() & static_cast<int>( Qgis::ProcessingProviderFlag::DeemphasiseSearchResults ) )
1042 {
1043 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
1044 QColor fadedTextColor = brush.color();
1045 fadedTextColor.setAlpha( 100 );
1046 brush.setColor( fadedTextColor );
1047 return brush;
1048 }
1049 }
1050 return QSortFilterProxyModel::data( index, role );
1051}
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
Definition qgis.h:3737
@ DeemphasiseSearchResults
Algorithms should be de-emphasised in the search results when searching for algorithms....
Definition qgis.h:3559
@ HideFromToolbox
Algorithm should be hidden from the toolbox.
Definition qgis.h:3583
@ SupportsInPlaceEdits
Algorithm supports in-place editing.
Definition qgis.h:3590
@ HideFromModeler
Algorithm should be hidden from the modeler.
Definition qgis.h:3584
@ KnownIssues
Algorithm has known issues.
Definition qgis.h:3591
Extends QApplication to provide access to QGIS specific resources such as theme paths,...
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Abstract base class for processing algorithms.
virtual QString group() const
Returns the name of the group this algorithm belongs to.
virtual QString groupId() const
Returns the unique ID of the group this algorithm belongs to.
virtual QString shortDescription() const
Returns an optional translated short description of the algorithm.
QString id() const
Returns the unique ID for the algorithm, which is a combination of the algorithm provider's ID and th...
virtual QString displayName() const =0
Returns the translated algorithm name, which should be used for any user-visible display of the algor...
virtual bool supportInPlaceEdit(const QgsMapLayer *layer) const
Checks whether this algorithm supports in-place editing on the given layer Default implementation ret...
Qgis::ProcessingAlgorithmFlags flags() const override
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Makes metadata of processing parameters available.
virtual QString name() const =0
A human readable and translatable short name for this parameter type.
virtual QString description() const =0
A human readable and translatable description for this parameter type.
virtual QString id() const =0
A static id for this type which will be used for storing this parameter type.
Abstract base class for processing providers.
virtual QIcon icon() const
Returns an icon for the provider.
virtual Qgis::ProcessingProviderFlags flags() const
Returns the flags indicating how and when the provider operates and should be exposed to users.
virtual QString name() const =0
Returns the provider name, which is used to describe the provider within the GUI.
void algorithmsLoaded()
Emitted when the provider has loaded (or refreshed) its list of available algorithms.
virtual QString id() const =0
Returns the unique provider id, used for identifying the provider.
virtual QString longName() const
Returns the longer version of the provider name, which can include extra details such as version numb...
QList< const QgsProcessingAlgorithm * > algorithms() const
Returns a list of algorithms supplied by this provider.
Registry for various processing components, including providers, algorithms and various parameters an...
void parameterTypeAdded(QgsProcessingParameterType *type)
Emitted when a new parameter type has been added to the registry.
void parameterTypeRemoved(QgsProcessingParameterType *type)
Emitted when a parameter type has been removed from the registry and is about to be deleted.
void providerAdded(const QString &id)
Emitted when a provider has been added to the registry.
QList< QgsProcessingParameterType * > parameterTypes() const
Returns a list with all known parameter types.
void providerRemoved(const QString &id)
Emitted when a provider is removed from the registry.
A model for providers and algorithms shown within the Processing toolbox.
int columnCount(const QModelIndex &=QModelIndex()) const override
QModelIndex indexForProvider(const QString &providerId) const
Returns the index corresponding to the specified providerId.
bool isParameter(const QModelIndex &index) const
Returns true if index corresponds to a parameter.
QModelIndex node2index(QgsProcessingToolboxModelNode *node) const
Returns the model index corresponding to the given node.
@ ProviderFlags
Returns the node's provider flags.
@ AlgorithmId
Algorithm ID, for algorithm nodes.
@ NodeType
Corresponds to the node's type.
@ AlgorithmFlags
Returns the node's algorithm flags, for algorithm nodes.
@ AlgorithmName
Untranslated algorithm name, for algorithm nodes.
@ AlgorithmShortDescription
Short algorithm description, for algorithm nodes.
@ AlgorithmTags
List of algorithm tags, for algorithm nodes.
@ ParameterTypeId
Untranslated parameter type unique identifier for parameter nodes.
QModelIndex indexOfParentTreeNode(QgsProcessingToolboxModelNode *parentNode) const
Returns the index corresponding to the parent of a given node.
Qt::ItemFlags flags(const QModelIndex &index) const override
QgsProcessingToolboxModel(QObject *parent=nullptr, QgsProcessingRegistry *registry=nullptr, QgsProcessingRecentAlgorithmLog *recentLog=nullptr, QgsProcessingFavoriteAlgorithmManager *favoriteManager=nullptr)
Constructor for QgsProcessingToolboxModel, with the given parent object.
QString providerIdForIndex(const QModelIndex &index) const
Returns the provider ID which corresponds to a given index, or an empty string if the index does not ...
QgsProcessingToolboxModelNode * index2node(const QModelIndex &index) const
Returns the model node corresponding to the given index.
bool isAlgorithm(const QModelIndex &index) const
Returns true if index corresponds to an algorithm.
void recentAlgorithmAdded()
Emitted whenever recent algorithms are added to the model.
QgsProcessingProvider * providerForIndex(const QModelIndex &index) const
Returns the provider which corresponds to a given index, or nullptr if the index does not represent a...
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void favoriteAlgorithmAdded()
Emitted whenever favorite algorithms are added to the model.
QModelIndex parent(const QModelIndex &index) const override
const QgsProcessingParameterType * parameterTypeForIndex(const QModelIndex &index) const
Returns the algorithm which corresponds to a given index, or nullptr if the index does not represent ...
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
const QgsProcessingAlgorithm * algorithmForIndex(const QModelIndex &index) const
Returns the algorithm which corresponds to a given index, or nullptr if the index does not represent ...
QMimeData * mimeData(const QModelIndexList &indexes) const override
Filters filters() const
Returns any filters that affect how toolbox content is filtered.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
void setFilters(QgsProcessingToolboxProxyModel::Filters filters)
Set filters that affect how toolbox content is filtered.
void setFilterString(const QString &filter)
Sets a filter string, such that only algorithms matching the specified string will be shown.
QgsProcessingToolboxProxyModel(QObject *parent=nullptr, QgsProcessingRegistry *registry=nullptr, QgsProcessingRecentAlgorithmLog *recentLog=nullptr, QgsProcessingFavoriteAlgorithmManager *favoriteManager=nullptr)
Constructor for QgsProcessingToolboxProxyModel, with the given parent object.
void setInPlaceLayer(QgsVectorLayer *layer)
Sets the vector layer for in-place algorithm filter.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
QgsProcessingToolboxModel * toolboxModel()
Returns the underlying source Processing toolbox model.
@ ShowKnownIssues
Show algorithms with known issues (hidden by default).
@ InPlace
Only show algorithms which support in-place edits.
@ Toolbox
Filters out any algorithms and content which should not be shown in the toolbox.
@ Modeler
Filters out any algorithms and content which should not be shown in the modeler.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Represents a vector layer which manages a vector based dataset.
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