QGIS – How to Sort Layers in QGIS Table of Contents

layersqgis

When I load a bunch of layers into QGIS from PostGIS, they seem to load in reverse alphabetical order.

Is there any tools or plugins that allow you to quickly sort the QGIS TOC in a different order? For example I can think of a couple of options one might want:

  1. Sort layers by geometry, where points are on top, then lines, polys, & rasters.
  2. Sort Layers by name (alphabetical, reverse alphabetical).
  3. Sort first by geometry, then by name. This would be a combination of #1 & #2. Points layers are sorted to the top (same as #1), but then points layers are sorted alphabetically (point-a, point-b, point-c, etc).

When loading a large number of layers, this would help to rapidly build up the right ordering for map production.

Best Answer

When I load a bunch of layers into QGIS from PostGIS, they seem to load in reverse alphabetical order.

This appears to be a bug (even in master branch) when using the add layer action Add PostGIS Layers... and should be reported on the QGIS bug tracker. However, if you use the new browser to load PostGIS layers, they will not be reversed. The new DB Manager does not allow multiple selections of tables, so no issue there (other than the lack of multiple selection).

Is there any tools or plugins that allow you to quickly sort the QGIS TOC in a different order? For example I can think of a couple of options one might want:

2) Sort Layers by name (alphabetical, reverse alphabetical).

This can currently be done by using the native column or item sorting of the base class of the QgsLegend (QTreeWidget). This will sort ALL ITEMS, both layers and groups together, not groups grouped above or below top-level layers. It will also sort within groups. There is NO UNDO of sorting like this.

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.utils import iface

mw = iface.mainWindow()
lgd = mw.findChild(QTreeWidget, "theMapLegend")  # get ref to object by type/objectName
lgd.sortItems(0, Qt.AscendingOrder)  # sort first column (Qt.DescendingOrder to reverse)

Caveat: If the main legend also controls layer render order (default), this should work OK. If the Layer Order dock widget is used separately to control render order, then the sort order in the legend is not honored and the following sort operation may lead to undesired results (continuing from above):

lo = mw.findChild(QListWidget, "theMapLayerOrder")
lo.sortItems(Qt.AscendingOrder)

From this code you can see that the Layer Order listing is a QListWidget, not a QTreeWidget, which means sorting will NOT take into account any groupings. All layers are presented in a flat list, as if they were all top-level. So, any layers currently nested in groups may sort above below top-level layers, if you apply the above sorting code to the QListWidget.

1) Sort layers by geometry, where points are on top, then lines, polys, & rasters.

3) Sort first by geometry, then by name. This would be a combination of #1 & #2. Points layers are sorted to the top (same as #1), but then points layers are sorted alphabetically (point-a, point-b, point-c, etc).

Currently, using Python, there is limited functionality for manipulating the QgsLegend. There is the QgsLegendInterface but this does not have all the goodies that are present in the QgsLegend, QgsLegendLayer, the inherited QgsLegendItem, or any of the other classes associated with QgsLegend.

Although, if you are starting with a clean project, and don't mind using groups, the following is currently possible (continuing from initial code block):

li = iface.legendInterface()
li.addGroup('A_Points')
li.addGroup('B_Lines')
li.addGroup('C_Polygons')
li.addGroup('D_Rasters')

for l in li.layers():
  if l.type() == QgsMapLayer.VectorLayer:
    if l.geometryType() == QGis.Point:
      li.moveLayer(l, 0)
    elif l.geometryType() == QGis.Line:
      li.moveLayer(l, 1)
    elif l.geometryType() == QGis.Polygon:
      li.moveLayer(l, 2)
  elif l.type() == QgsMapLayer.RasterLayer:
    li.moveLayer(l, 3)

lgd.sortItems(0, Qt.AscendingOrder)

(Assumes no other groups exist.) This would be run from console ( or ScriptRunner script, or Plugin, or loaded into new console in master) after the layers were added. It will create groups with sortable names, add layers to correct groups, then sort the whole thing. Same caveat applies, as above.

More checks for correct group indices would be needed for a more robust solution when adding new layers to an existing project with already-loaded layers and groups.