QGIS API Documentation  2.99.0-Master (e077efd)
qgslayertreegroup.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreegroup.cpp
3  --------------------------------------
4  Date : May 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
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 "qgslayertreegroup.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreeutils.h"
20 #include "qgsmaplayer.h"
21 #include "qgsmaplayerregistry.h"
22 
23 #include <QDomElement>
24 #include <QStringList>
25 
26 
27 QgsLayerTreeGroup::QgsLayerTreeGroup( const QString& name, Qt::CheckState checked )
28  : QgsLayerTreeNode( NodeGroup )
29  , mName( name )
30  , mChecked( checked )
31  , mChangingChildVisibility( false )
32  , mMutuallyExclusive( false )
33  , mMutuallyExclusiveChildIndex( -1 )
34 {
35  connect( this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
36 }
37 
39  : QgsLayerTreeNode( other )
40  , mName( other.mName )
41  , mChecked( other.mChecked )
45 {
46  connect( this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
47 }
48 
49 QString QgsLayerTreeGroup::name() const
50 {
51  return mName;
52 }
53 
54 void QgsLayerTreeGroup::setName( const QString& n )
55 {
56  if ( mName == n )
57  return;
58 
59  mName = n;
60  emit nameChanged( this, n );
61 }
62 
63 
65 {
66  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
67  insertChildNode( index, grp );
68  return grp;
69 }
70 
72 {
73  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
74  addChildNode( grp );
75  return grp;
76 }
77 
79 {
80  if ( !layer || QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) != layer )
81  return nullptr;
82 
83  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
84  insertChildNode( index, ll );
85  return ll;
86 }
87 
89 {
90  if ( !layer || QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) != layer )
91  return nullptr;
92 
93  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
94  addChildNode( ll );
95  return ll;
96 }
97 
99 {
100  QList<QgsLayerTreeNode*> nodes;
101  nodes << node;
102  insertChildNodes( index, nodes );
103 }
104 
105 void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode*>& nodes )
106 {
107  QgsLayerTreeNode* meChild = nullptr;
109  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
110 
111  // low-level insert
112  insertChildrenPrivate( index, nodes );
113 
114  if ( mMutuallyExclusive )
115  {
116  if ( meChild )
117  {
118  // the child could have change its index - or the new children may have been also set as visible
119  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
120  }
121  else if ( mChecked == Qt::Checked )
122  {
123  // we have not picked a child index yet, but we should pick one now
124  // ... so pick the first one from the newly added
125  if ( index == -1 )
126  index = mChildren.count() - nodes.count(); // get real insertion index
128  }
130  }
131 
133 }
134 
136 {
137  insertChildNode( -1, node );
138 }
139 
141 {
142  int i = mChildren.indexOf( node );
143  if ( i >= 0 )
144  removeChildren( i, 1 );
145 }
146 
148 {
149  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
150  {
151  if ( QgsLayerTree::isLayer( child ) )
152  {
153  QgsLayerTreeLayer* childLayer = QgsLayerTree::toLayer( child );
154  if ( childLayer->layer() == layer )
155  {
156  removeChildren( mChildren.indexOf( child ), 1 );
157  break;
158  }
159  }
160  }
161 }
162 
163 void QgsLayerTreeGroup::removeChildren( int from, int count )
164 {
165  QgsLayerTreeNode* meChild = nullptr;
167  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
168 
169  removeChildrenPrivate( from, count );
170 
171  if ( meChild )
172  {
173  // the child could have change its index - or may have been removed completely
174  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
175  // we need to uncheck this group
176  if ( mMutuallyExclusiveChildIndex == -1 )
177  setVisible( Qt::Unchecked );
178  }
179 
181 }
182 
184 {
185  // clean the layer tree by removing empty group
186  Q_FOREACH ( QgsLayerTreeNode* treeNode, children() )
187  {
188  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
189  {
190  QgsLayerTreeGroup* treeGroup = qobject_cast<QgsLayerTreeGroup*>( treeNode );
191  if ( treeGroup->findLayerIds().isEmpty() )
192  removeChildNode( treeNode );
193  else
195  }
196  }
197 }
198 
200 {
201  removeChildren( 0, mChildren.count() );
202 }
203 
204 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString& layerId ) const
205 {
206  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
207  {
208  if ( QgsLayerTree::isLayer( child ) )
209  {
210  QgsLayerTreeLayer* childLayer = QgsLayerTree::toLayer( child );
211  if ( childLayer->layerId() == layerId )
212  return childLayer;
213  }
214  else if ( QgsLayerTree::isGroup( child ) )
215  {
216  QgsLayerTreeLayer* res = QgsLayerTree::toGroup( child )->findLayer( layerId );
217  if ( res )
218  return res;
219  }
220  }
221  return nullptr;
222 }
223 
224 QList<QgsLayerTreeLayer*> QgsLayerTreeGroup::findLayers() const
225 {
226  QList<QgsLayerTreeLayer*> list;
227  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
228  {
229  if ( QgsLayerTree::isLayer( child ) )
230  list << QgsLayerTree::toLayer( child );
231  else if ( QgsLayerTree::isGroup( child ) )
232  list << QgsLayerTree::toGroup( child )->findLayers();
233  }
234  return list;
235 }
236 
238 {
239  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
240  {
241  if ( QgsLayerTree::isGroup( child ) )
242  {
243  QgsLayerTreeGroup* childGroup = QgsLayerTree::toGroup( child );
244  if ( childGroup->name() == name )
245  return childGroup;
246  else
247  {
248  QgsLayerTreeGroup* grp = childGroup->findGroup( name );
249  if ( grp )
250  return grp;
251  }
252  }
253  }
254  return nullptr;
255 }
256 
258 {
259  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
260  return nullptr;
261 
262  QString name = element.attribute( QStringLiteral( "name" ) );
263  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
264  Qt::CheckState checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) );
265  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
266  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
267 
268  QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( name, checked );
269  groupNode->setExpanded( isExpanded );
270 
271  groupNode->readCommonXml( element );
272 
273  groupNode->readChildrenFromXml( element );
274 
275  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
276 
277  return groupNode;
278 }
279 
280 void QgsLayerTreeGroup::writeXml( QDomElement& parentElement )
281 {
282  QDomDocument doc = parentElement.ownerDocument();
283  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
284  elem.setAttribute( QStringLiteral( "name" ), mName );
285  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
286  elem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( mChecked ) );
287  if ( mMutuallyExclusive )
288  {
289  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
290  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
291  }
292 
293  writeCommonXml( elem );
294 
295  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
296  node->writeXml( elem );
297 
298  parentElement.appendChild( elem );
299 }
300 
301 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement& element )
302 {
303  QList<QgsLayerTreeNode*> nodes;
304  QDomElement childElem = element.firstChildElement();
305  while ( !childElem.isNull() )
306  {
307  QgsLayerTreeNode* newNode = QgsLayerTreeNode::readXml( childElem );
308  if ( newNode )
309  nodes << newNode;
310 
311  childElem = childElem.nextSiblingElement();
312  }
313 
314  insertChildNodes( -1, nodes );
315 }
316 
317 QString QgsLayerTreeGroup::dump() const
318 {
319  QString header = QStringLiteral( "GROUP: %1 visible=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
320  QStringList childrenDump;
321  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
322  childrenDump << node->dump().split( '\n' );
323  for ( int i = 0; i < childrenDump.count(); ++i )
324  childrenDump[i].prepend( " " );
325  return header + childrenDump.join( QStringLiteral( "\n" ) );
326 }
327 
329 {
330  return new QgsLayerTreeGroup( *this );
331 }
332 
333 void QgsLayerTreeGroup::setVisible( Qt::CheckState state )
334 {
335  if ( mChecked == state )
336  return;
337 
338  mChecked = state;
339  emit visibilityChanged( this, state );
340 
341  if ( mMutuallyExclusive )
342  {
343  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
344  mMutuallyExclusiveChildIndex = 0; // just choose the first one if we have lost the active one
346  }
347  else if ( mChecked == Qt::Unchecked || mChecked == Qt::Checked )
348  {
350  }
351 }
352 
354 {
355  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
356 
357  // update children to have the correct visibility
358  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
359  {
360  if ( QgsLayerTree::isGroup( child ) )
362  else if ( QgsLayerTree::isLayer( child ) )
364  }
365 
366  mChangingChildVisibility = false;
367 }
368 
369 
370 static bool _nodeIsChecked( QgsLayerTreeNode* node )
371 {
372  Qt::CheckState state;
373  if ( QgsLayerTree::isGroup( node ) )
374  state = QgsLayerTree::toGroup( node )->isVisible();
375  else if ( QgsLayerTree::isLayer( node ) )
376  state = QgsLayerTree::toLayer( node )->isVisible();
377  else
378  return false;
379 
380  return state == Qt::Checked || state == Qt::PartiallyChecked;
381 }
382 
383 
385 {
386  return mMutuallyExclusive;
387 }
388 
389 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
390 {
391  mMutuallyExclusive = enabled;
392  mMutuallyExclusiveChildIndex = initialChildIndex;
393 
394  if ( !enabled )
395  {
397  return;
398  }
399 
400  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
401  {
402  // try to use first checked index
403  int index = 0;
404  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
405  {
406  if ( _nodeIsChecked( child ) )
407  {
409  break;
410  }
411  index++;
412  }
413  }
414 
416 }
417 
419 {
420  QStringList lst;
421  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
422  {
423  if ( QgsLayerTree::isGroup( child ) )
424  lst << QgsLayerTree::toGroup( child )->findLayerIds();
425  else if ( QgsLayerTree::isLayer( child ) )
426  lst << QgsLayerTree::toLayer( child )->layerId();
427  }
428  return lst;
429 }
430 
431 
433 {
434  //QgsMapLayer* layer = static_cast<QgsMapLayer*>( sender() );
435  //removeLayer( layer );
436 }
437 
438 
440 {
441  int childIndex = mChildren.indexOf( node );
442  if ( childIndex == -1 )
443  return; // not a direct child - ignore
444 
445  if ( mMutuallyExclusive )
446  {
447  if ( _nodeIsChecked( node ) )
448  mMutuallyExclusiveChildIndex = childIndex;
449 
450  // we need to update this node's check status in two cases:
451  // 1. it was unchecked and a child node got checked
452  // 2. it was checked and the only checked child got unchecked
454 
455  // we also need to make sure there is only one child node checked
457  }
458  else
459  {
461  }
462 }
463 
465 {
467  return;
468 
469  if ( mChildren.isEmpty() )
470  return;
471 
472  if ( mMutuallyExclusive )
473  {
474  // if in mutually exclusive mode, our check state depends only on the check state of the chosen child index
475 
476  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
477  return;
478 
479  Qt::CheckState meChildState = _nodeIsChecked( mChildren.at( mMutuallyExclusiveChildIndex ) ) ? Qt::Checked : Qt::Unchecked;
480 
481  setVisible( meChildState );
482  return;
483  }
484 
485  bool hasVisible = false, hasHidden = false;
486 
487  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
488  {
489  if ( QgsLayerTree::isLayer( child ) )
490  {
491  bool layerVisible = QgsLayerTree::toLayer( child )->isVisible() == Qt::Checked;
492  if ( layerVisible ) hasVisible = true;
493  if ( !layerVisible ) hasHidden = true;
494  }
495  else if ( QgsLayerTree::isGroup( child ) )
496  {
497  Qt::CheckState state = QgsLayerTree::toGroup( child )->isVisible();
498  if ( state == Qt::Checked || state == Qt::PartiallyChecked ) hasVisible = true;
499  if ( state == Qt::Unchecked || state == Qt::PartiallyChecked ) hasHidden = true;
500  }
501  }
502 
503  Qt::CheckState newState;
504  if ( hasVisible && !hasHidden )
505  newState = Qt::Checked;
506  else if ( hasHidden && !hasVisible )
507  newState = Qt::Unchecked;
508  else
509  newState = Qt::PartiallyChecked;
510 
511  setVisible( newState );
512 }
513 
515 {
516  if ( mChildren.isEmpty() )
517  return;
518 
519  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
520 
521  int index = 0;
522  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
523  {
524  Qt::CheckState checked = ( index == mMutuallyExclusiveChildIndex ? mChecked : Qt::Unchecked );
525  if ( QgsLayerTree::isGroup( child ) )
526  QgsLayerTree::toGroup( child )->setVisible( checked );
527  else if ( QgsLayerTree::isLayer( child ) )
528  QgsLayerTree::toLayer( child )->setVisible( checked );
529  ++index;
530  }
531 
532  mChangingChildVisibility = false;
533 }
void nodeVisibilityChanged(QgsLayerTreeNode *node)
Layer tree group node serves as a container for layers and further groups.
void removeChildren(int from, int count)
Remove child nodes from index "from". The nodes will be deleted.
static unsigned index
void removeChildrenGroupWithoutLayers()
Remove all child group nodes without layers. The groupnodes will be deleted.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QgsLayerTreeGroup * addGroup(const QString &name)
Append a new group node with given name. Newly created node is owned by this group.
static QString checkStateToXml(Qt::CheckState state)
Convert Qt::CheckState to QString.
void setIsMutuallyExclusive(bool enabled, int initialChildIndex=-1)
Set whether the group is mutually exclusive (only one child can be checked at a time).
void readChildrenFromXml(QDomElement &element)
Read children from XML and append them to the group.
void insertChildrenPrivate(int index, QList< QgsLayerTreeNode *> nodes)
Low-level insertion of children to the node. The children must not have any parent yet! ...
void removeAllChildren()
Remove all child nodes. The nodes will be deleted.
bool mExpanded
whether the node should be shown in GUI as expanded
NodeType nodeType()
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
virtual void writeXml(QDomElement &parentElement) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element...
Qt::CheckState isVisible() const
virtual QString dump() const =0
Return string with layer tree structure. For debug purposes only.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group. The node will be deleted.
static QgsLayerTreeGroup * readXml(QDomElement &element)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on ...
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
QString layerId() const
bool isMutuallyExclusive() const
Return whether the group is mutually exclusive (only one child can be checked at a time) ...
void removeLayer(QgsMapLayer *layer)
Remove map layer&#39;s node from this group. The node will be deleted.
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer. Newly created node is owned by this group.
void visibilityChanged(QgsLayerTreeNode *node, Qt::CheckState state)
Emitted when check state of a node within the tree has been changed.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsMapLayerRegistry.
QgsLayerTreeLayer * insertLayer(int index, QgsMapLayer *layer)
Insert a new layer node for given map layer at specified position. Newly created node is owned by thi...
void writeCommonXml(QDomElement &element)
void setName(const QString &n) override
Set group&#39;s name.
virtual void writeXml(QDomElement &parentElement)=0
Write layer tree to XML.
static QgsLayerTreeNode * readXml(QDomElement &element)
Read layer tree from XML. Returns new instance.
This class is a base class for nodes in a layer tree.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
void setVisible(Qt::CheckState visible)
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position. The nodes must not have a parent yet. The nodes will be owned by this group.
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
void updateChildVisibility()
Set check state of children (when this group&#39;s check state changes) - if not mutually exclusive...
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
Qt::CheckState mChecked
void setExpanded(bool expanded)
Set whether the node should be shown as expanded or collapsed in GUI.
void removeChildrenPrivate(int from, int count, bool destroy=true)
Low-level removal of children from the node.
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
QgsMapLayer * layer() const
void updateChildVisibilityMutuallyExclusive()
Set check state of children - if mutually exclusive.
static bool _nodeIsChecked(QgsLayerTreeNode *node)
virtual QString dump() const override
Return text representation of the tree. For debugging purposes only.
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position. The node must not have a parent yet. The node will be own...
virtual QgsLayerTreeGroup * clone() const override
Return a clone of the group. The children are cloned too.
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
void readCommonXml(QDomElement &element)
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name. Searches recursively the whole sub-tree.
bool mMutuallyExclusive
Whether the group is mutually exclusive (i.e. only one child can be checked at a time) ...
void addChildNode(QgsLayerTreeNode *node)
Append an existing node. The node must not have a parent yet. The node will be owned by this group...
Qt::CheckState isVisible() const
Return the check state of the group node.
QString name() const override
Get group&#39;s name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
Container of other groups and layers.
void updateVisibilityFromChildren()
Set check state of this group from its children.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
void setVisible(Qt::CheckState state)
Set check state of the group node - will also update children.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QgsLayerTreeGroup * insertGroup(int index, const QString &name)
Insert a new group node with given name at specified position. Newly created node is owned by this gr...
Layer tree node points to a map layer.
QgsLayerTreeGroup(const QString &name=QString(), Qt::CheckState checked=Qt::Checked)