QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgslocator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslocator.cpp
3 --------------
4 begin : May 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgslocator.h"
19#include "qgsmessagelog.h"
20#include "qgssettings.h"
21#include <QtConcurrent>
22#include <functional>
23
24const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiteral( "actions" )
25 << QStringLiteral( "processing_alg" )
26 << QStringLiteral( "layertree" )
27 << QStringLiteral( "layouts" )
28 << QStringLiteral( "features" )
29 << QStringLiteral( "allfeatures" )
30 << QStringLiteral( "calculator" )
31 << QStringLiteral( "bookmarks" )
32 << QStringLiteral( "optionpages" )
33 << QStringLiteral( "edit_features" )
34 << QStringLiteral( "goto" )
35 << QStringLiteral( "nominatimgeocoder" ) ;
36
37QgsLocator::QgsLocator( QObject *parent )
38 : QObject( parent )
39{
40 qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
41}
42
44{
45 cancelRunningQuery();
46 qDeleteAll( mFilters );
47}
48
50{
51 cancelRunningQuery();
52 mFilters.removeAll( filter );
53 delete filter;
54}
55
56QList<QgsLocatorFilter *> QgsLocator::filters( const QString &prefix )
57{
58 if ( !prefix.isEmpty() )
59 {
60 QList<QgsLocatorFilter *> filters = QList<QgsLocatorFilter *>();
61 for ( QgsLocatorFilter *filter : mFilters )
62 {
63 if ( !filter->activePrefix().isEmpty() && filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 )
64 {
65 filters << filter;
66 }
67 }
68 return filters;
69 }
70 else
71 {
72 return mFilters;
73 }
74}
75
76QMap<QString, QgsLocatorFilter *> QgsLocator::prefixedFilters() const
77{
78 QMap<QString, QgsLocatorFilter *> filters = QMap<QString, QgsLocatorFilter *>();
79 for ( QgsLocatorFilter *filter : mFilters )
80 {
81 if ( !filter->activePrefix().isEmpty() && filter->enabled() )
82 {
83#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
84 filters.insertMulti( filter->activePrefix(), filter );
85#else
86 filters.insert( filter->activePrefix(), filter );
87#endif
88 }
89 }
90 return filters;
91}
92
94{
95 mFilters.append( filter );
96 filter->setParent( this );
97
98 // restore settings
99 bool enabled = QgsLocator::settingsLocatorFilterEnabled.value( filter->name() );
101 QString prefix = QgsLocator::settingsLocatorFilterPrefix.valueWithDefaultOverride( filter->prefix(), filter->name() );
102 if ( prefix.isEmpty() )
103 {
104 prefix = filter->prefix();
105 }
106
107 if ( !prefix.isEmpty() )
108 {
109 if ( CORE_FILTERS.contains( filter->name() ) )
110 {
111 //inbuilt filter, no prefix check
112 filter->setActivePrefix( prefix );
113 }
114 else if ( prefix.length() >= 3 || prefix != filter->prefix() )
115 {
116 // for plugins either the native prefix is >3 char or it has been customized by user
117 filter->setActivePrefix( prefix );
118 }
119 else
120 {
121 // otherwise set it to empty string (not NULL)
122 filter->setActivePrefix( QString( "" ) );
123 }
124 }
125
126 filter->setEnabled( enabled );
127 filter->setUseWithoutPrefix( byDefault );
128}
129
130void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback )
131{
132 mAutocompletionList.clear();
133
134 QgsLocatorContext context( c );
135 // ideally this should not be required, as well behaved callers
136 // will NOT fire up a new fetchResults call while an existing one is
137 // operating/waiting to be canceled...
138 cancelRunningQuery();
139
140 // if no feedback object was passed, create one that is owned by this object
141 // to ensure that filters ALWAYS receive a valid feedback
142 if ( !feedback )
143 {
144 mOwnedFeedback.reset( new QgsFeedback() );
145 feedback = mOwnedFeedback.get();
146 }
147 else
148 {
149 mOwnedFeedback.reset( nullptr );
150 }
151 mFeedback = feedback;
152
153 QList< QgsLocatorFilter * > activeFilters;
154 QString searchString = string;
155 QString prefix = searchString.left( std::max( static_cast<int>( searchString.indexOf( ' ' ) ), 0 ) );
156 if ( !prefix.isEmpty() )
157 {
158 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
159 {
160 if ( filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 && filter->enabled() )
161 {
162 activeFilters << filter;
163 }
164 }
165 context.usingPrefix = !activeFilters.empty();
166 }
167 if ( !activeFilters.isEmpty() )
168 {
169 searchString = searchString.mid( prefix.length() + 1 );
170 }
171 else
172 {
173 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
174 {
175 if ( filter->useWithoutPrefix() && filter->enabled() )
176 {
177 activeFilters << filter;
178 }
179 }
180 }
181
182 QList< QgsLocatorFilter *> threadedFilters;
183 for ( QgsLocatorFilter *filter : std::as_const( activeFilters ) )
184 {
185 filter->clearPreviousResults();
186 std::unique_ptr< QgsLocatorFilter > clone( filter->clone() );
187 if ( ! clone )
188 {
189 QgsMessageLog::logMessage( tr( "QgsLocatorFilter '%1' could not provide a valid clone" ).arg( filter->name() ), QString(), Qgis::MessageLevel::Critical );
190 continue;
191 }
192 connect( clone.get(), &QgsLocatorFilter::resultFetched, clone.get(), [this, filter]( QgsLocatorResult result )
193 {
194 result.filter = filter;
195 filterSentResult( result );
196 } );
197 QStringList autoCompleteList = clone->prepare( searchString, context );
198 if ( context.usingPrefix )
199 {
200 for ( int i = 0; i < autoCompleteList.length(); i++ )
201 {
202 autoCompleteList[i].prepend( QStringLiteral( "%1 " ).arg( prefix ) );
203 }
204 }
205 mAutocompletionList.append( autoCompleteList );
206
207 if ( clone->flags() & QgsLocatorFilter::FlagFast )
208 {
209 // filter is fast enough to fetch results on the main thread
210 clone->fetchResults( searchString, context, feedback );
211 }
212 else
213 {
214 threadedFilters.append( clone.release() );
215 }
216 }
217
218 mActiveThreads.clear();
219 for ( QgsLocatorFilter *filter : std::as_const( threadedFilters ) )
220 {
221 QThread *thread = new QThread();
222 mActiveThreads.append( thread );
223 filter->moveToThread( thread );
224 connect( thread, &QThread::started, filter, [filter, searchString, context, feedback]
225 {
226 int delay = filter->fetchResultsDelay();
227 while ( delay > 0 )
228 {
229 if ( feedback->isCanceled() )
230 break;
231 QThread::msleep( 50 );
232 delay -= 50;
233 }
234 if ( !feedback->isCanceled() )
235 filter->fetchResults( searchString, context, feedback );
236 filter->emit finished();
237 }, Qt::QueuedConnection );
238 connect( filter, &QgsLocatorFilter::finished, thread, &QThread::quit );
239 connect( filter, &QgsLocatorFilter::finished, filter, &QgsLocatorFilter::deleteLater );
240 connect( thread, &QThread::finished, thread, [this, thread]
241 {
242 mActiveThreads.removeAll( thread );
243 if ( mActiveThreads.empty() )
244 emit finished();
245 } );
246 connect( thread, &QThread::finished, thread, &QThread::deleteLater );
247 thread->start();
248 }
249
250 emit searchPrepared();
251
252 if ( mActiveThreads.empty() )
253 emit finished();
254}
255
257{
258 cancelRunningQuery();
259}
260
262{
263 if ( mFeedback )
264 mFeedback->cancel();
265}
266
268{
269 return !mActiveThreads.empty();
270}
271
273{
274 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
275 {
276 if ( filter->enabled() )
277 {
278 filter->clearPreviousResults();
279 }
280 }
281}
282
283void QgsLocator::filterSentResult( QgsLocatorResult result )
284{
285 // if query has been canceled then discard any results we receive
286 if ( mFeedback->isCanceled() )
287 return;
288
289 emit foundResult( result );
290}
291
292void QgsLocator::cancelRunningQuery()
293{
294 if ( !mActiveThreads.empty() )
295 {
296 // cancel existing job
297 mFeedback->cancel();
298 while ( !mActiveThreads.empty() )
299 {
300 QCoreApplication::processEvents();
301 }
302 }
303}
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:108
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Encapsulates the properties relating to the context of a locator search.
bool usingPrefix
Will be true if search is being conducted using a filter prefix.
Abstract base class for filters which collect locator results.
void setEnabled(bool enabled)
Sets whether the filter is enabled.
void setActivePrefix(const QString &activePrefix)
Sets the prefix as being used by the locator.
virtual QString prefix() const
Returns the search prefix character(s) for this filter.
bool useWithoutPrefix() const
Returns true if the filter should be used when no prefix is entered.
@ FlagFast
Filter finds results quickly and can be safely run in the main thread.
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
void finished()
Emitted when the filter finishes fetching results.
virtual QString name() const =0
Returns the unique name for the filter.
void resultFetched(const QgsLocatorResult &result)
Should be emitted by filters whenever they encounter a matching result during within their fetchResul...
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
static const QList< QString > CORE_FILTERS
List of core filters (i.e. not plugin filters)
Definition: qgslocator.h:65
void searchPrepared()
Emitted when locator has prepared the search (.
void cancel()
Cancels any current running query, and blocks until query is completely canceled by all filters.
Definition: qgslocator.cpp:256
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:37
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:93
static const QgsSettingsEntryBool settingsLocatorFilterEnabled
Settings entry locator filter enabled.
Definition: qgslocator.h:158
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called.
void fetchResults(const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback=nullptr)
Triggers the background fetching of filter results for a specified search string.
Definition: qgslocator.cpp:130
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:272
static const QgsSettingsEntryBool settingsLocatorFilterDefault
Settings entry locator filter default value.
Definition: qgslocator.h:160
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:43
Q_DECL_DEPRECATED QMap< QString, QgsLocatorFilter * > prefixedFilters() const
Returns a map of prefix to filter, for all registered filters with valid prefixes.
Definition: qgslocator.cpp:76
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:267
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:261
static const QgsSettingsEntryString settingsLocatorFilterPrefix
Settings entry locator filter prefix.
Definition: qgslocator.h:162
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:56
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:49
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
T valueWithDefaultOverride(const T &defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
T valueWithDefaultOverride(T defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c