Skip to content

Commit 6275960

Browse files
committedJul 16, 2018
Start on filter string handling
1 parent dc737bf commit 6275960

File tree

5 files changed

+271
-9
lines changed

5 files changed

+271
-9
lines changed
 

‎python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ of this model.
223223
{
224224
RoleNodeType,
225225
RoleAlgorithmFlags,
226+
RoleAlgorithmId,
227+
RoleAlgorithmName,
228+
RoleAlgorithmShortDescription,
229+
RoleAlgorithmTags,
226230
};
227231

228232
QgsProcessingToolboxModel( QObject *parent /TransferThis/ = 0, QgsProcessingRegistry *registry = 0 );
@@ -359,6 +363,24 @@ Set ``filters`` that affect how toolbox content is filtered.
359363
Returns any filters that affect how toolbox content is filtered.
360364

361365
.. seealso:: :py:func:`setFilters`
366+
%End
367+
368+
void setFilterString( const QString &filter );
369+
%Docstring
370+
Sets a ``filter`` string, such that only algorithms matching the
371+
specified string will be shown.
372+
373+
Matches are performed using a variety of tests, including checking
374+
against the algorithm name, short description, tags, etc.
375+
376+
.. seealso:: :py:func:`filterString`
377+
%End
378+
379+
QString filterString() const;
380+
%Docstring
381+
Returns the current filter string, if set.
382+
383+
.. seealso:: :py:func:`setFilterString`
362384
%End
363385

364386
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;

‎python/gui/processing/qgsprocessingtoolboxmodel.sip.in

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ of this model.
223223
{
224224
RoleNodeType,
225225
RoleAlgorithmFlags,
226+
RoleAlgorithmId,
227+
RoleAlgorithmName,
228+
RoleAlgorithmTags,
226229
};
227230

228231
QgsProcessingToolboxModel( QObject *parent /TransferThis/ = 0, QgsProcessingRegistry *registry = 0 );
@@ -361,6 +364,16 @@ Returns any filters that affect how toolbox content is filtered.
361364
.. seealso:: :py:func:`setFilters`
362365
%End
363366

367+
void setFilterString(const QString &filter);
368+
%Docstring
369+
Sets a ``filter`` string, such that only algorithms matching the
370+
specified string will be shown.
371+
372+
.. seealso:: :py:func:`filterString`
373+
%End
374+
375+
QString filterString() const;
376+
364377
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;
365378

366379
virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;

‎src/gui/processing/qgsprocessingtoolboxmodel.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,70 @@ QVariant QgsProcessingToolboxModel::data( const QModelIndex &index, int role ) c
351351
}
352352
break;
353353

