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