Add a QgsLocatorModel subclass which automatically sets up

required connections to a QgsLocator

Use this QgsLocatorModel subclass when you want the connections
between a QgsLocator and the model to be automatically created
for you. If more flexibility in model behavior is required,
use the base QgsLocatorModel class instead and setup the
connections manually.
This commit is contained in:
Nyall Dawson 2017-09-03 16:35:51 +10:00
parent a8e1d335bb
commit 26830949d9
4 changed files with 249 additions and 5 deletions

View File

@ -14,6 +14,10 @@ class QgsLocatorModel : QAbstractTableModel
{
%Docstring
An abstract list model for displaying the results of locator searches.
Note that this class should generally be used with a QgsLocatorProxyModel
in order to ensure correct sorting of results by priority and match level.
.. versionadded:: 3.0
%End
@ -31,7 +35,7 @@ class QgsLocatorModel : QAbstractTableModel
ResultFilterNameRole,
};
QgsLocatorModel( QObject *parent = 0 );
QgsLocatorModel( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLocatorModel.
%End
@ -68,6 +72,64 @@ class QgsLocatorModel : QAbstractTableModel
};
class QgsLocatorAutomaticModel : QgsLocatorModel
{
%Docstring
A QgsLocatorModel which has is associated directly with a
QgsLocator, and is automatically populated with results
from locator searches.
Use this QgsLocatorModel subclass when you want the connections
between a QgsLocator and the model to be automatically created
for you. If more flexibility in model behavior is required,
use the base QgsLocatorModel class instead and setup the
connections manually.
Note that this class should generally be used with a QgsLocatorProxyModel
in order to ensure correct sorting of results by priority and match level.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgslocatormodel.h"
%End
public:
explicit QgsLocatorAutomaticModel( QgsLocator *locator /TransferThis/ );
%Docstring
Constructor for QgsLocatorAutomaticModel, linked with the specified ``locator``.
The ``locator`` is used as the model's parent.
%End
QgsLocator *locator();
%Docstring
Returns a pointer to the locator utilized by this model.
:rtype: QgsLocator
%End
void search( const QString &string );
%Docstring
Enqueues a search for a specified ``string`` within the model.
Note that the search may not begin immediately if an existing search request
is still running. In this case the existing search must be completely
terminated before the new search can begin. The model handles this
situation automatically, and will trigger a search for the new
search string as soon as possible.
%End
virtual QgsLocatorContext createContext();
%Docstring
Returns a new locator context for searches. The default implementation
returns a default constructed QgsLocatorContext. Subclasses can override
this method to implement custom context creation logic.
:rtype: QgsLocatorContext
%End
};
class QgsLocatorProxyModel : QSortFilterProxyModel
{
%Docstring
@ -81,7 +143,10 @@ class QgsLocatorProxyModel : QSortFilterProxyModel
%End
public:
explicit QgsLocatorProxyModel( QObject *parent = 0 );
explicit QgsLocatorProxyModel( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLocatorProxyModel, with the specified ``parent`` object.
%End
virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
};

View File

@ -193,6 +193,65 @@ void QgsLocatorModel::addResult( const QgsLocatorResult &result )
mDeferredClear = false;
}
//
// QgsLocatorAutomaticModel
//
QgsLocatorAutomaticModel::QgsLocatorAutomaticModel( QgsLocator *locator )
: QgsLocatorModel( locator )
, mLocator( locator )
{
Q_ASSERT( mLocator );
connect( mLocator, &QgsLocator::foundResult, this, &QgsLocatorAutomaticModel::addResult );
connect( mLocator, &QgsLocator::finished, this, &QgsLocatorAutomaticModel::searchFinished );
}
QgsLocator *QgsLocatorAutomaticModel::locator()
{
return mLocator;
}
void QgsLocatorAutomaticModel::search( const QString &string )
{
if ( mLocator->isRunning() )
{
// can't do anything while a query is running, and can't block
// here waiting for the current query to cancel
// so we queue up this string until cancel has happened
mLocator->cancelWithoutBlocking();
mNextRequestedString = string;
mHasQueuedRequest = true;
return;
}
else
{
deferredClear();
mLocator->fetchResults( string, createContext() );
}
}
QgsLocatorContext QgsLocatorAutomaticModel::createContext()
{
return QgsLocatorContext();
}
void QgsLocatorAutomaticModel::searchFinished()
{
if ( mHasQueuedRequest )
{
// a queued request was waiting for this - run the queued search now
QString nextSearch = mNextRequestedString;
mNextRequestedString.clear();
mHasQueuedRequest = false;
search( nextSearch );
}
}
//
// QgsLocatorProxyModel
//
@ -238,3 +297,4 @@ bool QgsLocatorProxyModel::lessThan( const QModelIndex &left, const QModelIndex
return QString::localeAwareCompare( leftFilter, rightFilter ) < 0;
}

View File

