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