Skip to content
Permalink
Browse files

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.
  • Loading branch information
nyalldawson committed Sep 3, 2017
1 parent a8e1d33 commit 26830949d9d8e7bdd2fb0897e906ccd431d1c353
@@ -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;

};
@@ -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;
}


@@ -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;
};

@@ -19,7 +19,8 @@
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 @@ def testModel(self):
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()

0 comments on commit 2683094

Please sign in to comment.
You can’t perform that action at this time.