@ -33,6 +33,10 @@ class QgsLocatorProxyModel;
* \class QgsLocatorModel
* \ingroup core
* An abstract list model for displaying the results of locator searches.
*
* Note that this class should generally be used with a QgsLocatorProxyModel
* in order to ensure correct sorting of results by priority and match level.
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel
@ -54,7 +58,7 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel
/**
* Constructor for QgsLocatorModel.
*/
QgsLocatorModel( QObject *parent = nullptr );
QgsLocatorModel( QObject *parent SIP_TRANSFERTHIS = nullptr );
/**
* Resets the model and clears all existing results.
@ -103,6 +107,72 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel
QTimer mDeferredClearTimer;
};
/**
* \class QgsLocatorAutomaticModel
* \ingroup core
* A QgsLocatorModel which has is associated directly with a
* QgsLocator, and is automatically populated with results
* from locator searches.
*
* Use this QgsLocatorModel subclass when you want the connections
* between a QgsLocator and the model to be automatically created
* for you. If more flexibility in model behavior is required,
* use the base QgsLocatorModel class instead and setup the
* connections manually.
*
* Note that this class should generally be used with a QgsLocatorProxyModel
* in order to ensure correct sorting of results by priority and match level.
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLocatorAutomaticModel : public QgsLocatorModel
{
Q_OBJECT
public:
/**
* Constructor for QgsLocatorAutomaticModel, linked with the specified \a locator.
*
* The \a locator is used as the model's parent.
*/
explicit QgsLocatorAutomaticModel( QgsLocator *locator SIP_TRANSFERTHIS );
/**
* Returns a pointer to the locator utilized by this model.
*/
QgsLocator *locator();
/**
* Enqueues a search for a specified \a string within the model.
*
* Note that the search may not begin immediately if an existing search request
* is still running. In this case the existing search must be completely
* terminated before the new search can begin. The model handles this
* situation automatically, and will trigger a search for the new
* search string as soon as possible.
*/
void search( const QString &string );
/**
* Returns a new locator context for searches. The default implementation
* returns a default constructed QgsLocatorContext. Subclasses can override
* this method to implement custom context creation logic.
*/
virtual QgsLocatorContext createContext();
private slots:
void searchFinished();
private:
QgsLocator *mLocator = nullptr;
QString mNextRequestedString;
bool mHasQueuedRequest = false;
};
/**
* \class QgsLocatorProxyModel
* \ingroup core
@ -116,7 +186,10 @@ class CORE_EXPORT QgsLocatorProxyModel : public QSortFilterProxyModel
public:
explicit QgsLocatorProxyModel( QObject *parent = nullptr );
/**
* Constructor for QgsLocatorProxyModel, with the specified \a parent object.
*/
explicit QgsLocatorProxyModel( QObject *parent SIP_TRANSFERTHIS = nullptr );
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;
};

View File

@ -19,7 +19,8 @@ from qgis.core import (QgsLocator,
QgsLocatorFilter,
QgsLocatorContext,
QgsLocatorResult,
QgsLocatorModel)
QgsLocatorModel,
QgsLocatorAutomaticModel)
from qgis.PyQt.QtCore import QVariant, pyqtSignal, QCoreApplication
from time import sleep
from qgis.testing import start_app, unittest
@ -210,6 +211,51 @@ class TestQgsLocator(unittest.TestCase):
QCoreApplication.processEvents()
self.assertEqual(m.rowCount(), 0)
def testAutoModel(self):
"""
Test automatic model, QgsLocatorAutomaticModel - should be no need
for any manual connections
"""
l = QgsLocator()
m = QgsLocatorAutomaticModel(l)
filter_a = test_filter('a')
l.registerFilter(filter_a)
m.search('a')
for i in range(100):
sleep(0.002)
QCoreApplication.processEvents()
# 4 results - one is locator name
self.assertEqual(m.rowCount(), 4)
self.assertEqual(m.data(m.index(0, 0)), 'test')
self.assertEqual(m.data(m.index(0, 0), QgsLocatorModel.ResultTypeRole), 0)
self.assertEqual(m.data(m.index(0, 0), QgsLocatorModel.ResultFilterNameRole), 'test')
self.assertEqual(m.data(m.index(1, 0)), 'a0')
self.assertEqual(m.data(m.index(1, 0), QgsLocatorModel.ResultTypeRole), 1)
self.assertEqual(m.data(m.index(1, 0), QgsLocatorModel.ResultFilterNameRole), 'test')
self.assertEqual(m.data(m.index(2, 0)), 'a1')
self.assertEqual(m.data(m.index(2, 0), QgsLocatorModel.ResultTypeRole), 1)
self.assertEqual(m.data(m.index(2, 0), QgsLocatorModel.ResultFilterNameRole), 'test')
self.assertEqual(m.data(m.index(3, 0)), 'a2')
self.assertEqual(m.data(m.index(3, 0), QgsLocatorModel.ResultTypeRole), 1)
self.assertEqual(m.data(m.index(3, 0), QgsLocatorModel.ResultFilterNameRole), 'test')
m.search('a')
for i in range(100):
sleep(0.002)
QCoreApplication.processEvents()
# 4 results - one is locator name
self.assertEqual(m.rowCount(), 4)
self.assertEqual(m.data(m.index(0, 0)), 'test')
self.assertEqual(m.data(m.index(1, 0)), 'a0')
self.assertEqual(m.data(m.index(2, 0)), 'a1')
self.assertEqual(m.data(m.index(3, 0)), 'a2')
if __name__ == '__main__':
unittest.main()