Add method to sort a list of layers by layer type

This commit is contained in:
Nyall Dawson 2022-04-12 09:54:55 +10:00 committed by Martin Dobias
parent 7c6baf8be8
commit 904a312f40
5 changed files with 176 additions and 1 deletions

View File

@ -56,6 +56,16 @@ uses a data provider which does not specify paths in a layer URI.
.. versionadded:: 3.22 .. versionadded:: 3.22
%End %End
static QList< QgsMapLayer * > sortLayersByType( const QList< QgsMapLayer * > &layers, const QList< QgsMapLayerType > &order );
%Docstring
Sorts a list of map ``layers`` by their layer type, respecting the ``order`` of types specified.
Layer types which appear earlier in the ``order`` list will result in matching layers appearing earlier in the
result list.
.. versionadded:: 3.26
%End
}; };

View File

@ -22,6 +22,108 @@ which are not wrapped by PyQt:
- NULL QVariant which is missing in PyQt5 with sip.enableautoconversion - NULL QVariant which is missing in PyQt5 with sip.enableautoconversion
*/ */
// adapted from the qpymultimedia_qlist.sip file from the PyQt6 sources
%MappedType QList<QgsMapLayerType>
/TypeHintIn="Iterable[QgsMapLayerType]",
TypeHintOut="List[QgsMapLayerType]", TypeHintValue="[]"/
{
%TypeHeaderCode
#include "qgis.h"
%End
%ConvertFromTypeCode
PyObject *l = PyList_New(sipCpp->size());
if (!l)
return 0;
for (int i = 0; i < sipCpp->size(); ++i)
{
PyObject *eobj = sipConvertFromEnum(static_cast<int>(sipCpp->at(i)),
sipType_QgsMapLayerType);
if (!eobj)
{
Py_DECREF(l);
return 0;
}
PyList_SetItem(l, i, eobj);
}
return l;
%End
%ConvertToTypeCode
PyObject *iter = PyObject_GetIter(sipPy);
if (!sipIsErr)
{
PyErr_Clear();
Py_XDECREF(iter);
return (iter && !PyBytes_Check(sipPy) && !PyUnicode_Check(sipPy));
}
if (!iter)
{
*sipIsErr = 1;
return 0;
}
QList<QgsMapLayerType> *ql = new QList<QgsMapLayerType>;
for (Py_ssize_t i = 0; ; ++i)
{
PyErr_Clear();
PyObject *itm = PyIter_Next(iter);
if (!itm)
{
if (PyErr_Occurred())
{
delete ql;
Py_DECREF(iter);
*sipIsErr = 1;
return 0;
}
break;
}
int v = sipConvertToEnum(itm, sipType_QgsMapLayerType);
if (PyErr_Occurred())
{
PyErr_Format(PyExc_TypeError,
"index %zd has type '%s' but 'QgsMapLayerType' is expected",
i, sipPyTypeName(Py_TYPE(itm)));
Py_DECREF(itm);
delete ql;
Py_DECREF(iter);
*sipIsErr = 1;
return 0;
}
ql->append(static_cast<QgsMapLayerType>(v));
Py_DECREF(itm);
}
Py_DECREF(iter);
*sipCppPtr = ql;
return sipGetState(sipTransferObj);
%End
};
template <TYPE> template <TYPE>
%MappedType QVector< QVector<TYPE> > %MappedType QVector< QVector<TYPE> >

View File

@ -137,3 +137,20 @@ bool QgsMapLayerUtils::updateLayerSourcePath( QgsMapLayer *layer, const QString
layer->setDataSource( newUri, layer->name(), layer->providerType() ); layer->setDataSource( newUri, layer->name(), layer->providerType() );
return true; return true;
} }
QList<QgsMapLayer *> QgsMapLayerUtils::sortLayersByType( const QList<QgsMapLayer *> &layers, const QList<QgsMapLayerType> &order )
{
QList< QgsMapLayer * > res = layers;
std::sort( res.begin(), res.end(), [&order]( const QgsMapLayer * a, const QgsMapLayer * b ) -> bool
{
for ( QgsMapLayerType type : order )
{
if ( a->type() == type && b->type() != type )
return true;
else if ( b->type() == type )
return false;
}
return false;
} );
return res;
}