354+
case RoleAlgorithmId:
355+
switch ( index.column() )
356+
{
357+
case 0:
358+
{
359+
if ( algorithm )
360+
return algorithm->id();
361+
else
362+
return QVariant();
363+
}
364+
365+
default:
366+
return QVariant();
367+
}
368+
break;
369+
370+
case RoleAlgorithmName:
371+
switch ( index.column() )
372+
{
373+
case 0:
374+
{
375+
if ( algorithm )
376+
return algorithm->name();
377+
else
378+
return QVariant();
379+
}
380+
381+
default:
382+
return QVariant();
383+
}
384+
break;
385+
386+
case RoleAlgorithmTags:
387+
switch ( index.column() )
388+
{
389+
case 0:
390+
{
391+
if ( algorithm )
392+
return algorithm->tags();
393+
else
394+
return QVariant();
395+
}
396+
397+
default:
398+
return QVariant();
399+
}
400+
break;
401+
402+
case RoleAlgorithmShortDescription:
403+
switch ( index.column() )
404+
{
405+
case 0:
406+
{
407+
if ( algorithm )
408+
return algorithm->shortDescription();
409+
else
410+
return QVariant();
411+
}
412+
413+
default:
414+
return QVariant();
415+
}
416+
break;
417+
354418
default:
355419
return QVariant();
356420
}
@@ -499,6 +563,48 @@ bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QMod
499563
QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
500564
if ( mModel->isAlgorithm( sourceIndex ) )
501565
{
566+
if ( !mFilterString.trimmed().isEmpty() )
567+
{
568+
const QString algId = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmId ).toString();
569+
const QString algName = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmName ).toString();
570+
const QStringList algTags = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmTags ).toStringList();
571+
const QString shortDesc = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmShortDescription ).toString();
572+
573+
QStringList parentText;
574+
QModelIndex parent = sourceIndex.parent();
575+
while ( parent.isValid() )
576+
{
577+
const QStringList parentParts = sourceModel()->data( parent, Qt::DisplayRole ).toString().split( ' ' );
578+
if ( !parentParts.empty() )
579+
parentText.append( parentParts );
580+
parent = parent.parent();
581+
}
582+
583+
const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
584+
585+
QStringList partsToSearch = sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString().split( ' ' );
586+
partsToSearch << algId << algName;
587+
partsToSearch.append( algTags );
588+
if ( !shortDesc.isEmpty() )
589+
partsToSearch.append( shortDesc.split( ' ' ) );
590+
partsToSearch.append( parentText );
591+
592+
for ( const QString &part : partsToMatch )
593+
{
594+
bool found = false;
595+
for ( const QString &partToSearch : qgis::as_const( partsToSearch ) )
596+
{
597+
if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
598+
{
599+
found = true;
600+
break;
601+
}
602+
}
603+
if ( !found )
604+
return false; // couldn't find a match for this word, so hide algorithm
605+
}
606+
}
607+
502608
if ( mFilters & FilterModeler )
503609
{
504610
bool isHiddenFromModeler = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmFlags ).toInt() & QgsProcessingAlgorithm::FlagHideFromModeler;

‎src/gui/processing/qgsprocessingtoolboxmodel.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ class GUI_EXPORT QgsProcessingToolboxModel : public QAbstractItemModel
250250
{
251251
RoleNodeType = Qt::UserRole, //!< Corresponds to the node's type
252252
RoleAlgorithmFlags, //!< Returns the node's algorithm flags, for algorithm nodes
253+
RoleAlgorithmId, //!< Algorithm ID, for algorithm nodes
254+
RoleAlgorithmName, //!< Untranslated algorithm name, for algorithm nodes
255+
RoleAlgorithmShortDescription, //!< Short algorithm description, for algorithm nodes
256+
RoleAlgorithmTags, //!< List of algorithm tags, for algorithm nodes
253257
};
254258

255259
/**
@@ -398,9 +402,24 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
398402
*/
399403
Filters filters() const { return mFilters; }
400404

401-
405+
/**
406+
* Sets a \a filter string, such that only algorithms matching the
407+
* specified string will be shown.
408+
*
409+
* Matches are performed using a variety of tests, including checking
410+
* against the algorithm name, short description, tags, etc.
411+
*
412+
* \see filterString()
413+
*/
402414
void setFilterString( const QString &filter );
403415

416+
/**
417+
* Returns the current filter string, if set.
418+
*
419+
* \see setFilterString()
420+
*/
421+
QString filterString() const { return mFilterString; }
422+
404423
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
405424
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;
406425

‎tests/src/gui/testqgsprocessingmodel.cpp

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,36 @@ class DummyAlgorithm : public QgsProcessingAlgorithm
2424
public:
2525

2626
DummyAlgorithm( const QString &name, const QString &group,
27-
QgsProcessingAlgorithm::Flags flags = 0 )
27+
QgsProcessingAlgorithm::Flags flags = 0,
28+
const QString &tags = QString(),
29+
const QString &shortDescription = QString(),
30+
const QString &displayName = QString() )
2831
: mName( name )
32+
, mDisplayName( displayName )
2933
, mGroup( group )
3034
, mFlags( flags )
35+
, mTags( tags.split( ',' ) )
36+
, mShortDescription( shortDescription )
3137
{}
3238

