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