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