QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayertree.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertree
3 ---------------------
4 begin : 22.3.2017
5 copyright : (C) 2017 by Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgslayertree.h"
18#include "qgsvectorlayer.h"
19
21{
22 init();
23}
24
26 : QgsLayerTreeGroup( other )
27 , mCustomLayerOrder( other.mCustomLayerOrder )
28 , mHasCustomLayerOrder( other.mHasCustomLayerOrder )
29{
30 init();
31}
32
33void QgsLayerTree::init()
34{
35 connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
36 connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
37}
38
39QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const
40{
41 return _qgis_listQPointerToRaw( mCustomLayerOrder );
42}
43
44void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder )
45{
46 QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder );
47
48 if ( newOrder == mCustomLayerOrder )
49 return;
50
51 mCustomLayerOrder = newOrder;
53
54 if ( mHasCustomLayerOrder )
55 emit layerOrderChanged();
56}
57
58void QgsLayerTree::setCustomLayerOrder( const QStringList &customLayerOrder )
59{
60 QList<QgsMapLayer *> layers;
61
62 for ( const auto &layerId : customLayerOrder )
63 {
64 QgsLayerTreeLayer *nodeLayer = findLayer( layerId );
65 if ( nodeLayer )
66 {
67 // configuration from 2.x projects might have non spatial layers
68 QgsMapLayer *layer = nodeLayer->layer();
69 if ( !layer || !layer->isSpatial() )
70 {
71 continue;
72 }
73 layers.append( layer );
74 }
75 }
76 setCustomLayerOrder( layers );
77}
78
79QList<QgsMapLayer *> QgsLayerTree::layerOrder() const
80{
81 if ( mHasCustomLayerOrder )
82 {
83 return customLayerOrder();
84 }
85 else
86 {
88 }
89}
90
92{
93 return mHasCustomLayerOrder;
94}
95
96void QgsLayerTree::setHasCustomLayerOrder( bool hasCustomLayerOrder )
97{
98 if ( hasCustomLayerOrder == mHasCustomLayerOrder )
99 return;
100
101 mHasCustomLayerOrder = hasCustomLayerOrder;
102
104 emit layerOrderChanged();
105}
106
107QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context )
108{
109 QgsLayerTree *tree = new QgsLayerTree();
110
111 tree->readCommonXml( element );
112
113 tree->readChildrenFromXml( element, context );
114
115 return tree;
116}
117
118void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
119{
120 QDomDocument doc = parentElement.ownerDocument();
121 QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
122
123 writeCommonXml( elem );
124
125 for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
126 node->writeXml( elem, context );
127
128 QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
129 customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
130 elem.appendChild( customOrderElem );
131
132 for ( QgsMapLayer *layer : std::as_const( mCustomLayerOrder ) )
133 {
134 // Safety belt, see https://github.com/qgis/QGIS/issues/26975
135 // Crash when deleting an item from the layout legend
136 if ( ! layer )
137 continue;
138 QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) );
139 layerElem.appendChild( doc.createTextNode( layer->id() ) );
140 customOrderElem.appendChild( layerElem );
141 }
142
143 elem.appendChild( customOrderElem );
144
145 parentElement.appendChild( elem );
146}
147
149{
150 return new QgsLayerTree( *this );
151}
152
154{
156 setHasCustomLayerOrder( false );
157 setCustomLayerOrder( QStringList() );
158}
159
160void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
161{
162 Q_ASSERT( node );
163
164 // collect layer IDs that have been added in order to put them into custom layer order
165 QList<QgsMapLayer *> layers;
166
167 QList<QgsLayerTreeNode *> children = node->children();
168 for ( int i = indexFrom; i <= indexTo; ++i )
169 {
170 QgsLayerTreeNode *child = children.at( i );
171 if ( QgsLayerTree::isLayer( child ) )
172 {
173 layers << QgsLayerTree::toLayer( child )->layer();
174 }
175 else if ( QgsLayerTree::isGroup( child ) )
176 {
177 const auto nodeLayers = QgsLayerTree::toGroup( child )->findLayers();
178 for ( QgsLayerTreeLayer *nodeL : nodeLayers )
179 layers << nodeL->layer();
180 }
181 }
182
183 for ( QgsMapLayer *layer : std::as_const( layers ) )
184 {
185 if ( !mCustomLayerOrder.contains( layer ) && layer )
186 mCustomLayerOrder.append( layer );
187 }
188
190 emit layerOrderChanged();
191}
192
193void QgsLayerTree::nodeRemovedChildren()
194{
195 QList<QgsMapLayer *> layers = customLayerOrder();
196 auto layer = layers.begin();
197
198 while ( layer != layers.end() )
199 {
200 if ( !findLayer( *layer ) )
201 layer = layers.erase( layer );
202 else
203 ++layer;
204 }
205
206 // we need to ensure that the customLayerOrderChanged signal is ALWAYS raised
207 // here, since that order HAS changed due to removal of the child!
208 // setCustomLayerOrder will only emit this signal when the layers list
209 // at this stage is different to the stored customer layer order. If this
210 // isn't the case (i.e. the lists ARE the same) then manually emit the
211 // signal
212 const bool emitSignal = _qgis_listRawToQPointer( layers ) == mCustomLayerOrder;
213
214 setCustomLayerOrder( layers );
215 if ( emitSignal )
217
218 emit layerOrderChanged();
219}
220
221void QgsLayerTree::addMissingLayers()
222{
223 bool changed = false;
224
225 const QList< QgsLayerTreeLayer * > layers = findLayers();
226 for ( const auto layer : layers )
227 {
228 if ( !mCustomLayerOrder.contains( layer->layer() ) &&
229 layer->layer() && layer->layer()->isSpatial() )
230 {
231 mCustomLayerOrder.append( layer->layer() );
232 changed = true;
233 }
234 }
235
236 if ( changed )
237 {
239 if ( mHasCustomLayerOrder )
240 emit layerOrderChanged();
241 }
242}
243
244void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem )
245{
246 QStringList order;
247
248 QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
249 if ( !customOrderElem.isNull() )
250 {
251 setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() );
252
253 QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
254 while ( !itemElem.isNull() )
255 {
256 order.append( itemElem.text() );
257 itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
258 }
259 }
260
261 setCustomLayerOrder( order );
262 addMissingLayers();
263}
Layer tree group node serves as a container for layers and further groups.
QList< QgsMapLayer * > layerOrderRespectingGroupLayers() const
Returns an ordered list of map layers in the group, ignoring any layers which are child layers of Qgs...
void removeAllChildren()
Remove all child nodes.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
This class is a base class for nodes in a layer tree.
void readCommonXml(QDomElement &element)
Read common XML elements.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void writeCommonXml(QDomElement &element)
Write common XML elements.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write layer tree to XML.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree()
Create a new empty layer tree.
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:41
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
void setCustomLayerOrder(const QList< QgsMapLayer * > &customLayerOrder)
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:60
void layerOrderChanged()
Emitted when the layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
The class is used as a container of context for various read/write operations on other objects.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:2355