Skip to content
Permalink
Browse files
Allow certain locator filters to operate on the main thread
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.
@@ -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.
@@ -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;
@@ -223,6 +235,9 @@ during within their fetchResults() implementation.

};

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





@@ -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()
@@ -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():
@@ -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)
@@ -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 )
@@ -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();
@@ -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 )
{
@@ -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();
@@ -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
@@ -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 ) );
@@ -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;
}
}
@@ -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
@@ -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
@@ -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 );

@@ -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 );
@@ -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()
@@ -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() )
@@ -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.
*/
@@ -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.
@@ -253,6 +264,8 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
};

Q_DECLARE_METATYPE( QgsLocatorResult )
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLocatorFilter::Flags )


#endif // QGSLOCATORFILTER_H

@@ -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() )
@@ -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(); }

0 comments on commit 7609ab7

Please sign in to comment.