QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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" ) ||
395 providerId == QLatin1String( "native" ) ||
396 providerId == QLatin1String( "3d" ) ||
397 providerId == QLatin1String( "pdal" );
398}
399
400QString QgsProcessingToolboxModel::toolTipForAlgorithm( const QgsProcessingAlgorithm *algorithm )
401{
402 return QStringLiteral( "<p><b>%1</b></p>%2<p>%3</p>%4" ).arg(
404 !algorithm->shortDescription().isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( algorithm->shortDescription() ) : QString(),
405 QObject::tr( "Algorithm ID: ‘%1’" ).arg( QStringLiteral( "<i>%1</i>" ).arg( algorithm->id() ) ),
406 ( algorithm->flags() & Qgis::ProcessingAlgorithmFlag::KnownIssues ) ? QStringLiteral( "<b style=\"color:red\">%1</b>" ).arg( QObject::tr( "Warning: Algorithm has known issues" ) ) : QString()
407 );
408}
409
410Qt::ItemFlags QgsProcessingToolboxModel::flags( const QModelIndex &index ) const
411{
412 if ( !index.isValid() )
413 return Qt::ItemFlags();
414
415 return QAbstractItemModel::flags( index );
416}
417
418QVariant QgsProcessingToolboxModel::data( const QModelIndex &index, int role ) const
419{
420 if ( !index.isValid() )
421 return QVariant();
422
423 if ( role == static_cast< int >( CustomRole::NodeType ) )
424 {
425 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
426 return static_cast< int >( node->nodeType() );
427 else
428 return QVariant();
429 }
430
431 bool isRecentNode = false;
432 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
433 isRecentNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Recent;
434
435 bool isFavoriteNode = false;
436 if ( QgsProcessingToolboxModelNode *node = index2node( index ) )
437 isFavoriteNode = node->nodeType() == QgsProcessingToolboxModelNode::NodeType::Favorite;
438
440 QgsProcessingToolboxModelGroupNode *groupNode = qobject_cast< QgsProcessingToolboxModelGroupNode * >( index2node( index ) );
442
443 switch ( role )
444 {
445 case Qt::DisplayRole:
446 {
447 switch ( index.column() )
448 {
449 case 0:
450 if ( provider )
451 return provider->name();
452 else if ( algorithm )
453 return algorithm->displayName();
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
461 return QVariant();
462
463 default:
464 return QVariant();
465 }
466 break;
467 }
468
469 case Qt::ToolTipRole:
470 {
471 if ( provider )
472 return provider->longName();
473 else if ( algorithm )
474 return toolTipForAlgorithm( algorithm );
475 else if ( groupNode )
476 return groupNode->name();
477 else
478 return QVariant();
479 }
480
481 case Qt::ForegroundRole:
482 {
484 return QBrush( QColor( Qt::red ) );
485 else
486 return QVariant();
487 }
488
489 case Qt::DecorationRole:
490 {
491 switch ( index.column() )
492 {
493 case 0:
494 {
495 if ( provider )
496 return provider->icon();
497 else if ( algorithm )
498 {
500 return QgsApplication::getThemeIcon( QStringLiteral( "mIconWarning.svg" ) );
501 return algorithm->icon();
502 }
503 else if ( isRecentNode )
504 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconHistory.svg" ) );
505 else if ( isFavoriteNode )
506 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFavorites.svg" ) );
507 else if ( !index.parent().isValid() )
508 // top level groups get the QGIS icon
509 return QgsApplication::getThemeIcon( QStringLiteral( "/providerQgis.svg" ) );
510 else
511 return QVariant();
512 }
513
514 default:
515 return QVariant();
516 }
517 break;
518 }
519
520 case static_cast< int >( CustomRole::AlgorithmFlags ):
521 switch ( index.column() )
522 {
523 case 0:
524 {
525 if ( algorithm )
526 return static_cast< int >( algorithm->flags() );
527 else
528 return QVariant();
529 }
530
531 default:
532 return QVariant();
533 }
534 break;
535
536 case static_cast< int >( CustomRole::ProviderFlags ):
537 switch ( index.column() )
538 {
539 case 0:
540 {
541 if ( provider )
542 return static_cast< int >( provider->flags() );
543 else if ( algorithm && algorithm->provider() )
544 return static_cast< int >( algorithm->provider()->flags() );
545 else if ( index.parent().data( static_cast< int >( CustomRole::ProviderFlags ) ).isValid() ) // group node
546 return static_cast< int >( index.parent().data( static_cast< int >( CustomRole::ProviderFlags ) ).toInt() );
547 else
548 return QVariant();
549 }
550
551 default:
552 return QVariant();
553 }
554 break;
555
556 case static_cast< int >( CustomRole::AlgorithmId ):
557 switch ( index.column() )
558 {
559 case 0:
560 {
561 if ( algorithm )
562 return algorithm->id();
563 else
564 return QVariant();
565 }
566
567 default:
568 return QVariant();
569 }
570 break;
571
572 case static_cast< int >( CustomRole::AlgorithmName ):
573 switch ( index.column() )
574 {
575 case 0:
576 {
577 if ( algorithm )
578 return algorithm->name();
579 else
580 return QVariant();
581 }
582
583 default:
584 return QVariant();
585 }
586 break;
587
588 case static_cast< int >( CustomRole::AlgorithmTags ):
589 switch ( index.column() )
590 {
591 case 0:
592 {
593 if ( algorithm )
594 return algorithm->tags();
595 else
596 return QVariant();
597 }
598
599 default:
600 return QVariant();
601 }
602 break;
603
604 case static_cast< int >( CustomRole::AlgorithmShortDescription ):
605 switch ( index.column() )
606 {
607 case 0:
608 {
609 if ( algorithm )
610 return algorithm->shortDescription();
611 else
612 return QVariant();
613 }
614
615 default:
616 return QVariant();
617 }
618 break;
619
620 default:
621 return QVariant();
622 }
623#ifndef _MSC_VER // avoid warning
624 return QVariant();
625#endif
626}
627
628int QgsProcessingToolboxModel::rowCount( const QModelIndex &parent ) const
629{
630 QgsProcessingToolboxModelNode *n = index2node( parent );
631 if ( !n )
632 return 0;
633
634 return n->children().count();
635}
636
637int QgsProcessingToolboxModel::columnCount( const QModelIndex & ) const
638{
639 return 1;
640}
641
642QModelIndex QgsProcessingToolboxModel::index( int row, int column, const QModelIndex &parent ) const
643{
644 if ( !hasIndex( row, column, parent ) )
645 return QModelIndex();
646
647 QgsProcessingToolboxModelNode *n = index2node( parent );
648 if ( !n )
649 return QModelIndex(); // have no children
650
651 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
652}
653
654QModelIndex QgsProcessingToolboxModel::parent( const QModelIndex &child ) const
655{
656 if ( !child.isValid() )
657 return QModelIndex();
658
659 if ( QgsProcessingToolboxModelNode *n = index2node( child ) )
660 {
661 return indexOfParentTreeNode( n->parent() ); // must not be null
662 }
663 else
664 {
665 Q_ASSERT( false ); // no other node types!
666 return QModelIndex();
667 }
668}
669
670QMimeData *QgsProcessingToolboxModel::mimeData( const QModelIndexList &indexes ) const
671{
672 if ( !indexes.isEmpty() && isAlgorithm( indexes.at( 0 ) ) )
673 {
674 QByteArray encodedData;
675 QDataStream stream( &encodedData, QIODevice::WriteOnly | QIODevice::Truncate );
676
677 std::unique_ptr< QMimeData > mimeData = std::make_unique< QMimeData >();
678 const QgsProcessingAlgorithm *algorithm = algorithmForIndex( indexes.at( 0 ) );
679 if ( algorithm )
680 {
681 stream << algorithm->id();
682 }
683 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ), encodedData );
684 return mimeData.release();
685 }
686 return nullptr;
687}
688
690{
691 QgsProcessingToolboxModelNode *n = index2node( index );
692 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
693 return nullptr;
694
695 return qobject_cast< QgsProcessingToolboxModelProviderNode * >( n )->provider();
696}
697
698QString QgsProcessingToolboxModel::providerIdForIndex( const QModelIndex &index ) const
699{
700 QgsProcessingToolboxModelNode *n = index2node( index );
701 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Provider )
702 return nullptr;
703
704 return qobject_cast< QgsProcessingToolboxModelProviderNode * >( n )->providerId();
705}
706
708{
709 QgsProcessingToolboxModelNode *n = index2node( index );
710 if ( !n || n->nodeType() != QgsProcessingToolboxModelNode::NodeType::Algorithm )
711 return nullptr;
712
713 return qobject_cast< QgsProcessingToolboxModelAlgorithmNode * >( n )->algorithm();
714}
715
716bool QgsProcessingToolboxModel::isAlgorithm( const QModelIndex &index ) const
717{
718 QgsProcessingToolboxModelNode *n = index2node( index );
719 return ( n && n->nodeType() == QgsProcessingToolboxModelNode::NodeType::Algorithm );
720}
721
722QModelIndex QgsProcessingToolboxModel::indexForProvider( const QString &providerId ) const
723{
724 std::function< QModelIndex( const QModelIndex &parent, const QString &providerId ) > findIndex = [&]( const QModelIndex & parent, const QString & providerId )->QModelIndex
725 {
726 for ( int row = 0; row < rowCount( parent ); ++row )
727 {
728 QModelIndex current = index( row, 0, parent );
729 const QString currentProviderId = providerIdForIndex( current );
730 if ( !currentProviderId.isEmpty() && currentProviderId == providerId )
731 return current;
732
733 QModelIndex checkChildren = findIndex( current, providerId );
734 if ( checkChildren.isValid() )
735 return checkChildren;
736 }
737 return QModelIndex();
738 };
739
740 return findIndex( QModelIndex(), providerId );
741}
742
743QModelIndex QgsProcessingToolboxModel::indexOfParentTreeNode( QgsProcessingToolboxModelNode *parentNode ) const
744{
745 Q_ASSERT( parentNode );
746
747 QgsProcessingToolboxModelNode *grandParentNode = parentNode->parent();
748 if ( !grandParentNode )
749 return QModelIndex(); // root node -> invalid index
750
751 int row = grandParentNode->children().indexOf( parentNode );
752 Q_ASSERT( row >= 0 );
753
754 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
755}
756
757//
758// QgsProcessingToolboxProxyModel
759//
760
762 QgsProcessingRecentAlgorithmLog *recentLog, QgsProcessingFavoriteAlgorithmManager *favoriteManager )
763 : QSortFilterProxyModel( parent )
764 , mModel( new QgsProcessingToolboxModel( this, registry, recentLog, favoriteManager ) )
765{
766 setSourceModel( mModel );
767 setDynamicSortFilter( true );
768 setSortLocaleAware( true );
769 setFilterCaseSensitivity( Qt::CaseInsensitive );
770 sort( 0 );
771
772 connect( mModel, &QgsProcessingToolboxModel::recentAlgorithmAdded, this, [ = ] { invalidateFilter(); } );
773 connect( mModel, &QgsProcessingToolboxModel::favoriteAlgorithmAdded, this, [ = ] { invalidateFilter(); } );
774}
775
780
785
787{
788 mFilters = filters;
789 invalidateFilter();
790}
791
793{
794 mInPlaceLayer = layer;
795 invalidateFilter();
796}
797
798
800{
801 mFilterString = filter;
802 invalidateFilter();
803}
804
805bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
806{
807 QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
808 if ( mModel->isAlgorithm( sourceIndex ) )
809 {
810 const bool hasKnownIssues = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast< int >( Qgis::ProcessingAlgorithmFlag::KnownIssues );
811 if ( hasKnownIssues && !( mFilters & Filter::ShowKnownIssues ) )
812 return false;
813
814 if ( !mFilterString.trimmed().isEmpty() )
815 {
816 const QString algId = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmId ) ).toString();
817 const QString algName = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmName ) ).toString();
818 const QStringList algTags = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmTags ) ).toStringList();
819 const QString shortDesc = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmShortDescription ) ).toString();
820
821 QStringList parentText;
822 QModelIndex parent = sourceIndex.parent();
823 while ( parent.isValid() )
824 {
825 const QStringList parentParts = sourceModel()->data( parent, Qt::DisplayRole ).toString().split( ' ' );
826 if ( !parentParts.empty() )
827 parentText.append( parentParts );
828 parent = parent.parent();
829 }
830
831 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
832
833 QStringList partsToSearch = sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
834 partsToSearch << algId << algName;
835 partsToSearch.append( algTags );
836 if ( !shortDesc.isEmpty() )
837 partsToSearch.append( shortDesc.split( ' ' ) );
838 partsToSearch.append( parentText );
839
840 for ( const QString &part : partsToMatch )
841 {
842 bool found = false;
843 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
844 {
845 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
846 {
847 found = true;
848 break;
849 }
850 }
851 if ( !found )
852 return false; // couldn't find a match for this word, so hide algorithm
853 }
854 }
855
856 if ( mFilters & Filter::InPlace )
857 {
858 const bool supportsInPlace = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast< int >( Qgis::ProcessingAlgorithmFlag::SupportsInPlaceEdits );
859 if ( !supportsInPlace )
860 return false;
861
862 const QgsProcessingAlgorithm *alg = mModel->algorithmForIndex( sourceIndex );
863 if ( !( mInPlaceLayer && alg && alg->supportInPlaceEdit( mInPlaceLayer ) ) )
864 {
865 return false;
866 }
867 }
868 if ( mFilters & Filter::Modeler )
869 {
870 bool isHiddenFromModeler = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast< int >( Qgis::ProcessingAlgorithmFlag::HideFromModeler );
871 return !isHiddenFromModeler;
872 }
873 if ( mFilters & Filter::Toolbox )
874 {
875 bool isHiddenFromToolbox = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::AlgorithmFlags ) ).toInt() & static_cast< int >( Qgis::ProcessingAlgorithmFlag::HideFromToolbox );
876 return !isHiddenFromToolbox;
877 }
878 return true;
879 }
880
881 bool hasChildren = false;
882 // groups/providers are shown only if they have visible children
883 int count = sourceModel()->rowCount( sourceIndex );
884 for ( int i = 0; i < count; ++i )
885 {
886 if ( filterAcceptsRow( i, sourceIndex ) )
887 {
888 hasChildren = true;
889 break;
890 }
891 }
892
893 return hasChildren;
894}
895
896bool QgsProcessingToolboxProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
897{
898 QgsProcessingToolboxModelNode::NodeType leftType = static_cast< QgsProcessingToolboxModelNode::NodeType >( sourceModel()->data( left, static_cast< int >( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() );
899 QgsProcessingToolboxModelNode::NodeType rightType = static_cast< QgsProcessingToolboxModelNode::NodeType >( sourceModel()->data( right, static_cast< int >( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() );
900
901 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Recent )
902 return true;
903 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Recent )
904 return false;
905 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Favorite )
906 return true;
907 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Favorite )
908 return false;
909 else if ( leftType != rightType )
910 {
911 if ( leftType == QgsProcessingToolboxModelNode::NodeType::Provider )
912 return false;
913 else if ( rightType == QgsProcessingToolboxModelNode::NodeType::Provider )
914 return true;
915 else if ( leftType == QgsProcessingToolboxModelNode::NodeType::Group )
916 return false;
917 else
918 return true;
919 }
920
921 // if node represents a recent algorithm, it's not sorted at all
922 bool isRecentNode = false;
923 QModelIndex parent = left.parent();
924 while ( parent.isValid() )
925 {
926 if ( mModel->data( parent, static_cast< int >( QgsProcessingToolboxModel::CustomRole::NodeType ) ).toInt() == static_cast< int >( QgsProcessingToolboxModelNode::NodeType::Recent ) )
927 {
928 isRecentNode = true;
929 break;
930 }
931 parent = parent.parent();
932 }
933 if ( isRecentNode )
934 {
935 return left.row() < right.row();
936 }
937
938 // default mode is alphabetical order
939 QString leftStr = sourceModel()->data( left ).toString();
940 QString rightStr = sourceModel()->data( right ).toString();
941 return QString::localeAwareCompare( leftStr, rightStr ) < 0;
942}
943
944QVariant QgsProcessingToolboxProxyModel::data( const QModelIndex &index, int role ) const
945{
946 if ( role == Qt::ForegroundRole && !mFilterString.isEmpty() )
947 {
948 QModelIndex sourceIndex = mapToSource( index );
949 const QVariant flags = sourceModel()->data( sourceIndex, static_cast< int >( QgsProcessingToolboxModel::CustomRole::ProviderFlags ) );
950 if ( flags.isValid() && flags.toInt() & static_cast< int >( Qgis::ProcessingProviderFlag::DeemphasiseSearchResults ) )
951 {
952 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
953 QColor fadedTextColor = brush.color();
954 fadedTextColor.setAlpha( 100 );
955 brush.setColor( fadedTextColor );
956 return brush;
957 }
958 }
959 return QSortFilterProxyModel::data( index, role );
960}
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