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