Skip to content
Permalink
Browse files
Add QgsProviderSublayerProxyModel for filtering/sorting
QgsProviderSublayerModel instances
  • Loading branch information
nyalldawson committed Jul 6, 2021
1 parent 73f45ee commit 129ab506070d6bb396cc781e4a6d53dd34c6b19a
@@ -14,6 +14,15 @@ class QgsProviderSublayerModel: QAbstractItemModel
{
%Docstring(signature="appended")

A model for representing the sublayers present in a URI.

:py:class:`QgsProviderSublayerModel` is designed to present a tree view of the sublayers
available for a URI, including any vector, raster or mesh sublayers present.

Additionally, :py:class:`QgsProviderSublayerModel` can include some non-sublayer items,
e.g. in order to represent other content available for a URI, such as
embedded project items. The non-sublayer items can be added by calling
:py:func:`~addNonLayerItem`.

.. versionadded:: 3.22
%End
@@ -49,6 +58,7 @@ class QgsProviderSublayerModel: QAbstractItemModel
{
%Docstring(signature="appended")

Contains details for a non-sublayer item to include in a QgsProviderSublayerModel.

.. versionadded:: 3.22
%End
@@ -169,6 +179,49 @@ Adds a non-layer item (e.g. an embedded QGIS project item) to the model.

};


class QgsProviderSublayerProxyModel: QSortFilterProxyModel
{
%Docstring(signature="appended")

A QSortFilterProxyModel for filtering and sorting a QgsProviderSublayerModel.

.. versionadded:: 3.22
%End

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

QgsProviderSublayerProxyModel( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsProviderSublayerProxyModel, with the specified ``parent`` object.
%End

QString filterString() const;
%Docstring
Returns the filter string used for filtering items in the model.

.. seealso:: :py:func:`setFilterString`
%End

void setFilterString( const QString &filter );
%Docstring
Sets the ``filter`` string used for filtering items in the model.

.. seealso:: :py:func:`filterString`
%End

protected:
virtual bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const;

virtual bool lessThan( const QModelIndex &source_left, const QModelIndex &source_right ) const;


};


/************************************************************************
* This file has been generated automatically from *
* *
@@ -348,3 +348,57 @@ void QgsProviderSublayerModel::NonLayerItem::setIcon( const QIcon &icon )
{
mIcon = icon;
}

//
// QgsProviderSublayerProxyModel
//

QgsProviderSublayerProxyModel::QgsProviderSublayerProxyModel( QObject *parent )
: QSortFilterProxyModel( parent )
{
setDynamicSortFilter( true );
sort( 0 );
}

bool QgsProviderSublayerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{
const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );

if ( mFilterString.trimmed().isEmpty() )
return true;

if ( sourceModel()->data( sourceIndex, static_cast< int >( QgsProviderSublayerModel::Role::Name ) ).toString().contains( mFilterString, Qt::CaseInsensitive ) )
return true;

if ( sourceModel()->data( sourceIndex, static_cast< int >( QgsProviderSublayerModel::Role::Description ) ).toString().contains( mFilterString, Qt::CaseInsensitive ) )
return true;

return false;
}

bool QgsProviderSublayerProxyModel::lessThan( const QModelIndex &source_left, const QModelIndex &source_right ) const
{
const bool leftIsNonLayer = sourceModel()->data( source_left, static_cast< int >( QgsProviderSublayerModel::Role::IsNonLayerItem ) ).toBool();
const bool rightIsNonLayer = sourceModel()->data( source_right, static_cast< int >( QgsProviderSublayerModel::Role::IsNonLayerItem ) ).toBool();

if ( leftIsNonLayer && !rightIsNonLayer )
return true;
else if ( rightIsNonLayer && !leftIsNonLayer )
return false;

const QString leftName = sourceModel()->data( source_left, static_cast< int >( QgsProviderSublayerModel::Role::Name ) ).toString();
const QString rightName = sourceModel()->data( source_right, static_cast< int >( QgsProviderSublayerModel::Role::Name ) ).toString();

return leftName.compare( rightName, Qt::CaseInsensitive );
}

QString QgsProviderSublayerProxyModel::filterString() const
{
return mFilterString;
}

void QgsProviderSublayerProxyModel::setFilterString( const QString &filter )
{
mFilterString = filter;
invalidateFilter();
}
@@ -20,14 +20,23 @@

#include "qgstaskmanager.h"
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>

class QgsProviderSublayerDetails;

/**
* \ingroup core
*
* \brief
* \brief A model for representing the sublayers present in a URI.
*
* QgsProviderSublayerModel is designed to present a tree view of the sublayers
* available for a URI, including any vector, raster or mesh sublayers present.
*
* Additionally, QgsProviderSublayerModel can include some non-sublayer items,
* e.g. in order to represent other content available for a URI, such as
* embedded project items. The non-sublayer items can be added by calling
* addNonLayerItem().
*
* \since QGIS 3.22
*/
class CORE_EXPORT QgsProviderSublayerModel: public QAbstractItemModel
@@ -63,7 +72,7 @@ class CORE_EXPORT QgsProviderSublayerModel: public QAbstractItemModel
/**
* \ingroup core
*
* \brief
* \brief Contains details for a non-sublayer item to include in a QgsProviderSublayerModel.
* \since QGIS 3.22
*/
@@ -179,4 +188,48 @@ class CORE_EXPORT QgsProviderSublayerModel: public QAbstractItemModel

};


/**
* \ingroup core
*
* \brief A QSortFilterProxyModel for filtering and sorting a QgsProviderSublayerModel.
*
* \since QGIS 3.22
*/
class CORE_EXPORT QgsProviderSublayerProxyModel: public QSortFilterProxyModel
{
Q_OBJECT

public:

/**
* Constructor for QgsProviderSublayerProxyModel, with the specified \a parent object.
*/
QgsProviderSublayerProxyModel( QObject *parent SIP_TRANSFERTHIS = nullptr );

/**
* Returns the filter string used for filtering items in the model.
*
* \see setFilterString()
*/
QString filterString() const;

/**
* Sets the \a filter string used for filtering items in the model.
*
* \see filterString()
*/
void setFilterString( const QString &filter );

protected:
bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const override;
bool lessThan( const QModelIndex &source_left, const QModelIndex &source_right ) const override;

private:

QString mFilterString;

};


#endif // QGSPROVIDERSUBLAYERMODEL_H
@@ -11,24 +11,19 @@
__copyright__ = 'Copyright 2020, The QGIS Project'

import qgis # NOQA
import os

from qgis.core import (
QgsProviderRegistry,
QgsMapLayerType,
QgsWkbTypes,
QgsProviderSublayerDetails,
Qgis,
QgsCoordinateTransformContext,
QgsVectorLayer,
QgsProviderSublayerProxyModel,
QgsProviderSublayerModel
)
from qgis.PyQt.QtCore import (
Qt,
QModelIndex
)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath

# Convenience instances in case you may need them
# to find the srs.db
@@ -239,6 +234,92 @@ def test_model_with_non_layer_items(self):
self.assertEqual(model.data(model.index(2, 0), QgsProviderSublayerModel.Role.IsNonLayerItem), True)
self.assertEqual(model.data(model.index(2, 0), QgsProviderSublayerModel.Role.NonLayerItemType), 'item type 2')

def test_proxy(self):
"""
Test QgsProviderSublayerProxyModel
"""
model = QgsProviderSublayerModel()
proxy = QgsProviderSublayerProxyModel()
proxy.setSourceModel(model)
self.assertEqual(model.rowCount(QModelIndex()), 0)
self.assertEqual(proxy.columnCount(QModelIndex()), 2)
self.assertEqual(proxy.headerData(0, Qt.Horizontal, Qt.DisplayRole), 'Layer')
self.assertEqual(proxy.headerData(0, Qt.Horizontal, Qt.ToolTipRole), 'Layer')
self.assertEqual(proxy.headerData(1, Qt.Horizontal, Qt.DisplayRole), 'Description')
self.assertEqual(proxy.headerData(1, Qt.Horizontal, Qt.ToolTipRole), 'Description')

layer1 = QgsProviderSublayerDetails()
layer1.setType(QgsMapLayerType.RasterLayer)
layer1.setName('layer 1')
layer1.setDescription('description 1')
layer1.setProviderKey('gdal')
layer1.setUri('uri 1')

layer2 = QgsProviderSublayerDetails()
layer2.setType(QgsMapLayerType.VectorLayer)
layer2.setName('another layer 2')
layer2.setDescription('description 2')
layer2.setProviderKey('ogr')
layer2.setUri('uri 2')
layer2.setFeatureCount(-1)
layer2.setWkbType(QgsWkbTypes.LineString)

model.setSublayerDetails([layer1, layer2])

item1 = QgsProviderSublayerModel.NonLayerItem()
item1.setUri('item uri 1')
item1.setName('item name 1')
item1.setType('item type 1')
item1.setDescription('item desc 1')

model.addNonLayerItem(item1)

self.assertEqual(proxy.rowCount(QModelIndex()), 3)

self.assertEqual(proxy.data(proxy.index(0, 0), Qt.DisplayRole), 'item name 1')
self.assertEqual(proxy.data(proxy.index(0, 1), Qt.DisplayRole), 'item desc 1')
self.assertEqual(proxy.data(proxy.index(0, 0), QgsProviderSublayerModel.Role.Uri), 'item uri 1')
self.assertEqual(proxy.data(proxy.index(0, 0), QgsProviderSublayerModel.Role.Name), 'item name 1')
self.assertEqual(proxy.data(proxy.index(0, 0), QgsProviderSublayerModel.Role.Description), 'item desc 1')
self.assertEqual(proxy.data(proxy.index(0, 0), QgsProviderSublayerModel.Role.IsNonLayerItem), True)

self.assertEqual(proxy.data(proxy.index(1, 0), Qt.DisplayRole), 'another layer 2')
self.assertEqual(proxy.data(proxy.index(1, 1), Qt.DisplayRole), 'description 2 - LineString (Uncounted)')
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.ProviderKey), 'ogr')
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.LayerType), QgsMapLayerType.VectorLayer)
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.Uri), 'uri 2')
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.Name), 'another layer 2')
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.Description), 'description 2')
self.assertEqual(proxy.data(proxy.index(1, 0), QgsProviderSublayerModel.Role.IsNonLayerItem), False)

