QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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() );
100  bool byDefault = QgsLocator::settingsLocatorFilterDefault.value( filter->name(), true, filter->useWithoutPrefix() );
101  QString prefix = QgsLocator::settingsLocatorFilterPrefix.value( filter->name(), true, filter->prefix() );
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 }
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).
bool value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, bool defaultValueOverride=false) const
Returns settings value.
QString value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, const QString &defaultValueOverride=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