[FEATURE] add new locator filter searching across all layers in display expression

This commit is contained in:
Denis Rouzaud 2018-08-30 17:33:17 +03:00
parent d6b2537e1e
commit d54b7b1d4d
4 changed files with 141 additions and 0 deletions

View File

@ -191,6 +191,10 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
}
}
//
// QgsActiveLayerFeaturesLocatorFilter
//
QgsActiveLayerFeaturesLocatorFilter::QgsActiveLayerFeaturesLocatorFilter( QObject *parent )
: QgsLocatorFilter( parent )
{
@ -298,6 +302,106 @@ void QgsActiveLayerFeaturesLocatorFilter::triggerResult( const QgsLocatorResult
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
}
//
// QgsAllLayersFeaturesLocatorFilter
//
QgsAllLayersFeaturesLocatorFilter::QgsAllLayersFeaturesLocatorFilter( QObject *parent )
: QgsLocatorFilter( parent )
{
setUseWithoutPrefix( false );
}
QgsAllLayersFeaturesLocatorFilter *QgsAllLayersFeaturesLocatorFilter::clone() const
{
return new QgsAllLayersFeaturesLocatorFilter();
}
void QgsAllLayersFeaturesLocatorFilter::prepare( const QString &string, const QgsLocatorContext & )
{
if ( string.length() < 3 )
return;
const QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
{
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( it.value() );
if ( !layer )
continue;
QgsExpression expression( layer->displayExpression() );
QgsExpressionContext context;
context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
expression.prepare( &context );
QgsFeatureRequest req;
req.setFlags( QgsFeatureRequest::NoGeometry );
req.setFilterExpression( QStringLiteral( "%1 ILIKE '%%2%'" )
.arg( layer->displayExpression() )
.arg( string ) );
req.setLimit( 30 );
PreparedLayer preparedLayer;
preparedLayer.expression = expression;
preparedLayer.context = context;
preparedLayer.layerId = layer->id();
preparedLayer.layerName = layer->name();
preparedLayer.iterator = layer->getFeatures( req );
preparedLayer.layerIcon = QgsMapLayerModel::iconForLayer( layer );
mPreparedLayers.append( preparedLayer );
}
}
void QgsAllLayersFeaturesLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
{
int foundInCurrentLayer;
int foundInTotal = 0;
QgsFeature f;
// we cannot used const loop since iterator::nextFeature is not const
for ( PreparedLayer preparedLayer : mPreparedLayers )
{
foundInCurrentLayer = 0;
while ( preparedLayer.iterator.nextFeature( f ) )
{
if ( feedback->isCanceled() )
return;
QgsLocatorResult result;
result.group = preparedLayer.layerName;
preparedLayer.context.setFeature( f );
result.displayString = preparedLayer.expression.evaluate( &( preparedLayer.context ) ).toString();
result.userData = QVariantList() << f.id() << preparedLayer.layerId;
result.icon = preparedLayer.layerIcon;
result.score = static_cast< double >( string.length() ) / result.displayString.size();
emit resultFetched( result );
foundInCurrentLayer++;
foundInTotal++;
if ( foundInCurrentLayer >= mMaxResultsPerLayer )
break;
}
if ( foundInTotal >= mMaxTotalResults )
break;
}
}
void QgsAllLayersFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QVariantList dataList = result.userData.toList();
QgsFeatureId id = dataList.at( 0 ).toLongLong();
QString layerId = dataList.at( 1 ).toString();
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
if ( !layer )
return;
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
}
//
// QgsExpressionCalculatorLocatorFilter
//

View File

@ -113,6 +113,41 @@ class QgsActiveLayerFeaturesLocatorFilter : public QgsLocatorFilter
QIcon mLayerIcon;
};
class QgsAllLayersFeaturesLocatorFilter : public QgsLocatorFilter
{
Q_OBJECT
public:
struct PreparedLayer
{
public:
QgsExpression expression;
QgsExpressionContext context;
QgsFeatureIterator iterator;
QString layerName;
QString layerId;
QIcon layerIcon;
} ;
QgsAllLayersFeaturesLocatorFilter( QObject *parent = nullptr );
QgsAllLayersFeaturesLocatorFilter *clone() const override;
QString name() const override { return QStringLiteral( "allfeatures" ); }
QString displayName() const override { return tr( "Features In All Layers" ); }
Priority priority() const override { return Medium; }
QString prefix() const override { return QStringLiteral( "a" ); }
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:
int mMaxResultsPerLayer = 6;
int mMaxTotalResults = 12;
QList<PreparedLayer> mPreparedLayers;
};
class APP_EXPORT QgsExpressionCalculatorLocatorFilter : public QgsLocatorFilter
{
Q_OBJECT

View File

@ -3121,6 +3121,7 @@ void QgisApp::createStatusBar()
mLocatorWidget->locator()->registerFilter( new QgsActionLocatorFilter( actionObjects ) );
mLocatorWidget->locator()->registerFilter( new QgsActiveLayerFeaturesLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsAllLayersFeaturesLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsExpressionCalculatorLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsBookmarkLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsSettingsLocatorFilter() );

View File

@ -25,6 +25,7 @@ const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiter
<< QStringLiteral( "layertree" )
<< QStringLiteral( "layouts" )
<< QStringLiteral( "features" )
<< QStringLiteral( "allfeatures" )
<< QStringLiteral( "calculator" )
<< QStringLiteral( "bookmarks" )
<< QStringLiteral( "optionpages" );