View File

@ -70,6 +70,16 @@ class CORE_EXPORT QgsMapLayerUtils
*/ */
static bool updateLayerSourcePath( QgsMapLayer *layer, const QString &newPath ); static bool updateLayerSourcePath( QgsMapLayer *layer, const QString &newPath );
/**
* Sorts a list of map \a layers by their layer type, respecting the \a order of types specified.
*
* Layer types which appear earlier in the \a order list will result in matching layers appearing earlier in the
* result list.
*
* \since QGIS 3.26
*/
static QList< QgsMapLayer * > sortLayersByType( const QList< QgsMapLayer * > &layers, const QList< QgsMapLayerType > &order );
}; };

View File

@ -20,7 +20,11 @@ from qgis.core import (
QgsCoordinateTransformContext, QgsCoordinateTransformContext,
QgsVectorLayer, QgsVectorLayer,
QgsRasterLayer, QgsRasterLayer,
QgsRectangle QgsAnnotationLayer,
QgsGroupLayer,
QgsRectangle,
QgsProject,
QgsMapLayerType
) )
from qgis.testing import start_app, unittest from qgis.testing import start_app, unittest
from utilities import unitTestDataPath from utilities import unitTestDataPath
@ -123,6 +127,38 @@ class TestQgsMapLayerUtils(unittest.TestCase):
self.assertFalse(QgsMapLayerUtils.updateLayerSourcePath(layer, unitTestDataPath() + '/mixed_layers22.gpkg')) self.assertFalse(QgsMapLayerUtils.updateLayerSourcePath(layer, unitTestDataPath() + '/mixed_layers22.gpkg'))
self.assertEqual(layer.source(), old_source) self.assertEqual(layer.source(), old_source)
def test_sort_layers_by_type(self):
vl1 = QgsVectorLayer("Point?field=x:string", 'vector 1', "memory")
vl2 = QgsVectorLayer("Point?field=x:string", 'vector 2', "memory")
options = QgsAnnotationLayer.LayerOptions(QgsProject.instance().transformContext())
al1 = QgsAnnotationLayer('annotations 1', options)
al2 = QgsAnnotationLayer('annotations 2', options)
rl1 = QgsRasterLayer(f'GPKG:{unitTestDataPath()}/mixed_layers.gpkg:band1', 'raster 1')
options = QgsGroupLayer.LayerOptions(QgsProject.instance().transformContext())
gp1 = QgsGroupLayer('group 1', options)
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], []), [vl1, rl1, gp1, vl2, al2, al1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], [QgsMapLayerType.VectorLayer]), [vl1, vl2, rl1, gp1, al2, al1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], [QgsMapLayerType.RasterLayer, QgsMapLayerType.VectorLayer]),
[rl1, vl1, vl2, gp1, al2, al1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], [QgsMapLayerType.GroupLayer, QgsMapLayerType.VectorLayer]),
[gp1, vl1, vl2, rl1, al2, al1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], [QgsMapLayerType.GroupLayer,
QgsMapLayerType.VectorLayer,
QgsMapLayerType.AnnotationLayer]),
[gp1, vl1, vl2, al2, al1, rl1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2, al2, al1], [QgsMapLayerType.GroupLayer,
QgsMapLayerType.VectorLayer,
QgsMapLayerType.RasterLayer,
QgsMapLayerType.AnnotationLayer]),
[gp1, vl1, vl2, rl1, al2, al1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2], [QgsMapLayerType.GroupLayer,
QgsMapLayerType.VectorLayer,
QgsMapLayerType.RasterLayer]),
[gp1, vl1, vl2, rl1])
self.assertEqual(QgsMapLayerUtils.sortLayersByType([vl1, rl1, gp1, vl2], [QgsMapLayerType.AnnotationLayer]),
[vl1, rl1, gp1, vl2])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()