QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
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.
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.
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.
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.
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.
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.