QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsmapthemecollection.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapthemecollection.cpp
3  --------------------------------------
4  Date : September 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 "qgsmapthemecollection.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreemodel.h"
21 #include "qgsmaplayerlistutils.h"
23 #include "qgsproject.h"
24 #include "qgsrenderer.h"
25 #include "qgsvectorlayer.h"
26 
27 #include <QInputDialog>
28 
30  : mProject( project )
31 {
32  connect( project, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
33 }
34 
35 QgsMapThemeCollection::MapThemeLayerRecord QgsMapThemeCollection::createThemeLayerRecord( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model )
36 {
37  MapThemeLayerRecord layerRec( nodeLayer->layer() );
38  layerRec.usingCurrentStyle = true;
39  layerRec.currentStyle = nodeLayer->layer()->styleManager()->currentStyle();
40  layerRec.expandedLayerNode = nodeLayer->isExpanded();
41  layerRec.expandedLegendItems = nodeLayer->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList().toSet();
42 
43  // get checked legend items
44  bool hasCheckableItems = false;
45  bool someItemsUnchecked = false;
46  QSet<QString> checkedItems;
47  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, model->layerLegendNodes( nodeLayer, true ) )
48  {
49  if ( legendNode->flags() & Qt::ItemIsUserCheckable )
50  {
51  hasCheckableItems = true;
52 
53  if ( legendNode->data( Qt::CheckStateRole ).toInt() == Qt::Checked )
54  checkedItems << legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
55  else
56  someItemsUnchecked = true;
57  }
58  }
59 
60  if ( hasCheckableItems && someItemsUnchecked )
61  {
62  layerRec.usingLegendItems = true;
63  layerRec.checkedLegendItems = checkedItems;
64  }
65  return layerRec;
66 }
67 
68 static QString _groupId( QgsLayerTreeNode *node )
69 {
70  QStringList lst;
71  while ( node->parent() )
72  {
73  lst.prepend( node->name() );
74  node = node->parent();
75  }
76  return lst.join( '/' );
77 }
78 
80 {
81  Q_FOREACH ( QgsLayerTreeNode *node, parent->children() )
82  {
83  if ( QgsLayerTree::isGroup( node ) )
84  {
86  if ( node->isExpanded() )
87  rec.mExpandedGroupNodes.insert( _groupId( node ) );
88  }
89  else if ( QgsLayerTree::isLayer( node ) )
90  {
91  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
92  if ( nodeLayer->isVisible() )
93  rec.mLayerRecords << createThemeLayerRecord( nodeLayer, model );
94  }
95  }
96 }
97 
99 {
101  rec.setHasExpandedStateInfo( true ); // all newly created theme records have expanded state info
102  createThemeFromCurrentState( root, model, rec );
103  return rec;
104 }
105 
106 bool QgsMapThemeCollection::findRecordForLayer( QgsMapLayer *layer, const QgsMapThemeCollection::MapThemeRecord &rec, QgsMapThemeCollection::MapThemeLayerRecord &layerRec )
107 {
108  Q_FOREACH ( const QgsMapThemeCollection::MapThemeLayerRecord &lr, rec.mLayerRecords )
109  {
110  if ( lr.layer() == layer )
111  {
112  layerRec = lr;
113  return true;
114  }
115  }
116  return false;
117 }
118 
119 void QgsMapThemeCollection::applyThemeToLayer( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model, const QgsMapThemeCollection::MapThemeRecord &rec )
120 {
121  MapThemeLayerRecord layerRec;
122  bool isVisible = findRecordForLayer( nodeLayer->layer(), rec, layerRec );
123 
124  // Make sure the whole tree is visible
125  if ( isVisible )
126  nodeLayer->setItemVisibilityCheckedParentRecursive( isVisible );
127  else
128  nodeLayer->setItemVisibilityChecked( isVisible );
129 
130  if ( !isVisible )
131  return;
132 
133  if ( layerRec.usingCurrentStyle )
134  {
135  // apply desired style first
136  nodeLayer->layer()->styleManager()->setCurrentStyle( layerRec.currentStyle );
137  }
138 
139  if ( layerRec.usingLegendItems )
140  {
141  // some nodes are not checked
142  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, model->layerLegendNodes( nodeLayer, true ) )
143  {
144  QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
145  Qt::CheckState shouldHaveState = layerRec.checkedLegendItems.contains( ruleKey ) ? Qt::Checked : Qt::Unchecked;
146  if ( ( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
147  legendNode->data( Qt::CheckStateRole ).toInt() != shouldHaveState )
148  legendNode->setData( shouldHaveState, Qt::CheckStateRole );
149  }
150  }
151  else
152  {
153  // all nodes should be checked
154  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, model->layerLegendNodes( nodeLayer, true ) )
155  {
156  if ( ( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
157  legendNode->data( Qt::CheckStateRole ).toInt() != Qt::Checked )
158  legendNode->setData( Qt::Checked, Qt::CheckStateRole );
159  }
160  }
161 
162  // apply expanded/collapsed state to the layer and its legend nodes
163  if ( rec.hasExpandedStateInfo() )
164  {
165  nodeLayer->setExpanded( layerRec.expandedLayerNode );
166  nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), QStringList( layerRec.expandedLegendItems.toList() ) );
167  }
168 }
169 
170 
171 void QgsMapThemeCollection::applyThemeToGroup( QgsLayerTreeGroup *parent, QgsLayerTreeModel *model, const QgsMapThemeCollection::MapThemeRecord &rec )
172 {
173  Q_FOREACH ( QgsLayerTreeNode *node, parent->children() )
174  {
175  if ( QgsLayerTree::isGroup( node ) )
176  {
177  applyThemeToGroup( QgsLayerTree::toGroup( node ), model, rec );
178  if ( rec.hasExpandedStateInfo() )
179  node->setExpanded( rec.expandedGroupNodes().contains( _groupId( node ) ) );
180  }
181  else if ( QgsLayerTree::isLayer( node ) )
182  applyThemeToLayer( QgsLayerTree::toLayer( node ), model, rec );
183  }
184 }
185 
186 
188 {
189  applyThemeToGroup( root, model, mapThemeState( name ) );
190 
191  // also make sure that the preset is up-to-date (not containing any non-existent legend items)
192  update( name, createThemeFromCurrentState( root, model ) );
193 }
194 
196 {
197  return mProject;
198 }
199 
201 {
202  if ( project == mProject )
203  return;
204 
205  disconnect( mProject, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
206  mProject = project;
207  connect( mProject, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
208  emit projectChanged();
209 }
210 
211 QList<QgsMapLayer *> QgsMapThemeCollection::masterLayerOrder() const
212 {
213  if ( !mProject )
214  return QList< QgsMapLayer * >();
215 
216  return mProject->layerTreeRoot()->layerOrder();
217 }
218 
219 QList<QgsMapLayer *> QgsMapThemeCollection::masterVisibleLayers() const
220 {
221  QList< QgsMapLayer *> allLayers = masterLayerOrder();
222  QList< QgsMapLayer * > visibleLayers = mProject->layerTreeRoot()->checkedLayers();
223 
224  if ( allLayers.isEmpty() )
225  {
226  // no project layer order set
227  return visibleLayers;
228  }
229  else
230  {
231  QList< QgsMapLayer * > orderedVisibleLayers;
232  Q_FOREACH ( QgsMapLayer *layer, allLayers )
233  {
234  if ( visibleLayers.contains( layer ) )
235  orderedVisibleLayers << layer;
236  }
237  return orderedVisibleLayers;
238  }
239 }
240 
241 
242 bool QgsMapThemeCollection::hasMapTheme( const QString &name ) const
243 {
244  return mMapThemes.contains( name );
245 }
246 
248 {
249  mMapThemes.insert( name, state );
250 
251  reconnectToLayersStyleManager();
252  emit mapThemeChanged( name );
253  emit mapThemesChanged();
254 }
255 
256 void QgsMapThemeCollection::update( const QString &name, const MapThemeRecord &state )
257 {
258  if ( !mMapThemes.contains( name ) )
259  return;
260 
261  mMapThemes[name] = state;
262 
263  reconnectToLayersStyleManager();
264  emit mapThemeChanged( name );
265  emit mapThemesChanged();
266 }
267 
268 void QgsMapThemeCollection::removeMapTheme( const QString &name )
269 {
270  if ( !mMapThemes.contains( name ) )
271  return;
272 
273  mMapThemes.remove( name );
274 
275  reconnectToLayersStyleManager();
276  emit mapThemesChanged();
277 }
278 
280 {
281  mMapThemes.clear();
282 
283  reconnectToLayersStyleManager();
284  emit mapThemesChanged();
285 }
286 
287 QStringList QgsMapThemeCollection::mapThemes() const
288 {
289  return mMapThemes.keys();
290 }
291 
292 QStringList QgsMapThemeCollection::mapThemeVisibleLayerIds( const QString &name ) const
293 {
294  QStringList layerIds;
295  Q_FOREACH ( QgsMapLayer *layer, mapThemeVisibleLayers( name ) )
296  {
297  layerIds << layer->id();
298  }
299  return layerIds;
300 }
301 
302 QList<QgsMapLayer *> QgsMapThemeCollection::mapThemeVisibleLayers( const QString &name ) const
303 {
304  QList<QgsMapLayer *> layers;
305  const QList<MapThemeLayerRecord> &recs = mMapThemes.value( name ).mLayerRecords;
306  QList<QgsMapLayer *> layerOrder = masterLayerOrder();
307  if ( layerOrder.isEmpty() )
308  {
309  // no master layer order - so we have to just use the stored theme layer order as a fallback
310  Q_FOREACH ( const MapThemeLayerRecord &layerRec, mMapThemes.value( name ).mLayerRecords )
311  {
312  if ( layerRec.layer() )
313  layers << layerRec.layer();
314  }
315  }
316  else
317  {
318  Q_FOREACH ( QgsMapLayer *layer, layerOrder )
319  {
320  Q_FOREACH ( const MapThemeLayerRecord &layerRec, recs )
321  {
322  if ( layerRec.layer() == layer )
323  layers << layerRec.layer();
324  }
325  }
326  }
327 
328  return layers;
329 }
330 
331 
332 void QgsMapThemeCollection::applyMapThemeCheckedLegendNodesToLayer( const MapThemeLayerRecord &layerRec, QgsMapLayer *layer )
333 {
334  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
335  if ( !vlayer || !vlayer->renderer() )
336  return;
337 
338  QgsFeatureRenderer *renderer = vlayer->renderer();
339  if ( !renderer->legendSymbolItemsCheckable() )
340  return; // no need to do anything
341 
342  bool someNodesUnchecked = layerRec.usingLegendItems;
343 
344  Q_FOREACH ( const QgsLegendSymbolItem &item, vlayer->renderer()->legendSymbolItems() )
345  {
346  bool checked = renderer->legendSymbolItemChecked( item.ruleKey() );
347  bool shouldBeChecked = someNodesUnchecked ? layerRec.checkedLegendItems.contains( item.ruleKey() ) : true;
348  if ( checked != shouldBeChecked )
349  renderer->checkLegendSymbolItem( item.ruleKey(), shouldBeChecked );
350  }
351 }
352 
353 
354 QMap<QString, QString> QgsMapThemeCollection::mapThemeStyleOverrides( const QString &presetName )
355 {
356  QMap<QString, QString> styleOverrides;
357  if ( !mMapThemes.contains( presetName ) )
358  return styleOverrides;
359 
360  Q_FOREACH ( const MapThemeLayerRecord &layerRec, mMapThemes.value( presetName ).mLayerRecords )
361  {
362  if ( !layerRec.layer() )
363  continue;
364 
365  if ( layerRec.usingCurrentStyle )
366  {
367  QgsMapLayer *layer = layerRec.layer();
368  QgsMapLayerStyleOverride styleOverride( layer );
369  styleOverride.setOverrideStyle( layerRec.currentStyle );
370 
371  // set the checked legend nodes
372  applyMapThemeCheckedLegendNodesToLayer( layerRec, layer );
373 
374  // save to overrides
375  QgsMapLayerStyle layerStyle;
376  layerStyle.readFromLayer( layer );
377  styleOverrides[layer->id()] = layerStyle.xmlData();
378  }
379  }
380  return styleOverrides;
381 }
382 
383 void QgsMapThemeCollection::reconnectToLayersStyleManager()
384 {
385  // disconnect( 0, 0, this, SLOT( layerStyleRenamed( QString, QString ) ) );
386 
387  QSet<QgsMapLayer *> layers;
388  Q_FOREACH ( const MapThemeRecord &rec, mMapThemes )
389  {
390  Q_FOREACH ( const MapThemeLayerRecord &layerRec, rec.mLayerRecords )
391  {
392  if ( layerRec.layer() )
393  layers << layerRec.layer();
394  }
395  }
396 
397  Q_FOREACH ( QgsMapLayer *ml, layers )
398  {
399  connect( ml->styleManager(), &QgsMapLayerStyleManager::styleRenamed, this, &QgsMapThemeCollection::layerStyleRenamed );
400  }
401 }
402 
403 void QgsMapThemeCollection::readXml( const QDomDocument &doc )
404 {
405  clear();
406 
407  QDomElement visPresetsElem = doc.firstChildElement( QStringLiteral( "qgis" ) ).firstChildElement( QStringLiteral( "visibility-presets" ) );
408  if ( visPresetsElem.isNull() )
409  return;
410 
411  QDomElement visPresetElem = visPresetsElem.firstChildElement( QStringLiteral( "visibility-preset" ) );
412  while ( !visPresetElem.isNull() )
413  {
414  QHash<QString, MapThemeLayerRecord> layerRecords; // key = layer ID
415 
416  bool expandedStateInfo = false;
417  if ( visPresetElem.hasAttribute( QStringLiteral( "has-expanded-info" ) ) )
418  expandedStateInfo = visPresetElem.attribute( QStringLiteral( "has-expanded-info" ) ).toInt();
419 
420  QString presetName = visPresetElem.attribute( QStringLiteral( "name" ) );
421  QDomElement visPresetLayerElem = visPresetElem.firstChildElement( QStringLiteral( "layer" ) );
422  while ( !visPresetLayerElem.isNull() )
423  {
424  QString layerID = visPresetLayerElem.attribute( QStringLiteral( "id" ) );
425  if ( QgsMapLayer *layer = mProject->mapLayer( layerID ) )
426  {
427  layerRecords[layerID] = MapThemeLayerRecord( layer );
428 
429  if ( visPresetLayerElem.hasAttribute( QStringLiteral( "style" ) ) )
430  {
431  layerRecords[layerID].usingCurrentStyle = true;
432  layerRecords[layerID].currentStyle = visPresetLayerElem.attribute( QStringLiteral( "style" ) );
433  }
434 
435  if ( visPresetLayerElem.hasAttribute( QStringLiteral( "expanded" ) ) )
436  layerRecords[layerID].expandedLayerNode = visPresetLayerElem.attribute( QStringLiteral( "expanded" ) ).toInt();
437  }
438  visPresetLayerElem = visPresetLayerElem.nextSiblingElement( QStringLiteral( "layer" ) );
439  }
440 
441  QDomElement checkedLegendNodesElem = visPresetElem.firstChildElement( QStringLiteral( "checked-legend-nodes" ) );
442  while ( !checkedLegendNodesElem.isNull() )
443  {
444  QSet<QString> checkedLegendNodes;
445 
446  QDomElement checkedLegendNodeElem = checkedLegendNodesElem.firstChildElement( QStringLiteral( "checked-legend-node" ) );
447  while ( !checkedLegendNodeElem.isNull() )
448  {
449  checkedLegendNodes << checkedLegendNodeElem.attribute( QStringLiteral( "id" ) );
450  checkedLegendNodeElem = checkedLegendNodeElem.nextSiblingElement( QStringLiteral( "checked-legend-node" ) );
451  }
452 
453  QString layerID = checkedLegendNodesElem.attribute( QStringLiteral( "id" ) );
454  if ( mProject->mapLayer( layerID ) ) // only use valid IDs
455  {
456  layerRecords[layerID].usingLegendItems = true;
457  layerRecords[layerID].checkedLegendItems = checkedLegendNodes;
458  }
459  checkedLegendNodesElem = checkedLegendNodesElem.nextSiblingElement( QStringLiteral( "checked-legend-nodes" ) );
460  }
461 
462  QSet<QString> expandedGroupNodes;
463  if ( expandedStateInfo )
464  {
465  // expanded state of legend nodes
466  QDomElement expandedLegendNodesElem = visPresetElem.firstChildElement( QStringLiteral( "expanded-legend-nodes" ) );
467  while ( !expandedLegendNodesElem.isNull() )
468  {
469  QSet<QString> expandedLegendNodes;
470 
471  QDomElement expandedLegendNodeElem = expandedLegendNodesElem.firstChildElement( QStringLiteral( "expanded-legend-node" ) );
472  while ( !expandedLegendNodeElem.isNull() )
473  {
474  expandedLegendNodes << expandedLegendNodeElem.attribute( QStringLiteral( "id" ) );
475  expandedLegendNodeElem = expandedLegendNodeElem.nextSiblingElement( QStringLiteral( "expanded-legend-node" ) );
476  }
477 
478  QString layerID = expandedLegendNodesElem.attribute( QStringLiteral( "id" ) );
479  if ( mProject->mapLayer( layerID ) ) // only use valid IDs
480  {
481  layerRecords[layerID].expandedLegendItems = expandedLegendNodes;
482  }
483  expandedLegendNodesElem = expandedLegendNodesElem.nextSiblingElement( QStringLiteral( "expanded-legend-nodes" ) );
484  }
485 
486  // expanded state of group nodes
487  QDomElement expandedGroupNodesElem = visPresetElem.firstChildElement( QStringLiteral( "expanded-group-nodes" ) );
488  if ( !expandedGroupNodesElem.isNull() )
489  {
490  QDomElement expandedGroupNodeElem = expandedGroupNodesElem.firstChildElement( QStringLiteral( "expanded-group-node" ) );
491  while ( !expandedGroupNodeElem.isNull() )
492  {
493  expandedGroupNodes << expandedGroupNodeElem.attribute( QStringLiteral( "id" ) );
494  expandedGroupNodeElem = expandedGroupNodeElem.nextSiblingElement( QStringLiteral( "expanded-group-node" ) );
495  }
496  }
497  }
498 
499  MapThemeRecord rec;
500  rec.setLayerRecords( layerRecords.values() );
501  rec.setHasExpandedStateInfo( expandedStateInfo );
502  rec.setExpandedGroupNodes( expandedGroupNodes );
503  mMapThemes.insert( presetName, rec );
504  emit mapThemeChanged( presetName );
505 
506  visPresetElem = visPresetElem.nextSiblingElement( QStringLiteral( "visibility-preset" ) );
507  }
508 
509  reconnectToLayersStyleManager();
510  emit mapThemesChanged();
511 }
512 
513 void QgsMapThemeCollection::writeXml( QDomDocument &doc )
514 {
515  QDomElement visPresetsElem = doc.createElement( QStringLiteral( "visibility-presets" ) );
516  MapThemeRecordMap::const_iterator it = mMapThemes.constBegin();
517  for ( ; it != mMapThemes.constEnd(); ++ it )
518  {
519  QString grpName = it.key();
520  const MapThemeRecord &rec = it.value();
521  QDomElement visPresetElem = doc.createElement( QStringLiteral( "visibility-preset" ) );
522  visPresetElem.setAttribute( QStringLiteral( "name" ), grpName );
523  if ( rec.hasExpandedStateInfo() )
524  visPresetElem.setAttribute( QStringLiteral( "has-expanded-info" ), QStringLiteral( "1" ) );
525  Q_FOREACH ( const MapThemeLayerRecord &layerRec, rec.mLayerRecords )
526  {
527  if ( !layerRec.layer() )
528  continue;
529  QString layerID = layerRec.layer()->id();
530  QDomElement layerElem = doc.createElement( QStringLiteral( "layer" ) );
531  layerElem.setAttribute( QStringLiteral( "id" ), layerID );
532  if ( layerRec.usingCurrentStyle )
533  layerElem.setAttribute( QStringLiteral( "style" ), layerRec.currentStyle );
534  visPresetElem.appendChild( layerElem );
535 
536  if ( layerRec.usingLegendItems )
537  {
538  QDomElement checkedLegendNodesElem = doc.createElement( QStringLiteral( "checked-legend-nodes" ) );
539  checkedLegendNodesElem.setAttribute( QStringLiteral( "id" ), layerID );
540  Q_FOREACH ( const QString &checkedLegendNode, layerRec.checkedLegendItems )
541  {
542  QDomElement checkedLegendNodeElem = doc.createElement( QStringLiteral( "checked-legend-node" ) );
543  checkedLegendNodeElem.setAttribute( QStringLiteral( "id" ), checkedLegendNode );
544  checkedLegendNodesElem.appendChild( checkedLegendNodeElem );
545  }
546  visPresetElem.appendChild( checkedLegendNodesElem );
547  }
548 
549  if ( rec.hasExpandedStateInfo() )
550  {
551  layerElem.setAttribute( QStringLiteral( "expanded" ), layerRec.expandedLayerNode ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
552 
553  QDomElement expandedLegendNodesElem = doc.createElement( QStringLiteral( "expanded-legend-nodes" ) );
554  expandedLegendNodesElem.setAttribute( QStringLiteral( "id" ), layerID );
555  for ( const QString &expandedLegendNode : qgis::as_const( layerRec.expandedLegendItems ) )
556  {
557  QDomElement expandedLegendNodeElem = doc.createElement( QStringLiteral( "expanded-legend-node" ) );
558  expandedLegendNodeElem.setAttribute( QStringLiteral( "id" ), expandedLegendNode );
559  expandedLegendNodesElem.appendChild( expandedLegendNodeElem );
560  }
561  visPresetElem.appendChild( expandedLegendNodesElem );
562  }
563  }
564 
565  if ( rec.hasExpandedStateInfo() )
566  {
567  QDomElement expandedGroupElems = doc.createElement( QStringLiteral( "expanded-group-nodes" ) );
568  const QSet<QString> expandedGroupNodes = rec.expandedGroupNodes();
569  for ( const QString &groupId : expandedGroupNodes )
570  {
571  QDomElement expandedGroupElem = doc.createElement( QStringLiteral( "expanded-group-node" ) );
572  expandedGroupElem.setAttribute( QStringLiteral( "id" ), groupId );
573  expandedGroupElems.appendChild( expandedGroupElem );
574  }
575  visPresetElem.appendChild( expandedGroupElems );
576  }
577 
578  visPresetsElem.appendChild( visPresetElem );
579  }
580 
581  doc.firstChildElement( QStringLiteral( "qgis" ) ).appendChild( visPresetsElem );
582 }
583 
584 void QgsMapThemeCollection::registryLayersRemoved( const QStringList &layerIDs )
585 {
586  // while layers are stored as weak pointers, this triggers the mapThemeChanged signal for
587  // affected themes
588  QSet< QString > changedThemes;
589  MapThemeRecordMap::iterator it = mMapThemes.begin();
590  for ( ; it != mMapThemes.end(); ++it )
591  {
592  MapThemeRecord &rec = it.value();
593  for ( int i = 0; i < rec.mLayerRecords.count(); ++i )
594  {
595  MapThemeLayerRecord &layerRec = rec.mLayerRecords[i];
596  if ( layerRec.layer() && layerIDs.contains( layerRec.layer()->id() ) )
597  {
598  rec.mLayerRecords.removeAt( i-- );
599  changedThemes << it.key();
600  }
601  }
602  }
603 
604  Q_FOREACH ( const QString &theme, changedThemes )
605  {
606  emit mapThemeChanged( theme );
607  }
608  emit mapThemesChanged();
609 }
610 
611 void QgsMapThemeCollection::layerStyleRenamed( const QString &oldName, const QString &newName )
612 {
613  QgsMapLayerStyleManager *styleMgr = qobject_cast<QgsMapLayerStyleManager *>( sender() );
614  if ( !styleMgr )
615  return;
616 
617  QSet< QString > changedThemes;
618 
619  MapThemeRecordMap::iterator it = mMapThemes.begin();
620  for ( ; it != mMapThemes.end(); ++it )
621  {
622  MapThemeRecord &rec = it.value();
623  for ( int i = 0; i < rec.mLayerRecords.count(); ++i )
624  {
625  MapThemeLayerRecord &layerRec = rec.mLayerRecords[i];
626  if ( layerRec.layer() == styleMgr->layer() )
627  {
628  if ( layerRec.currentStyle == oldName )
629  {
630  layerRec.currentStyle = newName;
631  changedThemes << it.key();
632  }
633  }
634  }
635  }
636  Q_FOREACH ( const QString &theme, changedThemes )
637  {
638  emit mapThemeChanged( theme );
639  }
640  emit mapThemesChanged();
641 }
642 
644 {
645  for ( int i = 0; i < mLayerRecords.length(); ++i )
646  {
647  if ( mLayerRecords.at( i ).layer() == layer )
648  mLayerRecords.removeAt( i );
649  }
650 }
651 
653 {
654  mLayerRecords.append( record );
655 }
656 
657 QHash<QgsMapLayer *, QgsMapThemeCollection::MapThemeLayerRecord> QgsMapThemeCollection::MapThemeRecord::validLayerRecords() const
658 {
659  QHash<QgsMapLayer *, MapThemeLayerRecord> validSet;
660  Q_FOREACH ( const MapThemeLayerRecord &layerRec, mLayerRecords )
661  {
662  if ( layerRec.layer() )
663  validSet.insert( layerRec.layer(), layerRec );
664  }
665  return validSet;
666 }
667 
669 {
670  mLayer = layer;
671 }
Layer tree group node serves as a container for layers and further groups.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:63
void setLayer(QgsMapLayer *layer)
Sets the map layer for this record.
void setProject(QgsProject *project)
The QgsProject on which this map theme collection works.
virtual QString name() const =0
Returns name of the node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
void styleRenamed(const QString &oldName, const QString &newName)
Emitted when a style has been renamed.
QSet< QString > expandedGroupNodes() const
Returns a set of group identifiers for group nodes that should have expanded state (other group nodes...
virtual bool legendSymbolItemsCheckable() const
items of symbology items in legend should be checkable
QMap< QString, QString > mapThemeStyleOverrides(const QString &name)
Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme...
Restore overridden layer style on destruction.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file...
QHash< QgsMapLayer *, QgsMapThemeCollection::MapThemeLayerRecord > validLayerRecords() const
Returns set with only records for valid layers.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false...
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer&#39;s style manager.
Individual map theme record of visible layers and styles.
void update(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Updates a map theme within the collection.
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
QList< QgsMapLayer * > checkedLayers() const
Returns a list of any checked layers which belong to this node or its children.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
QList< QgsMapLayer * > mapThemeVisibleLayers(const QString &name) const
Returns the list of layers that are visible for the specified map theme.
void clear()
Remove all map themes from the collection.
void setExpandedGroupNodes(const QSet< QString > &expandedGroupNodes)
Sets a set of group identifiers for group nodes that should have expanded state.
Individual record of a visible layer in a map theme record.
void mapThemesChanged()
Emitted when map themes within the collection are changed.
void setHasExpandedStateInfo(bool hasInfo)
Sets whether the map theme contains valid expanded/collapsed state of nodes.
void removeLayerRecord(QgsMapLayer *layer)
Removes a record for layer if present.
void readXml(const QDomDocument &doc)
Reads the map theme collection state from XML.
void projectChanged()
Emitted when the project changes.
void readFromLayer(QgsMapLayer *layer)
Store layer&#39;s active style information in the instance.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well) ...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Stores style information (renderer, opacity, labeling, diagrams etc.) applicable to a map layer...
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QgsMapThemeCollection(QgsProject *project=nullptr)
Create map theme collection that handles themes of the given project.
void removeMapTheme(const QString &name)
Remove an existing map theme from collection.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is a null pointer, the node is a root node. ...
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
void setLayerRecords(const QList< QgsMapThemeCollection::MapThemeLayerRecord > &records)
Sets layer records for the theme.
QString currentStyle() const
Returns name of the current style.
QSet< QString > checkedLegendItems
Rule keys of check legend items in layer tree model.
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.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
Reads and writes project states.
Definition: qgsproject.h:89
QList< QgsMapLayer * > masterLayerOrder() const
Returns the master layer order (this will always match the project&#39;s QgsProject::layerOrder() )...
virtual bool legendSymbolItemChecked(const QString &key)
items of symbology items in legend is checked
QgsFeatureRenderer * renderer()
Returns renderer.
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
QgsMapLayer * layer() const
Gets pointer to the associated map layer.
void applyTheme(const QString &name, QgsLayerTreeGroup *root, QgsLayerTreeModel *model)
Apply theme given by its name and modify layer tree, current style of layers and checked legend items...
virtual void checkLegendSymbolItem(const QString &key, bool state=true)
item in symbology was checked
QString xmlData() const
Returns XML content of the style.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
void insert(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Inserts a new map theme to the collection.
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
QgsMapLayer * layer() const
Returns map layer or null if the layer does not exist anymore.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
QStringList mapThemeVisibleLayerIds(const QString &name) const
Returns the list of layer IDs that are visible for the specified map theme.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QgsProject * project()
The QgsProject on which this map theme collection works.
QSet< QString > expandedLegendItems
Rule keys of expanded legend items in layer tree view.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QList< QgsMapLayer * > masterVisibleLayers() const
Returns the master list of visible layers.
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool expandedLayerNode
Whether the layer&#39;s tree node is expanded (only to be applied if the parent MapThemeRecord has the in...
bool hasExpandedStateInfo() const
Returns whether information about expanded/collapsed state of nodes has been recorded and thus whethe...
QString ruleKey() const
Returns unique identifier of the rule for identification of the item within renderer.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
static QgsMapThemeCollection::MapThemeRecord createThemeFromCurrentState(QgsLayerTreeGroup *root, QgsLayerTreeModel *model)
Static method to create theme from the current state of layer visibilities in layer tree...
Management of styles for use with one map layer.
Represents a vector layer which manages a vector based data sets.
QString currentStyle
Name of the current style of the layer.
bool usingCurrentStyle
Whether current style is valid and should be applied.
void writeXml(QDomDocument &doc)
Writes the map theme collection state to XML.
void addLayerRecord(const QgsMapThemeCollection::MapThemeLayerRecord &record)
Add a new record for a layer.
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
bool usingLegendItems
Whether checkedLegendItems should be applied.
QStringList mapThemes() const
Returns a list of existing map theme names.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.