Skip to content

Commit

Permalink
Allow certain locator filters to operate on the main thread
Browse files Browse the repository at this point in the history
Some filters are fast enough to return results that it's overkill
to run them in a background thread - add a flag to these filters
to allow them to run (blocking) in the main thread instead.
  • Loading branch information
nyalldawson committed Feb 12, 2018
1 parent d5e6492 commit 7609ab7
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 59 deletions.
15 changes: 15 additions & 0 deletions python/core/locator/qgslocatorfilter.sip.in
Expand Up @@ -69,6 +69,13 @@ Abstract base class for filters which collect locator results.
Lowest
};

enum Flag
{
FlagFast,
};
typedef QFlags<QgsLocatorFilter::Flag> Flags;


QgsLocatorFilter( QObject *parent = 0 );
%Docstring
Constructor for QgsLocatorFilter.
Expand All @@ -92,6 +99,11 @@ Returns the unique name for the filter. This should be an untranslated string id
Returns a translated, user-friendly name for the filter.

.. seealso:: :py:func:`name`
%End

virtual QgsLocatorFilter::Flags flags() const;
%Docstring
Returns flags which specify the filter's behavior.
%End

virtual Priority priority() const;
Expand Down Expand Up @@ -223,6 +235,9 @@ during within their fetchResults() implementation.

};

QFlags<QgsLocatorFilter::Flag> operator|(QgsLocatorFilter::Flag f1, QFlags<QgsLocatorFilter::Flag> f2);





Expand Down
12 changes: 5 additions & 7 deletions python/plugins/processing/gui/AlgorithmLocatorFilter.py
Expand Up @@ -39,7 +39,6 @@ class AlgorithmLocatorFilter(QgsLocatorFilter):

def __init__(self, parent=None):
super(AlgorithmLocatorFilter, self).__init__(parent)
self.found_results = []

def clone(self):
return AlgorithmLocatorFilter()
Expand All @@ -56,7 +55,10 @@ def priority(self):
def prefix(self):
return 'a'

def prepare(self, string, context):
def flags(self):
return QgsLocatorFilter.FlagFast

def fetchResults(self, string, context, feedback):
# collect results in main thread, since this method is inexpensive and
# accessing the processing registry is not thread safe
for a in QgsApplication.processingRegistry().algorithms():
Expand All @@ -73,11 +75,7 @@ def prepare(self, string, context):
result.score = float(len(string)) / len(a.displayName())
else:
result.score = 0
self.found_results.append(result)

def fetchResults(self, string, context, feedback):
for result in self.found_results:
self.resultFetched.emit(result)
self.resultFetched.emit(result)

def triggerResult(self, result):
alg = QgsApplication.processingRegistry().createAlgorithmById(result.userData)
Expand Down
41 changes: 6 additions & 35 deletions src/app/locator/qgsinbuiltlocatorfilters.cpp
Expand Up @@ -36,10 +36,8 @@ QgsLayerTreeLocatorFilter *QgsLayerTreeLocatorFilter::clone() const
return new QgsLayerTreeLocatorFilter();
}

void QgsLayerTreeLocatorFilter::prepare( const QString &string, const QgsLocatorContext & )
void QgsLayerTreeLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback * )
{
// collect results in main thread, since this method is inexpensive and
// accessing the layer tree root is not thread safe
QgsLayerTree *tree = QgsProject::instance()->layerTreeRoot();
const QList<QgsLayerTreeLayer *> layers = tree->findLayers();
for ( QgsLayerTreeLayer *layer : layers )
Expand All @@ -51,19 +49,11 @@ void QgsLayerTreeLocatorFilter::prepare( const QString &string, const QgsLocator
result.userData = layer->layerId();
result.icon = QgsMapLayerModel::iconForLayer( layer->layer() );
result.score = static_cast< double >( string.length() ) / layer->layer()->name().length();
mResults.append( result );
emit resultFetched( result );
}
}
}

void QgsLayerTreeLocatorFilter::fetchResults( const QString &, const QgsLocatorContext &, QgsFeedback * )
{
for ( const QgsLocatorResult &result : qgis::as_const( mResults ) )
{
emit resultFetched( result );
}
}

void QgsLayerTreeLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QString layerId = result.userData.toString();
Expand All @@ -84,11 +74,8 @@ QgsLayoutLocatorFilter *QgsLayoutLocatorFilter::clone() const
return new QgsLayoutLocatorFilter();
}

