Skip to content
Permalink
Browse files

[FEATURE] Implement a QtCreator style locator bar in the QGIS status bar

This adds a new "locator" bar to the QGIS status bar. If you're not
familiar with QtCreator's locator, it's a quick search bar
(activated by Ctrl+K) which displays matching search results
from any number of registered search filters.

Search filters are subclassed from QgsLocatorFilter, and
added to the app's locator via iface.registerLocatorFilter(...)

Searching is handled using threads, so that results always
become available as quickly as possible, regardless of whether
any slow search filters may be installed. They also appear
as soon as each result is encountered by each filter, which means
that e.g. a file search filter will show results one by one
as the file tree is scanned. This ensures that the UI is always
responsive even if a very slow search filter is present (e.g.
one which uses an online service).

This framework is designed to be extended by plugins, such as
OSM nominatim searches, direct database searching (i.e. Discovery
plugin), layer catalog searches, etc...
  • Loading branch information
nyalldawson committed May 17, 2017
1 parent afc9788 commit 71f7872251f8e4bf34372f92453f4e95e24823b8
@@ -78,6 +78,7 @@ IF(WITH_APIDOC)
${CMAKE_SOURCE_DIR}/src/gui/editorwidgets/core
${CMAKE_SOURCE_DIR}/src/gui/effects
${CMAKE_SOURCE_DIR}/src/gui/layertree
${CMAKE_SOURCE_DIR}/src/gui/locator
${CMAKE_SOURCE_DIR}/src/gui/raster
${CMAKE_SOURCE_DIR}/src/gui/symbology-ng
${CMAKE_SOURCE_DIR}/src/analysis
@@ -122,6 +122,7 @@ INCLUDE_DIRECTORIES(
../src/gui/editorwidgets/core
../src/gui/effects
../src/gui/layertree
../src/gui/locator

../src/plugins

@@ -211,6 +211,9 @@
%Include layertree/qgslayertreeview.sip
%Include layertree/qgslayertreeviewdefaultactions.sip

%Include locator/qgslocator.sip
%Include locator/qgslocatorfilter.sip

%Include raster/qgsmultibandcolorrendererwidget.sip
%Include raster/qgspalettedrendererwidget.sip
%Include raster/qgsrasterbandcombobox.sip
@@ -0,0 +1,130 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/locator/qgslocator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsLocator : QObject
{
%Docstring
Handles the management of QgsLocatorFilter objects and async collection of search results from them.

QgsLocator acts as both a registry for QgsLocatorFilter objects and a means of firing up
asynchronous queries against these filter objects.

Filters are first registered to the locator by calling registerFilter(). Registering filters
transfers their ownership to the locator object. Plugins which register filters to the locator
must take care to correctly call deregisterFilter() and deregister their filter upon plugin
unload to avoid crashes.

In order to trigger a search across registered filters, the fetchResults() method is called.
This triggers threaded calls to QgsLocatorFilter.fetchResults() for all registered filters.
As individual filters find matching results, the foundResult() signal will be triggered
for each result. Callers should connect this signal to an appropriate slot designed
to collect and handle these results. Since foundResult() is triggered whenever a filter
encounters an individual result, it will usually be triggered many times for a single
call to fetchResults().

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslocator.h"
%End
public:

QgsLocator( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLocator.
%End

~QgsLocator();
%Docstring
Destructor for QgsLocator. Destruction will block while any currently running query is terminated.
%End

void registerFilter( QgsLocatorFilter *filter /Transfer/ );
%Docstring
Registers a ``filter`` within the locator. Ownership of the filter is transferred to the
locator.
\warning Plugins which register filters to the locator must take care to correctly call
deregisterFilter() and deregister their filters upon plugin unload to avoid crashes.
.. seealso:: deregisterFilter()
%End

void deregisterFilter( QgsLocatorFilter *filter );
%Docstring
Deregisters a ``filter`` from the locator and deletes it. Calling this will block whilst
any currently running query is terminated.

Plugins which register filters to the locator must take care to correctly call
deregisterFilter() to deregister their filters upon plugin unload to avoid crashes.

.. seealso:: registerFilter()
%End

QList< QgsLocatorFilter *> filters();
%Docstring
Returns the list of filters registered in the locator.
:rtype: list of QgsLocatorFilter
%End

void fetchResults( const QString &string, QgsFeedback *feedback = 0 );
%Docstring
Triggers the background fetching of filter results for a specified search ``string``.
If specified, the ``feedback`` object must exist for the lifetime of this query.

The foundResult() signal will be emitted for each individual result encountered
by the registered filters.
%End

void cancel();
%Docstring
Cancels any current running query, and blocks until query is completely canceled by
all filters.
.. seealso:: cancelWithoutBlocking()
%End

void cancelWithoutBlocking();
%Docstring
Triggers cancelation of any current running query without blocking. The query may
take some time to cancel after calling this.
.. seealso:: cancel()
%End

bool isRunning() const;
%Docstring
Returns true if a query is currently being executed by the locator.
:rtype: bool
%End

signals:

void foundResult( const QgsLocatorResult &result );
%Docstring
Emitted whenever a filter encounters a matching ``result`` after the fetchResults() method
is called.
%End

void finished();
%Docstring
Emitted when locator has finished a query, either as a result
of successful completion or early cancelation.
%End

};



/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/locator/qgslocator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -0,0 +1,107 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/locator/qgslocatorfilter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsLocatorResult
{
%Docstring
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslocatorfilter.h"
%End
public:

QgsLocatorResult();

QgsLocatorResult( QgsLocatorFilter *filter, const QString &displayString, const QVariant &userData = QVariant() );
%Docstring
Constructor for QgsLocatorResult.
%End

QgsLocatorFilter *filter;
%Docstring
Filter from which the result was obtained.
%End

QString displayString;
%Docstring
String displayed for result.
%End

QVariant userData;
%Docstring
Custom reference or other data set by the filter.
%End

};

class QgsLocatorFilter : QObject
{
%Docstring
Abstract base class for filters which collect locator results.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslocatorfilter.h"
%End
public:

QgsLocatorFilter( QObject *parent = 0 );
%Docstring
Constructor for QgsLocatorFilter.
%End

virtual void fetchResults( const QString &string, QgsFeedback *feedback ) = 0;
%Docstring
Retrieves the filter results for a specified search ``string``.

Implementations of fetchResults() should emit the resultFetched()
signal whenever they encounter a matching result.

Subclasses should periodically check the ``feedback`` object to determine
whether the query has been canceled. If so, the subclass should return
from this method as soon as possible.
%End

virtual void triggerResult( const QgsLocatorResult &result ) = 0;
%Docstring
Triggers a filter ``result`` from this filter. This is called when
one of the results obtained by a call to fetchResults() is triggered
by a user. The filter subclass must implement logic here
to perform the desired operation for the search result.
E.g. a file search filter would open file associated with the triggered
result.
%End

signals:

void resultFetched( const QgsLocatorResult &result );
%Docstring
Should be emitted by filters whenever they encounter a matching result
during within their fetchResults() implementation.
%End

};




/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/locator/qgslocatorfilter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -509,6 +509,28 @@ class QgisInterface : QObject
/** Get timeout for timed messages: default of 5 seconds */
virtual int messageTimeout() = 0;

/**
* Registers a locator \a filter for the app's locator bar. Ownership of the filter is transferred to the
* locator.
* \warning Plugins which register filters to the locator bar must take care to correctly call
* deregisterLocatorFilter() and deregister their filters upon plugin unload to avoid crashes.
* \see deregisterLocatorFilter()
* \since QGIS 3.0
*/
virtual void registerLocatorFilter( QgsLocatorFilter *filter /Transfer/ ) = 0;

/**
* Deregisters a locator \a filter from the app's locator bar and deletes it. Calling this will block whilst
* any currently running query is terminated.
*
* Plugins which register filters to the locator bar must take care to correctly call
* deregisterLocatorFilter() to deregister their filters upon plugin unload to avoid crashes.
*
* \see registerLocatorFilter()
* \since QGIS 3.0
*/
virtual void deregisterLocatorFilter( QgsLocatorFilter *filter ) = 0;

signals:
void currentLayerChanged( QgsMapLayer *layer );
void currentThemeChanged( const QString &theme );
@@ -509,6 +509,7 @@ INCLUDE_DIRECTORIES(
../gui/symbology-ng
../gui/attributetable
../gui/auth
../gui/locator
../gui/raster
../gui/editorwidgets
../gui/editorwidgets/core
@@ -188,6 +188,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeview.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayoutmanager.h"
#include "qgslocatorwidget.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmapcanvasdockwidget.h"
@@ -2630,11 +2631,18 @@ void QgisApp::createStatusBar()
mMessageButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mMessageLogRead.svg" ) ) );
mMessageButton->setToolTip( tr( "Messages" ) );
mMessageButton->setWhatsThis( tr( "Messages" ) );
mMessageButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
mMessageButton->setObjectName( QStringLiteral( "mMessageLogViewerButton" ) );
mMessageButton->setMaximumHeight( mScaleWidget->height() );
mMessageButton->setCheckable( true );
statusBar()->addPermanentWidget( mMessageButton, 0 );

mLocatorWidget = new QgsLocatorWidget( statusBar() );
statusBar()->addPermanentWidget( mLocatorWidget );
QShortcut *locatorShortCut = new QShortcut( QKeySequence( tr( "Ctrl+K" ) ), this );
connect( locatorShortCut, &QShortcut::activated, mLocatorWidget, [ = ] { mLocatorWidget->search( QString() ); } );
locatorShortCut->setObjectName( QStringLiteral( "Locator" ) );
locatorShortCut->setWhatsThis( tr( "Trigger Locator" ) );

}

void QgisApp::setIconSizes( int size )
@@ -115,6 +115,7 @@ class QgsTileScaleWidget;
class QgsLabelingWidget;
class QgsLayerStylingWidget;
class QgsDiagramProperties;
class QgsLocatorWidget;

#include <QMainWindow>
#include <QToolBar>
@@ -1974,6 +1975,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

QSystemTrayIcon *mTray = nullptr;

QgsLocatorWidget *mLocatorWidget = nullptr;

friend class TestQgisAppPython;
};

@@ -44,6 +44,8 @@
#include "qgsfeatureaction.h"
#include "qgsactionmanager.h"
#include "qgsattributetabledialog.h"
#include "qgslocatorwidget.h"
#include "qgslocator.h"


QgisAppInterface::QgisAppInterface( QgisApp *_qgis )
@@ -733,3 +735,13 @@ int QgisAppInterface::messageTimeout()
{
return qgis->messageTimeout();
}

void QgisAppInterface::registerLocatorFilter( QgsLocatorFilter *filter )
{
qgis->mLocatorWidget->locator()->registerFilter( filter );
}

void QgisAppInterface::deregisterLocatorFilter( QgsLocatorFilter *filter )
{
qgis->mLocatorWidget->locator()->deregisterFilter( filter );
}
@@ -494,6 +494,9 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
//! Get timeout for timed messages: default of 5 seconds
virtual int messageTimeout() override;

void registerLocatorFilter( QgsLocatorFilter *filter ) override;
void deregisterLocatorFilter( QgsLocatorFilter *filter ) override;

private slots:

void cacheloadForm( const QString &uifile );

0 comments on commit 71f7872

Please sign in to comment.