QGIS API Documentation 3.99.0-Master (e9821da5c6b)
Loading...
Searching...
No Matches
qgslayoutitemlegend.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemlegend.cpp
3 -----------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include "qgslayoutitemlegend.h"
18
19#include <limits>
20
21#include "qgscolorutils.h"
23#include "qgslayertree.h"
26#include "qgslayertreemodel.h"
28#include "qgslayertreeutils.h"
29#include "qgslayout.h"
30#include "qgslayoutitemmap.h"
32#include "qgslayoutmodel.h"
35#include "qgslayoututils.h"
36#include "qgslegendrenderer.h"
37#include "qgslegendstyle.h"
38#include "qgslogger.h"
39#include "qgsmaplayerlegend.h"
40#include "qgsmapsettings.h"
41#include "qgsmeshlayer.h"
42#include "qgsproject.h"
43#include "qgsrasterlayer.h"
44#include "qgsrasterrenderer.h"
46#include "qgssettingstree.h"
48#include "qgsvectorlayer.h"
49
50#include <QDomDocument>
51#include <QDomElement>
52#include <QPainter>
53#include <QString>
54
55#include "moc_qgslayoutitemlegend.cpp"
56
57using namespace Qt::StringLiterals;
58
59//
60// QgsLegendFilterProxyModel
61//
62
68
70{
71 mIsDefaultLegend = isDefault;
72
73 // only show private layers when not in default mode
74 setShowPrivateLayers( !mIsDefaultLegend );
75
76 invalidateFilter();
77}
78
80{
81 if ( filter == mFilterToCheckedLayers )
82 return;
83
84 mFilterToCheckedLayers = filter;
85 invalidateFilter();
86}
87
88bool QgsLegendFilterProxyModel::layerShown( QgsMapLayer *layer ) const
89{
90 if ( !layer )
91 return true;
92
93 if ( QgsMapLayerLegend *layerLegend = layer->legend(); mIsDefaultLegend && layerLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
94 {
95 return false;
96 }
97
98 if ( mFilterToCheckedLayers && !isLayerChecked( layer ) )
99 return false;
100
101 return true;
102}
103
104//
105// QgsLayoutItemLegend
106//
107
109
112 , mLegendModel( new QgsLegendModel( nullptr, this ) )
113{
114#if 0 //no longer required?
115 connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
116#endif
117
118 mTitle = mSettings.title();
119
120 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestStarted, this, [this] { emit backgroundTaskCountChanged( 1 ); } );
121 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestCompleted, this, [this]
122 {
123 adjustBoxSize();
124 emit backgroundTaskCountChanged( 0 );
125 } );
126
127 // Connect to the main layertreeroot.
128 // It serves in "auto update mode" as a medium between the main app legend and this one
129 connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
130 connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::visibilityChanged, this, &QgsLayoutItemLegend::nodeVisibilityChanged );
131
132 // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
133 connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [this]
134 {
135 invalidateCache();
136 update();
137 } );
138 connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [this]
139 {
140 // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
141 // causing an endless loop.
142 invalidateCache();
143 update();
144 } );
145}
146
151
156
158{
159 return QgsApplication::getThemeIcon( u"/mLayoutItemLegend.svg"_s );
160}
161
166
167void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
168{
169 if ( !painter )
170 return;
171
172 const QPointF oldPos = pos();
173
174 ensureModelIsInitialized();
175
176 if ( mFilterAskedForUpdate )
177 {
178 mFilterAskedForUpdate = false;
179 doUpdateFilterByMap();
180 }
181
182 const int dpi = painter->device()->logicalDpiX();
183 const double dotsPerMM = dpi / 25.4;
184
185 if ( mLayout )
186 {
188 // no longer required, but left set for api stability
189 mSettings.setUseAdvancedEffects( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::UseAdvancedEffects );
190 mSettings.setDpi( dpi );
191 mSettings.setSynchronousLegendRequests( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::SynchronousLegendGraphics );
193 }
194 if ( mMap && mLayout )
195 {
197 // no longer required, but left set for api stability
198 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
200
201 // use a temporary QgsMapSettings to find out real map scale
202 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
203 const QgsRectangle mapExtent = mMap->extent();
204
205 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
206
207 // no longer required, but left set for api stability
209 mSettings.setMapScale( ms.scale() );
211 }
212 mInitialMapScaleCalculated = true;
213
214 QgsLegendRenderer legendRenderer = createRenderer();
215 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
216
217 //adjust box if width or height is too small
218 if ( mSizeToContents )
219 {
220 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
223
224 const QSizeF size = legendRenderer.minimumSize( &context );
225 if ( mForceResize )
226 {
227 mForceResize = false;
228
229 //set new rect, respecting position mode and data defined size/position
230 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
231 attemptResize( newSize );
232 }
233 else if ( size.height() > rect().height() || size.width() > rect().width() )
234 {
235 //need to resize box
236 QSizeF targetSize = rect().size();
237 if ( size.height() > targetSize.height() )
238 targetSize.setHeight( size.height() );
239 if ( size.width() > targetSize.width() )
240 targetSize.setWidth( size.width() );
241
242 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
243 //set new rect, respecting position mode and data defined size/position
244 attemptResize( newSize );
245 }
246 }
247
248 // attemptResize may change the legend position and would call setPos
249 // BUT the position is actually changed for the next draw, so we need to translate of the difference
250 // between oldPos and newPos
251 // the issue doesn't appear in desktop rendering but only in export because in the first one,
252 // Qt triggers a redraw on position change
253 painter->save();
254 painter->translate( pos() - oldPos );
255 QgsLayoutItem::paint( painter, itemStyle, pWidget );
256 painter->restore();
257}
258
260{
261 if ( !mMapUuid.isEmpty() )
262 {
263 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
264 }
265
266 if ( !mFilterByMapUuids.isEmpty() )
267 {
268 QList< QgsLayoutItemMap * > maps;
269 maps.reserve( mFilterByMapUuids.size() );
270 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
271 {
272 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
273 {
274 maps << map;
275 }
276 }
277 setFilterByMapItems( maps );
278 }
279}
280
282{
284 clearLegendCachedData();
285 onAtlasFeature();
286}
287
289{
290 clearLegendCachedData();
292}
293
295{
296 QPainter *painter = context.renderContext().painter();
297
298 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
300
303
304 const QgsScopedQPainterState painterState( painter );
305
306 // painter is scaled to dots, so scale back to layout units
307 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
308
309 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
310
311 if ( !mSizeToContents )
312 {
313 // set a clip region to crop out parts of legend which don't fit
314 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
315 painter->setClipRect( thisPaintRect );
316 }
317
318 if ( mLayout )
319 {
320 // no longer required, but left for API compatibility
322 mSettings.setDpi( mLayout->renderContext().dpi() );
324 }
325
326 QgsLegendRenderer legendRenderer = createRenderer();
327 legendRenderer.setLegendSize( rect().size() );
328
329 legendRenderer.drawLegend( rc );
330}
331
333{
334 if ( !mSizeToContents )
335 return;
336
337 if ( !mInitialMapScaleCalculated )
338 {
339 // this is messy - but until we have painted the item we have no knowledge of the current DPI
340 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
341 // for marker symbols with size in map units, causing the legends to initially expand to huge
342 // sizes if we attempt to calculate the box size first.
343 return;
344 }
345
346 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
349
350 QgsLegendRenderer legendRenderer = createRenderer();
351 const QSizeF size = legendRenderer.minimumSize( &context );
352 QgsDebugMsgLevel( u"width = %1 height = %2"_s.arg( size.width() ).arg( size.height() ), 2 );
353 if ( size.isValid() )
354 {
355 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
356 //set new rect, respecting position mode and data defined size/position
357 attemptResize( newSize );
358 }
359}
360
362{
363 mSizeToContents = enabled;
364}
365
367{
368 return mSizeToContents;
369}
370
371void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
372{
373 if ( !mDeferLegendModelInitialization )
374 {
375 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
376 }
377
378 mCustomLayerTree.reset( rootGroup );
379}
380
381void QgsLayoutItemLegend::ensureModelIsInitialized() const
382{
383 if ( mDeferLegendModelInitialization )
384 {
385 QgsLayoutItemLegend *mutableThis = const_cast< QgsLayoutItemLegend * >( this );
386 mutableThis->mDeferLegendModelInitialization = false;
387 mutableThis->setCustomLayerTree( mutableThis->mCustomLayerTree.release() );
388 }
389}
390
391QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const
392{
393 QgsLegendRenderer res( mLegendModel.get(), mSettings );
394
395 QgsLegendFilterProxyModel *proxy = new QgsLegendFilterProxyModel();
396 proxy->setIsDefaultLegend( !static_cast< bool >( mCustomLayerTree ) );
397 switch ( mSyncMode )
398 {
400 if ( mMap )
401 {
402 QgsExpressionContext expressionContext = mMap->createExpressionContext();
403 const QList<QgsMapLayer *> visibleLayers = mMap->layersToRender( &expressionContext );
404 proxy->setCheckedLayers( visibleLayers );
405 proxy->setFilterToCheckedLayers( true );
406 }
407 else if ( const QgsLayout *l = layout() )
408 {
409 // no linked map, so use project layer tree
410 if ( QgsProject *p = l->project() )
411 {
412 proxy->setCheckedLayers( p->layerTreeRoot()->checkedLayers() );
413 proxy->setFilterToCheckedLayers( true );
414 }
415 }
416 break;
417
420 break;
421 }
422
423 res.setProxyModel( proxy );
424
425 return res;
426}
427
429{
430 ensureModelIsInitialized();
431 return mLegendModel.get();
432}
433
435{
436 ensureModelIsInitialized();
437 return mLegendModel.get();
438}
439
444
446{
447 if ( mode == mSyncMode )
448 return;
449
450 const Qgis::LegendSyncMode oldMode = mSyncMode;
451 mSyncMode = mode;
452 switch ( mSyncMode )
453 {
456 setCustomLayerTree( nullptr );
457 break;
458
460 {
461 resetManualLayers( oldMode );
462 break;
463 }
464 }
465
467 updateFilterByMap( false );
468}
469
471{
472 if ( mSyncMode != Qgis::LegendSyncMode::Manual )
473 return;
474
475 std::unique_ptr< QgsLayerTree > customTree( mLayout->project()->layerTreeRoot()->clone() );
476
477 QList<QgsMapLayer *> mapVisibleLayers;
479 {
480 if ( mMap )
481 {
482 QgsExpressionContext expressionContext = mMap->createExpressionContext();
483 mapVisibleLayers = mMap->layersToRender( &expressionContext );
484 }
485 else if ( const QgsLayout *l = layout() )
486 {
487 // no linked map, so use project layer tree
488 if ( QgsProject *p = l->project() )
489 {
490 mapVisibleLayers = p->layerTreeRoot()->checkedLayers();
491 }
492 }
493 }
494
495 // filter out excluded by default items
496 std::function< void( QgsLayerTreeGroup * )> filterNodeChildren;
497 filterNodeChildren = [mapVisibleLayers, mode, &filterNodeChildren]( QgsLayerTreeGroup * group )
498 {
499 if ( !group )
500 return;
501
502 const QList<QgsLayerTreeNode *> children = group->children();
503 for ( QgsLayerTreeNode *child : children )
504 {
505 if ( !child )
506 {
507 group->removeChildNode( child );
508 continue;
509 }
510 else if ( QgsLayerTree::isGroup( child ) )
511 {
512 filterNodeChildren( QgsLayerTree::toGroup( child ) );
513 }
514 else if ( QgsLayerTree::isLayer( child ) )
515 {
517 if ( QgsMapLayer *mapLayer = layer->layer() )
518 {
519 if ( QgsMapLayerLegend *layerLegend = mapLayer->legend(); layerLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
520 {
521 group->removeChildNode( child );
522 }
523 else if ( mode == Qgis::LegendSyncMode::VisibleLayers && !mapVisibleLayers.contains( mapLayer ) )
524 {
525 group->removeChildNode( child );
526 }
527 }
528 }
529 }
530 };
531 filterNodeChildren( customTree.get() );
532
533 setCustomLayerTree( customTree.release() );
534}
535
536void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
537{
538 if ( key == "cached_name"_L1 )
539 return;
540
541 switch ( mSyncMode )
542 {
545 {
546 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
547 // we must then call updateItem to reflect the changes
548 updateFilterByMap( false );
549 break;
550 }
552 break;
553 }
554}
555
556void QgsLayoutItemLegend::nodeVisibilityChanged( QgsLayerTreeNode * )
557{
558 switch ( mSyncMode )
559 {
561 {
562 updateFilterByMap( false );
563 break;
564 }
567 break;
568 }
569}
570
572{
573 return mSyncMode != Qgis::LegendSyncMode::Manual;
574}
575
577{
578 return mSyncMode;
579}
580
582{
583 if ( mLegendFilterByMap == enabled )
584 return;
585
586 mLegendFilterByMap = enabled;
587 updateFilterByMap( false );
588}
589
590void QgsLayoutItemLegend::setTitle( const QString &t )
591{
592 mTitle = t;
593 mSettings.setTitle( t );
594
595 if ( mLayout && id().isEmpty() )
596 {
597 //notify the model that the display name has changed
598 mLayout->itemsModel()->updateItemDisplayName( this );
599 }
600}
602{
603 return mTitle;
604}
605
606Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
607{
608 return mSettings.titleAlignment();
609}
610
611void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
612{
613 mSettings.setTitleAlignment( alignment );
614}
615
617{
618 return mSettings.rstyle( s );
619}
620
622{
623 return mSettings.style( s );
624}
625
627{
628 mSettings.setStyle( s, style );
629}
630
632{
634 return mSettings.style( s ).font();
636}
637
644
646{
647 rstyle( s ).setMargin( margin );
648}
649
651{
652 rstyle( s ).setMargin( side, margin );
653}
654
656{
658 return mSettings.lineSpacing();
660}
661
663{
665 mSettings.setLineSpacing( spacing );
667}
668
670{
671 return mSettings.boxSpace();
672}
673
675{
676 mSettings.setBoxSpace( s );
677}
678
680{
681 return mSettings.columnSpace();
682}
683
685{
686 mSettings.setColumnSpace( s );
687}
688
690{
692 return mSettings.fontColor();
694}
695
697{
699 mSettings.setFontColor( c );
701}
702
704{
705 return mSettings.symbolSize().width();
706}
707
709{
710 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
711}
712
714{
715 return mSettings.maximumSymbolSize();
716}
717
719{
720 mSettings.setMaximumSymbolSize( size );
721}
722
724{
725 return mSettings.minimumSymbolSize();
726}
727
729{
730 mSettings.setMinimumSymbolSize( size );
731}
732
733void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
734{
735 mSettings.setSymbolAlignment( alignment );
736}
737
739{
740 return mSettings.symbolAlignment();
741}
742
744{
745 return mSettings.symbolSize().height();
746}
747
749{
750 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
751}
752
754{
755 return mSettings.wmsLegendSize().width();
756}
757
759{
760 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
761}
762
764{
765 return mSettings.wmsLegendSize().height();
766}
768{
769 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
770}
771
773{
774 mSettings.setWrapChar( t );
775}
776
778{
779 return mSettings.wrapChar();
780}
781
783{
784 return mColumnCount;
785}
786
788{
789 mColumnCount = c;
790 mSettings.setColumnCount( c );
791}
792
794{
795 return mSettings.splitLayer();
796}
797
799{
800 mSettings.setSplitLayer( s );
801}
802
804{
805 return mSettings.equalColumnWidth();
806}
807
809{
810 mSettings.setEqualColumnWidth( s );
811}
812
814{
815 return mSettings.drawRasterStroke();
816}
817
819{
820 mSettings.setDrawRasterStroke( enabled );
821}
822
824{
825 return mSettings.rasterStrokeColor();
826}
827
829{
830 mSettings.setRasterStrokeColor( color );
831}
832
834{
835 return mSettings.rasterStrokeWidth();
836}
837
839{
840 mSettings.setRasterStrokeWidth( width );
841}
842
844{
845 return mSettings.autoWrapLinesAfter();
846}
847
849{
850 mSettings.setAutoWrapLinesAfter( length );
851}
852
858
859bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
860{
861
862 //write general properties
863 legendElem.setAttribute( u"title"_s, mTitle );
864 legendElem.setAttribute( u"titleAlignment"_s, QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
865 legendElem.setAttribute( u"columnCount"_s, QString::number( mColumnCount ) );
866 legendElem.setAttribute( u"splitLayer"_s, QString::number( mSettings.splitLayer() ) );
867 legendElem.setAttribute( u"equalColumnWidth"_s, QString::number( mSettings.equalColumnWidth() ) );
868
869 legendElem.setAttribute( u"boxSpace"_s, QString::number( mSettings.boxSpace() ) );
870 legendElem.setAttribute( u"columnSpace"_s, QString::number( mSettings.columnSpace() ) );
871
872 legendElem.setAttribute( u"symbolWidth"_s, QString::number( mSettings.symbolSize().width() ) );
873 legendElem.setAttribute( u"symbolHeight"_s, QString::number( mSettings.symbolSize().height() ) );
874 legendElem.setAttribute( u"maxSymbolSize"_s, QString::number( mSettings.maximumSymbolSize() ) );
875 legendElem.setAttribute( u"minSymbolSize"_s, QString::number( mSettings.minimumSymbolSize() ) );
876
877 legendElem.setAttribute( u"symbolAlignment"_s, mSettings.symbolAlignment() );
878
879 legendElem.setAttribute( u"symbolAlignment"_s, mSettings.symbolAlignment() );
880
881 legendElem.setAttribute( u"rasterBorder"_s, mSettings.drawRasterStroke() );
882 legendElem.setAttribute( u"rasterBorderColor"_s, QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
883 legendElem.setAttribute( u"rasterBorderWidth"_s, QString::number( mSettings.rasterStrokeWidth() ) );
884
885 if ( mSettings.autoWrapLinesAfter() > 0 )
886 {
887 legendElem.setAttribute( u"autoWrapLinesAfter"_s, mSettings.autoWrapLinesAfter() );
888 }
889
890 legendElem.setAttribute( u"wmsLegendWidth"_s, QString::number( mSettings.wmsLegendSize().width() ) );
891 legendElem.setAttribute( u"wmsLegendHeight"_s, QString::number( mSettings.wmsLegendSize().height() ) );
892 legendElem.setAttribute( u"wrapChar"_s, mSettings.wrapChar() );
893
894 legendElem.setAttribute( u"resizeToContents"_s, mSizeToContents );
895
896 if ( mMap )
897 {
898 legendElem.setAttribute( u"map_uuid"_s, mMap->uuid() );
899 }
900
901 if ( !mFilterByMapItems.empty() )
902 {
903 QDomElement filterByMapsElem = doc.createElement( u"filterByMaps"_s );
904 for ( QgsLayoutItemMap *map : mFilterByMapItems )
905 {
906 if ( map )
907 {
908 QDomElement mapElem = doc.createElement( u"map"_s );
909 mapElem.setAttribute( u"uuid"_s, map->uuid() );
910 filterByMapsElem.appendChild( mapElem );
911 }
912 }
913 legendElem.appendChild( filterByMapsElem );
914 }
915
916 QDomElement legendStyles = doc.createElement( u"styles"_s );
917 legendElem.appendChild( legendStyles );
918
919 style( Qgis::LegendComponent::Title ).writeXml( u"title"_s, legendStyles, doc, context );
920 style( Qgis::LegendComponent::Group ).writeXml( u"group"_s, legendStyles, doc, context );
921 style( Qgis::LegendComponent::Subgroup ).writeXml( u"subgroup"_s, legendStyles, doc, context );
922 style( Qgis::LegendComponent::Symbol ).writeXml( u"symbol"_s, legendStyles, doc, context );
923 style( Qgis::LegendComponent::SymbolLabel ).writeXml( u"symbolLabel"_s, legendStyles, doc, context );
924
925 if ( mCustomLayerTree )
926 {
927 // if not using auto-update - store the custom layer tree
928 mCustomLayerTree->writeXml( legendElem, context );
929 }
930 else
931 {
932 legendElem.setAttribute( u"syncMode"_s, qgsEnumValueToKey( mSyncMode ) );
933 }
934
935 if ( mLegendFilterByMap )
936 {
937 legendElem.setAttribute( u"legendFilterByMap"_s, u"1"_s );
938 }
939 legendElem.setAttribute( u"legendFilterByAtlas"_s, mFilterOutAtlas ? u"1"_s : u"0"_s );
940
941 return true;
942}
943
944bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
945{
946 //read general properties
947 mTitle = itemElem.attribute( u"title"_s );
948 mSettings.setTitle( mTitle );
949 if ( !itemElem.attribute( u"titleAlignment"_s ).isEmpty() )
950 {
951 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( u"titleAlignment"_s ).toInt() ) );
952 }
953 int colCount = itemElem.attribute( u"columnCount"_s, u"1"_s ).toInt();
954 if ( colCount < 1 ) colCount = 1;
955 mColumnCount = colCount;
956 mSettings.setColumnCount( mColumnCount );
957 mSettings.setSplitLayer( itemElem.attribute( u"splitLayer"_s, u"0"_s ).toInt() == 1 );
958 mSettings.setEqualColumnWidth( itemElem.attribute( u"equalColumnWidth"_s, u"0"_s ).toInt() == 1 );
959
960 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( u"styles"_s );
961 if ( !stylesNodeList.isEmpty() )
962 {
963 const QDomNode stylesNode = stylesNodeList.at( 0 );
964 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
965 {
966 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
968 style.readXml( styleElem, doc, context );
969 const QString name = styleElem.attribute( u"name"_s );
971 if ( name == "title"_L1 ) s = Qgis::LegendComponent::Title;
972 else if ( name == "group"_L1 ) s = Qgis::LegendComponent::Group;
973 else if ( name == "subgroup"_L1 ) s = Qgis::LegendComponent::Subgroup;
974 else if ( name == "symbol"_L1 ) s = Qgis::LegendComponent::Symbol;
975 else if ( name == "symbolLabel"_L1 ) s = Qgis::LegendComponent::SymbolLabel;
976 else continue;
977 setStyle( s, style );
978 }
979 }
980
981 //font color
982 if ( itemElem.hasAttribute( u"fontColor"_s ) )
983 {
984 QColor fontClr;
985 fontClr.setNamedColor( itemElem.attribute( u"fontColor"_s, u"#000000"_s ) );
990 }
991
992 //spaces
993 mSettings.setBoxSpace( itemElem.attribute( u"boxSpace"_s, u"2.0"_s ).toDouble() );
994 mSettings.setColumnSpace( itemElem.attribute( u"columnSpace"_s, u"2.0"_s ).toDouble() );
995
996 mSettings.setSymbolSize( QSizeF( itemElem.attribute( u"symbolWidth"_s, u"7.0"_s ).toDouble(), itemElem.attribute( u"symbolHeight"_s, u"14.0"_s ).toDouble() ) );
997 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( u"symbolAlignment"_s, QString::number( Qt::AlignLeft ) ).toInt() ) );
998
999 mSettings.setMaximumSymbolSize( itemElem.attribute( u"maxSymbolSize"_s, u"0.0"_s ).toDouble() );
1000 mSettings.setMinimumSymbolSize( itemElem.attribute( u"minSymbolSize"_s, u"0.0"_s ).toDouble() );
1001
1002 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( u"wmsLegendWidth"_s, u"50"_s ).toDouble(), itemElem.attribute( u"wmsLegendHeight"_s, u"25"_s ).toDouble() ) );
1003
1004 if ( itemElem.hasAttribute( u"lineSpacing"_s ) )
1005 {
1006 const double spacing = itemElem.attribute( u"lineSpacing"_s, u"1.0"_s ).toDouble();
1007 // line spacing *was* a fixed amount (in mm) added between each line of text.
1009 // assume font sizes in points, since that was what we always had from before this method was deprecated
1010 f.setLineHeight( f.size() * 0.352778 + spacing );
1013
1015 f.setLineHeight( f.size() * 0.352778 + spacing );
1018
1020 f.setLineHeight( f.size() * 0.352778 + spacing );
1023
1025 f.setLineHeight( f.size() * 0.352778 + spacing );
1028 }
1029
1030 mSettings.setDrawRasterStroke( itemElem.attribute( u"rasterBorder"_s, u"1"_s ) != "0"_L1 );
1031 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( u"rasterBorderColor"_s, u"0,0,0"_s ) ) );
1032 mSettings.setRasterStrokeWidth( itemElem.attribute( u"rasterBorderWidth"_s, u"0"_s ).toDouble() );
1033
1034 mSettings.setAutoWrapLinesAfter( itemElem.attribute( u"autoWrapLinesAfter"_s, u"0"_s ).toDouble() );
1035
1036 mSettings.setWrapChar( itemElem.attribute( u"wrapChar"_s ) );
1037
1038 mSizeToContents = itemElem.attribute( u"resizeToContents"_s, u"1"_s ) != "0"_L1;
1039
1040 // map
1041 mLegendFilterByMap = itemElem.attribute( u"legendFilterByMap"_s, u"0"_s ).toInt();
1042
1043 mMapUuid.clear();
1044 if ( !itemElem.attribute( u"map_uuid"_s ).isEmpty() )
1045 {
1046 mMapUuid = itemElem.attribute( u"map_uuid"_s );
1047 }
1048
1049 mFilterByMapUuids.clear();
1050 {
1051 const QDomElement filterByMapsElem = itemElem.firstChildElement( u"filterByMaps"_s );
1052 if ( !filterByMapsElem.isNull() )
1053 {
1054 QDomElement mapsElem = filterByMapsElem.firstChildElement( u"map"_s );
1055 while ( !mapsElem.isNull() )
1056 {
1057 mFilterByMapUuids << mapsElem.attribute( u"uuid"_s );
1058 mapsElem = mapsElem.nextSiblingElement( u"map"_s );
1059 }
1060 }
1061 else if ( !mMapUuid.isEmpty() )
1062 {
1063 // for compatibility with < QGIS 3.32 projects
1064 mFilterByMapUuids << mMapUuid;
1065 }
1066 }
1067
1068 // disconnect current map
1069 setupMapConnections( mMap, false );
1070 mMap = nullptr;
1071
1072 mFilterOutAtlas = itemElem.attribute( u"legendFilterByAtlas"_s, u"0"_s ).toInt();
1073
1074 // QGIS >= 2.6
1075 QDomElement layerTreeElem = itemElem.firstChildElement( u"layer-tree"_s );
1076 if ( layerTreeElem.isNull() )
1077 layerTreeElem = itemElem.firstChildElement( u"layer-tree-group"_s );
1078
1079 if ( !layerTreeElem.isNull() )
1080 {
1081 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
1082 if ( mLayout )
1083 tree->resolveReferences( mLayout->project(), true );
1084 setCustomLayerTree( tree.release() );
1085 mSyncMode = Qgis::LegendSyncMode::Manual;
1086 }
1087 else
1088 {
1089 setCustomLayerTree( nullptr );
1090 mSyncMode = qgsEnumKeyToValue( itemElem.attribute( u"syncMode"_s ), Qgis::LegendSyncMode::AllProjectLayers );
1091 }
1092
1093 return true;
1094}
1095
1097{
1098 if ( !id().isEmpty() )
1099 {
1100 return id();
1101 }
1102
1103 //if no id, default to portion of title text
1104 QString text = mSettings.title();
1105 if ( text.isEmpty() )
1106 {
1107 return tr( "<Legend>" );
1108 }
1109 if ( text.length() > 25 )
1110 {
1111 return tr( "%1…" ).arg( text.left( 25 ) );
1112 }
1113 else
1114 {
1115 return text;
1116 }
1117}
1118
1120{
1121 return blendMode() != QPainter::CompositionMode_SourceOver;
1122}
1123
1125{
1126 return mEvaluatedOpacity < 1.0;
1127}
1128
1129void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
1130{
1131 if ( !map )
1132 return;
1133
1134 if ( !connectSlots )
1135 {
1136 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1137 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1138 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1139 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1140 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1141 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1142 }
1143 else
1144 {
1145 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1146 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1147 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1148 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1149 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1150 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1151 }
1152}
1153
1155{
1156 if ( mMap == map )
1157 return;
1158
1159 if ( mMap )
1160 {
1161 setupMapConnections( mMap, false );
1162 }
1163
1164 mMap = map;
1165
1166 if ( mMap )
1167 {
1168 setupMapConnections( mMap, true );
1169 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
1170 }
1171
1173}
1174
1175void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
1176{
1177 if ( filterByMapItems() == maps )
1178 return;
1179
1180 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
1181 {
1182 setupMapConnections( map, false );
1183 }
1184
1185 mFilterByMapItems.clear();
1186 mFilterByMapItems.reserve( maps.size() );
1187 for ( QgsLayoutItemMap *map : maps )
1188 {
1189 if ( map )
1190 {
1191 mFilterByMapItems.append( map );
1192 setupMapConnections( map, true );
1193 }
1194 }
1195
1197}
1198
1199QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
1200{
1201 QList<QgsLayoutItemMap *> res;
1202 res.reserve( mFilterByMapItems.size() );
1203 for ( QgsLayoutItemMap *map : mFilterByMapItems )
1204 {
1205 if ( map )
1206 res.append( map );
1207 }
1208 return res;
1209}
1210
1211void QgsLayoutItemLegend::invalidateCurrentMap()
1212{
1213 setLinkedMap( nullptr );
1214}
1215
1217{
1219
1220 bool forceUpdate = false;
1221 //updates data defined properties and redraws item to match
1223 {
1224 bool ok = false;
1225 const QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::LegendTitle, context, mTitle, &ok );
1226 if ( ok )
1227 {
1228 mSettings.setTitle( t );
1229 forceUpdate = true;
1230 }
1231 }
1233 {
1234 bool ok = false;
1235 const int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::DataDefinedProperty::LegendColumnCount, context, mColumnCount, &ok );
1236 if ( ok && cols >= 0 )
1237 {
1238 mSettings.setColumnCount( cols );
1239 forceUpdate = true;
1240 }
1241 }
1243 {
1244 bool ok = false;
1245 const double width = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::LegendAutoWrapWidth, context, mSettings.autoWrapLinesAfter(), &ok );
1246 if ( ok && width >= 0 )
1247 {
1248 mSettings.setAutoWrapLinesAfter( width );
1249 forceUpdate = true;
1250 }
1251 }
1252 if ( forceUpdate )
1253 {
1254 adjustBoxSize();
1255 update();
1256 }
1257
1259}
1260
1261
1262void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
1263{
1264 updateFilterByMap( true );
1265}
1266
1267void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
1268{
1269 mLegendModel->setLayerStyleOverrides( overrides );
1270 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1271 {
1272 const QList< QgsLayerTreeLayer * > layers = rootGroup->findLayers();
1273 for ( QgsLayerTreeLayer *nodeLayer : std::as_const( layers ) )
1274 mLegendModel->refreshLayerLegend( nodeLayer );
1275 }
1276}
1277
1278void QgsLayoutItemLegend::clearLegendCachedData()
1279{
1280 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1281 clearNodeCache = [&]( QgsLayerTreeNode * node )
1282 {
1283 mLegendModel->clearCachedData( node );
1284 if ( QgsLayerTree::isGroup( node ) )
1285 {
1286 QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
1287 const QList< QgsLayerTreeNode * > children = group->children();
1288 for ( QgsLayerTreeNode *child : children )
1289 {
1290 clearNodeCache( child );
1291 }
1292 }
1293 };
1294
1295 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1296 {
1297 clearNodeCache( rootGroup );
1298 }
1299}
1300
1301void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
1302{
1303 if ( !mMap )
1304 return;
1305
1306 // map's style has been changed, so make sure to update the legend here
1307 if ( mLegendFilterByMap )
1308 {
1309 // legend is being filtered by map, so we need to re run the hit test too
1310 // as the style overrides may also have affected the visible symbols
1311 updateFilterByMap( false );
1312 }
1313 else
1314 {
1315 setModelStyleOverrides( mMap->layerStyleOverrides() );
1316 }
1317
1318 adjustBoxSize();
1319
1320 updateFilterByMap( false );
1321}
1322
1323void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
1324{
1325 if ( mThemeName == theme )
1326 return;
1327
1328 mThemeName = theme;
1329
1330 // map's theme has been changed, so make sure to update the legend here
1331
1332 // legend is being filtered by map, so we need to re run the hit test too
1333 // as the style overrides may also have affected the visible symbols
1334 updateFilterByMap( false );
1335
1336 adjustBoxSize();
1337
1339}
1340
1342{
1343 // ask for update
1344 // the actual update will take place before the redraw.
1345 // This is to avoid multiple calls to the filter
1346 mFilterAskedForUpdate = true;
1347
1348 if ( redraw )
1349 update();
1350}
1351
1352void QgsLayoutItemLegend::doUpdateFilterByMap()
1353{
1354 // There's an incompatibility here between legend handling of linked map themes and layer style overrides vs
1355 // how expression evaluation is made in legend content text. The logic below is hacked together to get
1356 // all the existing unit tests passing, but these two features are incompatible with each other and fixing
1357 // this is extremely non-trivial. Let's just hope no-one tries to use those features together!
1358 // Ideally, all the branches below would be consistently using either "setModelStyleOverrides" (which forces
1359 // a rebuild of each layer's legend, and breaks legend text expression evaluation) OR
1360 // "mLegendModel->setLayerStyleOverrides" which just handles the expression updates but which doesn't correctly
1361 // generate the legend content from the associated theme settings.
1362 if ( mMap && !mThemeName.isEmpty() )
1363 {
1364 // get style overrides for theme
1365 const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
1366 setModelStyleOverrides( overrides );
1367 }
1368 else if ( mMap )
1369 {
1370 mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
1371 }
1372 else
1373 {
1374 mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
1375 }
1376
1377 // only use thread hit tests for preview renders. In other cases we'll need a blocking hit test anyway, and we run a risk
1378 // of deadlocks if a non-preview render is then started on the main thread.
1379 mLegendModel->setFlag( QgsLayerTreeModel::UseThreadedHitTest, mLayout->renderContext().isPreviewRender() );
1380
1381 const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
1382
1383 const bool hasValidFilter = filterByExpression
1384 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) )
1385 || mInAtlas
1386 || requiresFilteringBecauseOfRendererSetting();
1387
1388 if ( hasValidFilter )
1389 {
1390 const double dpi = mLayout->renderContext().dpi();
1391
1392 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1393 if ( mLegendFilterByMap )
1394 {
1395 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1396 if ( mMap )
1397 linkedFilterMaps.insert( mMap );
1398 }
1399
1400 QgsMapSettings mapSettings;
1401 QgsGeometry filterGeometry;
1402 if ( mMap )
1403 {
1404 // if a specific linked map has been set, use it for the reference scale and extent
1405 const QgsRectangle requestRectangle = mMap->requestedExtent();
1406 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1407 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1408 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1409
1410 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1411 }
1412 else if ( !linkedFilterMaps.empty() )
1413 {
1414 // otherwise just take the first linked filter map
1415 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1416 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1417 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1418 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1419
1420 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1421 }
1422
1424
1425 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1426
1427 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1428
1429 QList<QgsMapLayer *> layersToClip;
1430 if ( mMap )
1431 {
1432 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1433 {
1434 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1435 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1436 {
1437 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1438 mapLayers.removeAll( layer );
1439 filterSettings.mapSettings().setLayers( mapLayers );
1440 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1441 }
1442 }
1443 }
1444
1445 if ( !linkedFilterMaps.empty() )
1446 {
1447 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1448 {
1449
1450 if ( map == mMap )
1451 continue;
1452
1453 QgsGeometry mapExtent = QgsGeometry::fromQPolygonF( map->visibleExtentPolygon() );
1454
1455 //transform back to destination CRS
1456 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1457 try
1458 {
1459 mapExtent.transform( mapTransform );
1460 }
1461 catch ( QgsCsException & )
1462 {
1463 continue;
1464 }
1465
1466 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1467 for ( QgsMapLayer *layer : layersForMap )
1468 {
1469 if ( mInAtlas && !atlasGeometry.isNull() )
1470 {
1471 mapExtent = mapExtent.intersection( atlasGeometry );
1472 }
1473
1474 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1475 }
1476 }
1477 }
1478
1479 if ( mInAtlas )
1480 {
1481 if ( !filterGeometry.isEmpty() )
1482 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1483 else
1484 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1485 }
1486
1487 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1488 if ( !filterGeometry.isNull() )
1489 {
1490 filterSettings.setFilterPolygon( filterGeometry );
1491 }
1492 else
1493 {
1494 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1495 }
1496
1497 mLegendModel->setFilterSettings( &filterSettings );
1498 }
1499 else
1500 {
1501 mLegendModel->setFilterSettings( nullptr );
1502 }
1503
1504 clearLegendCachedData();
1505 mForceResize = true;
1506}
1507
1509{
1510 return mThemeName;
1511}
1512
1514{
1515 mFilterOutAtlas = doFilter;
1516}
1517
1519{
1520 return mFilterOutAtlas;
1521}
1522
1523void QgsLayoutItemLegend::onAtlasFeature()
1524{
1525 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1526 return;
1527 mInAtlas = mFilterOutAtlas;
1529}
1530
1531void QgsLayoutItemLegend::onAtlasEnded()
1532{
1533 mInAtlas = false;
1535}
1536
1538{
1540
1541 // We only want the last scope from the map's expression context, as this contains
1542 // the map specific variables. We don't want the rest of the map's context, because that
1543 // will contain duplicate global, project, layout, etc scopes.
1544 if ( mMap )
1545 context.appendScope( mMap->createExpressionContext().popScope() );
1546
1547 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1548
1549 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_title"_s, title(), true ) );
1550 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_column_count"_s, columnCount(), true ) );
1551 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_split_layers"_s, splitLayer(), true ) );
1552 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_wrap_string"_s, wrapString(), true ) );
1553 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_filter_by_map"_s, legendFilterByMapEnabled(), true ) );
1554 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_filter_out_atlas"_s, legendFilterOutAtlas(), true ) );
1555
1556 context.appendScope( scope );
1557 return context;
1558}
1559
1564
1566{
1567 std::function<bool( QgsLayerTreeGroup *group ) >visit;
1568
1569 visit = [this, visitor, &visit]( QgsLayerTreeGroup * group ) -> bool
1570 {
1571 const QList<QgsLayerTreeNode *> childNodes = group->children();
1572 for ( QgsLayerTreeNode *node : childNodes )
1573 {
1574 if ( QgsLayerTree::isGroup( node ) )
1575 {
1576 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1577 if ( !visit( nodeGroup ) )
1578 return false;
1579 }
1580 else if ( QgsLayerTree::isLayer( node ) )
1581 {
1582 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1583 if ( !nodeLayer->patchShape().isNull() )
1584 {
1585 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1586 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1587 return false;
1588 }
1589 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1590 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1591 {
1592 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1593 {
1594 if ( !symbolNode->patchShape().isNull() )
1595 {
1596 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1597 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1598 return false;
1599 }
1600 }
1601 }
1602 }
1603 }
1604 return true;
1605 };
1606 return visit( model()->rootGroup( ) );
1607}
1608
1610{
1611 return mLegendModel->hitTestInProgress();
1612}
1613
1614
1615bool QgsLayoutItemLegend::requiresFilteringBecauseOfRendererSetting()
1616{
1617 const QList<QgsLayerTreeLayer *> layers = model()->rootGroup()->findLayers();
1618
1619 for ( QgsLayerTreeLayer *layerTreeLayer : layers )
1620 {
1621 QgsMapLayer *mapLayer = layerTreeLayer->layer();
1622
1623 if ( !mapLayer || !mapLayer->isValid() )
1624 continue;
1625 if ( QgsRasterLayer *rl = qobject_cast<QgsRasterLayer *>( mapLayer ) )
1626 {
1627 if ( rl->renderer() && rl->renderer()->minMaxOrigin().extent() == Qgis::RasterRangeExtent::UpdatedCanvas )
1628 {
1629 return true;
1630 }
1631 }
1632 else if ( QgsMeshLayer *ml = qobject_cast<QgsMeshLayer *>( mapLayer ) )
1633 {
1634 const QgsMeshDatasetIndex activeDatasetIndex = ml->staticScalarDatasetIndex();
1635
1636 if ( activeDatasetIndex.isValid() )
1637 {
1638 QgsMeshRendererScalarSettings scalarRendererSettings = ml->rendererSettings().scalarSettings( activeDatasetIndex.group() );
1639
1640 if ( scalarRendererSettings.extent() == Qgis::MeshRangeExtent::UpdatedCanvas )
1641 {
1642 return true;
1643 }
1644 }
1645 }
1646 }
1647
1648 return false;
1649}
1650
1651// -------------------------------------------------------------------------
1652
1654 : QgsLayerTreeModel( rootNode, parent )
1655 , mLayoutLegend( layout )
1656{
1660 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1661}
1662
1664 : QgsLayerTreeModel( rootNode )
1665 , mLayoutLegend( layout )
1666{
1670 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1671}
1672
1673QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1674{
1675 // handle custom layer node labels
1676
1678 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1679 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1680 {
1681 QString name = node->customProperty( u"cached_name"_s ).toString();
1682 if ( !name.isEmpty() )
1683 return name;
1684
1685 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1686
1687 //finding the first label that is stored
1688 name = nodeLayer->customProperty( u"legend/title-label"_s ).toString();
1689 if ( name.isEmpty() )
1690 name = nodeLayer->name();
1691 if ( name.isEmpty() )
1692 name = node->customProperty( u"legend/title-label"_s ).toString();
1693 if ( name.isEmpty() )
1694 name = node->name();
1695 if ( nodeLayer->customProperty( u"showFeatureCount"_s, 0 ).toInt() )
1696 {
1697 if ( vlayer && vlayer->featureCount() >= 0 )
1698 {
1699 name += u" [%1]"_s.arg( vlayer->featureCount() );
1700 node->setCustomProperty( u"cached_name"_s, name );
1701 return name;
1702 }
1703 }
1704 node->setCustomProperty( u"cached_name"_s, name );
1705 return name;
1706 }
1707 return QgsLayerTreeModel::data( index, role );
1708}
1709
1710Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1711{
1712 // make the legend nodes selectable even if they are not by default
1713 if ( index2legendNode( index ) )
1714 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1715
1717}
1718
1719QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1720{
1721 if ( !mLegend.contains( nodeLayer ) )
1722 return QList<QgsLayerTreeModelLegendNode *>();
1723
1724 const LayerLegendData &data = mLegend[nodeLayer];
1725 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1726 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1727 lst.prepend( data.embeddedNodeInParent );
1728 return lst;
1729}
1730
1732{
1733 node->removeCustomProperty( u"cached_name"_s );
1734}
1735
1736void QgsLegendModel::forceRefresh()
1737{
1738 emit refreshLegend();
1739}
@ Millimeters
Millimeters.
Definition qgis.h:5300
LegendComponent
Component of legends which can be styled.
Definition qgis.h:4658
@ Symbol
Symbol icon (excluding label).
Definition qgis.h:4664
@ Group
Legend group title.
Definition qgis.h:4662
@ Subgroup
Legend subgroup title.
Definition qgis.h:4663
@ Title
Legend title.
Definition qgis.h:4661
@ SymbolLabel
Symbol label (excluding icon).
Definition qgis.h:4665
LegendSyncMode
Legend synchronization mode.
Definition qgis.h:4676
@ VisibleLayers
Synchronize to map layers. The legend will include layers which are included in the linked map only.
Definition qgis.h:4678
@ AllProjectLayers
Synchronize to all project layers.
Definition qgis.h:4677
@ Manual
No automatic synchronization of legend layers. The legend will be manually populated.
Definition qgis.h:4679
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
Definition qgis.h:4619
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
Definition qgis.h:6353
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4638
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
Definition qgis.h:1621
@ Millimeters
Millimeters.
Definition qgis.h:5280
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition qgis.h:2820
@ SynchronousLegendGraphics
Query legend graphics synchronously.
Definition qgis.h:5344
@ UseAdvancedEffects
Enable advanced effects such as blend modes.
Definition qgis.h:5337
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
virtual bool isLayerChecked(QgsMapLayer *layer) const
Returns if the layer is checked or not.
void setCheckedLayers(const QList< QgsMapLayer * > layers)
Initialize the list of checked layers.
void setShowPrivateLayers(bool showPrivate)
Determines if private layers are shown.
QModelIndex parent(const QModelIndex &child) const override
QgsLayerTreeFilterProxyModel(QObject *parent=nullptr)
Constructor.
Layer tree group node serves as a container for layers and further groups.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
An abstract interface for legend items returned from QgsMapLayerLegend implementation.
Flags flags() const
Returns OR-ed combination of model flags.
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
QModelIndex parent(const QModelIndex &child) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend).
@ UseThreadedHitTest
Run legend hit tests in a background thread.
Base class for nodes in a layer tree.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
virtual QString name() const =0
Returns name of the node.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
Namespace with helper functions for layer tree operations.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
static std::unique_ptr< QgsLayerTree > readXml(const QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
A layout item subclass for map legends.
Q_DECL_DEPRECATED bool autoUpdateModel() const
Returns whether the legend content should auto update to reflect changes in the project's layer tree.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QString title() const
Returns the legend title.
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
QgsLegendModel * model()
Returns the legend model.
QgsLegendStyle & rstyle(Qgis::LegendComponent s)
Returns reference to modifiable legend style.
void adjustBoxSize()
Sets the legend's item bounds to fit the whole legend content.
double wmsLegendWidth() const
Returns the WMS legend width.
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setBoxSpace(double space)
Sets the legend box space.
Qgis::LegendSyncMode syncMode() const
Returns the legend's synchronization mode.
bool splitLayer() const
Returns whether the legend items from a single layer can be split over multiple columns.
double symbolHeight() const
Returns the legend symbol height.
void updateFilterByMap(bool redraw=true)
Updates the legend content when filtered by map.
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
static QgsLayoutItemLegend * create(QgsLayout *layout)
Returns a new legend item for the specified layout.
bool requiresRasterization() const override
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
static const QgsSettingsEntryEnumFlag< Qgis::LegendSyncMode > * settingDefaultLegendSyncMode
Settings entry - Layout legend synchronization mode.
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
void setSymbolWidth(double width)
Sets the legend symbol width.
QString wrapString() const
Returns the legend text wrapping string.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
void updateLegend()
Updates the model and all legend entries.
QgsLayoutItemLegend(QgsLayout *layout)
Constructor for QgsLayoutItemLegend, with the specified parent layout.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
double autoWrapLinesAfter() const
Returns the maximum line length (in millimeters) allowed before lines of text in the legend will be a...
Q_DECL_DEPRECATED QColor fontColor() const
Returns the legend font color.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
void setWmsLegendWidth(double width)
Sets the WMS legend width.
void setTitle(const QString &title)
Sets the legend title.
Q_DECL_DEPRECATED void setFontColor(const QColor &color)
Sets the legend font color.
double boxSpace() const
Returns the legend box space.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
QString displayName() const override
Gets item display name.
bool isRefreshing() const override
Returns true if the item is currently refreshing content in the background.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
double symbolWidth() const
Returns the legend symbol width.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
int type() const override
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool legendFilterOutAtlas() const
Returns whether to filter out legend elements outside of the current atlas feature.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
Q_DECL_DEPRECATED QFont styleFont(Qgis::LegendComponent component) const
Returns the font settings for a legend component.
Q_DECL_DEPRECATED void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
double columnSpace() const
Returns the legend column spacing.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void setFilterByMapItems(const QList< QgsLayoutItemMap * > &maps)
Sets the maps to use when filtering legend content by map extents.
void setAutoWrapLinesAfter(double length)
Sets the maximum line length (in millimeters) allowed before lines of text in the legend will be auto...
void setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map.
QList< QgsLayoutItemMap * > filterByMapItems() const
Returns the maps to use when filtering legend content by map extents.
void setSymbolHeight(double height)
Sets the legend symbol height.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setStyleMargin(Qgis::LegendComponent component, double margin)
Set the margin for a legend component.
void setSyncMode(Qgis::LegendSyncMode mode)
Sets the legend's synchronization mode.
Q_DECL_DEPRECATED void setAutoUpdateModel(bool autoUpdate)
Sets whether the legend content should auto update to reflect changes in the project's layer tree.
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
QIcon icon() const override
Returns the item's icon.
double wmsLegendHeight() const
Returns the WMS legend height.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
QgsLegendStyle style(Qgis::LegendComponent s) const
Returns legend style.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
void setStyle(Qgis::LegendComponent component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
double rasterStrokeWidth() const
Returns the stroke width (in layout units) for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED double lineSpacing() const
Returns the spacing in-between lines in layout units.
void invalidateCache() override
Q_DECL_DEPRECATED void setStyleFont(Qgis::LegendComponent component, const QFont &font)
Sets the style font for a legend component.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void resetManualLayers(Qgis::LegendSyncMode mode)
Resets the current legend manual configuration, including layer set and settings.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
QString themeName() const
Returns the name of the theme currently linked to the legend.
void setColumnCount(int count)
Sets the legend column count.
bool equalColumnWidth() const
Returns whether column widths should be equalized.
int columnCount() const
Returns the legend column count.
bool legendFilterByMapEnabled() const
Find out whether legend items are filtered to show just the ones visible in the associated map.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed... a means to let associated legend items know they sh...
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QList< QgsMapLayer * > layersToRender(const QgsExpressionContext *context=nullptr, bool includeInvalidLayers=false) const
Returns a list of the layers which will be rendered within this map item, considering any locked laye...
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
friend class QgsLayout
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item's content.
virtual void redraw()
Triggers a redraw (update) of the item.
friend class QgsLayoutItemMap
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
@ FlagOverridesPaint
Item overrides the default layout item painting method.
virtual void invalidateCache()
Forces a deferred update of any cached image the item uses.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
friend class QgsLayoutItemLegend
QFlags< Flag > Flags
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ LegendAutoWrapWidth
Legend text automatic wrapping width.
@ AllProperties
All properties for item.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
void setIsDefaultLegend(bool isDefault)
Sets whether the legend is showing the default legend for a project (as opposed to a customized legen...
void setFilterToCheckedLayers(bool filter)
Sets whether only checked layers should be shown.
QgsLegendFilterProxyModel(QObject *parent=nullptr)
Constructor for QgsLegendFilterProxyModel, with the specified parent object.
Item model implementation based on layer tree model for layout legend.
void clearCachedData(QgsLayerTreeNode *node) const
Clears any previously cached data for the specified node.
void refreshLegend()
Emitted to refresh the legend.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr, QgsLayoutItemLegend *layout=nullptr)
Construct the model based on the given layer tree.
QVariant data(const QModelIndex &index, int role) const override
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false) const
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Handles automatic layout and rendering of legends.
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
void setLegendSize(QSizeF s)
Sets the preferred resulting legend size.
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
Contains detailed styling information relating to how a layout legend should be rendered.
QgsTextFormat & textFormat()
Returns the text format used for rendering this legend component.
void setMargin(Side side, double margin)
Sets the margin (in mm) for the specified side of the component.
Side
Margin sides.
Q_DECL_DEPRECATED void setFont(const QFont &font)
Sets the font used for rendering this legend component.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext()) const
Writes the component's style definition to an XML element.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for rendering this legend component.
An abstract interface for implementations of legends for one map layer.
Qgis::MapLayerLegendFlags flags() const
Returns flags associated with the legend.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QgsMapLayerLegend * legend() const
Can be nullptr.
Contains configuration for rendering maps.
double scale() const
Returns the calculated map scale.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
int group() const
Returns a group index.
Qgis::MeshRangeExtent extent() const
Returns the mesh extent for minimum maximum calculation.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
Represents a raster layer.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
Scoped object for saving and restoring a QPainter object's state.
static QgsSettingsTreeNode * sTreeLayout
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:1521
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setLineHeightUnit(Qgis::RenderUnit unit)
Sets the unit for the line height for text.
double size() const
Returns the size for rendered text.
void setLineHeight(double height)
Sets the line height for text.
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7134
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7475
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7115
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7474
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
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.