QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
24 const 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 
37 QgsLocator::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 
56 QList<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 
76 QMap<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 
130 void 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 
283 void 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 
292 void 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 }
QgsLocatorFilter
Abstract base class for filters which collect locator results.
Definition: qgslocatorfilter.h:152
QgsLocatorFilter::prefix
virtual QString prefix() const
Returns the search prefix character(s) for this filter.
Definition: qgslocatorfilter.h:228
QgsLocator::CORE_FILTERS
static const QList< QString > CORE_FILTERS
List of core filters (i.e. not plugin filters)
Definition: qgslocator.h:65
QgsSettingsEntryByReference::valueWithDefaultOverride
T valueWithDefaultOverride(const T &defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
Definition: qgssettingsentry.h:390
QgsLocator::prefixedFilters
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
QgsLocatorFilter::finished
void finished()
Emitted when the filter finishes fetching results.
QgsLocatorFilter::name
virtual QString name() const =0
Returns the unique name for the filter.
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
qgslocator.h
QgsFeedback::cancel
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:121
QgsLocator::clearPreviousResults
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:272
QgsLocatorResult
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
Definition: qgslocatorfilter.h:38
QgsLocator::settingsLocatorFilterEnabled
static const QgsSettingsEntryBool settingsLocatorFilterEnabled
Settings entry locator filter enabled.
Definition: qgslocator.h:158
QgsLocator::deregisterFilter
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:49
QgsLocator::settingsLocatorFilterDefault
static const QgsSettingsEntryBool settingsLocatorFilterDefault
Settings entry locator filter default value.
Definition: qgslocator.h:160
QgsLocatorContext
Encapsulates the properties relating to the context of a locator search.
Definition: qgslocatorcontext.h:32
QgsLocator::searchPrepared
void searchPrepared()
Emitted when locator has prepared the search (.
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsSettingsEntryByValue::valueWithDefaultOverride
T valueWithDefaultOverride(T defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
Definition: qgssettingsentry.h:531
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsLocator::fetchResults
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
QgsLocatorFilter::resultFetched
void resultFetched(const QgsLocatorResult &result)
Should be emitted by filters whenever they encounter a matching result during within their fetchResul...
QgsLocator::~QgsLocator
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:43
c
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
Definition: porting_processing.dox:1
QgsLocator::cancel
void cancel()
Cancels any current running query, and blocks until query is completely canceled by all filters.
Definition: qgslocator.cpp:256
qgssettings.h
QgsLocator::isRunning
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:267
QgsLocatorFilter::setEnabled
void setEnabled(bool enabled)
Sets whether the filter is enabled.
Definition: qgslocatorfilter.cpp:57
QgsLocatorContext::usingPrefix
bool usingPrefix
Will be true if search is being conducted using a filter prefix.
Definition: qgslocatorcontext.h:66
QgsLocator::cancelWithoutBlocking
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:261
QgsLocator::registerFilter
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:93
QgsLocator::finished
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
QgsLocator::foundResult
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called.
QgsLocatorFilter::useWithoutPrefix
bool useWithoutPrefix() const
Returns true if the filter should be used when no prefix is entered.
Definition: qgslocatorfilter.cpp:72
QgsLocatorFilter::setActivePrefix
void setActivePrefix(const QString &activePrefix)
Sets the prefix as being used by the locator.
Definition: qgslocatorfilter.cpp:93
QgsLocatorFilter::FlagFast
@ FlagFast
Filter finds results quickly and can be safely run in the main thread.
Definition: qgslocatorfilter.h:172
QgsLocator::filters
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:56
QgsLocator::settingsLocatorFilterPrefix
static const QgsSettingsEntryString settingsLocatorFilterPrefix
Settings entry locator filter prefix.
Definition: qgslocator.h:162
QgsLocator::QgsLocator
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:37
QgsSettingsEntryByValue::value
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
Definition: qgssettingsentry.h:520
QgsLocatorFilter::setUseWithoutPrefix
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
Definition: qgslocatorfilter.cpp:77
qgsmessagelog.h