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