QGIS API Documentation  3.21.0-Master (5b68dc587e)
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 
22 #include <QDomElement>
23 #include <QStringList>
24 
25 
26 QgsLayerTreeGroup::QgsLayerTreeGroup( const QString &name, bool checked )
27  : QgsLayerTreeNode( NodeGroup, checked )
28  , mName( name )
29 {
31 }
32 
34  : QgsLayerTreeNode( other )
35  , mName( other.mName )
36  , mChangingChildVisibility( other.mChangingChildVisibility )
37  , mMutuallyExclusive( other.mMutuallyExclusive )
38  , mMutuallyExclusiveChildIndex( other.mMutuallyExclusiveChildIndex )
39 {
41 }
42 
43 QString QgsLayerTreeGroup::name() const
44 {
45  return mName;
46 }
47 
48 void QgsLayerTreeGroup::setName( const QString &n )
49 {
50  if ( mName == n )
51  return;
52 
53  mName = n;
54  emit nameChanged( this, n );
55 }
56 
57 
58 QgsLayerTreeGroup *QgsLayerTreeGroup::insertGroup( int index, const QString &name )
59 {
61  insertChildNode( index, grp );
62  return grp;
63 }
64 
66 {
68  addChildNode( grp );
69  return grp;
70 }
71 
73 {
74  if ( !layer )
75  return nullptr;
76 
77  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
78  insertChildNode( index, ll );
79  return ll;
80 }
81 
83 {
84  if ( !layer )
85  return nullptr;
86 
87  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
88  addChildNode( ll );
89  return ll;
90 }
91 
93 {
94  QList<QgsLayerTreeNode *> nodes;
95  nodes << node;
96  insertChildNodes( index, nodes );
97 }
98 
99 void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode *> &nodes )
100 {
101  QgsLayerTreeNode *meChild = nullptr;
103  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
104 
105  // low-level insert
106  insertChildrenPrivate( index, nodes );
107 
108  if ( mMutuallyExclusive )
109  {
110  if ( meChild )
111  {
112  // the child could have change its index - or the new children may have been also set as visible
113  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
114  }
115  else if ( mChecked )
116  {
117  // we have not picked a child index yet, but we should pick one now
118  // ... so pick the first one from the newly added
119  if ( index == -1 )
120  index = mChildren.count() - nodes.count(); // get real insertion index
122  }
124  }
125 }
126 
128 {
129  insertChildNode( -1, node );
130 }
131 
133 {
134  int i = mChildren.indexOf( node );
135  if ( i >= 0 )
136  removeChildren( i, 1 );
137 }
138 
140 {
141  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
142  {
143  if ( QgsLayerTree::isLayer( child ) )
144  {
145  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
146  if ( childLayer->layer() == layer )
147  {
148  removeChildren( mChildren.indexOf( child ), 1 );
149  break;
150  }
151  }
152  }
153 }
154 
155 void QgsLayerTreeGroup::removeChildren( int from, int count )
156 {
157  QgsLayerTreeNode *meChild = nullptr;
159  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
160 
161  removeChildrenPrivate( from, count );
162 
163  if ( meChild )
164  {
165  // the child could have change its index - or may have been removed completely
166  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
167  // we need to uncheck this group
168  //if ( mMutuallyExclusiveChildIndex == -1 )
169  // setItemVisibilityChecked( false );
170  }
171 }
172 
174 {
175  // clean the layer tree by removing empty group
176  const auto childNodes = children();
177  for ( QgsLayerTreeNode *treeNode : childNodes )
178  {
179  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
180  {
181  QgsLayerTreeGroup *treeGroup = qobject_cast<QgsLayerTreeGroup *>( treeNode );
182  if ( treeGroup->findLayerIds().isEmpty() )
183  removeChildNode( treeNode );
184  else
186  }
187  }
188 }
189 
191 {
192  removeChildren( 0, mChildren.count() );
193 }
194 
196 {
197  if ( !layer )
198  return nullptr;
199 
200  return findLayer( layer->id() );
201 }
202 
203 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString &layerId ) const
204 {
205  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
206  {
207  if ( QgsLayerTree::isLayer( child ) )
208  {
209  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
210  if ( childLayer->layerId() == layerId )
211  return childLayer;
212  }
213  else if ( QgsLayerTree::isGroup( child ) )
214  {
215  QgsLayerTreeLayer *res = QgsLayerTree::toGroup( child )->findLayer( layerId );
216  if ( res )
217  return res;
218  }
219  }
220  return nullptr;
221 }
222 
223 QList<QgsLayerTreeLayer *> QgsLayerTreeGroup::findLayers() const
224 {
225  QList<QgsLayerTreeLayer *> list;
226  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
227  {
228  if ( QgsLayerTree::isLayer( child ) )
229  list << QgsLayerTree::toLayer( child );
230  else if ( QgsLayerTree::isGroup( child ) )
231  list << QgsLayerTree::toGroup( child )->findLayers();
232  }
233  return list;
234 }
235 
237 {
238  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
239  {
240  if ( QgsLayerTree::isGroup( child ) )
241  {
242  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
243  if ( childGroup->name() == name )
244  return childGroup;
245  else
246  {
247  QgsLayerTreeGroup *grp = childGroup->findGroup( name );
248  if ( grp )
249  return grp;
250  }
251  }
252  }
253  return nullptr;
254 }
255 
256 QList<QgsLayerTreeGroup *> QgsLayerTreeGroup::findGroups( bool recursive ) const
257 {
258  QList<QgsLayerTreeGroup *> list;
259 
260  for ( QgsLayerTreeNode *child : mChildren )
261  {
262  if ( QgsLayerTree::isGroup( child ) )
263  {
264  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
265  list << childGroup;
266  if ( recursive )
267  list << childGroup->findGroups( recursive );
268  }
269  }
270  return list;
271 }
272 
273 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsReadWriteContext &context )
274 {
275  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
276  return nullptr;
277 
278  QString name = context.projectTranslator()->translate( QStringLiteral( "project:layergroups" ), element.attribute( QStringLiteral( "name" ) ) );
279  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
280  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
281  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
282  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
283 
284  QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
285  groupNode->setExpanded( isExpanded );
286 
287  groupNode->readCommonXml( element );
288 
289  groupNode->readChildrenFromXml( element, context );
290 
291  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
292 
293  return groupNode;
294 }
295 
296 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project, const QgsReadWriteContext &context )
297 {
298  QgsLayerTreeGroup *node = readXml( element, context );
299  if ( node )
300  node->resolveReferences( project );
301  return node;
302 }
303 
304 void QgsLayerTreeGroup::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
305 {
306  QDomDocument doc = parentElement.ownerDocument();
307  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
308  elem.setAttribute( QStringLiteral( "name" ), mName );
309  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
310  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
311  if ( mMutuallyExclusive )
312  {
313  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
314  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
315  }
316 
317  writeCommonXml( elem );
318 
319  for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
320  node->writeXml( elem, context );
321 
322  parentElement.appendChild( elem );
323 }
324 
325 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element, const QgsReadWriteContext &context )
326 {
327  QList<QgsLayerTreeNode *> nodes;
328  QDomElement childElem = element.firstChildElement();
329  while ( !childElem.isNull() )
330  {
331  QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem, context );
332  if ( newNode )
333  nodes << newNode;
334 
335  childElem = childElem.nextSiblingElement();
336  }
337 
338  insertChildNodes( -1, nodes );
339 }
340 
341 QString QgsLayerTreeGroup::dump() const
342 {
343  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
344  QStringList childrenDump;
345  for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
346  childrenDump << node->dump().split( '\n' );
347  for ( int i = 0; i < childrenDump.count(); ++i )
348  childrenDump[i].prepend( " " );
349  return header + childrenDump.join( QLatin1Char( '\n' ) );
350 }
351 
353 {
354  return new QgsLayerTreeGroup( *this );
355 }
356 
357 void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
358 {
359  for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
360  node->resolveReferences( project, looseMatching );
361 }
362 
363 static bool _nodeIsChecked( QgsLayerTreeNode *node )
364 {
365  return node->itemVisibilityChecked();
366 }
367 
368 
370 {
371  return mMutuallyExclusive;
372 }
373 
374 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
375 {
376  mMutuallyExclusive = enabled;
377  mMutuallyExclusiveChildIndex = initialChildIndex;
378 
379  if ( !enabled )
380  {
381  return;
382  }
383 
384  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
385  {
386  // try to use first checked index
387  int index = 0;
388  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
389  {
390  if ( _nodeIsChecked( child ) )
391  {
393  break;
394  }
395  index++;
396  }
397  }
398 
400 }
401 
403 {
404  QStringList lst;
405  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
406  {
407  if ( QgsLayerTree::isGroup( child ) )
408  lst << QgsLayerTree::toGroup( child )->findLayerIds();
409  else if ( QgsLayerTree::isLayer( child ) )
410  lst << QgsLayerTree::toLayer( child )->layerId();
411  }
412  return lst;
413 }
414 
416 {
417  int childIndex = mChildren.indexOf( node );
418  if ( childIndex == -1 )
419  return; // not a direct child - ignore
420 
421  if ( mMutuallyExclusive )
422  {
423  if ( _nodeIsChecked( node ) )
424  mMutuallyExclusiveChildIndex = childIndex;
425  else if ( mMutuallyExclusiveChildIndex == childIndex )
427 
428  // we need to make sure there is only one child node checked
430  }
431 }
432 
434 {
435  if ( mChildren.isEmpty() )
436  return;
437 
438  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
439 
440  int index = 0;
441  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
442  {
443  child->setItemVisibilityChecked( index == mMutuallyExclusiveChildIndex );
444  ++index;
445  }
446 
447  mChangingChildVisibility = false;
448 }
449 
451 {
453  // Reconnect internal signals
455 }
456 
458 {
460 
461  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
462 
463  int index = 0;
464  for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
465  {
466  child->setItemVisibilityCheckedRecursive( checked && ( mMutuallyExclusiveChildIndex < 0 || index == mMutuallyExclusiveChildIndex ) );
467  ++index;
468  }
469 
470  mChangingChildVisibility = false;
471 }
Layer tree group node serves as a container for layers and further groups.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
void setName(const QString &n) override
Sets the group's name.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QgsLayerTreeGroup * insertGroup(int index, const QString &name)
Insert a new group node with given name at specified position.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element.
QString name() const override
Returns the group's name.
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void addChildNode(QgsLayerTreeNode *node)
Append an existing node.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void removeAllChildren()
Remove all child nodes.
bool mMutuallyExclusive
Whether the group is mutually exclusive (i.e. only one child can be checked at a time)
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, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
void setItemVisibilityCheckedRecursive(bool checked) override
Check or uncheck a node and all its children (taking into account exclusion rules)
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
bool isMutuallyExclusive() const
Returns whether the group is mutually exclusive (only one child can be checked at a time)
void updateChildVisibilityMutuallyExclusive()
Set check state of children - if mutually exclusive.
static QgsLayerTreeGroup * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or nullptr ...
QgsLayerTreeGroup * addGroup(const QString &name)
Append a new group node with given name.
void removeChildren(int from, int count)
Remove child nodes from index "from".
void removeChildrenGroupWithoutLayers()
Remove all child group nodes without layers.
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
virtual void makeOrphan() override
Sets parent to nullptr and disconnects all external and forwarded signals.
void removeLayer(QgsMapLayer *layer)
Remove map layer's node from this group.
QgsLayerTreeLayer * insertLayer(int index, QgsMapLayer *layer)
Insert a new layer node for given map layer at specified position.
QString dump() const override
Returns text representation of the tree.
void nodeVisibilityChanged(QgsLayerTreeNode *node)
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
Layer tree node points to a map layer.
QString layerId() const
Returns the ID for the map layer associated with this node.
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.
virtual void makeOrphan()
Sets parent to nullptr and disconnects all external and forwarded signals.
@ NodeGroup
Container of other groups and layers.
void insertChildrenPrivate(int index, QList< QgsLayerTreeNode * > nodes)
Low-level insertion of children to the node. The children must not have any parent yet!
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
void writeCommonXml(QDomElement &element)
Write common XML elements.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
bool mExpanded
whether the node should be shown in GUI as expanded
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void removeChildrenPrivate(int from, int count, bool destroy=true)
Low-level removal of children from the node.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
The derived translate() translates with QTranslator and qm file the sourceText.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:101
The class is used as a container of context for various read/write operations on other objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.