In part 1 we covered how to access the project's layer treeand read its data. Now let's focus on building and manipulating layer trees.We'll also look at how to receive updates about changes within a layer tree.
Starting with an empty project, first we will get access to the layer tree and create some memorylayers for testing:
root = QgsProject.instance().layerTreeRoot()
layer1 = QgsVectorLayer("Point", "Layer 1", "memory")
layer2 = QgsVectorLayer("Polygon", "Layer 2", "memory")
Now let's add some layers to the project's layer tree. There are two ways of doing that:
addLayer()
or insertLayer()
call of the QgsLayerTreeGroup
class. The former appends to the group node, while the latter allows you to specify the index at which the layer should be added.True
):This behaviour is facilitated by the QgsLayerTreeRegistryBridge
class. By default it inserts layers at the first position of the root node. The insertion point for new layers can be changed - within the QGIS application the insertion point is updated whenever the current selection in the layer tree view changes.
Groups can be added using the addGroup()
or insertGroup()
calls of the QgsLayerTreeGroup
class:
node_group1 = root.insertGroup(0, "Group 1")
# add another sub-group to group1
node_subgroup1 = node_group1.addGroup("Sub-group 1")
There are also the general addChildNode()
, insertChildNode()
and insertChildNodes()
callsthat allow the addition of existing nodes:
QgsMapLayerRegistry.instance().addMapLayer(layer2, False)
node_layer2 = QgsLayerTreeLayer(layer2)
root.insertChildNode(0, node_layer2)
node_group2 = QgsLayerTreeGroup("Group 2")
root.addChildNode(node_group2)
Nodes that are being added must not have any parent yet (i.e. being part of some layer tree).On the other hand, the nodes that get inserted may already have children, so it is possibleto create a whole sub-tree and then add it in one operation to the project's layer tree.
The removal of nodes from a layer tree is always done from the parent group node. For example, nodes displayedas top-level items need to be removed from the root node. There are several ways of removing them.The most general form is to use the removeChildren()
method that takes two arguments: the index of the firstchild node to be removed and how many child nodes to remove. Removal of a group node will also remove allof its children.
There are several convenience methods for removal:
root.removeChildNode(node_group2)
root.removeLayer(layer1)
There is one more way to remove layers from the project's layer tree:
QgsMapLayerRegistry.instance().addMapLayer(layer1)
The project's layer tree is notified when any map layers are being removed from the map layer registryand the layer nodes representing affected layers will be automatically removed from the layer tree.This is handled by the QgsLayerTreeRegistryBridge
class mentioned earlier.
When managing the layer tree, it is often necessary to move some nodes to a different position - within the same parent node or to a different parent node (group). Moving a node is done in threesteps: 1. clone the existing node, 2. add the cloned node to the desired place in layer tree, 3. removethe original node. The following code assumes that the existing node we move is a child of the root node:
cloned_group1 = node_group1.clone()
root.insertChildNode(0, cloned_group1)
root.removeChildNode(node_group1)
There are a number of operations one can do with nodes:
Use the customProperties()
call to get a list of keys of custom properties, then the customProperty()
method for getting the value of a particular key. To modify properties, there is a setCustomProperty()
method which sets a key-value pair and a removeCustomProperty()
method to remove a pair.
node_group1.setCustomProperty("test_key", "test_value")
print node_group1.customProperties()
print node_group1.customProperty("test_key")
node_group1.removeCustomProperty("test_key")
print node_group1.customProperties()
There are various signals emitted by nodes which may be used by client code to follow changes to the layer tree.Signals from children are automatically propagated to their parent node, so it is enough to connectto the root node to listen for changes from any level of the tree.
The addition of new nodes always emits a pair of signals - before and after the actual addition.Signals pass information about which node is the parent node and the range of child indices:
willAddChildren(node, indexFrom, indexTo)
addedChildren(node, indexFrom, indexTo)
In order to access the newly added nodes, it is necessary to use the addedChildren
signal.
The following code sample illustrates how to connect to signals emitted from the layer tree. When the last line is executed, two lines from the newly defined methods should be printed to the console:
def onWillAddChildren(node, indexFrom, indexTo):
print "WILL ADD", node, indexFrom, indexTo
def onAddedChildren(node, indexFrom, indexTo):
print "ADDED", node, indexFrom, indexTo
root.willAddChildren.connect(onWillAddChildren)
root.addedChildren.connect(onAddedChildren)
g = root.addGroup("My Group")
Removal of nodes is handled in a very similar manner to the addition - there is also a pair of signals:
willRemoveChildren(node, indexFrom, indexTo)
removedChildren(node, indexFrom, indexTo)
This time in order to access nodes being removed it is necessary to connect to the willRemoveChildren
signal.
There are a few more signals that allow monitoring of internal changes to nodes:
visibilityChanged(node, state)
customPropertyChanged(node, key)
expandedChanged(node, expanded)
We have covered how to make changes to a layer tree structure and how to listen for changespossibly made by other pieces of code. In a future post we look at GUI componentsfor displaying and modifying the layer tree and the connection between map canvas and layer tree.
Let's make the QGIS work for you
Lutra Consulting is a QGIS-focused expert provider of geospatial software development, consulting, training, and support services.