QGIS API Documentation 3.37.0-Master (c2d15952569)
Loading...
Searching...
No Matches
qgslayoutitemlegend.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemlegend.cpp
3 -----------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include <limits>
18
19#include "qgslayoutitemlegend.h"
21#include "qgslayoutitemmap.h"
22#include "qgslayoutmodel.h"
23#include "qgslayertree.h"
24#include "qgslayertreemodel.h"
25#include "qgslegendrenderer.h"
26#include "qgslegendstyle.h"
27#include "qgslogger.h"
28#include "qgsmapsettings.h"
29#include "qgsproject.h"
30#include "qgscolorutils.h"
31#include "qgslayertreeutils.h"
32#include "qgslayoututils.h"
33#include "qgslayout.h"
36#include "qgsvectorlayer.h"
41
42#include <QDomDocument>
43#include <QDomElement>
44#include <QPainter>
46
48 : QgsLayoutItem( layout )
49 , mLegendModel( new QgsLegendModel( layout->project()->layerTreeRoot(), this ) )
50{
51#if 0 //no longer required?
52 connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
53#endif
54
55 mTitle = mSettings.title();
56
57 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestStarted, this, [this] { emit backgroundTaskCountChanged( 1 ); } );
58 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestCompleted, this, [this]
59 {
60 adjustBoxSize();
61 emit backgroundTaskCountChanged( 0 );
62 } );
63
64 // Connect to the main layertreeroot.
65 // It serves in "auto update mode" as a medium between the main app legend and this one
66 connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
67
68 // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
69 connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [this]
70 {
71 invalidateCache();
72 update();
73 } );
74 connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [this]
75 {
76 // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
77 // causing an endless loop.
78 invalidateCache();
79 update();
80 } );
81}
82
87
92
94{
95 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
96}
97
98QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
99{
101}
102
103void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
104{
105 if ( !painter )
106 return;
107
108 if ( mFilterAskedForUpdate )
109 {
110 mFilterAskedForUpdate = false;
111 doUpdateFilterByMap();
112 }
113
114 if ( mLayout )
115 {
116 if ( !mLayout->renderContext().isPreviewRender() && mLegendModel->hitTestInProgress() )
117 {
118 mLegendModel->waitForHitTestBlocking();
119 }
120 }
121
122 const int dpi = painter->device()->logicalDpiX();
123 const double dotsPerMM = dpi / 25.4;
124
125 if ( mLayout )
126 {
128 // no longer required, but left set for api stability
130 mSettings.setDpi( dpi );
133 }
134 if ( mMap && mLayout )
135 {
137 // no longer required, but left set for api stability
138 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
140
141 // use a temporary QgsMapSettings to find out real map scale
142 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
143 const QgsRectangle mapExtent = mMap->extent();
144
145 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
146
147 // no longer required, but left set for api stability
149 mSettings.setMapScale( ms.scale() );
151 }
152 mInitialMapScaleCalculated = true;
153
154 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
155 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
156
157 const QPointF oldPos = pos();
158
159 //adjust box if width or height is too small
160 if ( mSizeToContents )
161 {
162 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
164
165 const QSizeF size = legendRenderer.minimumSize( &context );
166 if ( mForceResize )
167 {
168 mForceResize = false;
169
170 //set new rect, respecting position mode and data defined size/position
171 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
172 attemptResize( newSize );
173 }
174 else if ( size.height() > rect().height() || size.width() > rect().width() )
175 {
176 //need to resize box
177 QSizeF targetSize = rect().size();
178 if ( size.height() > targetSize.height() )
179 targetSize.setHeight( size.height() );
180 if ( size.width() > targetSize.width() )
181 targetSize.setWidth( size.width() );
182
183 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
184 //set new rect, respecting position mode and data defined size/position
185 attemptResize( newSize );
186 }
187 }
188
189 // attemptResize may change the legend position and would call setPos
190 // BUT the position is actually changed for the next draw, so we need to translate of the difference
191 // between oldPos and newPos
192 // the issue doesn't appear in desktop rendering but only in export because in the first one,
193 // Qt triggers a redraw on position change
194 painter->save();
195 painter->translate( pos() - oldPos );
196 QgsLayoutItem::paint( painter, itemStyle, pWidget );
197 painter->restore();
198}
199
201{
202 if ( !mMapUuid.isEmpty() )
203 {
204 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
205 }
206
207 if ( !mFilterByMapUuids.isEmpty() )
208 {
209 QList< QgsLayoutItemMap * > maps;
210 maps.reserve( mFilterByMapUuids.size() );
211 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
212 {
213 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
214 {
215 maps << map;
216 }
217 }
218 setFilterByMapItems( maps );
219 }
220}
221
223{
225 clearLegendCachedData();
226 onAtlasFeature();
227}
228
230{
231 clearLegendCachedData();
233}
234
236{
237 QPainter *painter = context.renderContext().painter();
238
239 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
241
243
244 const QgsScopedQPainterState painterState( painter );
245
246 // painter is scaled to dots, so scale back to layout units
247 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
248
249 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
250
251 if ( !mSizeToContents )
252 {
253 // set a clip region to crop out parts of legend which don't fit
254 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
255 painter->setClipRect( thisPaintRect );
256 }
257
258 if ( mLayout )
259 {
260 // no longer required, but left for API compatibility
262 mSettings.setDpi( mLayout->renderContext().dpi() );
264 }
265
266 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
267 legendRenderer.setLegendSize( rect().size() );
268
269 legendRenderer.drawLegend( rc );
270}
271
273{
274 if ( !mSizeToContents )
275 return;
276
277 if ( !mInitialMapScaleCalculated )
278 {
279 // this is messy - but until we have painted the item we have no knowledge of the current DPI
280 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
281 // for marker symbols with size in map units, causing the legends to initially expand to huge
282 // sizes if we attempt to calculate the box size first.
283 return;
284 }
285
286 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
288
289 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
290 const QSizeF size = legendRenderer.minimumSize( &context );
291 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ), 2 );
292 if ( size.isValid() )
293 {
294 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
295 //set new rect, respecting position mode and data defined size/position
296 attemptResize( newSize );
297 }
298}
299
301{
302 mSizeToContents = enabled;
303}
304
306{
307 return mSizeToContents;
308}
309
310void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
311{
312 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
313
314 mCustomLayerTree.reset( rootGroup );
315}
316
317
319{
320 if ( autoUpdate == autoUpdateModel() )
321 return;
322
323 setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
325 updateFilterByMap( false );
326}
327
328void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
329{
330 if ( key == QLatin1String( "cached_name" ) )
331 return;
332
333 if ( autoUpdateModel() )
334 {
335 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
336 // we must then call updateItem to reflect the changes
337 updateFilterByMap( false );
338 }
339}
340
342{
343 return !mCustomLayerTree;
344}
345
347{
348 if ( mLegendFilterByMap == enabled )
349 return;
350
351 mLegendFilterByMap = enabled;
352 updateFilterByMap( false );
353}
354
355void QgsLayoutItemLegend::setTitle( const QString &t )
356{
357 mTitle = t;
358 mSettings.setTitle( t );
359
360 if ( mLayout && id().isEmpty() )
361 {
362 //notify the model that the display name has changed
363 mLayout->itemsModel()->updateItemDisplayName( this );
364 }
365}
367{
368 return mTitle;
369}
370
371Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
372{
373 return mSettings.titleAlignment();
374}
375
376void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
377{
378 mSettings.setTitleAlignment( alignment );
379}
380
385
387{
388 return mSettings.style( s );
389}
390
392{
393 mSettings.setStyle( s, style );
394}
395
402
409
411{
412 rstyle( s ).setMargin( margin );
413}
414
416{
417 rstyle( s ).setMargin( side, margin );
418}
419
426
428{
430 mSettings.setLineSpacing( spacing );
432}
433
435{
436 return mSettings.boxSpace();
437}
438
440{
441 mSettings.setBoxSpace( s );
442}
443
445{
446 return mSettings.columnSpace();
447}
448
450{
451 mSettings.setColumnSpace( s );
452}
453
455{
457 return mSettings.fontColor();
459}
460
467
469{
470 return mSettings.symbolSize().width();
471}
472
474{
475 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
476}
477
479{
480 return mSettings.maximumSymbolSize();
481}
482
484{
485 mSettings.setMaximumSymbolSize( size );
486}
487
489{
490 return mSettings.minimumSymbolSize();
491}
492
494{
495 mSettings.setMinimumSymbolSize( size );
496}
497
498void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
499{
500 mSettings.setSymbolAlignment( alignment );
501}
502
504{
505 return mSettings.symbolAlignment();
506}
507
509{
510 return mSettings.symbolSize().height();
511}
512
514{
515 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
516}
517
519{
520 return mSettings.wmsLegendSize().width();
521}
522
524{
525 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
526}
527
529{
530 return mSettings.wmsLegendSize().height();
531}
533{
534 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
535}
536
538{
539 mSettings.setWrapChar( t );
540}
541
543{
544 return mSettings.wrapChar();
545}
546
548{
549 return mColumnCount;
550}
551
553{
554 mColumnCount = c;
555 mSettings.setColumnCount( c );
556}
557
559{
560 return mSettings.splitLayer();
561}
562
564{
565 mSettings.setSplitLayer( s );
566}
567
569{
570 return mSettings.equalColumnWidth();
571}
572
574{
575 mSettings.setEqualColumnWidth( s );
576}
577
579{
580 return mSettings.drawRasterStroke();
581}
582
584{
585 mSettings.setDrawRasterStroke( enabled );
586}
587
589{
590 return mSettings.rasterStrokeColor();
591}
592
594{
595 mSettings.setRasterStrokeColor( color );
596}
597
599{
600 return mSettings.rasterStrokeWidth();
601}
602
604{
605 mSettings.setRasterStrokeWidth( width );
606}
607
613
614bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
615{
616
617 //write general properties
618 legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
619 legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
620 legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
621 legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
622 legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
623
624 legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
625 legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
626
627 legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
628 legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
629 legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
630 legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
631
632 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
633
634 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
635
636 legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
637 legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
638 legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
639
640 legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
641 legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
642 legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
643
644 legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
645
646 if ( mMap )
647 {
648 legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
649 }
650
651 if ( !mFilterByMapItems.empty() )
652 {
653 QDomElement filterByMapsElem = doc.createElement( QStringLiteral( "filterByMaps" ) );
654 for ( QgsLayoutItemMap *map : mFilterByMapItems )
655 {
656 if ( map )
657 {
658 QDomElement mapElem = doc.createElement( QStringLiteral( "map" ) );
659 mapElem.setAttribute( QStringLiteral( "uuid" ), map->uuid() );
660 filterByMapsElem.appendChild( mapElem );
661 }
662 }
663 legendElem.appendChild( filterByMapsElem );
664 }
665
666 QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
667 legendElem.appendChild( legendStyles );
668
669 style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc, context );
670 style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc, context );
671 style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc, context );
672 style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc, context );
673 style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc, context );
674
675 if ( mCustomLayerTree )
676 {
677 // if not using auto-update - store the custom layer tree
678 mCustomLayerTree->writeXml( legendElem, context );
679 }
680
681 if ( mLegendFilterByMap )
682 {
683 legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
684 }
685 legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
686
687 return true;
688}
689
690bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
691{
692 //read general properties
693 mTitle = itemElem.attribute( QStringLiteral( "title" ) );
694 mSettings.setTitle( mTitle );
695 if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
696 {
697 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
698 }
699 int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
700 if ( colCount < 1 ) colCount = 1;
701 mColumnCount = colCount;
702 mSettings.setColumnCount( mColumnCount );
703 mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
704 mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
705
706 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
707 if ( !stylesNodeList.isEmpty() )
708 {
709 const QDomNode stylesNode = stylesNodeList.at( 0 );
710 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
711 {
712 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
714 style.readXml( styleElem, doc, context );
715 const QString name = styleElem.attribute( QStringLiteral( "name" ) );
717 if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
718 else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
719 else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
720 else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
721 else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
722 else continue;
723 setStyle( s, style );
724 }
725 }
726
727 //font color
728 if ( itemElem.hasAttribute( QStringLiteral( "fontColor" ) ) )
729 {
730 QColor fontClr;
731 fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
736 }
737
738 //spaces
739 mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
740 mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
741
742 mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
743 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
744
745 mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
746 mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
747
748 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
749
750 if ( itemElem.hasAttribute( QStringLiteral( "lineSpacing" ) ) )
751 {
752 const double spacing = itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble();
753 // line spacing *was* a fixed amount (in mm) added between each line of text.
755 // assume font sizes in points, since that was what we always had from before this method was deprecated
756 f.setLineHeight( f.size() * 0.352778 + spacing );
759
761 f.setLineHeight( f.size() * 0.352778 + spacing );
764
766 f.setLineHeight( f.size() * 0.352778 + spacing );
769
771 f.setLineHeight( f.size() * 0.352778 + spacing );
774 }
775
776 mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
777 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
778 mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
779
780 mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
781
782 mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
783
784 // map
785 mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
786
787 mMapUuid.clear();
788 if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
789 {
790 mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
791 }
792
793 mFilterByMapUuids.clear();
794 {
795 const QDomElement filterByMapsElem = itemElem.firstChildElement( QStringLiteral( "filterByMaps" ) );
796 if ( !filterByMapsElem.isNull() )
797 {
798 QDomElement mapsElem = filterByMapsElem.firstChildElement( QStringLiteral( "map" ) );
799 while ( !mapsElem.isNull() )
800 {
801 mFilterByMapUuids << mapsElem.attribute( QStringLiteral( "uuid" ) );
802 mapsElem = mapsElem.nextSiblingElement( QStringLiteral( "map" ) );
803 }
804 }
805 else if ( !mMapUuid.isEmpty() )
806 {
807 // for compatibility with < QGIS 3.32 projects
808 mFilterByMapUuids << mMapUuid;
809 }
810 }
811
812 // disconnect current map
813 setupMapConnections( mMap, false );
814 mMap = nullptr;
815
816 mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
817
818 // QGIS >= 2.6
819 QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
820 if ( layerTreeElem.isNull() )
821 layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
822
823 if ( !layerTreeElem.isNull() )
824 {
825 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
826 if ( mLayout )
827 tree->resolveReferences( mLayout->project(), true );
828 setCustomLayerTree( tree.release() );
829 }
830 else
831 setCustomLayerTree( nullptr );
832
833 return true;
834}
835
837{
838 if ( !id().isEmpty() )
839 {
840 return id();
841 }
842
843 //if no id, default to portion of title text
844 QString text = mSettings.title();
845 if ( text.isEmpty() )
846 {
847 return tr( "<Legend>" );
848 }
849 if ( text.length() > 25 )
850 {
851 return tr( "%1…" ).arg( text.left( 25 ) );
852 }
853 else
854 {
855 return text;
856 }
857}
858
860{
861 return blendMode() != QPainter::CompositionMode_SourceOver;
862}
863
865{
866 return mEvaluatedOpacity < 1.0;
867}
868
869void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
870{
871 if ( !map )
872 return;
873
874 if ( !connectSlots )
875 {
876 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
877 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
878 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
879 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
880 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
881 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
882 }
883 else
884 {
885 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
886 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
887 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
888 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
889 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
890 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
891 }
892}
893
895{
896 if ( mMap == map )
897 return;
898
899 if ( mMap )
900 {
901 setupMapConnections( mMap, false );
902 }
903
904 mMap = map;
905
906 if ( mMap )
907 {
908 setupMapConnections( mMap, true );
909 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
910 }
911
913}
914
915void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
916{
917 if ( filterByMapItems() == maps )
918 return;
919
920 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
921 {
922 setupMapConnections( map, false );
923 }
924
925 mFilterByMapItems.clear();
926 mFilterByMapItems.reserve( maps.size() );
927 for ( QgsLayoutItemMap *map : maps )
928 {
929 if ( map )
930 {
931 mFilterByMapItems.append( map );
932 setupMapConnections( map, true );
933 }
934 }
935
937}
938
939QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
940{
941 QList<QgsLayoutItemMap *> res;
942 res.reserve( mFilterByMapItems.size() );
943 for ( QgsLayoutItemMap *map : mFilterByMapItems )
944 {
945 if ( map )
946 res.append( map );
947 }
948 return res;
949}
950
951void QgsLayoutItemLegend::invalidateCurrentMap()
952{
953 setLinkedMap( nullptr );
954}
955
957{
959
960 bool forceUpdate = false;
961 //updates data defined properties and redraws item to match
963 {
964 bool ok = false;
966 if ( ok )
967 {
968 mSettings.setTitle( t );
969 forceUpdate = true;
970 }
971 }
973 {
974 bool ok = false;
976 if ( ok && cols >= 0 )
977 {
978 mSettings.setColumnCount( cols );
979 forceUpdate = true;
980 }
981 }
982 if ( forceUpdate )
983 {
985 update();
986 }
987
989}
990
991
992void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
993{
994 updateFilterByMap( true );
995}
996
997void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
998{
999 mLegendModel->setLayerStyleOverrides( overrides );
1000 const QList< QgsLayerTreeLayer * > layers = mLegendModel->rootGroup()->findLayers();
1001 for ( QgsLayerTreeLayer *nodeLayer : layers )
1002 mLegendModel->refreshLayerLegend( nodeLayer );
1003
1004}
1005
1006void QgsLayoutItemLegend::clearLegendCachedData()
1007{
1008 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1009 clearNodeCache = [&]( QgsLayerTreeNode * node )
1010 {
1011 mLegendModel->clearCachedData( node );
1012 if ( QgsLayerTree::isGroup( node ) )
1013 {
1015 const QList< QgsLayerTreeNode * > children = group->children();
1016 for ( QgsLayerTreeNode *child : children )
1017 {
1018 clearNodeCache( child );
1019 }
1020 }
1021 };
1022
1023 clearNodeCache( mLegendModel->rootGroup() );
1024}
1025
1026void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
1027{
1028 if ( !mMap )
1029 return;
1030
1031 // map's style has been changed, so make sure to update the legend here
1032 if ( mLegendFilterByMap )
1033 {
1034 // legend is being filtered by map, so we need to re run the hit test too
1035 // as the style overrides may also have affected the visible symbols
1036 updateFilterByMap( false );
1037 }
1038 else
1039 {
1040 setModelStyleOverrides( mMap->layerStyleOverrides() );
1041 }
1042
1043 adjustBoxSize();
1044
1045 updateFilterByMap( false );
1046}
1047
1048void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
1049{
1050 if ( mThemeName == theme )
1051 return;
1052
1053 mThemeName = theme;
1054
1055 // map's theme has been changed, so make sure to update the legend here
1056
1057 // legend is being filtered by map, so we need to re run the hit test too
1058 // as the style overrides may also have affected the visible symbols
1059 updateFilterByMap( false );
1060
1061 adjustBoxSize();
1062
1064}
1065
1067{
1068 // ask for update
1069 // the actual update will take place before the redraw.
1070 // This is to avoid multiple calls to the filter
1071 mFilterAskedForUpdate = true;
1072
1073 if ( redraw )
1074 update();
1075}
1076
1077void QgsLayoutItemLegend::doUpdateFilterByMap()
1078{
1079 // There's an incompatibility here between legend handling of linked map themes and layer style overrides vs
1080 // how expression evaluation is made in legend content text. The logic below is hacked together to get
1081 // all the existing unit tests passing, but these two features are incompatible with each other and fixing
1082 // this is extremely non-trivial. Let's just hope no-one tries to use those features together!
1083 // Ideally, all the branches below would be consistently using either "setModelStyleOverrides" (which forces
1084 // a rebuild of each layer's legend, and breaks legend text expression evaluation) OR
1085 // "mLegendModel->setLayerStyleOverrides" which just handles the expression updates but which doesn't correctly
1086 // generate the legend content from the associated theme settings.
1087 if ( mMap && !mThemeName.isEmpty() )
1088 {
1089 // get style overrides for theme
1090 const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
1091 setModelStyleOverrides( overrides );
1092 }
1093 else if ( mMap )
1094 {
1095 mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
1096 }
1097 else
1098 {
1099 mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
1100 }
1101
1102 const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
1103
1104 const bool hasValidFilter = filterByExpression
1105 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) )
1106 || mInAtlas;
1107
1108 if ( hasValidFilter )
1109 {
1110 const double dpi = mLayout->renderContext().dpi();
1111
1112 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1113 if ( mLegendFilterByMap )
1114 {
1115 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1116 if ( mMap )
1117 linkedFilterMaps.insert( mMap );
1118 }
1119
1120 QgsMapSettings mapSettings;
1121 QgsGeometry filterGeometry;
1122 if ( mMap )
1123 {
1124 // if a specific linked map has been set, use it for the reference scale and extent
1125 const QgsRectangle requestRectangle = mMap->requestedExtent();
1126 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1127 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1128 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1129
1130 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1131 }
1132 else if ( !linkedFilterMaps.empty() )
1133 {
1134 // otherwise just take the first linked filter map
1135 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1136 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1137 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1138 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1139
1140 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1141 }
1142
1144
1145 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1146
1147 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1148
1149 QList<QgsMapLayer *> layersToClip;
1150 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1151 {
1152 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1153 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1154 {
1155 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1156 mapLayers.removeAll( layer );
1157 filterSettings.mapSettings().setLayers( mapLayers );
1158 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1159 }
1160 }
1161
1162
1163 if ( !linkedFilterMaps.empty() )
1164 {
1165 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1166 {
1167
1168 if ( map == mMap )
1169 continue;
1170
1172
1173 //transform back to destination CRS
1174 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1175 try
1176 {
1177 mapExtent.transform( mapTransform );
1178 }
1179 catch ( QgsCsException &cse )
1180 {
1181 continue;
1182 }
1183
1184 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1185 for ( QgsMapLayer *layer : layersForMap )
1186 {
1187 if ( mInAtlas && !atlasGeometry.isNull() )
1188 {
1189 mapExtent = mapExtent.intersection( atlasGeometry );
1190 }
1191
1192 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1193 }
1194 }
1195 }
1196
1197 if ( mInAtlas )
1198 {
1199 if ( !filterGeometry.isEmpty() )
1200 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1201 else
1202 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1203 }
1204
1205 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1206 if ( !filterGeometry.isNull() )
1207 {
1208 filterSettings.setFilterPolygon( filterGeometry );
1209 }
1210 else
1211 {
1212 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1213 }
1214
1215 mLegendModel->setFilterSettings( &filterSettings );
1216 }
1217 else
1218 {
1219 mLegendModel->setFilterSettings( nullptr );
1220 }
1221
1222 clearLegendCachedData();
1223 mForceResize = true;
1224}
1225
1227{
1228 return mThemeName;
1229}
1230
1232{
1233 mFilterOutAtlas = doFilter;
1234}
1235
1237{
1238 return mFilterOutAtlas;
1239}
1240
1241void QgsLayoutItemLegend::onAtlasFeature()
1242{
1243 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1244 return;
1245 mInAtlas = mFilterOutAtlas;
1247}
1248
1249void QgsLayoutItemLegend::onAtlasEnded()
1250{
1251 mInAtlas = false;
1253}
1254
1256{
1258
1259 // We only want the last scope from the map's expression context, as this contains
1260 // the map specific variables. We don't want the rest of the map's context, because that
1261 // will contain duplicate global, project, layout, etc scopes.
1262 if ( mMap )
1263 context.appendScope( mMap->createExpressionContext().popScope() );
1264
1265 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1266
1267 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
1268 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
1269 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
1270 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
1271 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
1272 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
1273
1274 context.appendScope( scope );
1275 return context;
1276}
1277
1282
1284{
1285 std::function<bool( QgsLayerTreeGroup *group ) >visit;
1286
1287 visit = [this, visitor, &visit]( QgsLayerTreeGroup * group ) -> bool
1288 {
1289 const QList<QgsLayerTreeNode *> childNodes = group->children();
1290 for ( QgsLayerTreeNode *node : childNodes )
1291 {
1292 if ( QgsLayerTree::isGroup( node ) )
1293 {
1294 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1295 if ( !visit( nodeGroup ) )
1296 return false;
1297 }
1298 else if ( QgsLayerTree::isLayer( node ) )
1299 {
1300 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1301 if ( !nodeLayer->patchShape().isNull() )
1302 {
1303 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1304 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1305 return false;
1306 }
1307 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1308 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1309 {
1310 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1311 {
1312 if ( !symbolNode->patchShape().isNull() )
1313 {
1314 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1315 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1316 return false;
1317 }
1318 }
1319 }
1320 }
1321 }
1322 return true;
1323 };
1324 return visit( mLegendModel->rootGroup( ) );
1325}
1326
1328{
1329 return mLegendModel->hitTestInProgress();
1330}
1331
1332
1333// -------------------------------------------------------------------------
1334
1336 : QgsLayerTreeModel( rootNode, parent )
1337 , mLayoutLegend( layout )
1338{
1342 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1343}
1344
1346 : QgsLayerTreeModel( rootNode )
1347 , mLayoutLegend( layout )
1348{
1352 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1353}
1354
1355QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1356{
1357 // handle custom layer node labels
1358
1360 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1361 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1362 {
1363 QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
1364 if ( !name.isEmpty() )
1365 return name;
1366
1367 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1368
1369 //finding the first label that is stored
1370 name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1371 if ( name.isEmpty() )
1372 name = nodeLayer->name();
1373 if ( name.isEmpty() )
1374 name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1375 if ( name.isEmpty() )
1376 name = node->name();
1377 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
1378 {
1379 if ( vlayer && vlayer->featureCount() >= 0 )
1380 {
1381 name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
1382 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1383 return name;
1384 }
1385 }
1386
1387 const bool evaluate = ( vlayer && !nodeLayer->labelExpression().isEmpty() ) || name.contains( "[%" );
1388 if ( evaluate )
1389 {
1390 QgsExpressionContext expressionContext;
1391 if ( vlayer )
1392 {
1393 connect( vlayer, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsLegendModel::forceRefresh, Qt::UniqueConnection );
1394 // counting is done here to ensure that a valid vector layer needs to be evaluated, count is used to validate previous count or update the count if invalidated
1395 vlayer->countSymbolFeatures();
1396 }
1397
1398 if ( mLayoutLegend )
1399 expressionContext = mLayoutLegend->createExpressionContext();
1400
1401 const QList<QgsLayerTreeModelLegendNode *> legendnodes = layerLegendNodes( nodeLayer, false );
1402 if ( ! legendnodes.isEmpty() )
1403 {
1404 if ( legendnodes.count() > 1 ) // evaluate all existing legend nodes but leave the name for the legend evaluator
1405 {
1406 for ( QgsLayerTreeModelLegendNode *treenode : legendnodes )
1407 {
1408 if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( treenode ) )
1409 symnode->evaluateLabel( expressionContext );
1410 }
1411 }
1412 else if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( legendnodes.first() ) )
1413 symnode->evaluateLabel( expressionContext );
1414 }
1415 }
1416 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1417 return name;
1418 }
1419 return QgsLayerTreeModel::data( index, role );
1420}
1421
1422Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1423{
1424 // make the legend nodes selectable even if they are not by default
1425 if ( index2legendNode( index ) )
1426 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1427
1429}
1430
1431QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1432{
1433 if ( !mLegend.contains( nodeLayer ) )
1434 return QList<QgsLayerTreeModelLegendNode *>();
1435
1436 const LayerLegendData &data = mLegend[nodeLayer];
1437 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1438 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1439 lst.prepend( data.embeddedNodeInParent );
1440 return lst;
1441}
1442
1444{
1445 node->removeCustomProperty( QStringLiteral( "cached_name" ) );
1446}
1447
1448void QgsLegendModel::forceRefresh()
1449{
1450 emit refreshLegend();
1451}
@ Millimeters
Millimeters.
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
@ Millimeters
Millimeters.
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
A geometry is the spatial representation of a feature.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Contains settings relating to filtering the contents of QgsLayerTreeModel and views.
Layer tree group node serves as a container for layers and further groups.
Layer tree node points to a map layer.
QString labelExpression() const
Returns the expression member of the LayerTreeNode.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Flags flags() const
Returns OR-ed combination of model flags.
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
@ UseThreadedHitTest
Run legend hit tests in a background thread (since QGIS 3.30)
This class is a base class for nodes in a layer tree.
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.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
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.
virtual QString name() const =0
Returns name of the node.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
Namespace with helper functions for layer tree operations.
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 QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
A layout item subclass for map legends.
bool autoUpdateModel() const
Returns whether the legend content should auto update to reflect changes in the project's layer tree.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setStyleMargin(QgsLegendStyle::Style component, double margin)
Set the margin for a legend component.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QString title() const
Returns the legend title.
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
void adjustBoxSize()
Sets the legend's item bounds to fit the whole legend content.
double wmsLegendWidth() const
Returns the WMS legend width.
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setBoxSpace(double space)
Sets the legend box space.
bool splitLayer() const
Returns whether the legend items from a single layer can be split over multiple columns.
double symbolHeight() const
Returns the legend symbol height.
void updateFilterByMap(bool redraw=true)
Updates the legend content when filtered by map.
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
Q_DECL_DEPRECATED QFont styleFont(QgsLegendStyle::Style component) const
Returns the font settings for a legend component.
static QgsLayoutItemLegend * create(QgsLayout *layout)
Returns a new legend item for the specified layout.
bool requiresRasterization() const override
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
void setSymbolWidth(double width)
Sets the legend symbol width.
QString wrapString() const
Returns the legend text wrapping string.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
void updateLegend()
Updates the model and all legend entries.
QgsLayoutItemLegend(QgsLayout *layout)
Constructor for QgsLayoutItemLegend, with the specified parent layout.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable legend style.
Q_DECL_DEPRECATED QColor fontColor() const
Returns the legend font color.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
void setWmsLegendWidth(double width)
Sets the WMS legend width.
void setTitle(const QString &title)
Sets the legend title.
Q_DECL_DEPRECATED void setFontColor(const QColor &color)
Sets the legend font color.
double boxSpace() const
Returns the legend box space.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
QString displayName() const override
Gets item display name.
bool isRefreshing() const override
Returns true if the item is currently refreshing content in the background.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
double symbolWidth() const
Returns the legend symbol width.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
int type() const override
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool legendFilterOutAtlas() const
Returns whether to filter out legend elements outside of the current atlas feature.
void setStyle(QgsLegendStyle::Style component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
Q_DECL_DEPRECATED void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
double columnSpace() const
Returns the legend column spacing.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void setFilterByMapItems(const QList< QgsLayoutItemMap * > &maps)
Sets the maps to use when filtering legend content by map extents.
void setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map.
QList< QgsLayoutItemMap * > filterByMapItems() const
Returns the maps to use when filtering legend content by map extents.
void setSymbolHeight(double height)
Sets the legend symbol height.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setAutoUpdateModel(bool autoUpdate)
Sets whether the legend content should auto update to reflect changes in the project's layer tree.
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
QIcon icon() const override
Returns the item's icon.
double wmsLegendHeight() const
Returns the WMS legend height.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in layout units) for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED double lineSpacing() const
Returns the spacing in-between lines in layout units.
void invalidateCache() override
Q_DECL_DEPRECATED void setStyleFont(QgsLegendStyle::Style component, const QFont &font)
Sets the style font for a legend component.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns legend style.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
QString themeName() const
Returns the name of the theme currently linked to the legend.
void setColumnCount(int count)
Sets the legend column count.
bool equalColumnWidth() const
Returns whether column widths should be equalized.
int columnCount() const
Returns the legend column count.
bool legendFilterByMapEnabled() const
Find out whether legend items are filtered to show just the ones visible in the associated map.
QList< QgsMapLayer * > layersToClip() const
Returns the list of map layers to clip to the atlas feature.
bool enabled() const
Returns true if the map content should be clipped to the current atlas feature.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings) const
Returns map settings that will be used for drawing of the map.
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed... a means to let associated legend items know they sh...
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
QgsRectangle requestedExtent() const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
QList< QgsMapLayer * > layersToRender(const QgsExpressionContext *context=nullptr) const
Returns a list of the layers which will be rendered within this map item, considering any locked laye...
QMap< QString, QString > layerStyleOverrides() const
Returns stored overrides of styles for layers.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
QgsRectangle extent() const
Returns the current map extent.
QgsLayoutItemMapAtlasClippingSettings * atlasClippingSettings()
Returns the map's atlas clipping settings.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Base class for graphical items within a QgsLayout.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item's content.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
@ FlagOverridesPaint
Item overrides the default layout item painting method.
virtual void invalidateCache()
Forces a deferred update of any cached image the item uses.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ LegendColumnCount
Legend column count.
@ AllProperties
All properties for item.
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
@ FlagSynchronousLegendGraphics
Query legend graphics synchronously.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
Item model implementation based on layer tree model for layout legend.
void clearCachedData(QgsLayerTreeNode *node) const
Clears any previously cached data for the specified node.
void refreshLegend()
Emitted to refresh the legend.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr, QgsLayoutItemLegend *layout=nullptr)
Construct the model based on the given layer tree.
QVariant data(const QModelIndex &index, int role) const override
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false) const
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
void setLegendSize(QSizeF s)
Sets the preferred resulting legend size.
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QString wrapChar() const
Returns the string used as a wrapping character.
Q_DECL_DEPRECATED void setFontColor(const QColor &c)
Sets the font color used for legend items.
void setWrapChar(const QString &t)
Sets a string to use as a wrapping character.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
Sets the style for a legend component.
void setColumnSpace(double s)
Sets the margin space between adjacent columns (in millimeters).
Q_DECL_DEPRECATED void setMapScale(double scale)
Sets the legend map scale.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
void setTitle(const QString &t)
Sets the title for the legend, which will be rendered above all legend items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
QSizeF wmsLegendSize() const
Returns the size (in millimeters) of WMS legend graphics shown in the legend.
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED void setLineSpacing(double s)
Sets the line spacing to use between lines of legend text.
void setColumnCount(int c)
Sets the desired minimum number of columns to show in the legend.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
Q_DECL_DEPRECATED void setMmPerMapUnit(double mmPerMapUnit)
Q_DECL_DEPRECATED void setDpi(int dpi)
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
QSizeF symbolSize() const
Returns the default symbol size (in millimeters) used for legend items.
Q_DECL_DEPRECATED QColor fontColor() const
Returns the font color used for legend items.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED double lineSpacing() const
Returns the line spacing to use between lines of legend text.
Q_DECL_DEPRECATED void setUseAdvancedEffects(bool use)
void setSplitLayer(bool s)
Sets whether layer components can be split over multiple columns.
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns modifiable reference to the style for a legend component.
void setEqualColumnWidth(bool s)
Sets whether all columns should have equal widths.
void setBoxSpace(double s)
Sets the legend box space (in millimeters), which is the empty margin around the inside of the legend...
void setSynchronousLegendRequests(bool b)
Sets whether to request legend graphics synchronously.
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
void setSymbolSize(QSizeF s)
Sets the default symbol size (in millimeters) used for legend items.
void setWmsLegendSize(QSizeF s)
Sets the desired size (in millimeters) of WMS legend graphics shown in the legend.
Contains detailed styling information relating to how a layout legend should be rendered.
Q_DECL_DEPRECATED QFont font() const
Returns the font used for rendering this legend component.
QgsTextFormat & textFormat()
Returns the text format used for rendering this legend component.
void setMargin(Side side, double margin)
Sets the margin (in mm) for the specified side of the component.
Side
Margin sides.
void readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads the component's style definition from an XML element.
Style
Component of legends which can be styled.
@ Group
Legend group title.
@ Symbol
Symbol icon (excluding label)
@ Subgroup
Legend subgroup title.
@ Title
Legend title.
@ SymbolLabel
Symbol label (excluding icon)
Q_DECL_DEPRECATED void setFont(const QFont &font)
Sets the font used for rendering this legend component.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext()) const
Writes the component's style definition to an XML element.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for rendering this legend component.
Base class for all map layer types.
Definition qgsmaplayer.h:75
The QgsMapSettings class contains configuration for rendering of the map.
double scale() const
Returns the calculated map scale.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
double width() const
Returns the width of the rectangle.
double height() const
Returns the height of the rectangle.
A QgsGeometry with associated coordinate reference system.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A legend patch shape entity for QgsStyle databases.
Definition qgsstyle.h:1495
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setLineHeightUnit(Qgis::RenderUnit unit)
Sets the unit for the line height for text.
double size() const
Returns the size for rendered text.
void setLineHeight(double height)
Sets the line height for text.
Represents a vector layer which manages a vector based data sets.
QgsVectorLayerFeatureCounter * countSymbolFeatures(bool storeSymbolFids=false)
Count features for symbols.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void symbolFeatureCountMapChanged()
Emitted when the feature count for symbols on this layer has been recalculated.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:5713
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:5712
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Single variable definition for use within a QgsExpressionContextScope.
Structure that stores all data associated with one map layer.
Contains information relating to the style entity currently being visited.