QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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 "qgslayoutitemlegend.h"
18
19#include <limits>
20
21#include "qgscolorutils.h"
23#include "qgslayertree.h"
26#include "qgslayertreemodel.h"
28#include "qgslayertreeutils.h"
29#include "qgslayout.h"
30#include "qgslayoutitemmap.h"
32#include "qgslayoutmodel.h"
35#include "qgslayoututils.h"
36#include "qgslegendrenderer.h"
37#include "qgslegendstyle.h"
38#include "qgslogger.h"
39#include "qgsmaplayerlegend.h"
40#include "qgsmapsettings.h"
41#include "qgsproject.h"
44#include "qgsvectorlayer.h"
45
46#include <QDomDocument>
47#include <QDomElement>
48#include <QPainter>
49
50#include "moc_qgslayoutitemlegend.cpp"
51
52//
53// QgsLegendFilterProxyModel
54//
55
61
63{
64 mIsDefaultLegend = isDefault;
65
66 // only show private layers when not in default mode
67 setShowPrivateLayers( !mIsDefaultLegend );
68
69 invalidateFilter();
70}
71
72bool QgsLegendFilterProxyModel::layerShown( QgsMapLayer *layer ) const
73{
74 if ( !layer )
75 return true;
76
77 if ( QgsMapLayerLegend *layerLegend = layer->legend(); mIsDefaultLegend && layerLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
78 {
79 return false;
80 }
81
82 return true;
83}
84
85//
86// QgsLayoutItemLegend
87//
88
91 , mLegendModel( new QgsLegendModel( nullptr, this ) )
92{
93#if 0 //no longer required?
94 connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
95#endif
96
97 mTitle = mSettings.title();
98
99 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestStarted, this, [this] { emit backgroundTaskCountChanged( 1 ); } );
100 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestCompleted, this, [this]
101 {
102 adjustBoxSize();
103 emit backgroundTaskCountChanged( 0 );
104 } );
105
106 // Connect to the main layertreeroot.
107 // It serves in "auto update mode" as a medium between the main app legend and this one
108 connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
109
110 // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
111 connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [this]
112 {
113 invalidateCache();
114 update();
115 } );
116 connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [this]
117 {
118 // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
119 // causing an endless loop.
120 invalidateCache();
121 update();
122 } );
123}
124
129
134
136{
137 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
138}
139
144
145void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
146{
147 if ( !painter )
148 return;
149
150 const QPointF oldPos = pos();
151
152 ensureModelIsInitialized();
153
154 if ( mFilterAskedForUpdate )
155 {
156 mFilterAskedForUpdate = false;
157 doUpdateFilterByMap();
158 }
159
160 const int dpi = painter->device()->logicalDpiX();
161 const double dotsPerMM = dpi / 25.4;
162
163 if ( mLayout )
164 {
166 // no longer required, but left set for api stability
167 mSettings.setUseAdvancedEffects( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::UseAdvancedEffects );
168 mSettings.setDpi( dpi );
169 mSettings.setSynchronousLegendRequests( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::SynchronousLegendGraphics );
171 }
172 if ( mMap && mLayout )
173 {
175 // no longer required, but left set for api stability
176 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
178
179 // use a temporary QgsMapSettings to find out real map scale
180 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
181 const QgsRectangle mapExtent = mMap->extent();
182
183 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
184
185 // no longer required, but left set for api stability
187 mSettings.setMapScale( ms.scale() );
189 }
190 mInitialMapScaleCalculated = true;
191
192 QgsLegendRenderer legendRenderer = createRenderer();
193 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
194
195 //adjust box if width or height is too small
196 if ( mSizeToContents )
197 {
198 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
201
202 const QSizeF size = legendRenderer.minimumSize( &context );
203 if ( mForceResize )
204 {
205 mForceResize = false;
206
207 //set new rect, respecting position mode and data defined size/position
208 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
209 attemptResize( newSize );
210 }
211 else if ( size.height() > rect().height() || size.width() > rect().width() )
212 {
213 //need to resize box
214 QSizeF targetSize = rect().size();
215 if ( size.height() > targetSize.height() )
216 targetSize.setHeight( size.height() );
217 if ( size.width() > targetSize.width() )
218 targetSize.setWidth( size.width() );
219
220 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
221 //set new rect, respecting position mode and data defined size/position
222 attemptResize( newSize );
223 }
224 }
225
226 // attemptResize may change the legend position and would call setPos
227 // BUT the position is actually changed for the next draw, so we need to translate of the difference
228 // between oldPos and newPos
229 // the issue doesn't appear in desktop rendering but only in export because in the first one,
230 // Qt triggers a redraw on position change
231 painter->save();
232 painter->translate( pos() - oldPos );
233 QgsLayoutItem::paint( painter, itemStyle, pWidget );
234 painter->restore();
235}
236
238{
239 if ( !mMapUuid.isEmpty() )
240 {
241 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
242 }
243
244 if ( !mFilterByMapUuids.isEmpty() )
245 {
246 QList< QgsLayoutItemMap * > maps;
247 maps.reserve( mFilterByMapUuids.size() );
248 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
249 {
250 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
251 {
252 maps << map;
253 }
254 }
255 setFilterByMapItems( maps );
256 }
257}
258
260{
262 clearLegendCachedData();
263 onAtlasFeature();
264}
265
267{
268 clearLegendCachedData();
270}
271
273{
274 QPainter *painter = context.renderContext().painter();
275
276 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
278
281
282 const QgsScopedQPainterState painterState( painter );
283
284 // painter is scaled to dots, so scale back to layout units
285 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
286
287 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
288
289 if ( !mSizeToContents )
290 {
291 // set a clip region to crop out parts of legend which don't fit
292 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
293 painter->setClipRect( thisPaintRect );
294 }
295
296 if ( mLayout )
297 {
298 // no longer required, but left for API compatibility
300 mSettings.setDpi( mLayout->renderContext().dpi() );
302 }
303
304 QgsLegendRenderer legendRenderer = createRenderer();
305 legendRenderer.setLegendSize( rect().size() );
306
307 legendRenderer.drawLegend( rc );
308}
309
311{
312 if ( !mSizeToContents )
313 return;
314
315 if ( !mInitialMapScaleCalculated )
316 {
317 // this is messy - but until we have painted the item we have no knowledge of the current DPI
318 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
319 // for marker symbols with size in map units, causing the legends to initially expand to huge
320 // sizes if we attempt to calculate the box size first.
321 return;
322 }
323
324 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
327
328 QgsLegendRenderer legendRenderer = createRenderer();
329 const QSizeF size = legendRenderer.minimumSize( &context );
330 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ), 2 );
331 if ( size.isValid() )
332 {
333 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
334 //set new rect, respecting position mode and data defined size/position
335 attemptResize( newSize );
336 }
337}
338
340{
341 mSizeToContents = enabled;
342}
343
345{
346 return mSizeToContents;
347}
348
349void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
350{
351 if ( !mDeferLegendModelInitialization )
352 {
353 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
354 }
355
356 mCustomLayerTree.reset( rootGroup );
357}
358
359void QgsLayoutItemLegend::ensureModelIsInitialized() const
360{
361 if ( mDeferLegendModelInitialization )
362 {
363 QgsLayoutItemLegend *mutableThis = const_cast< QgsLayoutItemLegend * >( this );
364 mutableThis->mDeferLegendModelInitialization = false;
365 mutableThis->setCustomLayerTree( mutableThis->mCustomLayerTree.release() );
366 }
367}
368
369QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const
370{
371 QgsLegendRenderer res( mLegendModel.get(), mSettings );
372
373 QgsLegendFilterProxyModel *proxy = new QgsLegendFilterProxyModel();
374 proxy->setIsDefaultLegend( !static_cast< bool >( mCustomLayerTree ) );
375 res.setProxyModel( proxy );
376
377 return res;
378}
379
381{
382 ensureModelIsInitialized();
383 return mLegendModel.get();
384}
385
387{
388 ensureModelIsInitialized();
389 return mLegendModel.get();
390}
391
393{
394 if ( autoUpdate == autoUpdateModel() )
395 return;
396
397 if ( autoUpdate )
398 {
399 setCustomLayerTree( nullptr );
400 }
401 else
402 {
403 std::unique_ptr< QgsLayerTree > customTree( mLayout->project()->layerTreeRoot()->clone() );
404
405 // filter out excluded by default items
406 std::function< void( QgsLayerTreeGroup * )> filterNodeChildren;
407 filterNodeChildren = [&filterNodeChildren]( QgsLayerTreeGroup * group )
408 {
409 if ( !group )
410 return;
411
412 const QList<QgsLayerTreeNode *> children = group->children();
413 for ( QgsLayerTreeNode *child : children )
414 {
415 if ( !child )
416 {
417 group->removeChildNode( child );
418 continue;
419 }
420 else if ( QgsLayerTree::isGroup( child ) )
421 {
422 filterNodeChildren( QgsLayerTree::toGroup( child ) );
423 }
424 else if ( QgsLayerTree::isLayer( child ) )
425 {
427 if ( QgsMapLayer *mapLayer = layer->layer() )
428 {
429 if ( QgsMapLayerLegend *layerLegend = mapLayer->legend(); layerLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
430 {
431 group->removeChildNode( child );
432 }
433 }
434 }
435 }
436 };
437 filterNodeChildren( customTree.get() );
438
439 setCustomLayerTree( customTree.release() );
440 }
441
443 updateFilterByMap( false );
444}
445
446void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
447{
448 if ( key == QLatin1String( "cached_name" ) )
449 return;
450
451 if ( autoUpdateModel() )
452 {
453 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
454 // we must then call updateItem to reflect the changes
455 updateFilterByMap( false );
456 }
457}
458
460{
461 return !mCustomLayerTree;
462}
463
465{
466 if ( mLegendFilterByMap == enabled )
467 return;
468
469 mLegendFilterByMap = enabled;
470 updateFilterByMap( false );
471}
472
473void QgsLayoutItemLegend::setTitle( const QString &t )
474{
475 mTitle = t;
476 mSettings.setTitle( t );
477
478 if ( mLayout && id().isEmpty() )
479 {
480 //notify the model that the display name has changed
481 mLayout->itemsModel()->updateItemDisplayName( this );
482 }
483}
485{
486 return mTitle;
487}
488
489Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
490{
491 return mSettings.titleAlignment();
492}
493
494void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
495{
496 mSettings.setTitleAlignment( alignment );
497}
498
500{
501 return mSettings.rstyle( s );
502}
503
505{
506 return mSettings.style( s );
507}
508
510{
511 mSettings.setStyle( s, style );
512}
513
515{
517 return mSettings.style( s ).font();
519}
520
527
529{
530 rstyle( s ).setMargin( margin );
531}
532
534{
535 rstyle( s ).setMargin( side, margin );
536}
537
539{
541 return mSettings.lineSpacing();
543}
544
546{
548 mSettings.setLineSpacing( spacing );
550}
551
553{
554 return mSettings.boxSpace();
555}
556
558{
559 mSettings.setBoxSpace( s );
560}
561
563{
564 return mSettings.columnSpace();
565}
566
568{
569 mSettings.setColumnSpace( s );
570}
571
573{
575 return mSettings.fontColor();
577}
578
580{
582 mSettings.setFontColor( c );
584}
585
587{
588 return mSettings.symbolSize().width();
589}
590
592{
593 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
594}
595
597{
598 return mSettings.maximumSymbolSize();
599}
600
602{
603 mSettings.setMaximumSymbolSize( size );
604}
605
607{
608 return mSettings.minimumSymbolSize();
609}
610
612{
613 mSettings.setMinimumSymbolSize( size );
614}
615
616void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
617{
618 mSettings.setSymbolAlignment( alignment );
619}
620
622{
623 return mSettings.symbolAlignment();
624}
625
627{
628 return mSettings.symbolSize().height();
629}
630
632{
633 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
634}
635
637{
638 return mSettings.wmsLegendSize().width();
639}
640
642{
643 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
644}
645
647{
648 return mSettings.wmsLegendSize().height();
649}
651{
652 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
653}
654
656{
657 mSettings.setWrapChar( t );
658}
659
661{
662 return mSettings.wrapChar();
663}
664
666{
667 return mColumnCount;
668}
669
671{
672 mColumnCount = c;
673 mSettings.setColumnCount( c );
674}
675
677{
678 return mSettings.splitLayer();
679}
680
682{
683 mSettings.setSplitLayer( s );
684}
685
687{
688 return mSettings.equalColumnWidth();
689}
690
692{
693 mSettings.setEqualColumnWidth( s );
694}
695
697{
698 return mSettings.drawRasterStroke();
699}
700
702{
703 mSettings.setDrawRasterStroke( enabled );
704}
705
707{
708 return mSettings.rasterStrokeColor();
709}
710
712{
713 mSettings.setRasterStrokeColor( color );
714}
715
717{
718 return mSettings.rasterStrokeWidth();
719}
720
722{
723 mSettings.setRasterStrokeWidth( width );
724}
725
727{
728 return mSettings.autoWrapLinesAfter();
729}
730
732{
733 mSettings.setAutoWrapLinesAfter( length );
734}
735
741
742bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
743{
744
745 //write general properties
746 legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
747 legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
748 legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
749 legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
750 legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
751
752 legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
753 legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
754
755 legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
756 legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
757 legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
758 legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
759
760 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
761
762 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
763
764 legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
765 legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
766 legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
767
768 if ( mSettings.autoWrapLinesAfter() > 0 )
769 {
770 legendElem.setAttribute( QStringLiteral( "autoWrapLinesAfter" ), mSettings.autoWrapLinesAfter() );
771 }
772
773 legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
774 legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
775 legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
776
777 legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
778
779 if ( mMap )
780 {
781 legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
782 }
783
784 if ( !mFilterByMapItems.empty() )
785 {
786 QDomElement filterByMapsElem = doc.createElement( QStringLiteral( "filterByMaps" ) );
787 for ( QgsLayoutItemMap *map : mFilterByMapItems )
788 {
789 if ( map )
790 {
791 QDomElement mapElem = doc.createElement( QStringLiteral( "map" ) );
792 mapElem.setAttribute( QStringLiteral( "uuid" ), map->uuid() );
793 filterByMapsElem.appendChild( mapElem );
794 }
795 }
796 legendElem.appendChild( filterByMapsElem );
797 }
798
799 QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
800 legendElem.appendChild( legendStyles );
801
802 style( Qgis::LegendComponent::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc, context );
803 style( Qgis::LegendComponent::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc, context );
804 style( Qgis::LegendComponent::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc, context );
805 style( Qgis::LegendComponent::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc, context );
806 style( Qgis::LegendComponent::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc, context );
807
808 if ( mCustomLayerTree )
809 {
810 // if not using auto-update - store the custom layer tree
811 mCustomLayerTree->writeXml( legendElem, context );
812 }
813
814 if ( mLegendFilterByMap )
815 {
816 legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
817 }
818 legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
819
820 return true;
821}
822
823bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
824{
825 //read general properties
826 mTitle = itemElem.attribute( QStringLiteral( "title" ) );
827 mSettings.setTitle( mTitle );
828 if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
829 {
830 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
831 }
832 int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
833 if ( colCount < 1 ) colCount = 1;
834 mColumnCount = colCount;
835 mSettings.setColumnCount( mColumnCount );
836 mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
837 mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
838
839 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
840 if ( !stylesNodeList.isEmpty() )
841 {
842 const QDomNode stylesNode = stylesNodeList.at( 0 );
843 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
844 {
845 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
847 style.readXml( styleElem, doc, context );
848 const QString name = styleElem.attribute( QStringLiteral( "name" ) );
850 if ( name == QLatin1String( "title" ) ) s = Qgis::LegendComponent::Title;
851 else if ( name == QLatin1String( "group" ) ) s = Qgis::LegendComponent::Group;
852 else if ( name == QLatin1String( "subgroup" ) ) s = Qgis::LegendComponent::Subgroup;
853 else if ( name == QLatin1String( "symbol" ) ) s = Qgis::LegendComponent::Symbol;
854 else if ( name == QLatin1String( "symbolLabel" ) ) s = Qgis::LegendComponent::SymbolLabel;
855 else continue;
856 setStyle( s, style );
857 }
858 }
859
860 //font color
861 if ( itemElem.hasAttribute( QStringLiteral( "fontColor" ) ) )
862 {
863 QColor fontClr;
864 fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
869 }
870
871 //spaces
872 mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
873 mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
874
875 mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
876 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
877
878 mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
879 mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
880
881 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
882
883 if ( itemElem.hasAttribute( QStringLiteral( "lineSpacing" ) ) )
884 {
885 const double spacing = itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble();
886 // line spacing *was* a fixed amount (in mm) added between each line of text.
888 // assume font sizes in points, since that was what we always had from before this method was deprecated
889 f.setLineHeight( f.size() * 0.352778 + spacing );
892
894 f.setLineHeight( f.size() * 0.352778 + spacing );
897
899 f.setLineHeight( f.size() * 0.352778 + spacing );
902
904 f.setLineHeight( f.size() * 0.352778 + spacing );
907 }
908
909 mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
910 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
911 mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
912
913 mSettings.setAutoWrapLinesAfter( itemElem.attribute( QStringLiteral( "autoWrapLinesAfter" ), QStringLiteral( "0" ) ).toDouble() );
914
915 mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
916
917 mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
918
919 // map
920 mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
921
922 mMapUuid.clear();
923 if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
924 {
925 mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
926 }
927
928 mFilterByMapUuids.clear();
929 {
930 const QDomElement filterByMapsElem = itemElem.firstChildElement( QStringLiteral( "filterByMaps" ) );
931 if ( !filterByMapsElem.isNull() )
932 {
933 QDomElement mapsElem = filterByMapsElem.firstChildElement( QStringLiteral( "map" ) );
934 while ( !mapsElem.isNull() )
935 {
936 mFilterByMapUuids << mapsElem.attribute( QStringLiteral( "uuid" ) );
937 mapsElem = mapsElem.nextSiblingElement( QStringLiteral( "map" ) );
938 }
939 }
940 else if ( !mMapUuid.isEmpty() )
941 {
942 // for compatibility with < QGIS 3.32 projects
943 mFilterByMapUuids << mMapUuid;
944 }
945 }
946
947 // disconnect current map
948 setupMapConnections( mMap, false );
949 mMap = nullptr;
950
951 mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
952
953 // QGIS >= 2.6
954 QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
955 if ( layerTreeElem.isNull() )
956 layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
957
958 if ( !layerTreeElem.isNull() )
959 {
960 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
961 if ( mLayout )
962 tree->resolveReferences( mLayout->project(), true );
963 setCustomLayerTree( tree.release() );
964 }
965 else
966 setCustomLayerTree( nullptr );
967
968 return true;
969}
970
972{
973 if ( !id().isEmpty() )
974 {
975 return id();
976 }
977
978 //if no id, default to portion of title text
979 QString text = mSettings.title();
980 if ( text.isEmpty() )
981 {
982 return tr( "<Legend>" );
983 }
984 if ( text.length() > 25 )
985 {
986 return tr( "%1…" ).arg( text.left( 25 ) );
987 }
988 else
989 {
990 return text;
991 }
992}
993
995{
996 return blendMode() != QPainter::CompositionMode_SourceOver;
997}
998
1000{
1001 return mEvaluatedOpacity < 1.0;
1002}
1003
1004void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
1005{
1006 if ( !map )
1007 return;
1008
1009 if ( !connectSlots )
1010 {
1011 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1012 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1013 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1014 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1015 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1016 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1017 }
1018 else
1019 {
1020 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1021 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1022 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1023 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1024 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1025 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1026 }
1027}
1028
1030{
1031 if ( mMap == map )
1032 return;
1033
1034 if ( mMap )
1035 {
1036 setupMapConnections( mMap, false );
1037 }
1038
1039 mMap = map;
1040
1041 if ( mMap )
1042 {
1043 setupMapConnections( mMap, true );
1044 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
1045 }
1046
1048}
1049
1050void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
1051{
1052 if ( filterByMapItems() == maps )
1053 return;
1054
1055 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
1056 {
1057 setupMapConnections( map, false );
1058 }
1059
1060 mFilterByMapItems.clear();
1061 mFilterByMapItems.reserve( maps.size() );
1062 for ( QgsLayoutItemMap *map : maps )
1063 {
1064 if ( map )
1065 {
1066 mFilterByMapItems.append( map );
1067 setupMapConnections( map, true );
1068 }
1069 }
1070
1072}
1073
1074QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
1075{
1076 QList<QgsLayoutItemMap *> res;
1077 res.reserve( mFilterByMapItems.size() );
1078 for ( QgsLayoutItemMap *map : mFilterByMapItems )
1079 {
1080 if ( map )
1081 res.append( map );
1082 }
1083 return res;
1084}
1085
1086void QgsLayoutItemLegend::invalidateCurrentMap()
1087{
1088 setLinkedMap( nullptr );
1089}
1090
1092{
1094
1095 bool forceUpdate = false;
1096 //updates data defined properties and redraws item to match
1098 {
1099 bool ok = false;
1100 const QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::LegendTitle, context, mTitle, &ok );
1101 if ( ok )
1102 {
1103 mSettings.setTitle( t );
1104 forceUpdate = true;
1105 }
1106 }
1108 {
1109 bool ok = false;
1110 const int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::DataDefinedProperty::LegendColumnCount, context, mColumnCount, &ok );
1111 if ( ok && cols >= 0 )
1112 {
1113 mSettings.setColumnCount( cols );
1114 forceUpdate = true;
1115 }
1116 }
1118 {
1119 bool ok = false;
1120 const double width = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::LegendAutoWrapWidth, context, mSettings.autoWrapLinesAfter(), &ok );
1121 if ( ok && width >= 0 )
1122 {
1123 mSettings.setAutoWrapLinesAfter( width );
1124 forceUpdate = true;
1125 }
1126 }
1127 if ( forceUpdate )
1128 {
1129 adjustBoxSize();
1130 update();
1131 }
1132
1134}
1135
1136
1137void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
1138{
1139 updateFilterByMap( true );
1140}
1141
1142void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
1143{
1144 mLegendModel->setLayerStyleOverrides( overrides );
1145 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1146 {
1147 const QList< QgsLayerTreeLayer * > layers = rootGroup->findLayers();
1148 for ( QgsLayerTreeLayer *nodeLayer : std::as_const( layers ) )
1149 mLegendModel->refreshLayerLegend( nodeLayer );
1150 }
1151}
1152
1153void QgsLayoutItemLegend::clearLegendCachedData()
1154{
1155 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1156 clearNodeCache = [&]( QgsLayerTreeNode * node )
1157 {
1158 mLegendModel->clearCachedData( node );
1159 if ( QgsLayerTree::isGroup( node ) )
1160 {
1161 QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
1162 const QList< QgsLayerTreeNode * > children = group->children();
1163 for ( QgsLayerTreeNode *child : children )
1164 {
1165 clearNodeCache( child );
1166 }
1167 }
1168 };
1169
1170 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1171 {
1172 clearNodeCache( rootGroup );
1173 }
1174}
1175
1176void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
1177{
1178 if ( !mMap )
1179 return;
1180
1181 // map's style has been changed, so make sure to update the legend here
1182 if ( mLegendFilterByMap )
1183 {
1184 // legend is being filtered by map, so we need to re run the hit test too
1185 // as the style overrides may also have affected the visible symbols
1186 updateFilterByMap( false );
1187 }
1188 else
1189 {
1190 setModelStyleOverrides( mMap->layerStyleOverrides() );
1191 }
1192
1193 adjustBoxSize();
1194
1195 updateFilterByMap( false );
1196}
1197
1198void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
1199{
1200 if ( mThemeName == theme )
1201 return;
1202
1203 mThemeName = theme;
1204
1205 // map's theme has been changed, so make sure to update the legend here
1206
1207 // legend is being filtered by map, so we need to re run the hit test too
1208 // as the style overrides may also have affected the visible symbols
1209 updateFilterByMap( false );
1210
1211 adjustBoxSize();
1212
1214}
1215
1217{
1218 // ask for update
1219 // the actual update will take place before the redraw.
1220 // This is to avoid multiple calls to the filter
1221 mFilterAskedForUpdate = true;
1222
1223 if ( redraw )
1224 update();
1225}
1226
1227void QgsLayoutItemLegend::doUpdateFilterByMap()
1228{
1229 // There's an incompatibility here between legend handling of linked map themes and layer style overrides vs
1230 // how expression evaluation is made in legend content text. The logic below is hacked together to get
1231 // all the existing unit tests passing, but these two features are incompatible with each other and fixing
1232 // this is extremely non-trivial. Let's just hope no-one tries to use those features together!
1233 // Ideally, all the branches below would be consistently using either "setModelStyleOverrides" (which forces
1234 // a rebuild of each layer's legend, and breaks legend text expression evaluation) OR
1235 // "mLegendModel->setLayerStyleOverrides" which just handles the expression updates but which doesn't correctly
1236 // generate the legend content from the associated theme settings.
1237 if ( mMap && !mThemeName.isEmpty() )
1238 {
1239 // get style overrides for theme
1240 const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
1241 setModelStyleOverrides( overrides );
1242 }
1243 else if ( mMap )
1244 {
1245 mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
1246 }
1247 else
1248 {
1249 mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
1250 }
1251
1252 // only use thread hit tests for preview renders. In other cases we'll need a blocking hit test anyway, and we run a risk
1253 // of deadlocks if a non-preview render is then started on the main thread.
1254 mLegendModel->setFlag( QgsLayerTreeModel::UseThreadedHitTest, mLayout->renderContext().isPreviewRender() );
1255
1256 const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
1257
1258 const bool hasValidFilter = filterByExpression
1259 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) )
1260 || mInAtlas;
1261
1262 if ( hasValidFilter )
1263 {
1264 const double dpi = mLayout->renderContext().dpi();
1265
1266 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1267 if ( mLegendFilterByMap )
1268 {
1269 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1270 if ( mMap )
1271 linkedFilterMaps.insert( mMap );
1272 }
1273
1274 QgsMapSettings mapSettings;
1275 QgsGeometry filterGeometry;
1276 if ( mMap )
1277 {
1278 // if a specific linked map has been set, use it for the reference scale and extent
1279 const QgsRectangle requestRectangle = mMap->requestedExtent();
1280 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1281 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1282 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1283
1284 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1285 }
1286 else if ( !linkedFilterMaps.empty() )
1287 {
1288 // otherwise just take the first linked filter map
1289 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1290 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1291 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1292 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1293
1294 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1295 }
1296
1298
1299 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1300
1301 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1302
1303 QList<QgsMapLayer *> layersToClip;
1304 if ( mMap )
1305 {
1306 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1307 {
1308 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1309 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1310 {
1311 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1312 mapLayers.removeAll( layer );
1313 filterSettings.mapSettings().setLayers( mapLayers );
1314 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1315 }
1316 }
1317 }
1318
1319 if ( !linkedFilterMaps.empty() )
1320 {
1321 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1322 {
1323
1324 if ( map == mMap )
1325 continue;
1326
1327 QgsGeometry mapExtent = QgsGeometry::fromQPolygonF( map->visibleExtentPolygon() );
1328
1329 //transform back to destination CRS
1330 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1331 try
1332 {
1333 mapExtent.transform( mapTransform );
1334 }
1335 catch ( QgsCsException & )
1336 {
1337 continue;
1338 }
1339
1340 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1341 for ( QgsMapLayer *layer : layersForMap )
1342 {
1343 if ( mInAtlas && !atlasGeometry.isNull() )
1344 {
1345 mapExtent = mapExtent.intersection( atlasGeometry );
1346 }
1347
1348 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1349 }
1350 }
1351 }
1352
1353 if ( mInAtlas )
1354 {
1355 if ( !filterGeometry.isEmpty() )
1356 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1357 else
1358 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1359 }
1360
1361 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1362 if ( !filterGeometry.isNull() )
1363 {
1364 filterSettings.setFilterPolygon( filterGeometry );
1365 }
1366 else
1367 {
1368 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1369 }
1370
1371 mLegendModel->setFilterSettings( &filterSettings );
1372 }
1373 else
1374 {
1375 mLegendModel->setFilterSettings( nullptr );
1376 }
1377
1378 clearLegendCachedData();
1379 mForceResize = true;
1380}
1381
1383{
1384 return mThemeName;
1385}
1386
1388{
1389 mFilterOutAtlas = doFilter;
1390}
1391
1393{
1394 return mFilterOutAtlas;
1395}
1396
1397void QgsLayoutItemLegend::onAtlasFeature()
1398{
1399 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1400 return;
1401 mInAtlas = mFilterOutAtlas;
1403}
1404
1405void QgsLayoutItemLegend::onAtlasEnded()
1406{
1407 mInAtlas = false;
1409}
1410
1412{
1414
1415 // We only want the last scope from the map's expression context, as this contains
1416 // the map specific variables. We don't want the rest of the map's context, because that
1417 // will contain duplicate global, project, layout, etc scopes.
1418 if ( mMap )
1419 context.appendScope( mMap->createExpressionContext().popScope() );
1420
1421 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1422
1423 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
1424 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
1425 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
1426 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
1427 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
1428 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
1429
1430 context.appendScope( scope );
1431 return context;
1432}
1433
1438
1440{
1441 std::function<bool( QgsLayerTreeGroup *group ) >visit;
1442
1443 visit = [this, visitor, &visit]( QgsLayerTreeGroup * group ) -> bool
1444 {
1445 const QList<QgsLayerTreeNode *> childNodes = group->children();
1446 for ( QgsLayerTreeNode *node : childNodes )
1447 {
1448 if ( QgsLayerTree::isGroup( node ) )
1449 {
1450 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1451 if ( !visit( nodeGroup ) )
1452 return false;
1453 }
1454 else if ( QgsLayerTree::isLayer( node ) )
1455 {
1456 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1457 if ( !nodeLayer->patchShape().isNull() )
1458 {
1459 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1460 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1461 return false;
1462 }
1463 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1464 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1465 {
1466 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1467 {
1468 if ( !symbolNode->patchShape().isNull() )
1469 {
1470 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1471 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1472 return false;
1473 }
1474 }
1475 }
1476 }
1477 }
1478 return true;
1479 };
1480 return visit( model()->rootGroup( ) );
1481}
1482
1484{
1485 return mLegendModel->hitTestInProgress();
1486}
1487
1488
1489// -------------------------------------------------------------------------
1490
1492 : QgsLayerTreeModel( rootNode, parent )
1493 , mLayoutLegend( layout )
1494{
1498 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1499}
1500
1502 : QgsLayerTreeModel( rootNode )
1503 , mLayoutLegend( layout )
1504{
1508 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1509}
1510
1511QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1512{
1513 // handle custom layer node labels
1514
1516 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1517 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1518 {
1519 QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
1520 if ( !name.isEmpty() )
1521 return name;
1522
1523 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1524
1525 //finding the first label that is stored
1526 name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1527 if ( name.isEmpty() )
1528 name = nodeLayer->name();
1529 if ( name.isEmpty() )
1530 name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1531 if ( name.isEmpty() )
1532 name = node->name();
1533 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
1534 {
1535 if ( vlayer && vlayer->featureCount() >= 0 )
1536 {
1537 name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
1538 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1539 return name;
1540 }
1541 }
1542 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1543 return name;
1544 }
1545 return QgsLayerTreeModel::data( index, role );
1546}
1547
1548Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1549{
1550 // make the legend nodes selectable even if they are not by default
1551 if ( index2legendNode( index ) )
1552 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1553
1555}
1556
1557QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1558{
1559 if ( !mLegend.contains( nodeLayer ) )
1560 return QList<QgsLayerTreeModelLegendNode *>();
1561
1562 const LayerLegendData &data = mLegend[nodeLayer];
1563 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1564 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1565 lst.prepend( data.embeddedNodeInParent );
1566 return lst;
1567}
1568
1570{
1571 node->removeCustomProperty( QStringLiteral( "cached_name" ) );
1572}
1573
1574void QgsLegendModel::forceRefresh()
1575{
1576 emit refreshLegend();
1577}
@ Millimeters
Millimeters.
Definition qgis.h:5204
LegendComponent
Component of legends which can be styled.
Definition qgis.h:4575
@ Symbol
Symbol icon (excluding label).
Definition qgis.h:4581
@ Group
Legend group title.
Definition qgis.h:4579
@ Subgroup
Legend subgroup title.
Definition qgis.h:4580
@ Title
Legend title.
Definition qgis.h:4578
@ SymbolLabel
Symbol label (excluding icon).
Definition qgis.h:4582
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
Definition qgis.h:4536
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4555
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition qgis.h:2762
@ SynchronousLegendGraphics
Query legend graphics synchronously.
Definition qgis.h:5248
@ UseAdvancedEffects
Enable advanced effects such as blend modes.
Definition qgis.h:5241
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.
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...
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.
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...
void setShowPrivateLayers(bool showPrivate)
Determines if private layers are shown.
QModelIndex parent(const QModelIndex &child) const override
QgsLayerTreeFilterProxyModel(QObject *parent=nullptr)
Constructor.
Layer tree group node serves as a container for layers and further groups.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
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.
An abstract interface for legend items returned from QgsMapLayerLegend implementation.
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
QgsLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
QModelIndex parent(const QModelIndex &child) 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.
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 std::unique_ptr< QgsLayerTree > readXml(const QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
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...
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.
QgsLegendModel * model()
Returns the legend model.
QgsLegendStyle & rstyle(Qgis::LegendComponent s)
Returns reference to modifiable legend style.
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
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.
double autoWrapLinesAfter() const
Returns the maximum line length (in millimeters) allowed before lines of text in the legend will be a...
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.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
Q_DECL_DEPRECATED QFont styleFont(Qgis::LegendComponent component) const
Returns the font settings for a legend component.
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 setAutoWrapLinesAfter(double length)
Sets the maximum line length (in millimeters) allowed before lines of text in the legend will be auto...
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 setStyleMargin(Qgis::LegendComponent component, double margin)
Set the margin for a legend component.
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.
QgsLegendStyle style(Qgis::LegendComponent s) const
Returns legend style.
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).
void setStyle(Qgis::LegendComponent component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
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(Qgis::LegendComponent component, const QFont &font)
Sets the style font for a legend component.
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.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
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.
QList< QgsMapLayer * > layersToRender(const QgsExpressionContext *context=nullptr, bool includeInvalidLayers=false) const
Returns a list of the layers which will be rendered within this map item, considering any locked laye...
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
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.
friend class 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.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
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.
friend class QgsLayoutItemMap
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.
friend class QgsLayoutItemLegend
QFlags< Flag > Flags
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.
@ LegendAutoWrapWidth
Legend text automatic wrapping width.
@ AllProperties
All properties for item.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
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.
void setIsDefaultLegend(bool isDefault)
Sets whether the legend is showing the default legend for a project (as opposed to a customized legen...
QgsLegendFilterProxyModel(QObject *parent=nullptr)
Constructor for QgsLegendFilterProxyModel, with the specified parent object.
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...
Handles automatic layout and rendering of legends.
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.
Contains detailed styling information relating to how a layout legend should be rendered.
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.
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.
An abstract interface for implementations of legends for one map layer.
Qgis::MapLayerLegendFlags flags() const
Returns flags associated with the legend.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QgsMapLayerLegend * legend() const
Can be nullptr.
Contains configuration for rendering maps.
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.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
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.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
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:1520
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 dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
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:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
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.