QGIS API Documentation 3.39.0-Master (3aed037ce22)
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( nullptr, 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
102
103void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
104{
105 if ( !painter )
106 return;
107
108 ensureModelIsInitialized();
109
110 if ( mFilterAskedForUpdate )
111 {
112 mFilterAskedForUpdate = false;
113 doUpdateFilterByMap();
114 }
115
116 if ( mLayout )
117 {
118 if ( !mLayout->renderContext().isPreviewRender() && mLegendModel->hitTestInProgress() )
119 {
120 mLegendModel->waitForHitTestBlocking();
121 }
122 }
123
124 const int dpi = painter->device()->logicalDpiX();
125 const double dotsPerMM = dpi / 25.4;
126
127 if ( mLayout )
128 {
130 // no longer required, but left set for api stability
132 mSettings.setDpi( dpi );
135 }
136 if ( mMap && mLayout )
137 {
139 // no longer required, but left set for api stability
140 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
142
143 // use a temporary QgsMapSettings to find out real map scale
144 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
145 const QgsRectangle mapExtent = mMap->extent();
146
147 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
148
149 // no longer required, but left set for api stability
151 mSettings.setMapScale( ms.scale() );
153 }
154 mInitialMapScaleCalculated = true;
155
156 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
157 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
158
159 const QPointF oldPos = pos();
160
161 //adjust box if width or height is too small
162 if ( mSizeToContents )
163 {
164 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
166
167 const QSizeF size = legendRenderer.minimumSize( &context );
168 if ( mForceResize )
169 {
170 mForceResize = false;
171
172 //set new rect, respecting position mode and data defined size/position
173 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
174 attemptResize( newSize );
175 }
176 else if ( size.height() > rect().height() || size.width() > rect().width() )
177 {
178 //need to resize box
179 QSizeF targetSize = rect().size();
180 if ( size.height() > targetSize.height() )
181 targetSize.setHeight( size.height() );
182 if ( size.width() > targetSize.width() )
183 targetSize.setWidth( size.width() );
184
185 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
186 //set new rect, respecting position mode and data defined size/position
187 attemptResize( newSize );
188 }
189 }
190
191 // attemptResize may change the legend position and would call setPos
192 // BUT the position is actually changed for the next draw, so we need to translate of the difference
193 // between oldPos and newPos
194 // the issue doesn't appear in desktop rendering but only in export because in the first one,
195 // Qt triggers a redraw on position change
196 painter->save();
197 painter->translate( pos() - oldPos );
198 QgsLayoutItem::paint( painter, itemStyle, pWidget );
199 painter->restore();
200}
201
203{
204 if ( !mMapUuid.isEmpty() )
205 {
206 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
207 }
208
209 if ( !mFilterByMapUuids.isEmpty() )
210 {
211 QList< QgsLayoutItemMap * > maps;
212 maps.reserve( mFilterByMapUuids.size() );
213 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
214 {
215 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
216 {
217 maps << map;
218 }
219 }
220 setFilterByMapItems( maps );
221 }
222}
223
225{
227 clearLegendCachedData();
228 onAtlasFeature();
229}
230
232{
233 clearLegendCachedData();
235}
236
238{
239 QPainter *painter = context.renderContext().painter();
240
241 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
243
245
246 const QgsScopedQPainterState painterState( painter );
247
248 // painter is scaled to dots, so scale back to layout units
249 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
250
251 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
252
253 if ( !mSizeToContents )
254 {
255 // set a clip region to crop out parts of legend which don't fit
256 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
257 painter->setClipRect( thisPaintRect );
258 }
259
260 if ( mLayout )
261 {
262 // no longer required, but left for API compatibility
264 mSettings.setDpi( mLayout->renderContext().dpi() );
266 }
267
268 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
269 legendRenderer.setLegendSize( rect().size() );
270
271 legendRenderer.drawLegend( rc );
272}
273
275{
276 if ( !mSizeToContents )
277 return;
278
279 if ( !mInitialMapScaleCalculated )
280 {
281 // this is messy - but until we have painted the item we have no knowledge of the current DPI
282 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
283 // for marker symbols with size in map units, causing the legends to initially expand to huge
284 // sizes if we attempt to calculate the box size first.
285 return;
286 }
287
288 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
290
291 QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
292 const QSizeF size = legendRenderer.minimumSize( &context );
293 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ), 2 );
294 if ( size.isValid() )
295 {
296 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
297 //set new rect, respecting position mode and data defined size/position
298 attemptResize( newSize );
299 }
300}
301
303{
304 mSizeToContents = enabled;
305}
306
308{
309 return mSizeToContents;
310}
311
312void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
313{
314 if ( !mDeferLegendModelInitialization )
315 {
316 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
317 }
318
319 mCustomLayerTree.reset( rootGroup );
320}
321
322void QgsLayoutItemLegend::ensureModelIsInitialized() const
323{
324 if ( mDeferLegendModelInitialization )
325 {
326 QgsLayoutItemLegend *mutableThis = const_cast< QgsLayoutItemLegend * >( this );
327 mutableThis->mDeferLegendModelInitialization = false;
328 mutableThis->setCustomLayerTree( mutableThis->mCustomLayerTree.release() );
329 }
330}
331
333{
334 ensureModelIsInitialized();
335 return mLegendModel.get();
336}
337
339{
340 ensureModelIsInitialized();
341 return mLegendModel.get();
342}
343
345{
346 if ( autoUpdate == autoUpdateModel() )
347 return;
348
349 setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
351 updateFilterByMap( false );
352}
353
354void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
355{
356 if ( key == QLatin1String( "cached_name" ) )
357 return;
358
359 if ( autoUpdateModel() )
360 {
361 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
362 // we must then call updateItem to reflect the changes
363 updateFilterByMap( false );
364 }
365}
366
368{
369 return !mCustomLayerTree;
370}
371
373{
374 if ( mLegendFilterByMap == enabled )
375 return;
376
377 mLegendFilterByMap = enabled;
378 updateFilterByMap( false );
379}
380
381void QgsLayoutItemLegend::setTitle( const QString &t )
382{
383 mTitle = t;
384 mSettings.setTitle( t );
385
386 if ( mLayout && id().isEmpty() )
387 {
388 //notify the model that the display name has changed
389 mLayout->itemsModel()->updateItemDisplayName( this );
390 }
391}
393{
394 return mTitle;
395}
396
397Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
398{
399 return mSettings.titleAlignment();
400}
401
402void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
403{
404 mSettings.setTitleAlignment( alignment );
405}
406
411
413{
414 return mSettings.style( s );
415}
416
418{
419 mSettings.setStyle( s, style );
420}
421
428
435
437{
438 rstyle( s ).setMargin( margin );
439}
440
442{
443 rstyle( s ).setMargin( side, margin );
444}
445
452
454{
456 mSettings.setLineSpacing( spacing );
458}
459
461{
462 return mSettings.boxSpace();
463}
464
466{
467 mSettings.setBoxSpace( s );
468}
469
471{
472 return mSettings.columnSpace();
473}
474
476{
477 mSettings.setColumnSpace( s );
478}
479
481{
483 return mSettings.fontColor();
485}
486
493
495{
496 return mSettings.symbolSize().width();
497}
498
500{
501 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
502}
503
505{
506 return mSettings.maximumSymbolSize();
507}
508
510{
511 mSettings.setMaximumSymbolSize( size );
512}
513
515{
516 return mSettings.minimumSymbolSize();
517}
518
520{
521 mSettings.setMinimumSymbolSize( size );
522}
523
524void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
525{
526 mSettings.setSymbolAlignment( alignment );
527}
528
530{
531 return mSettings.symbolAlignment();
532}
533
535{
536 return mSettings.symbolSize().height();
537}
538
540{
541 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
542}
543
545{
546 return mSettings.wmsLegendSize().width();
547}
548
550{
551 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
552}
553
555{
556 return mSettings.wmsLegendSize().height();
557}
559{
560 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
561}
562
564{
565 mSettings.setWrapChar( t );
566}
567
569{
570 return mSettings.wrapChar();
571}
572
574{
575 return mColumnCount;
576}
577
579{
580 mColumnCount = c;
581 mSettings.setColumnCount( c );
582}
583
585{
586 return mSettings.splitLayer();
587}
588
590{
591 mSettings.setSplitLayer( s );
592}
593
595{
596 return mSettings.equalColumnWidth();
597}
598
600{
601 mSettings.setEqualColumnWidth( s );
602}
603
605{
606 return mSettings.drawRasterStroke();
607}
608
610{
611 mSettings.setDrawRasterStroke( enabled );
612}
613
615{
616 return mSettings.rasterStrokeColor();
617}
618
620{
621 mSettings.setRasterStrokeColor( color );
622}
623
625{
626 return mSettings.rasterStrokeWidth();
627}
628
630{
631 mSettings.setRasterStrokeWidth( width );
632}
633
639
640bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
641{
642
643 //write general properties
644 legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
645 legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
646 legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
647 legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
648 legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
649
650 legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
651 legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
652
653 legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
654 legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
655 legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
656 legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
657
658 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
659
660 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
661
662 legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
663 legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
664 legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
665
666 legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
667 legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
668 legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
669
670 legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
671
672 if ( mMap )
673 {
674 legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
675 }
676
677 if ( !mFilterByMapItems.empty() )
678 {
679 QDomElement filterByMapsElem = doc.createElement( QStringLiteral( "filterByMaps" ) );
680 for ( QgsLayoutItemMap *map : mFilterByMapItems )
681 {
682 if ( map )
683 {
684 QDomElement mapElem = doc.createElement( QStringLiteral( "map" ) );
685 mapElem.setAttribute( QStringLiteral( "uuid" ), map->uuid() );
686 filterByMapsElem.appendChild( mapElem );
687 }
688 }
689 legendElem.appendChild( filterByMapsElem );
690 }
691
692 QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
693 legendElem.appendChild( legendStyles );
694
695 style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc, context );
696 style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc, context );
697 style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc, context );
698 style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc, context );
699 style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc, context );
700
701 if ( mCustomLayerTree )
702 {
703 // if not using auto-update - store the custom layer tree
704 mCustomLayerTree->writeXml( legendElem, context );
705 }
706
707 if ( mLegendFilterByMap )
708 {
709 legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
710 }
711 legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
712
713 return true;
714}
715
716bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
717{
718 //read general properties
719 mTitle = itemElem.attribute( QStringLiteral( "title" ) );
720 mSettings.setTitle( mTitle );
721 if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
722 {
723 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
724 }
725 int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
726 if ( colCount < 1 ) colCount = 1;
727 mColumnCount = colCount;
728 mSettings.setColumnCount( mColumnCount );
729 mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
730 mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
731
732 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
733 if ( !stylesNodeList.isEmpty() )
734 {
735 const QDomNode stylesNode = stylesNodeList.at( 0 );
736 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
737 {
738 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
740 style.readXml( styleElem, doc, context );
741 const QString name = styleElem.attribute( QStringLiteral( "name" ) );
743 if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
744 else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
745 else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
746 else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
747 else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
748 else continue;
749 setStyle( s, style );
750 }
751 }
752
753 //font color
754 if ( itemElem.hasAttribute( QStringLiteral( "fontColor" ) ) )
755 {
756 QColor fontClr;
757 fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
762 }
763
764 //spaces
765 mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
766 mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
767
768 mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
769 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
770
771 mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
772 mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
773
774 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
775
776 if ( itemElem.hasAttribute( QStringLiteral( "lineSpacing" ) ) )
777 {
778 const double spacing = itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble();
779 // line spacing *was* a fixed amount (in mm) added between each line of text.
781 // assume font sizes in points, since that was what we always had from before this method was deprecated
782 f.setLineHeight( f.size() * 0.352778 + spacing );
785
787 f.setLineHeight( f.size() * 0.352778 + spacing );
790
792 f.setLineHeight( f.size() * 0.352778 + spacing );
795
797 f.setLineHeight( f.size() * 0.352778 + spacing );
800 }
801
802 mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
803 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
804 mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
805
806 mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
807
808 mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
809
810 // map
811 mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
812
813 mMapUuid.clear();
814 if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
815 {
816 mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
817 }
818
819 mFilterByMapUuids.clear();
820 {
821 const QDomElement filterByMapsElem = itemElem.firstChildElement( QStringLiteral( "filterByMaps" ) );
822 if ( !filterByMapsElem.isNull() )
823 {
824 QDomElement mapsElem = filterByMapsElem.firstChildElement( QStringLiteral( "map" ) );
825 while ( !mapsElem.isNull() )
826 {
827 mFilterByMapUuids << mapsElem.attribute( QStringLiteral( "uuid" ) );
828 mapsElem = mapsElem.nextSiblingElement( QStringLiteral( "map" ) );
829 }
830 }
831 else if ( !mMapUuid.isEmpty() )
832 {
833 // for compatibility with < QGIS 3.32 projects
834 mFilterByMapUuids << mMapUuid;
835 }
836 }
837
838 // disconnect current map
839 setupMapConnections( mMap, false );
840 mMap = nullptr;
841
842 mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
843
844 // QGIS >= 2.6
845 QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
846 if ( layerTreeElem.isNull() )
847 layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
848
849 if ( !layerTreeElem.isNull() )
850 {
851 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
852 if ( mLayout )
853 tree->resolveReferences( mLayout->project(), true );
854 setCustomLayerTree( tree.release() );
855 }
856 else
857 setCustomLayerTree( nullptr );
858
859 return true;
860}
861
863{
864 if ( !id().isEmpty() )
865 {
866 return id();
867 }
868
869 //if no id, default to portion of title text
870 QString text = mSettings.title();
871 if ( text.isEmpty() )
872 {
873 return tr( "<Legend>" );
874 }
875 if ( text.length() > 25 )
876 {
877 return tr( "%1…" ).arg( text.left( 25 ) );
878 }
879 else
880 {
881 return text;
882 }
883}
884
886{
887 return blendMode() != QPainter::CompositionMode_SourceOver;
888}
889
891{
892 return mEvaluatedOpacity < 1.0;
893}
894
895void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
896{
897 if ( !map )
898 return;
899
900 if ( !connectSlots )
901 {
902 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
903 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
904 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
905 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
906 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
907 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
908 }
909 else
910 {
911 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
912 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
913 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
914 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
915 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
916 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
917 }
918}
919
921{
922 if ( mMap == map )
923 return;
924
925 if ( mMap )
926 {
927 setupMapConnections( mMap, false );
928 }
929
930 mMap = map;
931
932 if ( mMap )
933 {
934 setupMapConnections( mMap, true );
935 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
936 }
937
939}
940
941void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
942{
943 if ( filterByMapItems() == maps )
944 return;
945
946 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
947 {
948 setupMapConnections( map, false );
949 }
950
951 mFilterByMapItems.clear();
952 mFilterByMapItems.reserve( maps.size() );
953 for ( QgsLayoutItemMap *map : maps )
954 {
955 if ( map )
956 {
957 mFilterByMapItems.append( map );
958 setupMapConnections( map, true );
959 }
960 }
961
963}
964
965QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
966{
967 QList<QgsLayoutItemMap *> res;
968 res.reserve( mFilterByMapItems.size() );
969 for ( QgsLayoutItemMap *map : mFilterByMapItems )
970 {
971 if ( map )
972 res.append( map );
973 }
974 return res;
975}
976
977void QgsLayoutItemLegend::invalidateCurrentMap()
978{
979 setLinkedMap( nullptr );
980}
981
983{
985
986 bool forceUpdate = false;
987 //updates data defined properties and redraws item to match
989 {
990 bool ok = false;
992 if ( ok )
993 {
994 mSettings.setTitle( t );
995 forceUpdate = true;
996 }
997 }
999 {
1000 bool ok = false;
1002 if ( ok && cols >= 0 )
1003 {
1004 mSettings.setColumnCount( cols );
1005 forceUpdate = true;
1006 }
1007 }
1008 if ( forceUpdate )
1009 {
1010 adjustBoxSize();
1011 update();
1012 }
1013
1015}
1016
1017
1018void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
1019{
1020 updateFilterByMap( true );
1021}
1022
1023void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
1024{
1025 mLegendModel->setLayerStyleOverrides( overrides );
1026 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1027 {
1028 const QList< QgsLayerTreeLayer * > layers = rootGroup->findLayers();
1029 for ( QgsLayerTreeLayer *nodeLayer : std::as_const( layers ) )
1030 mLegendModel->refreshLayerLegend( nodeLayer );
1031 }
1032}
1033
1034void QgsLayoutItemLegend::clearLegendCachedData()
1035{
1036 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1037 clearNodeCache = [&]( QgsLayerTreeNode * node )
1038 {
1039 mLegendModel->clearCachedData( node );
1040 if ( QgsLayerTree::isGroup( node ) )
1041 {
1043 const QList< QgsLayerTreeNode * > children = group->children();
1044 for ( QgsLayerTreeNode *child : children )
1045 {
1046 clearNodeCache( child );
1047 }
1048 }
1049 };
1050
1051 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1052 {
1053 clearNodeCache( rootGroup );
1054 }
1055}
1056
1057void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
1058{
1059 if ( !mMap )
1060 return;
1061
1062 // map's style has been changed, so make sure to update the legend here
1063 if ( mLegendFilterByMap )
1064 {
1065 // legend is being filtered by map, so we need to re run the hit test too
1066 // as the style overrides may also have affected the visible symbols
1067 updateFilterByMap( false );
1068 }
1069 else
1070 {
1071 setModelStyleOverrides( mMap->layerStyleOverrides() );
1072 }
1073
1074 adjustBoxSize();
1075
1076 updateFilterByMap( false );
1077}
1078
1079void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
1080{
1081 if ( mThemeName == theme )
1082 return;
1083
1084 mThemeName = theme;
1085
1086 // map's theme has been changed, so make sure to update the legend here
1087
1088 // legend is being filtered by map, so we need to re run the hit test too
1089 // as the style overrides may also have affected the visible symbols
1090 updateFilterByMap( false );
1091
1092 adjustBoxSize();
1093
1095}
1096
1098{
1099 // ask for update
1100 // the actual update will take place before the redraw.
1101 // This is to avoid multiple calls to the filter
1102 mFilterAskedForUpdate = true;
1103
1104 if ( redraw )
1105 update();
1106}
1107
1108void QgsLayoutItemLegend::doUpdateFilterByMap()
1109{
1110 // There's an incompatibility here between legend handling of linked map themes and layer style overrides vs
1111 // how expression evaluation is made in legend content text. The logic below is hacked together to get
1112 // all the existing unit tests passing, but these two features are incompatible with each other and fixing
1113 // this is extremely non-trivial. Let's just hope no-one tries to use those features together!
1114 // Ideally, all the branches below would be consistently using either "setModelStyleOverrides" (which forces
1115 // a rebuild of each layer's legend, and breaks legend text expression evaluation) OR
1116 // "mLegendModel->setLayerStyleOverrides" which just handles the expression updates but which doesn't correctly
1117 // generate the legend content from the associated theme settings.
1118 if ( mMap && !mThemeName.isEmpty() )
1119 {
1120 // get style overrides for theme
1121 const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
1122 setModelStyleOverrides( overrides );
1123 }
1124 else if ( mMap )
1125 {
1126 mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
1127 }
1128 else
1129 {
1130 mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
1131 }
1132
1133 const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
1134
1135 const bool hasValidFilter = filterByExpression
1136 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) )
1137 || mInAtlas;
1138
1139 if ( hasValidFilter )
1140 {
1141 const double dpi = mLayout->renderContext().dpi();
1142
1143 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1144 if ( mLegendFilterByMap )
1145 {
1146 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1147 if ( mMap )
1148 linkedFilterMaps.insert( mMap );
1149 }
1150
1151 QgsMapSettings mapSettings;
1152 QgsGeometry filterGeometry;
1153 if ( mMap )
1154 {
1155 // if a specific linked map has been set, use it for the reference scale and extent
1156 const QgsRectangle requestRectangle = mMap->requestedExtent();
1157 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1158 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1159 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1160
1161 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1162 }
1163 else if ( !linkedFilterMaps.empty() )
1164 {
1165 // otherwise just take the first linked filter map
1166 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1167 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1168 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1169 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1170
1171 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1172 }
1173
1175
1176 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1177
1178 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1179
1180 QList<QgsMapLayer *> layersToClip;
1181 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1182 {
1183 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1184 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1185 {
1186 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1187 mapLayers.removeAll( layer );
1188 filterSettings.mapSettings().setLayers( mapLayers );
1189 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1190 }
1191 }
1192
1193
1194 if ( !linkedFilterMaps.empty() )
1195 {
1196 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1197 {
1198
1199 if ( map == mMap )
1200 continue;
1201
1203
1204 //transform back to destination CRS
1205 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1206 try
1207 {
1208 mapExtent.transform( mapTransform );
1209 }
1210 catch ( QgsCsException &cse )
1211 {
1212 continue;
1213 }
1214
1215 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1216 for ( QgsMapLayer *layer : layersForMap )
1217 {
1218 if ( mInAtlas && !atlasGeometry.isNull() )
1219 {
1220 mapExtent = mapExtent.intersection( atlasGeometry );
1221 }
1222
1223 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1224 }
1225 }
1226 }
1227
1228 if ( mInAtlas )
1229 {
1230 if ( !filterGeometry.isEmpty() )
1231 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1232 else
1233 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1234 }
1235
1236 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1237 if ( !filterGeometry.isNull() )
1238 {
1239 filterSettings.setFilterPolygon( filterGeometry );
1240 }
1241 else
1242 {
1243 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1244 }
1245
1246 mLegendModel->setFilterSettings( &filterSettings );
1247 }
1248 else
1249 {
1250 mLegendModel->setFilterSettings( nullptr );
1251 }
1252
1253 clearLegendCachedData();
1254 mForceResize = true;
1255}
1256
1258{
1259 return mThemeName;
1260}
1261
1263{
1264 mFilterOutAtlas = doFilter;
1265}
1266
1268{
1269 return mFilterOutAtlas;
1270}
1271
1272void QgsLayoutItemLegend::onAtlasFeature()
1273{
1274 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1275 return;
1276 mInAtlas = mFilterOutAtlas;
1278}
1279
1280void QgsLayoutItemLegend::onAtlasEnded()
1281{
1282 mInAtlas = false;
1284}
1285
1287{
1289
1290 // We only want the last scope from the map's expression context, as this contains
1291 // the map specific variables. We don't want the rest of the map's context, because that
1292 // will contain duplicate global, project, layout, etc scopes.
1293 if ( mMap )
1294 context.appendScope( mMap->createExpressionContext().popScope() );
1295
1296 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1297
1298 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
1299 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
1300 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
1301 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
1302 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
1303 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
1304
1305 context.appendScope( scope );
1306 return context;
1307}
1308
1313
1315{
1316 std::function<bool( QgsLayerTreeGroup *group ) >visit;
1317
1318 visit = [this, visitor, &visit]( QgsLayerTreeGroup * group ) -> bool
1319 {
1320 const QList<QgsLayerTreeNode *> childNodes = group->children();
1321 for ( QgsLayerTreeNode *node : childNodes )
1322 {
1323 if ( QgsLayerTree::isGroup( node ) )
1324 {
1325 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1326 if ( !visit( nodeGroup ) )
1327 return false;
1328 }
1329 else if ( QgsLayerTree::isLayer( node ) )
1330 {
1331 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1332 if ( !nodeLayer->patchShape().isNull() )
1333 {
1334 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1335 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1336 return false;
1337 }
1338 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1339 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1340 {
1341 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1342 {
1343 if ( !symbolNode->patchShape().isNull() )
1344 {
1345 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1346 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1347 return false;
1348 }
1349 }
1350 }
1351 }
1352 }
1353 return true;
1354 };
1355 return visit( model()->rootGroup( ) );
1356}
1357
1359{
1360 return mLegendModel->hitTestInProgress();
1361}
1362
1363
1364// -------------------------------------------------------------------------
1365
1367 : QgsLayerTreeModel( rootNode, parent )
1368 , mLayoutLegend( layout )
1369{
1373 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1374}
1375
1377 : QgsLayerTreeModel( rootNode )
1378 , mLayoutLegend( layout )
1379{
1383 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1384}
1385
1386QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1387{
1388 // handle custom layer node labels
1389
1391 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1392 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1393 {
1394 QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
1395 if ( !name.isEmpty() )
1396 return name;
1397
1398 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1399
1400 //finding the first label that is stored
1401 name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1402 if ( name.isEmpty() )
1403 name = nodeLayer->name();
1404 if ( name.isEmpty() )
1405 name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1406 if ( name.isEmpty() )
1407 name = node->name();
1408 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
1409 {
1410 if ( vlayer && vlayer->featureCount() >= 0 )
1411 {
1412 name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
1413 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1414 return name;
1415 }
1416 }
1417 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1418 return name;
1419 }
1420 return QgsLayerTreeModel::data( index, role );
1421}
1422
1423Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1424{
1425 // make the legend nodes selectable even if they are not by default
1426 if ( index2legendNode( index ) )
1427 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1428
1430}
1431
1432QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1433{
1434 if ( !mLegend.contains( nodeLayer ) )
1435 return QList<QgsLayerTreeModelLegendNode *>();
1436
1437 const LayerLegendData &data = mLegend[nodeLayer];
1438 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1439 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1440 lst.prepend( data.embeddedNodeInParent );
1441 return lst;
1442}
1443
1445{
1446 node->removeCustomProperty( QStringLiteral( "cached_name" ) );
1447}
1448
1449void QgsLegendModel::forceRefresh()
1450{
1451 emit refreshLegend();
1452}
@ 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.
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.
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.
QgsLegendModel * model()
Returns the legend model.
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.
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.
@ 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:1499
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.
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:6229
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6228
#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.