3339
void initAlgorithm( const QVariantMap & = QVariantMap() ) override {}
3440
QgsProcessingAlgorithm::Flags flags() const override { return mFlags; }
3541
QString name() const override { return mName; }
36-
QString displayName() const override { return mName; }
42+
QString displayName() const override { return mDisplayName.isEmpty() ? mName : mDisplayName; }
3743
QString group() const override { return mGroup; }
3844
QString groupId() const override { return mGroup; }
45+
QString shortDescription() const override { return mShortDescription; }
46+
QStringList tags() const override { return mTags; }
3947
QVariantMap processAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * ) override { return QVariantMap(); }
4048

41-
DummyAlgorithm *createInstance() const override { return new DummyAlgorithm( mName, mGroup, mFlags ); }
49+
DummyAlgorithm *createInstance() const override { return new DummyAlgorithm( mName, mGroup, mFlags, mTags.join( ',' ), mShortDescription, mDisplayName ); }
4250

4351
QString mName;
52+
QString mDisplayName;
4453
QString mGroup;
45-
QgsProcessingAlgorithm::Flags mFlags = 0;
54+
QgsProcessingAlgorithm::Flags mFlags = nullptr;
55+
QStringList mTags;
56+
QString mShortDescription;
4657

4758
};
4859
//dummy provider for testing
@@ -165,7 +176,7 @@ void TestQgsProcessingModel::testModel()
165176
QVERIFY( !model.data( model.index( 2, 0, QModelIndex() ), Qt::ToolTipRole ).isValid() );
166177

167178
// provider with algs and groups
168-
DummyAlgorithm *a1 = new DummyAlgorithm( "a1", "group1", QgsProcessingAlgorithm::FlagHideFromModeler );
179+
DummyAlgorithm *a1 = new DummyAlgorithm( "a1", "group1", QgsProcessingAlgorithm::FlagHideFromModeler, QStringLiteral( "tag1,tag2" ), QStringLiteral( "short desc a" ) );
169180
DummyAlgorithm *a2 = new DummyAlgorithm( "a2", "group2", QgsProcessingAlgorithm::FlagHideFromToolbox );
170181
DummyProvider *p3 = new DummyProvider( "p3", "provider3", QList< QgsProcessingAlgorithm * >() << a1 << a2 );
171182
registry.addProvider( p3 );
@@ -198,8 +209,13 @@ void TestQgsProcessingModel::testModel()
198209
QModelIndex alg1Index = model.index( 0, 0, group1Index );
199210
QVERIFY( !model.providerForIndex( alg1Index ) );
200211
QCOMPARE( model.data( alg1Index, Qt::DisplayRole ).toString(), QStringLiteral( "a1" ) );
201-
QCOMPARE( model.data( alg1Index, Qt::ToolTipRole ).toString(), QStringLiteral( "<p><b>a1</b></p><p>Algorithm ID: \u2018<i>p3:a1</i>\u2019</p>" ) );
212+
QCOMPARE( model.data( alg1Index, Qt::ToolTipRole ).toString(), QStringLiteral( "<p><b>a1</b></p><p>short desc a</p><p>Algorithm ID: \u2018<i>p3:a1</i>\u2019</p>" ) );
202213
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmFlags ).toInt(), static_cast< int >( QgsProcessingAlgorithm::FlagHideFromModeler ) );
214+
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p3:a1" ) );
215+
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmName ).toString(), QStringLiteral( "a1" ) );
216+
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmTags ).toStringList().join( ',' ), QStringLiteral( "tag1,tag2" ) );
217+
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmShortDescription ).toString(), QStringLiteral( "short desc a" ) );
218+
203219
QCOMPARE( model.algorithmForIndex( alg1Index )->id(), QStringLiteral( "p3:a1" ) );
204220

