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