void QgsLayoutLocatorFilter::prepare( const QString &string, const QgsLocatorContext & )
void QgsLayoutLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback * )
{
// collect results in main thread, since this method is inexpensive and
// accessing the project layouts is not thread safe

const QList< QgsMasterLayoutInterface * > layouts = QgsProject::instance()->layoutManager()->layouts();
for ( QgsMasterLayoutInterface *layout : layouts )
{
Expand All @@ -99,19 +86,11 @@ void QgsLayoutLocatorFilter::prepare( const QString &string, const QgsLocatorCon
result.userData = layout->name();
//result.icon = QgsMapLayerModel::iconForLayer( layer->layer() );
result.score = static_cast< double >( string.length() ) / layout->name().length();
mResults.append( result );
emit resultFetched( result );
}
}
}

void QgsLayoutLocatorFilter::fetchResults( const QString &, const QgsLocatorContext &, QgsFeedback * )
{
for ( const QgsLocatorResult &result : qgis::as_const( mResults ) )
{
emit resultFetched( result );
}
}

void QgsLayoutLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QString layoutName = result.userData.toString();
Expand All @@ -137,7 +116,7 @@ QgsActionLocatorFilter *QgsActionLocatorFilter::clone() const
return new QgsActionLocatorFilter( mActionParents );
}

void QgsActionLocatorFilter::prepare( const QString &string, const QgsLocatorContext & )
void QgsActionLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback * )
{
// collect results in main thread, since this method is inexpensive and
// accessing the gui actions is not thread safe
Expand All @@ -150,14 +129,6 @@ void QgsActionLocatorFilter::prepare( const QString &string, const QgsLocatorCon
}
}

void QgsActionLocatorFilter::fetchResults( const QString &, const QgsLocatorContext &, QgsFeedback * )
{
for ( const QgsLocatorResult &result : qgis::as_const( mResults ) )
{
emit resultFetched( result );
}
}

void QgsActionLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QAction *action = qobject_cast< QAction * >( qvariant_cast<QObject *>( result.userData ) );
Expand Down Expand Up @@ -194,7 +165,7 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
result.userData = QVariant::fromValue( action );
result.icon = action->icon();
result.score = static_cast< double >( string.length() ) / searchText.length();
mResults.append( result );
emit resultFetched( result );
found << action;
}
}
Expand Down
14 changes: 3 additions & 11 deletions src/app/locator/qgsinbuiltlocatorfilters.h
Expand Up @@ -36,15 +36,11 @@ class QgsLayerTreeLocatorFilter : public QgsLocatorFilter
QString displayName() const override { return tr( "Project Layers" ); }
Priority priority() const override { return Highest; }
QString prefix() const override { return QStringLiteral( "l" ); }
QgsLocatorFilter::Flags flags() const override { return QgsLocatorFilter::FlagFast; }

void prepare( const QString &string, const QgsLocatorContext &context ) override;
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;

private:

QVector< QgsLocatorResult > mResults;

};

class QgsLayoutLocatorFilter : public QgsLocatorFilter
Expand All @@ -59,14 +55,11 @@ class QgsLayoutLocatorFilter : public QgsLocatorFilter
QString displayName() const override { return tr( "Project Layouts" ); }
Priority priority() const override { return Highest; }
QString prefix() const override { return QStringLiteral( "pl" ); }
QgsLocatorFilter::Flags flags() const override { return QgsLocatorFilter::FlagFast; }

void prepare( const QString &string, const QgsLocatorContext &context ) override;
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;

private:

QVector< QgsLocatorResult > mResults;
};

class QgsActionLocatorFilter : public QgsLocatorFilter
Expand All @@ -81,14 +74,13 @@ class QgsActionLocatorFilter : public QgsLocatorFilter
QString displayName() const override { return tr( "Actions" ); }
Priority priority() const override { return Lowest; }
QString prefix() const override { return QStringLiteral( "." ); }
QgsLocatorFilter::Flags flags() const override { return QgsLocatorFilter::FlagFast; }

void prepare( const QString &string, const QgsLocatorContext &context ) override;
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;
private:

QList< QWidget * > mActionParents;
QVector< QgsLocatorResult > mResults;