205221
QModelIndex group2Index = model.index( 1, 0, providerIndex );
@@ -209,6 +225,10 @@ void TestQgsProcessingModel::testModel()
209225
QCOMPARE( model.data( alg2Index, Qt::DisplayRole ).toString(), QStringLiteral( "a2" ) );
210226
QCOMPARE( model.data( alg2Index, Qt::ToolTipRole ).toString(), QStringLiteral( "<p><b>a2</b></p><p>Algorithm ID: \u2018<i>p3:a2</i>\u2019</p>" ) );
211227
QCOMPARE( model.data( alg2Index, QgsProcessingToolboxModel::RoleAlgorithmFlags ).toInt(), static_cast< int >( QgsProcessingAlgorithm::FlagHideFromToolbox ) );
228+
QCOMPARE( model.data( alg2Index, QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p3:a2" ) );
229+
QCOMPARE( model.data( alg2Index, QgsProcessingToolboxModel::RoleAlgorithmName ).toString(), QStringLiteral( "a2" ) );
230+
QCOMPARE( model.data( alg2Index, QgsProcessingToolboxModel::RoleAlgorithmTags ).toStringList().join( ',' ), QString() );
231+
QCOMPARE( model.data( alg2Index, QgsProcessingToolboxModel::RoleAlgorithmShortDescription ).toString(), QString() );
212232
QCOMPARE( model.algorithmForIndex( alg2Index )->id(), QStringLiteral( "p3:a2" ) );
213233

214234
// combined groups
@@ -328,7 +348,8 @@ void TestQgsProcessingModel::testProxyModel()
328348
DummyProvider *p1 = new DummyProvider( "p2", "provider2", QList< QgsProcessingAlgorithm * >() << a1 );
329349
registry.addProvider( p1 );
330350
// second provider
331-
DummyAlgorithm *a2 = new DummyAlgorithm( "a2", "group2", QgsProcessingAlgorithm::FlagHideFromModeler );
351+
DummyAlgorithm *a2 = new DummyAlgorithm( "a2", "group2", QgsProcessingAlgorithm::FlagHideFromModeler,
352+
QStringLiteral( "buffer,vector" ), QStringLiteral( "short desc" ), QStringLiteral( "algorithm2" ) );
332353
DummyProvider *p2 = new DummyProvider( "p1", "provider1", QList< QgsProcessingAlgorithm * >() << a2 );
333354
registry.addProvider( p2 );
334355

@@ -368,12 +389,93 @@ void TestQgsProcessingModel::testProxyModel()
368389
QCOMPARE( model.rowCount( group2Index ), 1 );
369390
QCOMPARE( model.data( model.index( 0, 0, group1Index ), Qt::DisplayRole ).toString(), QStringLiteral( "a1" ) );
370391

392+
// test filter strings
393+
model.setFilters( nullptr );
394+
// filter by algorithm id
395+
model.setFilterString( "a1" );
396+
QCOMPARE( model.rowCount(), 2 );
397+
group1Index = model.index( 0, 0, QModelIndex() );
398+
QModelIndex provider2Index = model.index( 1, 0, QModelIndex() );
399+
QCOMPARE( model.rowCount( group1Index ), 1 );
400+
QCOMPARE( model.data( group1Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
401+
QCOMPARE( model.data( model.index( 0, 0, group1Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "qgis:a1" ) );
402+
QCOMPARE( model.rowCount( group2Index ), 1 );
403+
QCOMPARE( model.data( provider2Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider2" ) );
404+
QCOMPARE( model.rowCount( provider2Index ), 1 );
405+
group2Index = model.index( 0, 0, provider2Index );
406+
QCOMPARE( model.rowCount( group2Index ), 1 );
407+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
408+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) );
409+
410+
// filter by algorithm display name
411+
model.setFilterString( QStringLiteral( "ALGOR" ) );
412+
QCOMPARE( model.rowCount(), 1 );
413+
QModelIndex provider1Index = model.index( 0, 0, QModelIndex() );
414+
QCOMPARE( model.rowCount( provider1Index ), 1 );
415+
QCOMPARE( model.data( provider1Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider1" ) );
416+
QCOMPARE( model.rowCount( provider1Index ), 1 );
417+
group2Index = model.index( 0, 0, provider1Index );
418+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
419+
QCOMPARE( model.rowCount( group2Index ), 1 );
420+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) );
421+
422+
// filter by algorithm tags
423+
model.setFilterString( QStringLiteral( "buff CTOR" ) );
424+
QCOMPARE( model.rowCount(), 1 );
425+
provider1Index = model.index( 0, 0, QModelIndex() );
426+
QCOMPARE( model.rowCount( provider1Index ), 1 );
427+
QCOMPARE( model.data( provider1Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider1" ) );
428+
QCOMPARE( model.rowCount( provider1Index ), 1 );
429+
group2Index = model.index( 0, 0, provider1Index );
430+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
431+
QCOMPARE( model.rowCount( group2Index ), 1 );
432+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) );
433+
434+
// filter by algorithm short desc
435+
model.setFilterString( QStringLiteral( "buff CTOR desc" ) );
436+
QCOMPARE( model.rowCount(), 1 );
437+
provider1Index = model.index( 0, 0, QModelIndex() );
438+
QCOMPARE( model.rowCount( provider1Index ), 1 );
439+
QCOMPARE( model.data( provider1Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider1" ) );
440+
QCOMPARE( model.rowCount( provider1Index ), 1 );
441+
group2Index = model.index( 0, 0, provider1Index );
442+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
443+
QCOMPARE( model.rowCount( group2Index ), 1 );
444+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) );
445+
446+
// filter by group
447+
model.setFilterString( QStringLiteral( "group2" ) );
448+
QCOMPARE( model.rowCount(), 3 );
449+
group2Index = model.index( 0, 0, QModelIndex() );
450+
QCOMPARE( model.rowCount( group2Index ), 1 );
451+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
452+
alg1Index = model.index( 0, 0, group2Index );
453+
QCOMPARE( model.data( alg1Index, QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "qgis:a1" ) );
454+
provider1Index = model.index( 1, 0, QModelIndex() );
455+
QCOMPARE( model.rowCount( provider1Index ), 1 );
456+
QCOMPARE( model.data( provider1Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider1" ) );
457+
QCOMPARE( model.rowCount( provider1Index ), 1 );
458+
group2Index = model.index( 0, 0, provider1Index );
459+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
460+
QCOMPARE( model.rowCount( group2Index ), 1 );
461+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) );
462+
provider2Index = model.index( 2, 0, QModelIndex() );
463+
QCOMPARE( model.rowCount( provider2Index ), 1 );
464+
QCOMPARE( model.data( provider2Index, Qt::DisplayRole ).toString(), QStringLiteral( "provider2" ) );
465+
group2Index = model.index( 0, 0, provider2Index );
466+
QCOMPARE( model.data( group2Index, Qt::DisplayRole ).toString(), QStringLiteral( "group2" ) );
467+
QCOMPARE( model.rowCount( group2Index ), 1 );
468+
QCOMPARE( model.data( model.index( 0, 0, group2Index ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) );
469+
470+
model.setFilterString( QString() );
471+
371472
// inactive provider - should not be visible
473+
QCOMPARE( model.rowCount(), 4 );
372474
DummyAlgorithm *qgisA31 = new DummyAlgorithm( "a3", "group1" );
373475
DummyProvider *p3 = new DummyProvider( "p3", "provider3", QList< QgsProcessingAlgorithm * >() << qgisA31 );
374476
p3->mActive = false;
375477
registry.addProvider( p3 );
376-
QCOMPARE( model.rowCount(), 3 );
478+
QCOMPARE( model.rowCount(), 4 );
377479
}
378480

379481
QGSTEST_MAIN( TestQgsProcessingModel )

0 commit comments

Comments
 (0)
Please sign in to comment.