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