QGIS Layer Sorting – How to Sort Layer Order Based on Mixed String/Numerical Names in PyQGIS

layersorderpyqgisqgissorting

In QGIS, is there a way to automatically order the layers in the Layers Panel? I'm looking to sort 50 layers, output of a Split vector layer operation (see here) in a predefined order that is not exclusively alphabetic. The tool does not open the layers in QGIS, but saves them to the disk.

Layers are named area_100, area_400, area_900, area_1400 etc. and I want to have them appear in this order, the smallest number on top so that the larger ones do not cover the smaller ones. However, alphabetic order is area_100, area_1400, area_400 etc.

A similar question has been asked almost 8 years before, but the solution recommending a plugin has become obsolete as it does not seem to have been updated to the current QGIS versions. The feature request linked in the other answer does not seem to have been realized.

This is how the folder with the saved Geopackage files looks like:

enter image description here

Best Answer

One option is to use pyqgis:

List the layers, Sort the list (using a lambda function if needed), Add the layers, they will be added in the sorted order.

import os
gpkg_folder = r'C:\GIS\data\testdata\geopackages'

layerlist = []
for root, folder, files in os.walk(gpkg_folder):
    for file in files:
        if file.endswith('.gpkg'): #For every geopackage in the folder
            fullname = os.path.join(root, file)
            templayer = QgsVectorLayer(path=fullname, baseName="test", providerLib="ogr") #https://stackoverflow.com/questions/57015320/how-to-list-all-layers-on-geopackage-using-pyqgis
            for sublayer in templayer.dataProvider().subLayers(): #For each layer in each geopackage
                #print(sublayer)
                name = sublayer.split('!!::!!')[1]
                uri = "%s|layername=%s" % (fullname, name,)
                sub_vlayer = QgsVectorLayer(uri, name, 'ogr') #Create a layer and add to layerlist
                layerlist.append(sub_vlayer)

#layerlist is now: [<QgsVectorLayer: 'area_1400' (ogr)>, <QgsVectorLayer: 'area_400' (ogr)>, <QgsVectorLayer: 'area_900' (ogr)>, <QgsVectorLayer: 'area_1500' (ogr)>]

layerlist.sort(key=lambda x: int(x.name().split('_')[1]))
#[<QgsVectorLayer: 'area_400' (ogr)>, <QgsVectorLayer: 'area_900' (ogr)>, <QgsVectorLayer: 'area_1400' (ogr)>, <QgsVectorLayer: 'area_1500' (ogr)>]

QgsProject.instance().addMapLayers(layerlist)

enter image description here

The lambda part works like this:

teststring = 'area_400'
print(teststring)
print(teststring.split('_'))
print(teststring.split('_')[1])
print(int(teststring.split('_')[1]))

area_400
['area', '400']
400 #It is still a string/text
400 #This is an integer which can be used to sort by
Related Question