From e53e078bcaea8adc64246263ba3d237cccdcb4e8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 16 Jan 2024 15:24:31 +1000 Subject: [PATCH] Add proxy model for recent crs model --- ...gsrecentcoordinatereferencesystemsmodel.py | 6 ++ ...centcoordinatereferencesystemsmodel.sip.in | 63 +++++++++++++++ ...centcoordinatereferencesystemsmodel.sip.in | 63 +++++++++++++++ ...srecentcoordinatereferencesystemsmodel.cpp | 80 +++++++++++++++++++ ...qgsrecentcoordinatereferencesystemsmodel.h | 68 ++++++++++++++++ ...gsrecentcoordinatereferencesystemsmodel.py | 78 +++++++++++++++++- 6 files changed, 357 insertions(+), 1 deletion(-) diff --git a/python/PyQt6/gui/auto_additions/qgsrecentcoordinatereferencesystemsmodel.py b/python/PyQt6/gui/auto_additions/qgsrecentcoordinatereferencesystemsmodel.py index a558f23a46c..e4e39a888d0 100644 --- a/python/PyQt6/gui/auto_additions/qgsrecentcoordinatereferencesystemsmodel.py +++ b/python/PyQt6/gui/auto_additions/qgsrecentcoordinatereferencesystemsmodel.py @@ -7,3 +7,9 @@ QgsRecentCoordinateReferenceSystemsModel.RoleType = QgsRecentCoordinateReference QgsRecentCoordinateReferenceSystemsModel.RoleGroupId = QgsRecentCoordinateReferenceSystemsModel.Roles.RoleGroupId QgsRecentCoordinateReferenceSystemsModel.RoleWkt = QgsRecentCoordinateReferenceSystemsModel.Roles.RoleWkt QgsRecentCoordinateReferenceSystemsModel.RoleProj = QgsRecentCoordinateReferenceSystemsModel.Roles.RoleProj +QgsRecentCoordinateReferenceSystemsProxyModel.FilterHorizontal = QgsRecentCoordinateReferenceSystemsProxyModel.Filter.FilterHorizontal +QgsRecentCoordinateReferenceSystemsProxyModel.FilterVertical = QgsRecentCoordinateReferenceSystemsProxyModel.Filter.FilterVertical +QgsRecentCoordinateReferenceSystemsProxyModel.FilterCompound = QgsRecentCoordinateReferenceSystemsProxyModel.Filter.FilterCompound +QgsRecentCoordinateReferenceSystemsProxyModel.Filters = lambda flags=0: QgsRecentCoordinateReferenceSystemsProxyModel.Filter(flags) +QgsRecentCoordinateReferenceSystemsProxyModel.Filters.baseClass = QgsRecentCoordinateReferenceSystemsProxyModel +Filters = QgsRecentCoordinateReferenceSystemsProxyModel # dirty hack since SIP seems to introduce the flags in module diff --git a/python/PyQt6/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in b/python/PyQt6/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in index d7f80300b0b..42aaa113bd1 100644 --- a/python/PyQt6/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in +++ b/python/PyQt6/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in @@ -63,6 +63,69 @@ Returns an invalid CRS if the index is not valid. }; +class QgsRecentCoordinateReferenceSystemsProxyModel: QSortFilterProxyModel +{ +%Docstring(signature="appended") +A sort/filter proxy model for recent coordinate reference systems. + +.. versionadded:: 3.36 +%End + +%TypeHeaderCode +#include "qgsrecentcoordinatereferencesystemsmodel.h" +%End + public: + + enum Filter + { + FilterHorizontal, + FilterVertical, + FilterCompound, + }; + typedef QFlags Filters; + + + explicit QgsRecentCoordinateReferenceSystemsProxyModel( QObject *parent /TransferThis/ = 0 ); +%Docstring +Constructor for QgsRecentCoordinateReferenceSystemsProxyModel, with the given ``parent`` object. +%End + + QgsRecentCoordinateReferenceSystemsModel *recentCoordinateReferenceSystemsModel(); +%Docstring +Returns the underlying source model. +%End + + + void setFilters( QgsRecentCoordinateReferenceSystemsProxyModel::Filters filters ); +%Docstring +Set ``filters`` that affect how CRS are filtered. + +.. seealso:: :py:func:`filters` +%End + + Filters filters() const; +%Docstring +Returns any filters that affect how CRS are filtered. + +.. seealso:: :py:func:`setFilters` +%End + + virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const; + + + QgsCoordinateReferenceSystem crs( const QModelIndex &index ) const; +%Docstring +Returns the CRS for the corresponding ``index``. + +Returns an invalid CRS if the index is not valid. +%End + +}; + +QFlags operator|(QgsRecentCoordinateReferenceSystemsProxyModel::Filter f1, QFlags f2); + + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in b/python/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in index d7f80300b0b..42aaa113bd1 100644 --- a/python/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in +++ b/python/gui/auto_generated/proj/qgsrecentcoordinatereferencesystemsmodel.sip.in @@ -63,6 +63,69 @@ Returns an invalid CRS if the index is not valid. }; +class QgsRecentCoordinateReferenceSystemsProxyModel: QSortFilterProxyModel +{ +%Docstring(signature="appended") +A sort/filter proxy model for recent coordinate reference systems. + +.. versionadded:: 3.36 +%End + +%TypeHeaderCode +#include "qgsrecentcoordinatereferencesystemsmodel.h" +%End + public: + + enum Filter + { + FilterHorizontal, + FilterVertical, + FilterCompound, + }; + typedef QFlags Filters; + + + explicit QgsRecentCoordinateReferenceSystemsProxyModel( QObject *parent /TransferThis/ = 0 ); +%Docstring +Constructor for QgsRecentCoordinateReferenceSystemsProxyModel, with the given ``parent`` object. +%End + + QgsRecentCoordinateReferenceSystemsModel *recentCoordinateReferenceSystemsModel(); +%Docstring +Returns the underlying source model. +%End + + + void setFilters( QgsRecentCoordinateReferenceSystemsProxyModel::Filters filters ); +%Docstring +Set ``filters`` that affect how CRS are filtered. + +.. seealso:: :py:func:`filters` +%End + + Filters filters() const; +%Docstring +Returns any filters that affect how CRS are filtered. + +.. seealso:: :py:func:`setFilters` +%End + + virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const; + + + QgsCoordinateReferenceSystem crs( const QModelIndex &index ) const; +%Docstring +Returns the CRS for the corresponding ``index``. + +Returns an invalid CRS if the index is not valid. +%End + +}; + +QFlags operator|(QgsRecentCoordinateReferenceSystemsProxyModel::Filter f1, QFlags f2); + + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.cpp b/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.cpp index 85d41576194..7a9c8b81d84 100644 --- a/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.cpp +++ b/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.cpp @@ -129,3 +129,83 @@ void QgsRecentCoordinateReferenceSystemsModel::recentCrsCleared() mCrs.clear(); endResetModel(); } + + + +// +// QgsRecentCoordinateReferenceSystemsProxyModel +// + +QgsRecentCoordinateReferenceSystemsProxyModel::QgsRecentCoordinateReferenceSystemsProxyModel( QObject *parent ) + : QSortFilterProxyModel( parent ) + , mModel( new QgsRecentCoordinateReferenceSystemsModel( this ) ) +{ + setSourceModel( mModel ); + setDynamicSortFilter( true ); +} + +QgsRecentCoordinateReferenceSystemsModel *QgsRecentCoordinateReferenceSystemsProxyModel::recentCoordinateReferenceSystemsModel() +{ + return mModel; +} + +const QgsRecentCoordinateReferenceSystemsModel *QgsRecentCoordinateReferenceSystemsProxyModel::recentCoordinateReferenceSystemsModel() const +{ + return mModel; +} + +void QgsRecentCoordinateReferenceSystemsProxyModel::setFilters( QgsRecentCoordinateReferenceSystemsProxyModel::Filters filters ) +{ + if ( mFilters == filters ) + return; + + mFilters = filters; + invalidateFilter(); +} + +bool QgsRecentCoordinateReferenceSystemsProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const +{ + if ( !mFilters ) + return true; + + const QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent ); + + const Qgis::CrsType type = mModel->crs( sourceIndex ).type(); + switch ( type ) + { + case Qgis::CrsType::Unknown: + case Qgis::CrsType::Other: + break; + + case Qgis::CrsType::Geodetic: + case Qgis::CrsType::Geocentric: + case Qgis::CrsType::Geographic2d: + case Qgis::CrsType::Geographic3d: + case Qgis::CrsType::Projected: + case Qgis::CrsType::Temporal: + case Qgis::CrsType::Engineering: + case Qgis::CrsType::Bound: + case Qgis::CrsType::DerivedProjected: + if ( !mFilters.testFlag( Filter::FilterHorizontal ) ) + return false; + break; + + case Qgis::CrsType::Vertical: + if ( !mFilters.testFlag( Filter::FilterVertical ) ) + return false; + break; + + case Qgis::CrsType::Compound: + if ( !mFilters.testFlag( Filter::FilterCompound ) ) + return false; + break; + } + + return true; +} + +QgsCoordinateReferenceSystem QgsRecentCoordinateReferenceSystemsProxyModel::crs( const QModelIndex &index ) const +{ + const QModelIndex sourceIndex = mapToSource( index ); + return mModel->crs( sourceIndex ); +} diff --git a/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.h b/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.h index d7a39541044..630e3ebe181 100644 --- a/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.h +++ b/src/gui/proj/qgsrecentcoordinatereferencesystemsmodel.h @@ -84,4 +84,72 @@ class GUI_EXPORT QgsRecentCoordinateReferenceSystemsModel : public QAbstractItem }; +/** + * \brief A sort/filter proxy model for recent coordinate reference systems. + * + * \ingroup gui + * \since QGIS 3.36 + */ +class GUI_EXPORT QgsRecentCoordinateReferenceSystemsProxyModel: public QSortFilterProxyModel +{ + Q_OBJECT + + public: + + //! Available filter flags for filtering the model + enum Filter + { + FilterHorizontal = 1 << 1, //!< Include horizontal CRS (excludes compound CRS containing a horizontal component) + FilterVertical = 1 << 2, //!< Include vertical CRS (excludes compound CRS containing a vertical component) + FilterCompound = 1 << 3, //!< Include compound CRS + }; + Q_DECLARE_FLAGS( Filters, Filter ) + Q_FLAG( Filters ) + + /** + * Constructor for QgsRecentCoordinateReferenceSystemsProxyModel, with the given \a parent object. + */ + explicit QgsRecentCoordinateReferenceSystemsProxyModel( QObject *parent SIP_TRANSFERTHIS = nullptr ); + + /** + * Returns the underlying source model. + */ + QgsRecentCoordinateReferenceSystemsModel *recentCoordinateReferenceSystemsModel(); + + /** + * Returns the underlying source model. + * \note Not available in Python bindings + */ + const QgsRecentCoordinateReferenceSystemsModel *recentCoordinateReferenceSystemsModel() const SIP_SKIP; + + /** + * Set \a filters that affect how CRS are filtered. + * \see filters() + */ + void setFilters( QgsRecentCoordinateReferenceSystemsProxyModel::Filters filters ); + + /** + * Returns any filters that affect how CRS are filtered. + * \see setFilters() + */ + Filters filters() const { return mFilters; } + + bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; + + /** + * Returns the CRS for the corresponding \a index. + * + * Returns an invalid CRS if the index is not valid. + */ + QgsCoordinateReferenceSystem crs( const QModelIndex &index ) const; + + private: + + QgsRecentCoordinateReferenceSystemsModel *mModel = nullptr; + Filters mFilters = Filters(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QgsRecentCoordinateReferenceSystemsProxyModel::Filters ) + + #endif // QGSRECENTCOORDINATEREFERENCESYSTEMSMODEL_H diff --git a/tests/src/python/test_qgsrecentcoordinatereferencesystemsmodel.py b/tests/src/python/test_qgsrecentcoordinatereferencesystemsmodel.py index ac410b08882..565027494a6 100644 --- a/tests/src/python/test_qgsrecentcoordinatereferencesystemsmodel.py +++ b/tests/src/python/test_qgsrecentcoordinatereferencesystemsmodel.py @@ -19,11 +19,11 @@ from qgis.core import ( Qgis, QgsApplication, QgsCoordinateReferenceSystem, - QgsCoordinateReferenceSystemUtils, QgsSettings, ) from qgis.gui import ( QgsRecentCoordinateReferenceSystemsModel, + QgsRecentCoordinateReferenceSystemsProxyModel ) import unittest @@ -155,6 +155,82 @@ class TestQgsRecentCoordinateReferenceSystemsModel(QgisTestCase): ) self.assertEqual(model.rowCount(), 46) + def test_proxy(self): + registry = QgsApplication.coordinateReferenceSystemRegistry() + registry.clearRecent() + + model = QgsRecentCoordinateReferenceSystemsProxyModel() + # should initially be nothing in the model + self.assertEqual(model.rowCount(), 0) + self.assertFalse(model.index(-1, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(0, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(1, 0, QModelIndex()).isValid()) + self.assertIsNone( + model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole)) + self.assertFalse(model.crs(model.index(0, 0, QModelIndex())).isValid()) + self.assertFalse( + model.crs(model.index(-1, 0, QModelIndex())).isValid()) + self.assertFalse(model.crs(model.index(1, 0, QModelIndex())).isValid()) + + # push a crs + registry.pushRecent(QgsCoordinateReferenceSystem('EPSG:3111')) + self.assertEqual(model.rowCount(), 1) + self.assertTrue(model.index(0, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(1, 0, QModelIndex()).isValid()) + self.assertEqual( + model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), + 'EPSG:3111 - GDA94 / Vicgrid') + self.assertIsNone( + model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole)) + self.assertIsNone( + model.data(model.index(-1, 0, QModelIndex()), Qt.DisplayRole)) + self.assertEqual(model.crs(model.index(0, 0, QModelIndex())), + QgsCoordinateReferenceSystem('EPSG:3111')) + + # push a vertical crs + registry.pushRecent(QgsCoordinateReferenceSystem('ESRI:115851')) + self.assertEqual(model.rowCount(), 2) + self.assertTrue(model.index(0, 0, QModelIndex()).isValid()) + self.assertTrue(model.index(1, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(2, 0, QModelIndex()).isValid()) + self.assertEqual( + model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), + 'ESRI:115851 - SIRGAS-CON_DGF00P01') + self.assertEqual( + model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), + 'EPSG:3111 - GDA94 / Vicgrid') + self.assertIsNone( + model.data(model.index(2, 0, QModelIndex()), Qt.DisplayRole)) + self.assertEqual(model.crs(model.index(0, 0, QModelIndex())), + QgsCoordinateReferenceSystem('ESRI:115851')) + self.assertEqual(model.crs(model.index(1, 0, QModelIndex())), + QgsCoordinateReferenceSystem('EPSG:3111')) + + # add filter + model.setFilters(QgsRecentCoordinateReferenceSystemsProxyModel.Filter.FilterHorizontal) + self.assertEqual(model.rowCount(), 1) + self.assertTrue(model.index(0, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(1, 0, QModelIndex()).isValid()) + self.assertEqual( + model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), + 'EPSG:3111 - GDA94 / Vicgrid') + self.assertIsNone( + model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole)) + self.assertEqual(model.crs(model.index(0, 0, QModelIndex())), + QgsCoordinateReferenceSystem('EPSG:3111')) + + model.setFilters(QgsRecentCoordinateReferenceSystemsProxyModel.Filter.FilterVertical) + self.assertEqual(model.rowCount(), 1) + self.assertTrue(model.index(0, 0, QModelIndex()).isValid()) + self.assertFalse(model.index(1, 0, QModelIndex()).isValid()) + self.assertEqual( + model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), + 'ESRI:115851 - SIRGAS-CON_DGF00P01') + self.assertIsNone( + model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole)) + self.assertEqual(model.crs(model.index(0, 0, QModelIndex())), + QgsCoordinateReferenceSystem('ESRI:115851')) + if __name__ == '__main__': unittest.main()