QGIS API Documentation  3.0.2-Girona (307d082)
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
6  email : matthias@opengis.ch
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"
17 #include "qgsmaplayerlistutils.h"
18 #include "qgsvectorlayer.h"
19 
21 {
22  connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
23  connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
24 }
25 
27  : QgsLayerTreeGroup( other )
28  , mCustomLayerOrder( other.mCustomLayerOrder )
29  , mHasCustomLayerOrder( other.mHasCustomLayerOrder )
30 {
31  connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
32  connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
33 }
34 
35 QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const
36 {
37  return _qgis_listQPointerToRaw( mCustomLayerOrder );
38 }
39 
40 void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder )
41 {
42  QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder );
43 
44  if ( newOrder == mCustomLayerOrder )
45  return;
46 
47  mCustomLayerOrder = newOrder;
49 
50  if ( mHasCustomLayerOrder )
51  emit layerOrderChanged();
52 }
53 
55 {
56  QList<QgsMapLayer *> layers;
57 
58  for ( const auto &layerId : customLayerOrder )
59  {
60  QgsLayerTreeLayer *nodeLayer = findLayer( layerId );
61  if ( nodeLayer )
62  {
63  // configuration from 2.x projects might have non spatial layers
64  QgsMapLayer *layer = nodeLayer->layer();
65  if ( !layer || !layer->isSpatial() )
66  {
67  continue;
68  }
69  layers.append( layer );
70  }
71  }
72  setCustomLayerOrder( layers );
73 }
74 
75 QList<QgsMapLayer *> QgsLayerTree::layerOrder() const
76 {
77  if ( mHasCustomLayerOrder )
78  {
79  return customLayerOrder();
80  }
81  else
82  {
83  QList<QgsMapLayer *> layers;
84  const QList< QgsLayerTreeLayer * > foundLayers = findLayers();
85  for ( const auto &treeLayer : foundLayers )
86  {
87  QgsMapLayer *layer = treeLayer->layer();
88  if ( !layer || !layer->isSpatial() )
89  {
90  continue;
91  }
92  layers.append( layer );
93  }
94  return layers;
95  }
96 }
97 
99 {
100  return mHasCustomLayerOrder;
101 }
102 
104 {
105  if ( hasCustomLayerOrder == mHasCustomLayerOrder )
106  return;
107 
108  mHasCustomLayerOrder = hasCustomLayerOrder;
109 
110  emit hasCustomLayerOrderChanged( hasCustomLayerOrder );
111  emit layerOrderChanged();
112 }
113 
114 QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context )
115 {
116  QgsLayerTree *tree = new QgsLayerTree();
117 
118  tree->readCommonXml( element );
119 
120  tree->readChildrenFromXml( element, context );
121 
122  return tree;
123 }
124 
125 void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
126 {
127  QDomDocument doc = parentElement.ownerDocument();
128  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
129 
130  writeCommonXml( elem );
131 
132  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
133  node->writeXml( elem, context );
134 
135  QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
136  customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
137  elem.appendChild( customOrderElem );
138 
139  Q_FOREACH ( QgsMapLayer *layer, mCustomLayerOrder )
140  {
141  QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) );
142  layerElem.appendChild( doc.createTextNode( layer->id() ) );
143  customOrderElem.appendChild( layerElem );
144  }
145 
146  elem.appendChild( customOrderElem );
147 
148  parentElement.appendChild( elem );
149 }
150 
152 {
153  return new QgsLayerTree( *this );
154 }
155 
157 {
159  setHasCustomLayerOrder( false );
160  setCustomLayerOrder( QStringList() );
161 }
162 
163 void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
164 {
165  Q_ASSERT( node );
166 
167  // collect layer IDs that have been added in order to put them into custom layer order
168  QList<QgsMapLayer *> layers;
169 
170  QList<QgsLayerTreeNode *> children = node->children();
171  for ( int i = indexFrom; i <= indexTo; ++i )
172  {
173  QgsLayerTreeNode *child = children.at( i );
174  if ( QgsLayerTree::isLayer( child ) )
175  {
176  layers << QgsLayerTree::toLayer( child )->layer();
177  }
178  else if ( QgsLayerTree::isGroup( child ) )
179  {
180  Q_FOREACH ( QgsLayerTreeLayer *nodeL, QgsLayerTree::toGroup( child )->findLayers() )
181  layers << nodeL->layer();
182  }
183  }
184 
185  Q_FOREACH ( QgsMapLayer *layer, layers )
186  {
187  if ( !mCustomLayerOrder.contains( layer ) && layer )
188  mCustomLayerOrder.append( layer );
189  }
190 
192  emit layerOrderChanged();
193 }
194 
195 void QgsLayerTree::nodeRemovedChildren()
196 {
197  QList<QgsMapLayer *> layers = customLayerOrder();
198  auto layer = layers.begin();
199 
200  while ( layer != layers.end() )
201  {
202  if ( !findLayer( *layer ) )
203  layer = layers.erase( layer );
204  else
205  ++layer;
206  }
207 
208  setCustomLayerOrder( layers );
209  emit layerOrderChanged();
210 }
211 
212 void QgsLayerTree::addMissingLayers()
213 {
214  bool changed = false;
215 
216  const QList< QgsLayerTreeLayer * > foundLayers = findLayers();
217  for ( const auto layer : foundLayers )
218  {
219  if ( !mCustomLayerOrder.contains( layer->layer() ) &&
220  layer->layer() && layer->layer()->isSpatial() )
221  {
222  mCustomLayerOrder.append( layer->layer() );
223  changed = true;
224  }
225  }
226 
227  if ( changed )
228  {
230  if ( mHasCustomLayerOrder )
231  emit layerOrderChanged();
232  }
233 }
234 
235 void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem )
236 {
237  QStringList order;
238 
239  QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
240  if ( !customOrderElem.isNull() )
241  {
242  setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() );
243 
244  QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
245  while ( !itemElem.isNull() )
246  {
247  order.append( itemElem.text() );
248  itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
249  }
250  }
251 
252  setCustomLayerOrder( order );
253  addMissingLayers();
254 }
The class is used as a container of context for various read/write operations on other objects...
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:56
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeAllChildren()
Remove all child nodes.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
void layerOrderChanged()
Emitted when the layer order has changed.
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
void writeCommonXml(QDomElement &element)
Write common XML elements.
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
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:53
This class is a base class for nodes in a layer tree.
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
QgsMapLayer * layer() const
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
void setCustomLayerOrder(const QList< QgsMapLayer *> &customLayerOrder)
The order in which layers will be rendered on the canvas.
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write layer tree to XML.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:1356
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
void readCommonXml(QDomElement &element)
Read common XML elements.
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsLayerTree()
Create a new empty layer tree.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Layer tree node points to a map layer.