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