self.assertEqual(proxy.data(proxy.index(2, 0), Qt.DisplayRole), 'layer 1')
self.assertEqual(proxy.data(proxy.index(2, 1), Qt.DisplayRole), 'description 1')
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.ProviderKey), 'gdal')
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.LayerType), QgsMapLayerType.RasterLayer)
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.Uri), 'uri 1')
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.Name), 'layer 1')
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.Description), 'description 1')
self.assertEqual(proxy.data(proxy.index(2, 0), QgsProviderSublayerModel.Role.IsNonLayerItem), False)

proxy.setFilterString(' 1')
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0), Qt.DisplayRole), 'item name 1')
self.assertEqual(proxy.data(proxy.index(1, 0), Qt.DisplayRole), 'layer 1')

proxy.setFilterString(' 2')
self.assertEqual(proxy.rowCount(QModelIndex()), 1)
self.assertEqual(proxy.data(proxy.index(0, 0), Qt.DisplayRole), 'another layer 2')

proxy.setFilterString('ITEM')
self.assertEqual(proxy.rowCount(QModelIndex()), 1)
self.assertEqual(proxy.data(proxy.index(0, 0), Qt.DisplayRole), 'item name 1')

proxy.setFilterString('')
self.assertEqual(proxy.rowCount(QModelIndex()), 3)
self.assertEqual(proxy.data(proxy.index(0, 0), Qt.DisplayRole), 'item name 1')
self.assertEqual(proxy.data(proxy.index(1, 0), Qt.DisplayRole), 'another layer 2')
self.assertEqual(proxy.data(proxy.index(2, 0), Qt.DisplayRole), 'layer 1')


if __name__ == '__main__':
unittest.main()

0 comments on commit 129ab50

Please sign in to comment.