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