void searchActions( const QString &string, QWidget *parent, QList< QAction *> &found );

Expand Down
25 changes: 19 additions & 6 deletions src/core/locator/qgslocator.cpp
Expand Up @@ -121,21 +121,31 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
}
}

QList< QgsLocatorFilter *> clonedFilters;
QList< QgsLocatorFilter *> threadedFilters;
for ( QgsLocatorFilter *filter : qgis::as_const( activeFilters ) )
{
QgsLocatorFilter *clone = filter->clone();
connect( clone, &QgsLocatorFilter::resultFetched, clone, [this, filter]( QgsLocatorResult result )
std::unique_ptr< QgsLocatorFilter > clone( filter->clone() );
connect( clone.get(), &QgsLocatorFilter::resultFetched, clone.get(), [this, filter]( QgsLocatorResult result )
{
result.filter = filter;
emit filterSentResult( result );
}, Qt::QueuedConnection );
} );
clone->prepare( searchString, context );
clonedFilters.append( clone );

if ( clone->flags() & QgsLocatorFilter::FlagFast )
{
// filter is fast enough to fetch results on the main thread
clone->fetchResults( searchString, context, feedback );
}
else
{
// run filter in background
threadedFilters.append( clone.release() );
}
}

mActiveThreads.clear();
for ( QgsLocatorFilter *filter : qgis::as_const( clonedFilters ) )
for ( QgsLocatorFilter *filter : qgis::as_const( threadedFilters ) )
{
QThread *thread = new QThread();
mActiveThreads.append( thread );
Expand All @@ -158,6 +168,9 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
connect( thread, &QThread::finished, thread, &QThread::deleteLater );
thread->start();
}

if ( mActiveThreads.empty() )
emit finished();
}

void QgsLocator::cancel()
Expand Down
5 changes: 5 additions & 0 deletions src/core/locator/qgslocatorfilter.cpp
Expand Up @@ -27,6 +27,11 @@ QgsLocatorFilter::QgsLocatorFilter( QObject *parent )

}

QgsLocatorFilter::Flags QgsLocatorFilter::flags() const
{
return nullptr;
}

void QgsLocatorFilter::executeSearchAndDelete( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback )
{
if ( !feedback->isCanceled() )
Expand Down
13 changes: 13 additions & 0 deletions src/core/locator/qgslocatorfilter.h
Expand Up @@ -106,6 +106,12 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
Lowest //!< Lowest priority
};

enum Flag
{
FlagFast = 1 << 1, //!< Filter finds results quickly and can be safely run in the main thread
};
Q_DECLARE_FLAGS( Flags, Flag )

/**
* Constructor for QgsLocatorFilter.
*/
Expand All @@ -129,6 +135,11 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
*/
virtual QString displayName() const = 0;

/**
* Returns flags which specify the filter's behavior.
*/
virtual QgsLocatorFilter::Flags flags() const;

/**
* Returns the priority for the filter, which controls how results are
* ordered in the locator.
Expand Down Expand Up @@ -253,6 +264,8 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
};

Q_DECLARE_METATYPE( QgsLocatorResult )
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLocatorFilter::Flags )


#endif // QGSLOCATORFILTER_H

Expand Down
5 changes: 5 additions & 0 deletions src/gui/locator/qgslocatorwidget.cpp
Expand Up @@ -409,6 +409,11 @@ QgsLocatorFilterFilter *QgsLocatorFilterFilter::clone() const
return new QgsLocatorFilterFilter( mLocator );
}

QgsLocatorFilter::Flags QgsLocatorFilterFilter::flags() const
{
return QgsLocatorFilter::FlagFast;
}

void QgsLocatorFilterFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
{
if ( !string.isEmpty() )
Expand Down
1 change: 1 addition & 0 deletions src/gui/locator/qgslocatorwidget.h
Expand Up @@ -135,6 +135,7 @@ class QgsLocatorFilterFilter : public QgsLocatorFilter
QgsLocatorFilterFilter( QgsLocatorWidget *widget, QObject *parent = nullptr );

QgsLocatorFilterFilter *clone() const override SIP_FACTORY;
QgsLocatorFilter::Flags flags() const override;

QString name() const override { return QStringLiteral( "filters" );}
QString displayName() const override { return QString(); }
Expand Down

0 comments on commit 7609ab7

Please sign in to comment.