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