QGIS API Documentation  2.17.0-Master (973e4b0)
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 
51 {
52  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
53  insertChildNode( index, grp );
54  return grp;
55 }
56 
58 {
59  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
60  addChildNode( grp );
61  return grp;
62 }
63 
65 {
66  if ( !layer || QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) != layer )
67  return nullptr;
68 
69  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
70  insertChildNode( index, ll );
71  return ll;
72 }
73 
75 {
76  if ( !layer || QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) != layer )
77  return nullptr;
78 
79  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
80  addChildNode( ll );
81  return ll;
82 }
83 
85 {
87  nodes << node;
88  insertChildNodes( index, nodes );
89 }
90 
92 {
93  QgsLayerTreeNode* meChild = nullptr;
96 
97  // low-level insert
98  insertChildrenPrivate( index, nodes );
99 
100  if ( mMutuallyExclusive )
101  {
102  if ( meChild )
103  {
104  // the child could have change its index - or the new children may have been also set as visible
106  }
107  else if ( mChecked == Qt::Checked )
108  {
109  // we have not picked a child index yet, but we should pick one now
110  // ... so pick the first one from the newly added
111  if ( index == -1 )
112  index = mChildren.count() - nodes.count(); // get real insertion index
114  }
116  }
117 
119 }
120 
122 {
123  insertChildNode( -1, node );
124 }
125 
127 {
128  int i = mChildren.indexOf( node );
129  if ( i >= 0 )
130  removeChildren( i, 1 );
131 }
132 
134 {
135  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
136  {
137  if ( QgsLayerTree::isLayer( child ) )
138  {
139  QgsLayerTreeLayer* childLayer = QgsLayerTree::toLayer( child );
140  if ( childLayer->layer() == layer )
141  {
142  removeChildren( mChildren.indexOf( child ), 1 );
143  break;
144  }
145  }
146  }
147 }
148 
149 void QgsLayerTreeGroup::removeChildren( int from, int count )
150 {
151  QgsLayerTreeNode* meChild = nullptr;
154 
155  removeChildrenPrivate( from, count );
156 
157  if ( meChild )
158  {
159  // the child could have change its index - or may have been removed completely
161  // we need to uncheck this group
162  if ( mMutuallyExclusiveChildIndex == -1 )
163  setVisible( Qt::Unchecked );
164  }
165 
167 }
168 
170 {
171  // clean the layer tree by removing empty group
172  Q_FOREACH ( QgsLayerTreeNode* treeNode, children() )
173  {
174  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
175  {
176  QgsLayerTreeGroup* treeGroup = qobject_cast<QgsLayerTreeGroup*>( treeNode );
177  if ( treeGroup->findLayerIds().isEmpty() )
178  removeChildNode( treeNode );
179  else
181  }
182  }
183 }
184 
186 {
188 }
189 
191 {
192  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
193  {
194  if ( QgsLayerTree::isLayer( child ) )
195  {
196  QgsLayerTreeLayer* childLayer = QgsLayerTree::toLayer( child );
197  if ( childLayer->layerId() == layerId )
198  return childLayer;
199  }
200  else if ( QgsLayerTree::isGroup( child ) )
201  {
202  QgsLayerTreeLayer* res = QgsLayerTree::toGroup( child )->findLayer( layerId );
203  if ( res )
204  return res;
205  }
206  }
207  return nullptr;
208 }
209 
211 {
213  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
214  {
215  if ( QgsLayerTree::isLayer( child ) )
216  list << QgsLayerTree::toLayer( child );
217  else if ( QgsLayerTree::isGroup( child ) )
218  list << QgsLayerTree::toGroup( child )->findLayers();
219  }
220  return list;
221 }
222 
224 {
225  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
226  {
227  if ( QgsLayerTree::isGroup( child ) )
228  {
229  QgsLayerTreeGroup* childGroup = QgsLayerTree::toGroup( child );
230  if ( childGroup->name() == name )
231  return childGroup;
232  else
233  {
234  QgsLayerTreeGroup* grp = childGroup->findGroup( name );
235  if ( grp )
236  return grp;
237  }
238  }
239  }
240  return nullptr;
241 }
242 
244 {
245  if ( element.tagName() != "layer-tree-group" )
246  return nullptr;
247 
248  QString name = element.attribute( "name" );
249  bool isExpanded = ( element.attribute( "expanded", "1" ) == "1" );
250  Qt::CheckState checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( "checked" ) );
251  bool isMutuallyExclusive = element.attribute( "mutually-exclusive", "0" ) == "1";
252  int mutuallyExclusiveChildIndex = element.attribute( "mutually-exclusive-child", "-1" ).toInt();
253 
254  QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( name, checked );
255  groupNode->setExpanded( isExpanded );
256 
257  groupNode->readCommonXML( element );
258 
259  groupNode->readChildrenFromXML( element );
260 
261  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
262 
263  return groupNode;
264 }
265 
267 {
268  QDomDocument doc = parentElement.ownerDocument();
269  QDomElement elem = doc.createElement( "layer-tree-group" );
270  elem.setAttribute( "name", mName );
271  elem.setAttribute( "expanded", mExpanded ? "1" : "0" );
273  if ( mMutuallyExclusive )
274  {
275  elem.setAttribute( "mutually-exclusive", "1" );
276  elem.setAttribute( "mutually-exclusive-child", mMutuallyExclusiveChildIndex );
277  }
278 
279  writeCommonXML( elem );
280 
281  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
282  node->writeXML( elem );
283 
284  parentElement.appendChild( elem );
285 }
286 
288 {
290  QDomElement childElem = element.firstChildElement();
291  while ( !childElem.isNull() )
292  {
293  QgsLayerTreeNode* newNode = QgsLayerTreeNode::readXML( childElem );
294  if ( newNode )
295  nodes << newNode;
296 
297  childElem = childElem.nextSiblingElement();
298  }
299 
300  insertChildNodes( -1, nodes );
301 }
302 
304 {
305  QString header = QString( "GROUP: %1 visible=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
306  QStringList childrenDump;
307  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
308  childrenDump << node->dump().split( '\n' );
309  for ( int i = 0; i < childrenDump.count(); ++i )
310  childrenDump[i].prepend( " " );
311  return header + childrenDump.join( "\n" );
312 }
313 
315 {
316  return new QgsLayerTreeGroup( *this );
317 }
318 
319 void QgsLayerTreeGroup::setVisible( Qt::CheckState state )
320 {
321  if ( mChecked == state )
322  return;
323 
324  mChecked = state;
325  emit visibilityChanged( this, state );
326 
327  if ( mMutuallyExclusive )
328  {
329  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
330  mMutuallyExclusiveChildIndex = 0; // just choose the first one if we have lost the active one
332  }
333  else if ( mChecked == Qt::Unchecked || mChecked == Qt::Checked )
334  {
336  }
337 }
338 
340 {
341  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
342 
343  // update children to have the correct visibility
344  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
345  {
346  if ( QgsLayerTree::isGroup( child ) )
348  else if ( QgsLayerTree::isLayer( child ) )
350  }
351 
352  mChangingChildVisibility = false;
353 }
354 
355 
356 static bool _nodeIsChecked( QgsLayerTreeNode* node )
357 {
358  Qt::CheckState state;
359  if ( QgsLayerTree::isGroup( node ) )
360  state = QgsLayerTree::toGroup( node )->isVisible();
361  else if ( QgsLayerTree::isLayer( node ) )
362  state = QgsLayerTree::toLayer( node )->isVisible();
363  else
364  return false;
365 
366  return state == Qt::Checked || state == Qt::PartiallyChecked;
367 }
368 
369 
371 {
372  return mMutuallyExclusive;
373 }
374 
375 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
376 {
377  mMutuallyExclusive = enabled;
378  mMutuallyExclusiveChildIndex = initialChildIndex;
379 
380  if ( !enabled )
381  {
383  return;
384  }
385 
386  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
387  {
388  // try to use first checked index
389  int index = 0;
390  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
391  {
392  if ( _nodeIsChecked( child ) )
393  {
395  break;
396  }
397  index++;
398  }
399  }
400 
402 }
403 
405 {
406  QStringList lst;
407  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
408  {
409  if ( QgsLayerTree::isGroup( child ) )
410  lst << QgsLayerTree::toGroup( child )->findLayerIds();
411  else if ( QgsLayerTree::isLayer( child ) )
412  lst << QgsLayerTree::toLayer( child )->layerId();
413  }
414  return lst;
415 }
416 
417 
419 {
420  //QgsMapLayer* layer = static_cast<QgsMapLayer*>( sender() );
421  //removeLayer( layer );
422 }
423 
424 
426 {
427  int childIndex = mChildren.indexOf( node );
428  if ( childIndex == -1 )
429  return; // not a direct child - ignore
430 
431  if ( mMutuallyExclusive )
432  {
433  if ( _nodeIsChecked( node ) )
434  mMutuallyExclusiveChildIndex = childIndex;
435 
436  // we need to update this node's check status in two cases:
437  // 1. it was unchecked and a child node got checked
438  // 2. it was checked and the only checked child got unchecked
440 
441  // we also need to make sure there is only one child node checked
443  }
444  else
445  {
447  }
448 }
449 
451 {
453  return;
454 
455  if ( mChildren.isEmpty() )
456  return;
457 
458  if ( mMutuallyExclusive )
459  {
460  // if in mutually exclusive mode, our check state depends only on the check state of the chosen child index
461 
462  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
463  return;
464 
465  Qt::CheckState meChildState = _nodeIsChecked( mChildren.at( mMutuallyExclusiveChildIndex ) ) ? Qt::Checked : Qt::Unchecked;
466 
467  setVisible( meChildState );
468  return;
469  }
470 
471  bool hasVisible = false, hasHidden = false;
472 
473  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
474  {
475  if ( QgsLayerTree::isLayer( child ) )
476  {
477  bool layerVisible = QgsLayerTree::toLayer( child )->isVisible() == Qt::Checked;
478  if ( layerVisible ) hasVisible = true;
479  if ( !layerVisible ) hasHidden = true;
480  }
481  else if ( QgsLayerTree::isGroup( child ) )
482  {
483  Qt::CheckState state = QgsLayerTree::toGroup( child )->isVisible();
484  if ( state == Qt::Checked || state == Qt::PartiallyChecked ) hasVisible = true;
485  if ( state == Qt::Unchecked || state == Qt::PartiallyChecked ) hasHidden = true;
486  }
487  }
488 
489  Qt::CheckState newState;
490  if ( hasVisible && !hasHidden )
491  newState = Qt::Checked;
492  else if ( hasHidden && !hasVisible )
493  newState = Qt::Unchecked;
494  else
495  newState = Qt::PartiallyChecked;
496 
497  setVisible( newState );
498 }
499 
501 {
502  if ( mChildren.isEmpty() )
503  return;
504 
505  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
506 
507  int index = 0;
508  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
509  {
510  Qt::CheckState checked = ( index == mMutuallyExclusiveChildIndex ? mChecked : Qt::Unchecked );
511  if ( QgsLayerTree::isGroup( child ) )
512  QgsLayerTree::toGroup( child )->setVisible( checked );
513  else if ( QgsLayerTree::isLayer( child ) )
514  QgsLayerTree::toLayer( child )->setVisible( checked );
515  ++index;
516  }
517 
518  mChangingChildVisibility = false;
519 }
void nodeVisibilityChanged(QgsLayerTreeNode *node)
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
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.
void readChildrenFromXML(QDomElement &element)
Read children from XML and append them to the 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).
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
static QgsLayerTreeGroup * readXML(QDomElement &element)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on ...
QgsMapLayer * layer() const
bool isMutuallyExclusive() const
Return whether the group is mutually exclusive (only one child can be checked at a time) ...
void removeAllChildren()
Remove all child nodes. The nodes will be deleted.
bool mExpanded
whether the node should be shown in GUI as expanded
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
const T & at(int i) const
NodeType nodeType()
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
virtual QString dump() const =0
Return string with layer tree structure. For debug purposes only.
QDomElement nextSiblingElement(const QString &tagName) const
void readCommonXML(QDomElement &element)
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group. The node will be deleted.
QString join(const QString &separator) const
virtual void writeXML(QDomElement &parentElement)=0
Write layer tree to XML.
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.
int indexOf(const T &value, int from) const
Qt::CheckState isVisible() const
void visibilityChanged(QgsLayerTreeNode *node, Qt::CheckState state)
Emitted when check state of a node within the tree has been changed.
int count(const T &value) const
QDomDocument ownerDocument() const
void setAttribute(const QString &name, const QString &value)
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 insertChildrenPrivate(int index, QList< QgsLayerTreeNode * > nodes)
Low-level insertion of children to the node. The children must not have any parent yet! ...
int toInt(bool *ok, int base) const
bool isEmpty() const
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
This class is a base class for nodes in a layer tree.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
QString layerId() const
void setVisible(Qt::CheckState visible)
static QgsLayerTreeNode * readXML(QDomElement &element)
Read layer tree from XML. Returns new instance.
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
QString name() const
Get group&#39;s name.
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 isVisible() const
Return the check state of the group node.
Qt::CheckState mChecked
virtual void writeXML(QDomElement &parentElement) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element...
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.
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.
bool isNull() 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 writeCommonXML(QDomElement &element)
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
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
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) ...
QDomElement firstChildElement(const QString &tagName) const
void addChildNode(QgsLayerTreeNode *node)
Append an existing node. The node must not have a parent yet. The node will be owned by this group...
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
QString tagName() const
QDomElement createElement(const QString &tagName)
container of other groups and layers
void updateVisibilityFromChildren()
Set check state of this group from its children.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
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)