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