12

The following is a simple setup containing a group with three layers.

Simple setup

The code I use adds a new layer at the end of this group:

root = QgsProject.instance().layerTreeRoot()
group = root.findGroup('Main group')

vlayer = QgsVectorLayer('LineString?crs=epsg:27700', 'vlayer', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(vlayer, False)
group.insertChildNode(-1, QgsLayerTreeLayer(vlayer))  

New layer added at end of group

In the Layer Order Panel, the newly added layer is at the end.

Layer Order Panel

Is it possible to move this to the top without moving the layer in the Layers Panel?

4 Answers 4

11

You can set layer order in the Layer Order Panel manually using QgsLayerTreeCanvasBridge.setCustomLayerOrder() method, which receives an ordered list of layer IDs.

For instance (assuming you just loaded vlayer):

bridge = iface.layerTreeCanvasBridge() 
order = bridge.customLayerOrder()
order.insert(0, order.pop(order.index(vlayer.id()))) # vlayer to the top
bridge.setCustomLayerOrder(order)

To automatically move newly added layers to the top of Layer Order Panel, you could use the legendLayersAdded SIGNAL (this signal is appropriate because it's emitted after the Layer Order Panel gets the new layer) from QgsMapLayerRegistry and reorder layers in this way:

def rearrange(layers):
    order = iface.layerTreeCanvasBridge().customLayerOrder()
    for layer in layers: # How many layers we need to move
        order.insert(0, order.pop()) # Last layer to first position
    iface.layerTreeCanvasBridge().setCustomLayerOrder(order)

QgsMapLayerRegistry.instance().legendLayersAdded.connect(rearrange)

NOTE: Since you're loading your vlayer calling QgsMapLayerRegistry.instance().addMapLayer(vlayer, False), that False parameter prevents the legendLayersAdded SIGNAL from being emitted. So, the automatic approach won't work for your case and you will need to rearrange layers manually (first approach of this answer).

2
  • I'm inexperienced using Python in QGIS- Where do you enter this script text? Is it saved to the specific QGIS project file?
    – Theo F
    Commented Nov 14, 2017 at 16:54
  • It is intended to be executed in the QGIS Python Console. If you need it to be more perdurable, check this answer to see how a code snippet can be executed each time QGIS is open or each time a particular QGIS project is open: gis.stackexchange.com/questions/194055/… Commented Nov 14, 2017 at 18:18
9

For QGIS 3:

I've found maybe a substitute command:

bridge = iface.layerTreeCanvasBridge() 
bridge.rootGroup().customLayerOrder()

From https://qgis.org/api/classQgsLayerTree.html#aab7a55f2e61f0ff1dbea6ceb89e8b366:

The order in which layers will be rendered on the canvas. Will only be used if the property hasCustomLayerOrder is true. If you need the current layer order that is active, prefer using layerOrder().

4

Based on this (old) question and some other research, I've successfully added a handler to my QGIS 3.10 project to move all newly added layers to the top of the custom layer order. Here's the relevant project macro code with v3 object references.

from qgis.core import QgsProject
from qgis.utils import iface

def openProject():
    global layersToTopHandler # keep so can disconnect() it in closeProject()
    def layersToTopHandler( layers ):
        # moves last len(layers) layers to top of custom layer order
        # note pays no attention to what is in the list given, always moves bottom layers
        # intended as a legendLayersAdded signal handler so the new layers have just been put at bottom! 
        rg = iface.layerTreeCanvasBridge().rootGroup()
        if rg.hasCustomLayerOrder():
            # Only do anything if custom layer order is turned on
            order = rg.customLayerOrder()
            for layer in layers: # How many layers we need to move
                order.insert( 0, order.pop() ) # Last layer to first position
            rg.setCustomLayerOrder( order )
    QgsProject.instance().legendLayersAdded.connect( layersToTopHandler )

def saveProject():
    pass

def closeProject():
    QgsProject.instance().legendLayersAdded.disconnect(layersToTopHandler)
    # needed since otherwise signal would go to slot even when change projects 

Edit: My answer originally didn't have the global line and just a disconnect() in closeProject(). This subtly failed in 3.10.2 (making it impossible to add layers in a subsequently opened project) since the argument-less disconnect() would also clobber some core slots on this signal. There may be more elegant ways to keep track of our own slot than to globalize the event handler itself but this seems to work. Note it's tempting to not bother disconnecting, assuming closing the project and opening a new one will reinitialize everything, but slots/signals stay persistent!

1
  • Thank you for your answer, this will be useful to know when I port over the relevant code to QGIS 3 :)
    – Joseph
    Commented Nov 8, 2019 at 14:39
2

I am not so familiar with pyqgis, but when I changed the line in your code from

group.insertChildNode(-1, QgsLayerTreeLayer(vlayer)) 

to

group.insertChildNode(0, QgsLayerTreeLayer(vlayer)) 

It was intserted at the top of the group, not the bottom.

4
  • 1
    Thanks for your answer, you are correct in that replacing -1 with 0 places the layer at the top of the Layers Panel but not in the Layer Order Panel. Also, I would like the new layer to be added to the bottom of the Layers Panel :)
    – Joseph
    Commented Feb 23, 2017 at 11:03
  • 1
    You are right, I missed that.
    – ahmadhanb
    Commented Feb 23, 2017 at 11:05
  • 1
    It would be somewhat of a workaround to add the layer at the top of the group and disable the Control rendering order as this would place the layer at the top of the Layer Order Panel. But for my use-case, it wouldn't be tidy :)
    – Joseph
    Commented Feb 23, 2017 at 11:08
  • 1
    I am sure someone with more experience than me in pyqgis will find a solution for you.
    – ahmadhanb
    Commented Feb 23, 2017 at 11:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.