Skip to content

Commit a6358b1

Browse files
authored
Merge pull request #4606 from nyalldawson/feature_locator
[FEATURE] Active layer feature locator filter
2 parents 2f9bfc5 + 19ca0e0 commit a6358b1

5 files changed

Lines changed: 120 additions & 4 deletions

File tree

‎src/app/locator/qgsinbuiltlocatorfilters.cpp‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "qgsmaplayermodel.h"
2626
#include "qgscomposition.h"
2727
#include "qgslayoutmanager.h"
28+
#include "qgsmapcanvas.h"
2829
#include <QToolButton>
2930

3031
QgsLayerTreeLocatorFilter::QgsLayerTreeLocatorFilter( QObject *parent )
@@ -163,3 +164,98 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
163164
}
164165
}
165166
}
167+
168+
QgsActiveLayerFeaturesLocatorFilter::QgsActiveLayerFeaturesLocatorFilter( QObject *parent )
169+
: QgsLocatorFilter( parent )
170+
{
171+
setUseWithoutPrefix( false );
172+
}
173+
174+
void QgsActiveLayerFeaturesLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
175+
{
176+
if ( string.length() < 3 )
177+
return;
178+
179+
bool allowNumeric = false;
180+
double numericValue = string.toDouble( &allowNumeric );
181+
182+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgisApp::instance()->activeLayer() );
183+
if ( !layer )
184+
return;
185+
186+
int found = 0;
187+
QgsExpression dispExpression( layer->displayExpression() );
188+
QgsExpressionContext context;
189+
context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
190+
dispExpression.prepare( &context );
191+
192+
// build up request expression
193+
QStringList expressionParts;
194+
Q_FOREACH ( const QgsField &field, layer->fields() )
195+
{
196+
if ( field.type() == QVariant::String )
197+
{
198+
expressionParts << QStringLiteral( "%1 ILIKE '%%2%'" ).arg( QgsExpression::quotedColumnRef( field.name() ),
199+
string );
200+
}
201+
else if ( allowNumeric && field.isNumeric() )
202+
{
203+
expressionParts << QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( field.name() ) ).arg( numericValue );
204+
}
205+
}
206+
207+
QString expression = QStringLiteral( "(%1)" ).arg( expressionParts.join( QStringLiteral( " ) OR ( " ) ) );
208+
209+
QgsFeatureRequest req;
210+
req.setFlags( QgsFeatureRequest::NoGeometry );
211+
req.setFilterExpression( expression );
212+
req.setLimit( 30 );
213+
QgsFeature f;
214+
QgsFeatureIterator it = layer->getFeatures( req );
215+
while ( it.nextFeature( f ) )
216+
{
217+
if ( feedback->isCanceled() )
218+
return;
219+
220+
QgsLocatorResult result;
221+
result.filter = this;
222+
223+
context.setFeature( f );
224+
225+
// find matching field content
226+
Q_FOREACH ( const QVariant &var, f.attributes() )
227+
{
228+
QString attrString = var.toString();
229+
if ( attrString.contains( string, Qt::CaseInsensitive ) )
230+
{
231+
result.displayString = attrString;
232+
break;
233+
}
234+
}
235+
if ( result.displayString.isEmpty() )
236+
continue; //not sure how this result slipped through...
237+
238+
result.description = dispExpression.evaluate( &context ).toString();
239+
240+
result.userData = QVariantList() << f.id() << layer->id();
241+
result.icon = QgsMapLayerModel::iconForLayer( layer );
242+
result.score = static_cast< double >( string.length() ) / result.displayString.size();
243+
emit resultFetched( result );
244+
245+
found++;
246+
if ( found >= 30 )
247+
break;
248+
}
249+
}
250+
251+
void QgsActiveLayerFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
252+
{
253+
QVariantList dataList = result.userData.toList();
254+
QgsFeatureId id = dataList.at( 0 ).toLongLong();
255+
QString layerId = dataList.at( 1 ).toString();
256+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
257+
if ( !layer )
258+
return;
259+
260+
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
261+
}

‎src/app/locator/qgsinbuiltlocatorfilters.h‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ class QgsActionLocatorFilter : public QgsLocatorFilter
7777

7878
};
7979

80+
class QgsActiveLayerFeaturesLocatorFilter : public QgsLocatorFilter
81+
{
82+
Q_OBJECT
83+
84+
public:
85+
86+
QgsActiveLayerFeaturesLocatorFilter( QObject *parent = nullptr );
87+
virtual QString name() const override { return QStringLiteral( "features" ); }
88+
virtual QString displayName() const override { return tr( "Active Layer Features" ); }
89+
virtual Priority priority() const override { return Medium; }
90+
QString prefix() const override { return QStringLiteral( "f" ); }
91+
92+
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
93+
void triggerResult( const QgsLocatorResult &result ) override;
94+
};
95+
96+
8097
#endif // QGSINBUILTLOCATORFILTERS_H
8198

8299

‎src/app/qgisapp.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2673,7 +2673,7 @@ void QgisApp::createStatusBar()
26732673
<< mSnappingToolBar;
26742674

26752675
mLocatorWidget->locator()->registerFilter( new QgsActionLocatorFilter( actionObjects ) );
2676-
2676+
mLocatorWidget->locator()->registerFilter( new QgsActiveLayerFeaturesLocatorFilter() );
26772677
}
26782678

26792679
void QgisApp::setIconSizes( int size )

‎src/app/qgisapp.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
621621
//! Process the list of URIs that have been dropped in QGIS
622622
void handleDropUriList( const QgsMimeDataUtils::UriList &lst );
623623

624+
//! Returns the active map layer.
625+
QgsMapLayer *activeLayer();
626+
624627
public slots:
625628
void layerTreeViewDoubleClicked( const QModelIndex &index );
626629
//! Make sure the insertion point for new layers is up-to-date with the current item in layer tree view
@@ -1139,8 +1142,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
11391142
void hideDeselectedLayers();
11401143
//reimplements method from base (gui) class
11411144
void showSelectedLayers();
1142-
//! Return pointer to the active layer
1143-
QgsMapLayer *activeLayer();
1145+
11441146
//! Open the help contents in a browser
11451147
void helpContents();
11461148
//! Open the API documentation in a browser

‎src/gui/locator/qgslocator.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ void QgsLocator::registerFilter( QgsLocatorFilter *filter )
6363
if ( !filter->prefix().isEmpty() )
6464
{
6565
if ( filter->name() == QStringLiteral( "actions" ) || filter->name() == QStringLiteral( "processing_alg" )
66-
|| filter->name() == QStringLiteral( "layertree" ) || filter->name() == QStringLiteral( "layouts" ) )
66+
|| filter->name() == QStringLiteral( "layertree" ) || filter->name() == QStringLiteral( "layouts" )
67+
|| filter->name() == QStringLiteral( "features" ) )
6768
{
6869
//inbuilt filter, no prefix check
6970
mPrefixedFilters.insert( filter->prefix(), filter );

0 commit comments

Comments
 (0)