QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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(
410 !algorithm->shortDescription().isEmpty() ? u"<p>%1</p>"_s.arg( algorithm->shortDescription() ) : QString(),
411 QObject::tr( "Algorithm ID: ‘%1’" ).arg( u"<i>%1</i>"_s.arg( algorithm->id() ) ),
412 ( algorithm->flags() & Qgis::ProcessingAlgorithmFlag::KnownIssues ) ? u"<b style=\"color:red\">%1</b>"_s.arg( QObject::tr( "Warning: Algorithm has known issues" ) ) : QString()
413 );
414}
415
416Qt::ItemFlags QgsProcessingToolboxModel::flags( const QModelIndex &index ) const
417{
418 if ( !index.isValid() )
419 return Qt::ItemFlags();
420
421 return QAbstractItemModel::flags( index );
422}
423
424QVariant QgsProcessingToolboxModel::data( const QModelIndex &index, int role ) const
425{
426 if ( !index.isValid() )
427 return QVariant();
428
429 if ( role == static_cast<int>( CustomRole::NodeType ) )
430 {
431 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
432 return static_cast<int>( node->nodeType() );
433 else
434 return QVariant();
435 }
436
437 bool isRecentNode = false;
438 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
439 isRecentNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Recent;
440
441 bool isFavoriteNode = false;
442 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
443 isFavoriteNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Favorite;
444
445 bool isParameterGroupNode = false;
446 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
447 isParameterGroupNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::ParameterGroup;
448
450 QgsProcessingToolboxModelGroupNode *groupNode = qobject_cast<QgsProcessingToolboxModelGroupNode *>( index2node( index ) );
453
454 switch ( role )
455 {
456 case Qt::DisplayRole:
457 {
458 switch ( index.column() )
459 {
460 case 0:
461 if ( provider )
462 return provider->name();
463 else if ( algorithm )
464 return algorithm->displayName();
465 else if ( paramType )
466 return paramType->name();
467 else if ( groupNode )
468 return groupNode->name();
469 else if ( isRecentNode )
470 return tr( "Recently used" );
471 else if ( isFavoriteNode )
472 return tr( "Favorites" );
473 else if ( isParameterGroupNode )
474 return tr( "Input parameters" );
475 else
476 return QVariant();
477
478 default:
479 return QVariant();
480 }
481 break;
482 }
483
484 case Qt::ToolTipRole:
485 {
486 if ( provider )
487 return provider->longName();
488 else if ( algorithm )
489 return toolTipForAlgorithm( algorithm );
490 else if ( paramType )
491 return paramType->description();
492 else if ( groupNode )
493 return groupNode->name();
494 else if ( isParameterGroupNode )
495 return tr( "Input parameters used in the modeler" );
496 else
497 return QVariant();
498 }
499
500 case Qt::ForegroundRole:
501 {
503 return QBrush( QColor( Qt::red ) );
504 else
505 return QVariant();
506 }
507
508 case Qt::DecorationRole:
509 {
510 switch ( index.column() )
511 {
512 case 0:
513 {
514 if ( provider )
515 return provider->icon();
516 else if ( algorithm )
517 {
519 return QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
520 return algorithm->icon();
521 }
522 else if ( paramType )
523 return QgsApplication::getThemeIcon( u"mIconModelInput.svg"_s );
524 else if ( isRecentNode )
525 return QgsApplication::getThemeIcon( u"/mIconHistory.svg"_s );
526 else if ( isFavoriteNode )
527 return QgsApplication::getThemeIcon( u"/mIconFavorites.svg"_s );
528 else if ( isParameterGroupNode )
529 return QgsApplication::getThemeIcon( u"/mIconModelInput.svg"_s );
530 else if ( !index.parent().isValid() )
531 // top level groups get the QGIS icon
532 return QgsApplication::getThemeIcon( u"/providerQgis.svg"_s );
533 else
534 return QVariant();
535 }
536
537 default:
538 return QVariant();
539 }
540 break;
541 }
542
543 case static_cast<int>( CustomRole::AlgorithmFlags ):
544 switch ( index.column() )
545 {
546 case 0:
547 {
548 if ( algorithm )
549 return static_cast<int>( algorithm->flags() );
550 else
551 return QVariant();
552 }
553
554 default:
555 return QVariant();
556 }
557 break;
558
559 case static_cast<int>( CustomRole::ProviderFlags ):
560 switch ( index.column() )
561 {
562 case 0:
563 {
564 if ( provider )
565 return static_cast<int>( provider->flags() );
566 else if ( algorithm && algorithm->provider() )
567 return static_cast<int>( algorithm->provider()->flags() );
568 else if ( index.parent().data( static_cast<int>( CustomRole::ProviderFlags ) ).isValid() ) // group node
569 return static_cast<int>( index.parent().data( static_cast<int>( CustomRole::ProviderFlags ) ).toInt() );
570 else
571 return QVariant();
572 }
573
574 default:
575 return QVariant();
576 }
577 break;
578
579 case static_cast<int>( CustomRole::AlgorithmId ):
580 switch ( index.column() )
581 {
582 case 0:
583 {
584 if ( algorithm )
585 return algorithm->id();
586 else
587 return QVariant();
588 }
589
590 default:
591 return QVariant();
592 }
593 break;
594
595 case static_cast<int>( CustomRole::AlgorithmName ):
596 switch ( index.column() )
597 {
598 case 0:
599 {
600 if ( algorithm )
601 return algorithm->name();
602 else
603 return QVariant();
604 }
605
606 default:
607 return QVariant();
608 }
609 break;
610
611 case static_cast<int>( CustomRole::AlgorithmTags ):
612 switch ( index.column() )
613 {
614 case 0:
615 {
616 if ( algorithm )
617 return algorithm->tags();
618 else
619 return QVariant();
620 }
621
622 default:
623 return QVariant();
624 }
625 break;
626
627 case static_cast<int>( CustomRole::AlgorithmShortDescription ):
628 switch ( index.column() )
629 {
630 case 0:
631 {
632 if ( algorithm )
633 return algorithm->shortDescription();
634 else
635 return QVariant();
636 }
637
638 default:
639 return QVariant();
640 }
641 break;
642 case static_cast<int>( CustomRole::ParameterTypeId ):
643 switch ( index.column() )
644 {
645 case 0:
646 {
647 if ( paramType )
648 return paramType->id();
649 else
650 return QVariant();
651 }
652
653 default:
654 return QVariant();
655 }
656 break;
657 default:
658 return QVariant();
659 }
660#ifndef _MSC_VER // avoid warning
661 return QVariant();
662#endif
663}
664
665int QgsProcessingToolboxModel::rowCount( const QModelIndex &parent ) const
666{
667 QgsProcessingToolboxModelNode *n = index2node( parent );
668 if ( !n )
669 return 0;
670
671 return n->children().count();
672}
673
674int QgsProcessingToolboxModel::columnCount( const QModelIndex & ) const
675{
676 return 1;
677}
678
679QModelIndex QgsProcessingToolboxModel::index( int row, int column, const QModelIndex &parent ) const
680{
681 if ( !hasIndex( row, column, parent ) )
682 return QModelIndex();
683
684 QgsProcessingToolboxModelNode *n = index2node( parent );
685 if ( !n )
686 return QModelIndex(); // have no children
687
688 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
689}
690
691QModelIndex QgsProcessingToolboxModel::parent( const QModelIndex &child ) const
692{
693 if ( !child.isValid() )
694 return QModelIndex();
695
696 if ( QgsProcessingToolboxModelNode *n = index2node( child ) )
697 {
698 return indexOfParentTreeNode( n->parent() ); // must not be null
699 }
700 else
701 {
702 Q_ASSERT( false ); // no other node types!
703 return QModelIndex();
704 }
705}
706
707QMimeData *QgsProcessingToolboxModel::mimeData( const QModelIndexList &indexes ) const
708{
709 if ( indexes.isEmpty() )
710 {
711 return nullptr;
712 }
713 if ( isAlgorithm( indexes.at( 0 ) ) )
714 {
715 QByteArray encodedData;
716 QDataStream stream( &encodedData, QIODevice::WriteOnly | QIODevice::Truncate );
717
718 auto mimeData = std::make_unique<QMimeData>();
719 const QgsProcessingAlgorithm *algorithm = algorithmForIndex( indexes.at( 0 ) );
720 if ( algorithm )
721 {
722 stream << algorithm->id();
723 }
724 mimeData->setData( u"application/x-vnd.qgis.qgis.algorithmid"_s, encodedData );
725 return mimeData.release();
726 }
727 if ( isParameter( indexes.at( 0 ) ) )
728 {
729 QByteArray encodedData;
730 QDataStream stream( &encodedData, QIODevice::WriteOnly | QIODevice::Truncate );
731
732 auto mimeData = std::make_unique<QMimeData>();
733 const QgsProcessingParameterType *paramType = parameterTypeForIndex( indexes.at( 0 ) );
734 if ( paramType )
735 {
736 stream << paramType->id();
737 }
738 mimeData->setData( u"application/x-vnd.qgis.qgis.parametertypeid"_s, encodedData );
739 return mimeData.release();
740 }
741 return nullptr;
742}
743
745{
746 QgsProcessingToolboxModelNode *n = index2node( index );
747 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
748 return nullptr;
749
750 return qobject_cast<QgsProcessingToolboxModelProviderNode *>( n )->provider();
751}
752
753QString QgsProcessingToolboxModel::providerIdForIndex( const QModelIndex &index ) const
754{
755 QgsProcessingToolboxModelNode *n = index2node( index );
756 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
757 return nullptr;
758
759 return qobject_cast<QgsProcessingToolboxModelProviderNode *>( n )->providerId();
760}
761
763{
764 QgsProcessingToolboxModelNode *n = index2node( index );
765 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Algorithm )
766 return nullptr;
767
768 return qobject_cast<QgsProcessingToolboxModelAlgorithmNode *>( n )->algorithm();
769}
770
771bool QgsProcessingToolboxModel::isAlgorithm( const QModelIndex &index ) const
772{
773 QgsProcessingToolboxModelNode *n = index2node( index );
774 return ( n && n->nodeType() == QgsProcessingToolboxModelNode::NodeType::Algorithm );
775}
776
777bool QgsProcessingToolboxModel::isParameter( const QModelIndex &index ) const
778{
779 QgsProcessingToolboxModelNode *n = index2node( index );
780 return ( n && n->nodeType() == QgsProcessingToolboxModelNode::NodeType::Parameter );
781}
782
784{
785 QgsProcessingToolboxModelNode *n = index2node( index );
786 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Parameter )
787 return nullptr;
788
789 return qobject_cast<QgsProcessingToolboxModelParameterNode *>( n )->parameterType();
790}
791
792QModelIndex QgsProcessingToolboxModel::indexForProvider( const QString &providerId ) const
793{
794 std::function<QModelIndex( const QModelIndex &parent, const QString &providerId )> findIndex = [&]( const QModelIndex &parent, const QString &providerId ) -> QModelIndex {
795 for ( int row = 0; row < rowCount( parent ); ++row )
796 {
797 QModelIndex current = index( row, 0, parent );
798 const QString currentProviderId = providerIdForIndex( current );
799 if ( !currentProviderId.isEmpty() && currentProviderId == providerId )
800 return current;
801
802 QModelIndex checkChildren = findIndex( current, providerId );
803 if ( checkChildren.isValid() )
804 return checkChildren;
805 }
806 return QModelIndex();
807 };
808
809 return findIndex( QModelIndex(), providerId );
810}
811
812QModelIndex QgsProcessingToolboxModel::indexOfParentTreeNode( QgsProcessingToolboxModelNode *parentNode ) const
813{
814 Q_ASSERT( parentNode );
815
816 QgsProcessingToolboxModelNode *grandParentNode = parentNode->parent();
817 if ( !grandParentNode )
818 return QModelIndex(); // root node -> invalid index
819
820 int row = grandParentNode->children().indexOf( parentNode );
821 Q_ASSERT( row >= 0 );
822
823 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
824}
825
826//
827// QgsProcessingToolboxProxyModel
828//
829
831 QObject *parent, QgsProcessingRegistry *registry, QgsProcessingRecentAlgorithmLog *recentLog, QgsProcessingFavoriteAlgorithmManager *favoriteManager
832)
833 : QSortFilterProxyModel( parent )
834 , mModel( new QgsProcessingToolboxModel( this, registry, recentLog, favoriteManager ) )
835{
836 setSourceModel( mModel );
837 setDynamicSortFilter( true );
838 setSortLocaleAware( true );
839 setFilterCaseSensitivity( Qt::CaseInsensitive );
840 sort( 0 );
841
842 connect( mModel, &QgsProcessingToolboxModel::recentAlgorithmAdded, this, [this] { invalidateFilter(); } );
843 connect( mModel, &QgsProcessingToolboxModel::favoriteAlgorithmAdded, this, [this] { invalidateFilter(); } );
844}
845
850
855
861
863{
864 mInPlaceLayer = layer;
865 invalidateFilter();
866}
867
868
870{
871 mFilterString = filter;
872 invalidateFilter();
873}
874
875bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
876{
877 QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
878 if ( mModel->isAlgorithm( sourceIndex ) )
879 {
880 const bool hasKnownIssues = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt()
881 & static_cast<int>( Qgis::ProcessingAlgorithmFlag::KnownIssues );
882 if ( hasKnownIssues && !( mFilters & Filter::ShowKnownIssues ) )
883 return false;
884
885 if ( !mFilterString.trimmed().isEmpty() )
886 {
887 const QString algId = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmId ) ).toString();
888 const QString algName = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmName ) ).toString();
889 const QStringList algTags = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmTags ) ).toStringList();
890 const QString shortDesc = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmShortDescription ) ).toString();
891
892 QStringList parentText;
893 QModelIndex parent = sourceIndex.parent();
894 while ( parent.isValid() )
895 {
896 const QStringList parentParts = sourceModel()->data( parent, Qt::DisplayRole ).toString().split( ' ' );
897 if ( !parentParts.empty() )
898 parentText.append( parentParts );
899 parent = parent.parent();
900 }
901
902 QStringList partsToSearch;
903 partsToSearch << sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
904 partsToSearch << algId << algName;
905 partsToSearch.append( algTags );
906 if ( !shortDesc.isEmpty() )
907 partsToSearch.append( shortDesc.split( ' ' ) );
908 partsToSearch.append( parentText );
909
910 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
911 for ( const QString &part : partsToMatch )
912 {
913 bool found = false;
914 const QString unaccentedPart = QgsStringUtils::unaccent( part );
915 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
916 {
917 if ( QgsStringUtils::unaccent( partToSearch ).contains( unaccentedPart, Qt::CaseInsensitive ) )
918 {
919 found = true;
920 break;
921 }
922 }
923 if ( !found )
924 return false; // couldn't find a match for this word, so hide algorithm
925 }
926 }
927
928 if ( mFilters & Filter::InPlace )
929 {
930 const bool supportsInPlace = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt()
932 if ( !supportsInPlace )
933 return false;
934
935 const QgsProcessingAlgorithm *alg = mModel->algorithmForIndex( sourceIndex );
936 if ( !( mInPlaceLayer && alg && alg->supportInPlaceEdit( mInPlaceLayer ) ) )
937 {
938 return false;
939 }
940 }
941 if ( mFilters & Filter::Modeler )
942 {
943 bool isHiddenFromModeler = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt()
945 return !isHiddenFromModeler;
946 }
947 if ( mFilters & Filter::Toolbox )
948 {
949 bool isHiddenFromToolbox = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt()
951 return !isHiddenFromToolbox;
952 }
953 return true;
954 }
955 if ( mModel->isParameter( sourceIndex ) )
956 {
957 if ( mFilters & Filter::Toolbox )
958 {
959 /* Always hide in the toolbox */
960 return false;
961 }
962
963 if ( !mFilterString.trimmed().isEmpty() )
964 {
965 QStringList partsToSearch;
966 partsToSearch << sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
967 partsToSearch << sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::ParameterTypeId ) ).toString();
968
969 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
970 for ( const QString &part : partsToMatch )
971 {
972 bool found = false;
973 const QString unaccentedPart = QgsStringUtils::unaccent( part );
974 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
975 {
976 if ( QgsStringUtils::unaccent( partToSearch ).contains( unaccentedPart, Qt::CaseInsensitive ) )
977 {
978 found = true;
979 break;
980 }
981 }
982 if ( !found )
983 return false; // couldn't find a match for this word, so hide algorithm
984 }
985 }
986
987 return true;
988 }
989 bool hasChildren = false;
990 // groups/providers are shown only if they have visible children
991 int count = sourceModel()->rowCount( sourceIndex );
992 for ( int i = 0; i < count; ++i )
993 {
994 if ( filterAcceptsRow( i, sourceIndex ) )
995 {
996 hasChildren = true;
997 break;
998 }
999 }
1000
1001 return hasChildren;
1002}
1003
1004bool QgsProcessingToolboxProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
1005{
1006 QgsProcessingToolboxModelNode::NodeType leftType = static_cast<QgsProcessingToolboxModelNode::NodeType>(
1007 sourceModel()->data( left, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt()
1008 );
1009 QgsProcessingToolboxModelNode::NodeType rightType = static_cast<QgsProcessingToolboxModelNode::NodeType>(
1010 sourceModel()->data( right, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt()
1011 );
1012
1013 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Recent )
1014 return true;
1015 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Recent )
1016 return false;
1017 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Favorite )
1018 return true;
1019 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Favorite )
1020 return false;
1021 else if ( leftType != rightType )
1022 {
1023 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Provider )
1024 return false;
1025 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Provider )
1026 return true;
1027 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Group )
1028 return false;
1029 else
1030 return true;
1031 }
1032
1033 // if node represents a recent algorithm, it's not sorted at all
1034 bool isRecentNode = false;
1035 QModelIndex parent = left.parent();
1036 while ( parent.isValid() )
1037 {
1038 if ( mModel->data( parent, static_cast<int>( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() == static_cast<int>( QgsProcessingToolboxModelNode::NodeType::Recent ) )
1039 {
1040 isRecentNode = true;
1041 break;
1042 }
1043 parent = parent.parent();
1044 }
1045 if ( isRecentNode )
1046 {
1047 return left.row() < right.row();
1048 }
1049
1050 // default mode is alphabetical order
1051 QString leftStr = sourceModel()->data( left ).toString();
1052 QString rightStr = sourceModel()->data( right ).toString();
1053 return QString::localeAwareCompare( leftStr, rightStr ) < 0;
1054}
1055
1056QVariant QgsProcessingToolboxProxyModel::data( const QModelIndex &index, int role ) const
1057{
1058 if ( role == Qt::ForegroundRole && !mFilterString.isEmpty() )
1059 {
1060 QModelIndex sourceIndex = mapToSource( index );
1061 const QVariant flags = sourceModel()->data( sourceIndex, static_cast<int>( QgsProcessingToolboxModel::CustomRole::ProviderFlags ) );
1062 if ( flags.isValid() && flags.toInt() & static_cast<int>( Qgis::ProcessingProviderFlag::DeemphasiseSearchResults ) )
1063 {
1064 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
1065 QColor fadedTextColor = brush.color();
1066 fadedTextColor.setAlpha( 100 );
1067 brush.setColor( fadedTextColor );
1068 return brush;
1069 }
1070 }
1071 return QSortFilterProxyModel::data( index, role );
1072}
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
Definition qgis.h:3854
@ DeemphasiseSearchResults
Algorithms should be de-emphasised in the search results when searching for algorithms....
Definition qgis.h:3673
@ HideFromToolbox
Algorithm should be hidden from the toolbox.
Definition qgis.h:3698
@ SupportsInPlaceEdits
Algorithm supports in-place editing.
Definition qgis.h:3705
@ HideFromModeler
Algorithm should be hidden from the modeler.
Definition qgis.h:3699
@ KnownIssues
Algorithm has known issues.
Definition qgis.h:3706
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