Skip to content

Commit 55ed6f9

Browse files
committed
[FEATURE] Active layer feature locator filter
Searches for matching attributes in any field from the current active layer.
1 parent 5077e12 commit 55ed6f9

File tree

5 files changed

+102
-4
lines changed

5 files changed

+102
-4
lines changed

src/app/locator/qgsinbuiltlocatorfilters.cpp

+78
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,80 @@ 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+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgisApp::instance()->activeLayer() );
180+
if ( !layer )
181+
return;
182+
183+
int i = 0;
184+
int found = 0;
185+
QgsExpression dispExpression( layer->displayExpression() );
186+
QgsExpressionContext context;
187+
context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
188+
dispExpression.prepare( &context );
189+
190+
Q_FOREACH ( const QgsField &field, layer->fields() )
191+
{
192+
if ( feedback->isCanceled() )
193+
return;
194+
195+
QString exp = QStringLiteral( "%1 ILIKE '%%2%'" ).arg( QgsExpression::quotedColumnRef( field.name() ),
196+
string );
197+
QgsFeatureRequest req;
198+
req.setFlags( QgsFeatureRequest::NoGeometry );
199+
QStringList attrs;
200+
attrs << field.name();
201+
attrs.append( dispExpression.referencedColumns().toList() );
202+
req.setSubsetOfAttributes( attrs, layer->fields() );
203+
req.setFilterExpression( exp );
204+
req.setLimit( 30 );
205+
QgsFeature f;
206+
QgsFeatureIterator it = layer->getFeatures( req );
207+
while ( it.nextFeature( f ) )
208+
{
209+
if ( feedback->isCanceled() )
210+
return;
211+
212+
QgsLocatorResult result;
213+
result.filter = this;
214+
215+
context.setFeature( f );
216+
result.displayString = dispExpression.evaluate( &context ).toString();
217+
if ( result.displayString.isEmpty() )
218+
result.displayString = f.attribute( i ).toString();
219+
else
220+
result.description = f.attribute( i ).toString();
221+
222+
result.userData = f.id();
223+
result.icon = QgsMapLayerModel::iconForLayer( layer );
224+
result.score = static_cast< double >( string.length() ) / f.attribute( i ).toString().size();
225+
emit resultFetched( result );
226+
227+
found++;
228+
if ( found >= 30 )
229+
return;
230+
}
231+
i++;
232+
}
233+
}
234+
235+
void QgsActiveLayerFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
236+
{
237+
QgsFeatureId id = result.userData.toLongLong();
238+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgisApp::instance()->activeLayer() );
239+
if ( !layer )
240+
return;
241+
242+
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
243+
}

src/app/locator/qgsinbuiltlocatorfilters.h

+17
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2672,7 +2672,7 @@ void QgisApp::createStatusBar()
26722672
<< mSnappingToolBar;
26732673

26742674
mLocatorWidget->locator()->registerFilter( new QgsActionLocatorFilter( actionObjects ) );
2675-
2675+
mLocatorWidget->locator()->registerFilter( new QgsActiveLayerFeaturesLocatorFilter() );
26762676
}
26772677

26782678
void QgisApp::setIconSizes( int size )

src/app/qgisapp.h

+4-2
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

+2-1
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)