QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 Qgis::LegendSyncMode >( u"default-legend-sync-mode"_s, QgsSettingsTree::sTreeLayout, Qgis::LegendSyncMode::VisibleLayers, u"Default sync mode to use for legend content."_s );
110
113 , mLegendModel( new QgsLegendModel( nullptr, this ) )
114{
115#if 0 //no longer required?
116 connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
117#endif
118
119 mTitle = mSettings.title();
120
121 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestStarted, this, [this] { emit backgroundTaskCountChanged( 1 ); } );
122 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestCompleted, this, [this] {
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 invalidateCache();
135 update();
136 } );
137 connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [this] {
138 // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
139 // causing an endless loop.
140 invalidateCache();
141 update();
142 } );
143}
144
149
154
156{
157 return QgsApplication::getThemeIcon( u"/mLayoutItemLegend.svg"_s );
158}
159
164
165void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
166{
167 if ( !painter )
168 return;
169
170 const QPointF oldPos = pos();
171
172 ensureModelIsInitialized();
173
174 if ( mFilterAskedForUpdate )
175 {
176 mFilterAskedForUpdate = false;
177 doUpdateFilterByMap();
178 }
179
180 const int dpi = painter->device()->logicalDpiX();
181 const double dotsPerMM = dpi / 25.4;
182
183 if ( mLayout )
184 {
186 // no longer required, but left set for api stability
187 mSettings.setUseAdvancedEffects( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::UseAdvancedEffects );
188 mSettings.setDpi( dpi );
189 mSettings.setSynchronousLegendRequests( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::SynchronousLegendGraphics );
191 }
192 if ( mMap && mLayout )
193 {
195 // no longer required, but left set for api stability
196 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
198
199 // use a temporary QgsMapSettings to find out real map scale
200 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
201 const QgsRectangle mapExtent = mMap->extent();
202
203 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
204
205 // no longer required, but left set for api stability
207 mSettings.setMapScale( ms.scale() );
209 }
210 mInitialMapScaleCalculated = true;
211
212 QgsLegendRenderer legendRenderer = createRenderer();
213 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
214
215 //adjust box if width or height is too small
216 if ( mSizeToContents )
217 {
220
221 const QSizeF size = legendRenderer.minimumSize( &context );
222 if ( mForceResize )
223 {
224 mForceResize = false;
225
226 //set new rect, respecting position mode and data defined size/position
227 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
228 attemptResize( newSize );
229 }
230 else if ( size.height() > rect().height() || size.width() > rect().width() )
231 {
232 //need to resize box
233 QSizeF targetSize = rect().size();
234 if ( size.height() > targetSize.height() )
235 targetSize.setHeight( size.height() );
236 if ( size.width() > targetSize.width() )
237 targetSize.setWidth( size.width() );
238
239 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
240 //set new rect, respecting position mode and data defined size/position
241 attemptResize( newSize );
242 }
243 }
244
245 // attemptResize may change the legend position and would call setPos
246 // BUT the position is actually changed for the next draw, so we need to translate of the difference
247 // between oldPos and newPos
248 // the issue doesn't appear in desktop rendering but only in export because in the first one,
249 // Qt triggers a redraw on position change
250 painter->save();
251 painter->translate( pos() - oldPos );
252 QgsLayoutItem::paint( painter, itemStyle, pWidget );
253 painter->restore();
254}
255
257{
258 if ( !mMapUuid.isEmpty() )
259 {
260 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
261 }
262
263 if ( !mFilterByMapUuids.isEmpty() )
264 {
265 QList< QgsLayoutItemMap * > maps;
266 maps.reserve( mFilterByMapUuids.size() );
267 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
268 {
269 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
270 {
271 maps << map;
272 }
273 }
274 setFilterByMapItems( maps );
275 }
276}
277
279{
281 clearLegendCachedData();
282 onAtlasFeature();
283}
284
286{
287 clearLegendCachedData();
289}
290
292{
293 QPainter *painter = context.renderContext().painter();
294
295 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
297
300
301 const QgsScopedQPainterState painterState( painter );
302
303 // painter is scaled to dots, so scale back to layout units
304 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
305
306 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
307
308 if ( !mSizeToContents )
309 {
310 // set a clip region to crop out parts of legend which don't fit
311 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
312 painter->setClipRect( thisPaintRect );
313 }
314
315 if ( mLayout )
316 {
317 // no longer required, but left for API compatibility
319 mSettings.setDpi( mLayout->renderContext().dpi() );
321 }
322
323 QgsLegendRenderer legendRenderer = createRenderer();
324 legendRenderer.setLegendSize( rect().size() );
325
326 legendRenderer.drawLegend( rc );
327}
328
330{
331 if ( !mSizeToContents )
332 return;
333
334 if ( !mInitialMapScaleCalculated )
335 {
336 // this is messy - but until we have painted the item we have no knowledge of the current DPI
337 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
338 // for marker symbols with size in map units, causing the legends to initially expand to huge
339 // sizes if we attempt to calculate the box size first.
340 return;
341 }
342
345
346 QgsLegendRenderer legendRenderer = createRenderer();
347 const QSizeF size = legendRenderer.minimumSize( &context );
348 QgsDebugMsgLevel( u"width = %1 height = %2"_s.arg( size.width() ).arg( size.height() ), 2 );
349 if ( size.isValid() )
350 {
351 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
352 //set new rect, respecting position mode and data defined size/position
353 attemptResize( newSize );
354 }
355}
356
358{
359 mSizeToContents = enabled;
360}
361
363{
364 return mSizeToContents;
365}
366
367void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
368{
369 if ( !mDeferLegendModelInitialization )
370 {
371 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
372 }
373
374 mCustomLayerTree.reset( rootGroup );
375}
376
377void QgsLayoutItemLegend::ensureModelIsInitialized() const
378{
379 if ( mDeferLegendModelInitialization )
380 {
381 QgsLayoutItemLegend *mutableThis = const_cast< QgsLayoutItemLegend * >( this );
382 mutableThis->mDeferLegendModelInitialization = false;
383 mutableThis->setCustomLayerTree( mutableThis->mCustomLayerTree.release() );
384 }
385}
386
387QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const
388{
389 QgsLegendRenderer res( mLegendModel.get(), mSettings );
390
391 QgsLegendFilterProxyModel *proxy = new QgsLegendFilterProxyModel();
392 proxy->setIsDefaultLegend( !static_cast< bool >( mCustomLayerTree ) );
393 switch ( mSyncMode )
394 {
396 if ( mMap )
397 {
398 QgsExpressionContext expressionContext = mMap->createExpressionContext();
399 const QList<QgsMapLayer *> visibleLayers = mMap->layersToRender( &expressionContext );
400 proxy->setCheckedLayers( visibleLayers );
401 proxy->setFilterToCheckedLayers( true );
402 }
403 else if ( const QgsLayout *l = layout() )
404 {
405 // no linked map, so use project layer tree
406 if ( QgsProject *p = l->project() )
407 {
408 proxy->setCheckedLayers( p->layerTreeRoot()->checkedLayers() );
409 proxy->setFilterToCheckedLayers( true );
410 }
411 }
412 break;
413
416 break;
417 }
418
419 res.setProxyModel( proxy );
420
421 return res;
422}
423
425{
426 ensureModelIsInitialized();
427 return mLegendModel.get();
428}
429
431{
432 ensureModelIsInitialized();
433 return mLegendModel.get();
434}
435
440
442{
443 if ( mode == mSyncMode )
444 return;
445
446 const Qgis::LegendSyncMode oldMode = mSyncMode;
447 mSyncMode = mode;
448 switch ( mSyncMode )
449 {
452 setCustomLayerTree( nullptr );
453 break;
454
456 {
457 resetManualLayers( oldMode );
458 break;
459 }
460 }
461
463 updateFilterByMap( false );
464}
465
467{
468 if ( mSyncMode != Qgis::LegendSyncMode::Manual )
469 return;
470
471 std::unique_ptr< QgsLayerTree > customTree( mLayout->project()->layerTreeRoot()->clone() );
472
473 QList<QgsMapLayer *> mapVisibleLayers;
475 {
476 if ( mMap )
477 {
478 QgsExpressionContext expressionContext = mMap->createExpressionContext();
479 mapVisibleLayers = mMap->layersToRender( &expressionContext );
480 }
481 else if ( const QgsLayout *l = layout() )
482 {
483 // no linked map, so use project layer tree
484 if ( QgsProject *p = l->project() )
485 {
486 mapVisibleLayers = p->layerTreeRoot()->checkedLayers();
487 }
488 }
489 }
490
491 // filter out excluded by default items
492 std::function< void( QgsLayerTreeGroup * )> filterNodeChildren;
493 filterNodeChildren = [mapVisibleLayers, mode, &filterNodeChildren]( QgsLayerTreeGroup *group ) {
494 if ( !group )
495 return;
496
497 const QList<QgsLayerTreeNode *> children = group->children();
498 for ( QgsLayerTreeNode *child : children )
499 {
500 if ( !child )
501 {
502 group->removeChildNode( child );
503 continue;
504 }
505 else if ( QgsLayerTree::isGroup( child ) )
506 {
507 filterNodeChildren( QgsLayerTree::toGroup( child ) );
508 }
509 else if ( QgsLayerTree::isLayer( child ) )
510 {
512 if ( QgsMapLayer *mapLayer = layer->layer() )
513 {
514 if ( QgsMapLayerLegend *layerLegend = mapLayer->legend(); layerLegend && layerLegend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) )
515 {
516 group->removeChildNode( child );
517 }
518 else if ( mode == Qgis::LegendSyncMode::VisibleLayers && !mapVisibleLayers.contains( mapLayer ) )
519 {
520 group->removeChildNode( child );
521 }
522 }
523 }
524 }
525 };
526 filterNodeChildren( customTree.get() );
527
528 setCustomLayerTree( customTree.release() );
529}
530
531void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
532{
533 if ( key == "cached_name"_L1 )
534 return;
535
536 switch ( mSyncMode )
537 {
540 {
541 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
542 // we must then call updateItem to reflect the changes
543 updateFilterByMap( false );
544 break;
545 }
547 break;
548 }
549}
550
551void QgsLayoutItemLegend::nodeVisibilityChanged( QgsLayerTreeNode * )
552{
553 switch ( mSyncMode )
554 {
556 {
557 updateFilterByMap( false );
558 break;
559 }
562 break;
563 }
564}
565
567{
568 return mSyncMode != Qgis::LegendSyncMode::Manual;
569}
570
572{
573 return mSyncMode;
574}
575
577{
578 if ( mLegendFilterByMap == enabled )
579 return;
580
581 mLegendFilterByMap = enabled;
582 updateFilterByMap( false );
583}
584
585void QgsLayoutItemLegend::setTitle( const QString &t )
586{
587 mTitle = t;
588 mSettings.setTitle( t );
589
590 if ( mLayout && id().isEmpty() )
591 {
592 //notify the model that the display name has changed
593 mLayout->itemsModel()->updateItemDisplayName( this );
594 }
595}
597{
598 return mTitle;
599}
600
601Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
602{
603 return mSettings.titleAlignment();
604}
605
606void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
607{
608 mSettings.setTitleAlignment( alignment );
609}
610
612{
613 return mSettings.rstyle( s );
614}
615
617{
618 return mSettings.style( s );
619}
620
622{
623 mSettings.setStyle( s, style );
624}
625
627{
629 return mSettings.style( s ).font();
631}
632
639
641{
642 rstyle( s ).setMargin( margin );
643}
644
646{
647 rstyle( s ).setMargin( side, margin );
648}
649
651{
653 return mSettings.lineSpacing();
655}
656
658{
660 mSettings.setLineSpacing( spacing );
662}
663
665{
666 return mSettings.boxSpace();
667}
668
670{
671 mSettings.setBoxSpace( s );
672}
673
675{
676 return mSettings.columnSpace();
677}
678
680{
681 mSettings.setColumnSpace( s );
682}
683
685{
687 return mSettings.fontColor();
689}
690
692{
694 mSettings.setFontColor( c );
696}
697
699{
700 return mSettings.symbolSize().width();
701}
702
704{
705 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
706}
707
709{
710 return mSettings.maximumSymbolSize();
711}
712
714{
715 mSettings.setMaximumSymbolSize( size );
716}
717
719{
720 return mSettings.minimumSymbolSize();
721}
722
724{
725 mSettings.setMinimumSymbolSize( size );
726}
727
728void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
729{
730 mSettings.setSymbolAlignment( alignment );
731}
732
734{
735 return mSettings.symbolAlignment();
736}
737
739{
740 return mSettings.symbolSize().height();
741}
742
744{
745 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
746}
747
749{
750 return mSettings.wmsLegendSize().width();
751}
752
754{
755 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
756}
757
759{
760 return mSettings.wmsLegendSize().height();
761}
763{
764 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
765}
766
768{
769 mSettings.setWrapChar( t );
770}
771
773{
774 return mSettings.wrapChar();
775}
776
778{
779 return mColumnCount;
780}
781
783{
784 mColumnCount = c;
785 mSettings.setColumnCount( c );
786}
787
789{
790 return mSettings.splitLayer();
791}
792
794{
795 mSettings.setSplitLayer( s );
796}
797
799{
800 return mSettings.equalColumnWidth();
801}
802
804{
805 mSettings.setEqualColumnWidth( s );
806}
807
809{
810 return mSettings.drawRasterStroke();
811}
812
814{
815 mSettings.setDrawRasterStroke( enabled );
816}
817
819{
820 return mSettings.rasterStrokeColor();
821}
822
824{
825 mSettings.setRasterStrokeColor( color );
826}
827
829{
830 return mSettings.rasterStrokeWidth();
831}
832
834{
835 mSettings.setRasterStrokeWidth( width );
836}
837
839{
840 return mSettings.autoWrapLinesAfter();
841}
842
844{
845 mSettings.setAutoWrapLinesAfter( length );
846}
847
853
854bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
855{
856 //write general properties
857 legendElem.setAttribute( u"title"_s, mTitle );
858 legendElem.setAttribute( u"titleAlignment"_s, QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
859 legendElem.setAttribute( u"columnCount"_s, QString::number( mColumnCount ) );
860 legendElem.setAttribute( u"splitLayer"_s, QString::number( mSettings.splitLayer() ) );
861 legendElem.setAttribute( u"equalColumnWidth"_s, QString::number( mSettings.equalColumnWidth() ) );
862
863 legendElem.setAttribute( u"boxSpace"_s, QString::number( mSettings.boxSpace() ) );
864 legendElem.setAttribute( u"columnSpace"_s, QString::number( mSettings.columnSpace() ) );
865
866 legendElem.setAttribute( u"symbolWidth"_s, QString::number( mSettings.symbolSize().width() ) );
867 legendElem.setAttribute( u"symbolHeight"_s, QString::number( mSettings.symbolSize().height() ) );
868 legendElem.setAttribute( u"maxSymbolSize"_s, QString::number( mSettings.maximumSymbolSize() ) );
869 legendElem.setAttribute( u"minSymbolSize"_s, QString::number( mSettings.minimumSymbolSize() ) );
870
871 legendElem.setAttribute( u"symbolAlignment"_s, mSettings.symbolAlignment() );
872
873 legendElem.setAttribute( u"symbolAlignment"_s, mSettings.symbolAlignment() );
874
875 legendElem.setAttribute( u"rasterBorder"_s, mSettings.drawRasterStroke() );
876 legendElem.setAttribute( u"rasterBorderColor"_s, QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
877 legendElem.setAttribute( u"rasterBorderWidth"_s, QString::number( mSettings.rasterStrokeWidth() ) );
878
879 if ( mSettings.autoWrapLinesAfter() > 0 )
880 {
881 legendElem.setAttribute( u"autoWrapLinesAfter"_s, mSettings.autoWrapLinesAfter() );
882 }
883
884 legendElem.setAttribute( u"wmsLegendWidth"_s, QString::number( mSettings.wmsLegendSize().width() ) );
885 legendElem.setAttribute( u"wmsLegendHeight"_s, QString::number( mSettings.wmsLegendSize().height() ) );
886 legendElem.setAttribute( u"wrapChar"_s, mSettings.wrapChar() );
887
888 legendElem.setAttribute( u"resizeToContents"_s, mSizeToContents );
889
890 if ( mMap )
891 {
892 legendElem.setAttribute( u"map_uuid"_s, mMap->uuid() );
893 }
894
895 if ( !mFilterByMapItems.empty() )
896 {
897 QDomElement filterByMapsElem = doc.createElement( u"filterByMaps"_s );
898 for ( QgsLayoutItemMap *map : mFilterByMapItems )
899 {
900 if ( map )
901 {
902 QDomElement mapElem = doc.createElement( u"map"_s );
903 mapElem.setAttribute( u"uuid"_s, map->uuid() );
904 filterByMapsElem.appendChild( mapElem );
905 }
906 }
907 legendElem.appendChild( filterByMapsElem );
908 }
909
910 QDomElement legendStyles = doc.createElement( u"styles"_s );
911 legendElem.appendChild( legendStyles );
912
913 style( Qgis::LegendComponent::Title ).writeXml( u"title"_s, legendStyles, doc, context );
914 style( Qgis::LegendComponent::Group ).writeXml( u"group"_s, legendStyles, doc, context );
915 style( Qgis::LegendComponent::Subgroup ).writeXml( u"subgroup"_s, legendStyles, doc, context );
916 style( Qgis::LegendComponent::Symbol ).writeXml( u"symbol"_s, legendStyles, doc, context );
917 style( Qgis::LegendComponent::SymbolLabel ).writeXml( u"symbolLabel"_s, legendStyles, doc, context );
918
919 if ( mCustomLayerTree )
920 {
921 // if not using auto-update - store the custom layer tree
922 mCustomLayerTree->writeXml( legendElem, context );
923 }
924 else
925 {
926 legendElem.setAttribute( u"syncMode"_s, qgsEnumValueToKey( mSyncMode ) );
927 }
928
929 if ( mLegendFilterByMap )
930 {
931 legendElem.setAttribute( u"legendFilterByMap"_s, u"1"_s );
932 }
933 legendElem.setAttribute( u"legendFilterByAtlas"_s, mFilterOutAtlas ? u"1"_s : u"0"_s );
934
935 return true;
936}
937
938bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
939{
940 //read general properties
941 mTitle = itemElem.attribute( u"title"_s );
942 mSettings.setTitle( mTitle );
943 if ( !itemElem.attribute( u"titleAlignment"_s ).isEmpty() )
944 {
945 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( u"titleAlignment"_s ).toInt() ) );
946 }
947 int colCount = itemElem.attribute( u"columnCount"_s, u"1"_s ).toInt();
948 if ( colCount < 1 )
949 colCount = 1;
950 mColumnCount = colCount;
951 mSettings.setColumnCount( mColumnCount );
952 mSettings.setSplitLayer( itemElem.attribute( u"splitLayer"_s, u"0"_s ).toInt() == 1 );
953 mSettings.setEqualColumnWidth( itemElem.attribute( u"equalColumnWidth"_s, u"0"_s ).toInt() == 1 );
954
955 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( u"styles"_s );
956 if ( !stylesNodeList.isEmpty() )
957 {
958 const QDomNode stylesNode = stylesNodeList.at( 0 );
959 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
960 {
961 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
963 style.readXml( styleElem, doc, context );
964 const QString name = styleElem.attribute( u"name"_s );
966 if ( name == "title"_L1 )
968 else if ( name == "group"_L1 )
970 else if ( name == "subgroup"_L1 )
972 else if ( name == "symbol"_L1 )
974 else if ( name == "symbolLabel"_L1 )
976 else
977 continue;
978 setStyle( s, style );
979 }
980 }
981
982 //font color
983 if ( itemElem.hasAttribute( u"fontColor"_s ) )
984 {
985 QColor fontClr;
986 fontClr.setNamedColor( itemElem.attribute( u"fontColor"_s, u"#000000"_s ) );
991 }
992
993 //spaces
994 mSettings.setBoxSpace( itemElem.attribute( u"boxSpace"_s, u"2.0"_s ).toDouble() );
995 mSettings.setColumnSpace( itemElem.attribute( u"columnSpace"_s, u"2.0"_s ).toDouble() );
996
997 mSettings.setSymbolSize( QSizeF( itemElem.attribute( u"symbolWidth"_s, u"7.0"_s ).toDouble(), itemElem.attribute( u"symbolHeight"_s, u"14.0"_s ).toDouble() ) );
998 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( u"symbolAlignment"_s, QString::number( Qt::AlignLeft ) ).toInt() ) );
999
1000 mSettings.setMaximumSymbolSize( itemElem.attribute( u"maxSymbolSize"_s, u"0.0"_s ).toDouble() );
1001 mSettings.setMinimumSymbolSize( itemElem.attribute( u"minSymbolSize"_s, u"0.0"_s ).toDouble() );
1002
1003 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( u"wmsLegendWidth"_s, u"50"_s ).toDouble(), itemElem.attribute( u"wmsLegendHeight"_s, u"25"_s ).toDouble() ) );
1004
1005 if ( itemElem.hasAttribute( u"lineSpacing"_s ) )
1006 {
1007 const double spacing = itemElem.attribute( u"lineSpacing"_s, u"1.0"_s ).toDouble();
1008 // line spacing *was* a fixed amount (in mm) added between each line of text.
1010 // assume font sizes in points, since that was what we always had from before this method was deprecated
1011 f.setLineHeight( f.size() * 0.352778 + spacing );
1014
1016 f.setLineHeight( f.size() * 0.352778 + spacing );
1019
1021 f.setLineHeight( f.size() * 0.352778 + spacing );
1024
1026 f.setLineHeight( f.size() * 0.352778 + spacing );
1029 }
1030
1031 mSettings.setDrawRasterStroke( itemElem.attribute( u"rasterBorder"_s, u"1"_s ) != "0"_L1 );
1032 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( u"rasterBorderColor"_s, u"0,0,0"_s ) ) );
1033 mSettings.setRasterStrokeWidth( itemElem.attribute( u"rasterBorderWidth"_s, u"0"_s ).toDouble() );
1034
1035 mSettings.setAutoWrapLinesAfter( itemElem.attribute( u"autoWrapLinesAfter"_s, u"0"_s ).toDouble() );
1036
1037 mSettings.setWrapChar( itemElem.attribute( u"wrapChar"_s ) );
1038
1039 mSizeToContents = itemElem.attribute( u"resizeToContents"_s, u"1"_s ) != "0"_L1;
1040
1041 // map
1042 mLegendFilterByMap = itemElem.attribute( u"legendFilterByMap"_s, u"0"_s ).toInt();
1043
1044 mMapUuid.clear();
1045 if ( !itemElem.attribute( u"map_uuid"_s ).isEmpty() )
1046 {
1047 mMapUuid = itemElem.attribute( u"map_uuid"_s );
1048 }
1049
1050 mFilterByMapUuids.clear();
1051 {
1052 const QDomElement filterByMapsElem = itemElem.firstChildElement( u"filterByMaps"_s );
1053 if ( !filterByMapsElem.isNull() )
1054 {
1055 QDomElement mapsElem = filterByMapsElem.firstChildElement( u"map"_s );
1056 while ( !mapsElem.isNull() )
1057 {
1058 mFilterByMapUuids << mapsElem.attribute( u"uuid"_s );
1059 mapsElem = mapsElem.nextSiblingElement( u"map"_s );
1060 }
1061 }
1062 else if ( !mMapUuid.isEmpty() )
1063 {
1064 // for compatibility with < QGIS 3.32 projects
1065 mFilterByMapUuids << mMapUuid;
1066 }
1067 }
1068
1069 // disconnect current map
1070 setupMapConnections( mMap, false );
1071 mMap = nullptr;
1072
1073 mFilterOutAtlas = itemElem.attribute( u"legendFilterByAtlas"_s, u"0"_s ).toInt();
1074
1075 // QGIS >= 2.6
1076 QDomElement layerTreeElem = itemElem.firstChildElement( u"layer-tree"_s );
1077 if ( layerTreeElem.isNull() )
1078 layerTreeElem = itemElem.firstChildElement( u"layer-tree-group"_s );
1079
1080 if ( !layerTreeElem.isNull() )
1081 {
1082 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
1083 if ( mLayout )
1084 tree->resolveReferences( mLayout->project(), true );
1085 setCustomLayerTree( tree.release() );
1086 mSyncMode = Qgis::LegendSyncMode::Manual;
1087 }
1088 else
1089 {
1090 setCustomLayerTree( nullptr );
1091 mSyncMode = qgsEnumKeyToValue( itemElem.attribute( u"syncMode"_s ), Qgis::LegendSyncMode::AllProjectLayers );
1092 }
1093
1094 return true;
1095}
1096
1098{
1099 if ( !id().isEmpty() )
1100 {
1101 return id();
1102 }
1103
1104 //if no id, default to portion of title text
1105 QString text = mSettings.title();
1106 if ( text.isEmpty() )
1107 {
1108 return tr( "<Legend>" );
1109 }
1110 if ( text.length() > 25 )
1111 {
1112 return tr( "%1…" ).arg( text.left( 25 ) );
1113 }
1114 else
1115 {
1116 return text;
1117 }
1118}
1119
1121{
1122 return blendMode() != QPainter::CompositionMode_SourceOver;
1123}
1124
1126{
1127 return mEvaluatedOpacity < 1.0;
1128}
1129
1130void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
1131{
1132 if ( !map )
1133 return;
1134
1135 if ( !connectSlots )
1136 {
1137 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1138 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1139 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1140 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1141 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1142 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1143 }
1144 else
1145 {
1146 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
1147 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1148 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1149 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
1150 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
1151 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
1152 }
1153}
1154
1156{
1157 if ( mMap == map )
1158 return;
1159
1160 if ( mMap )
1161 {
1162 setupMapConnections( mMap, false );
1163 }
1164
1165 mMap = map;
1166
1167 if ( mMap )
1168 {
1169 setupMapConnections( mMap, true );
1170 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
1171 }
1172
1174}
1175
1176void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
1177{
1178 if ( filterByMapItems() == maps )
1179 return;
1180
1181 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
1182 {
1183 setupMapConnections( map, false );
1184 }
1185
1186 mFilterByMapItems.clear();
1187 mFilterByMapItems.reserve( maps.size() );
1188 for ( QgsLayoutItemMap *map : maps )
1189 {
1190 if ( map )
1191 {
1192 mFilterByMapItems.append( map );
1193 setupMapConnections( map, true );
1194 }
1195 }
1196
1198}
1199
1200QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
1201{
1202 QList<QgsLayoutItemMap *> res;
1203 res.reserve( mFilterByMapItems.size() );
1204 for ( QgsLayoutItemMap *map : mFilterByMapItems )
1205 {
1206 if ( map )
1207 res.append( map );
1208 }
1209 return res;
1210}
1211
1212void QgsLayoutItemLegend::invalidateCurrentMap()
1213{
1214 setLinkedMap( nullptr );
1215}
1216
1218{
1220
1221 bool forceUpdate = false;
1222 //updates data defined properties and redraws item to match
1224 {
1225 bool ok = false;
1226 const QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::LegendTitle, context, mTitle, &ok );
1227 if ( ok )
1228 {
1229 mSettings.setTitle( t );
1230 forceUpdate = true;
1231 }
1232 }
1234 {
1235 bool ok = false;
1236 const int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::DataDefinedProperty::LegendColumnCount, context, mColumnCount, &ok );
1237 if ( ok && cols >= 0 )
1238 {
1239 mSettings.setColumnCount( cols );
1240 forceUpdate = true;
1241 }
1242 }
1244 {
1245 bool ok = false;
1246 const double width = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::LegendAutoWrapWidth, context, mSettings.autoWrapLinesAfter(), &ok );
1247 if ( ok && width >= 0 )
1248 {
1249 mSettings.setAutoWrapLinesAfter( width );
1250 forceUpdate = true;
1251 }
1252 }
1253 if ( forceUpdate )
1254 {
1255 adjustBoxSize();
1256 update();
1257 }
1258
1260}
1261
1262
1263void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
1264{
1265 updateFilterByMap( true );
1266}
1267
1268void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
1269{
1270 mLegendModel->setLayerStyleOverrides( overrides );
1271 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1272 {
1273 const QList< QgsLayerTreeLayer * > layers = rootGroup->findLayers();
1274 for ( QgsLayerTreeLayer *nodeLayer : std::as_const( layers ) )
1275 mLegendModel->refreshLayerLegend( nodeLayer );
1276 }
1277}
1278
1279void QgsLayoutItemLegend::clearLegendCachedData()
1280{
1281 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1282 clearNodeCache = [&]( QgsLayerTreeNode *node ) {
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 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) ) || mInAtlas || requiresFilteringBecauseOfRendererSetting();
1384
1385 if ( hasValidFilter )
1386 {
1387 const double dpi = mLayout->renderContext().dpi();
1388
1389 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1390 if ( mLegendFilterByMap )
1391 {
1392 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1393 if ( mMap )
1394 linkedFilterMaps.insert( mMap );
1395 }
1396
1397 QgsMapSettings mapSettings;
1398 QgsGeometry filterGeometry;
1399 if ( mMap )
1400 {
1401 // if a specific linked map has been set, use it for the reference scale and extent
1402 const QgsRectangle requestRectangle = mMap->requestedExtent();
1403 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1404 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1405 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1406
1407 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1408 }
1409 else if ( !linkedFilterMaps.empty() )
1410 {
1411 // otherwise just take the first linked filter map
1412 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1413 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1414 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1415 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1416
1417 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1418 }
1419
1421
1422 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1423
1424 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1425
1426 QList<QgsMapLayer *> layersToClip;
1427 if ( mMap )
1428 {
1429 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1430 {
1431 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1432 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1433 {
1434 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1435 mapLayers.removeAll( layer );
1436 filterSettings.mapSettings().setLayers( mapLayers );
1437 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1438 }
1439 }
1440 }
1441
1442 if ( !linkedFilterMaps.empty() )
1443 {
1444 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1445 {
1446 if ( map == mMap )
1447 continue;
1448
1449 QgsGeometry mapExtent = QgsGeometry::fromQPolygonF( map->visibleExtentPolygon() );
1450
1451 //transform back to destination CRS
1452 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1453 try
1454 {
1455 mapExtent.transform( mapTransform );
1456 }
1457 catch ( QgsCsException & )
1458 {
1459 continue;
1460 }
1461
1462 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1463 for ( QgsMapLayer *layer : layersForMap )
1464 {
1465 if ( mInAtlas && !atlasGeometry.isNull() )
1466 {
1467 mapExtent = mapExtent.intersection( atlasGeometry );
1468 }
1469
1470 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1471 }
1472 }
1473 }
1474
1475 if ( mInAtlas )
1476 {
1477 if ( !filterGeometry.isEmpty() )
1478 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1479 else
1480 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1481 }
1482
1483 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1484 if ( !filterGeometry.isNull() )
1485 {
1486 filterSettings.setFilterPolygon( filterGeometry );
1487 }
1488 else
1489 {
1490 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1491 }
1492
1493 mLegendModel->setFilterSettings( &filterSettings );
1494 }
1495 else
1496 {
1497 mLegendModel->setFilterSettings( nullptr );
1498 }
1499
1500 clearLegendCachedData();
1501 mForceResize = true;
1502}
1503
1505{
1506 return mThemeName;
1507}
1508
1510{
1511 mFilterOutAtlas = doFilter;
1512}
1513
1515{
1516 return mFilterOutAtlas;
1517}
1518
1519void QgsLayoutItemLegend::onAtlasFeature()
1520{
1521 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1522 return;
1523 mInAtlas = mFilterOutAtlas;
1525}
1526
1527void QgsLayoutItemLegend::onAtlasEnded()
1528{
1529 mInAtlas = false;
1531}
1532
1534{
1536
1537 // We only want the last scope from the map's expression context, as this contains
1538 // the map specific variables. We don't want the rest of the map's context, because that
1539 // will contain duplicate global, project, layout, etc scopes.
1540 if ( mMap )
1541 context.appendScope( mMap->createExpressionContext().popScope() );
1542
1543 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1544
1545 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_title"_s, title(), true ) );
1546 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_column_count"_s, columnCount(), true ) );
1547 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_split_layers"_s, splitLayer(), true ) );
1548 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_wrap_string"_s, wrapString(), true ) );
1549 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_filter_by_map"_s, legendFilterByMapEnabled(), true ) );
1550 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"legend_filter_out_atlas"_s, legendFilterOutAtlas(), true ) );
1551
1552 context.appendScope( scope );
1553 return context;
1554}
1555
1560
1562{
1563 std::function<bool( QgsLayerTreeGroup * group ) > visit;
1564
1565 visit = [this, visitor, &visit]( QgsLayerTreeGroup *group ) -> bool {
1566 const QList<QgsLayerTreeNode *> childNodes = group->children();
1567 for ( QgsLayerTreeNode *node : childNodes )
1568 {
1569 if ( QgsLayerTree::isGroup( node ) )
1570 {
1571 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1572 if ( !visit( nodeGroup ) )
1573 return false;
1574 }
1575 else if ( QgsLayerTree::isLayer( node ) )
1576 {
1577 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1578 if ( !nodeLayer->patchShape().isNull() )
1579 {
1580 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1581 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1582 return false;
1583 }
1584 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1585 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1586 {
1587 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1588 {
1589 if ( !symbolNode->patchShape().isNull() )
1590 {
1591 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1592 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1593 return false;
1594 }
1595 }
1596 }
1597 }
1598 }
1599 return true;
1600 };
1601 return visit( model()->rootGroup() );
1602}
1603
1605{
1606 return mLegendModel->hitTestInProgress();
1607}
1608
1609
1610bool QgsLayoutItemLegend::requiresFilteringBecauseOfRendererSetting()
1611{
1612 const QList<QgsLayerTreeLayer *> layers = model()->rootGroup()->findLayers();
1613
1614 for ( QgsLayerTreeLayer *layerTreeLayer : layers )
1615 {
1616 QgsMapLayer *mapLayer = layerTreeLayer->layer();
1617
1618 if ( !mapLayer || !mapLayer->isValid() )
1619 continue;
1620 if ( QgsRasterLayer *rl = qobject_cast<QgsRasterLayer *>( mapLayer ) )
1621 {
1622 if ( rl->renderer() && rl->renderer()->minMaxOrigin().extent() == Qgis::RasterRangeExtent::UpdatedCanvas )
1623 {
1624 return true;
1625 }
1626 }
1627 else if ( QgsMeshLayer *ml = qobject_cast<QgsMeshLayer *>( mapLayer ) )
1628 {
1629 const QgsMeshDatasetIndex activeDatasetIndex = ml->staticScalarDatasetIndex();
1630
1631 if ( activeDatasetIndex.isValid() )
1632 {
1633 QgsMeshRendererScalarSettings scalarRendererSettings = ml->rendererSettings().scalarSettings( activeDatasetIndex.group() );
1634
1635 if ( scalarRendererSettings.extent() == Qgis::MeshRangeExtent::UpdatedCanvas )
1636 {
1637 return true;
1638 }
1639 }
1640 }
1641 }
1642
1643 return false;
1644}
1645
1646// -------------------------------------------------------------------------
1647
1649 : QgsLayerTreeModel( rootNode, parent )
1650 , mLayoutLegend( layout )
1651{
1655 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1656}
1657
1659 : QgsLayerTreeModel( rootNode )
1660 , mLayoutLegend( layout )
1661{
1665 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1666}
1667
1668QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1669{
1670 // handle custom layer node labels
1671
1673 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1674 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1675 {
1676 QString name = node->customProperty( u"cached_name"_s ).toString();
1677 if ( !name.isEmpty() )
1678 return name;
1679
1680 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1681
1682 //finding the first label that is stored
1683 name = nodeLayer->customProperty( u"legend/title-label"_s ).toString();
1684 if ( name.isEmpty() )
1685 name = nodeLayer->name();
1686 if ( name.isEmpty() )
1687 name = node->customProperty( u"legend/title-label"_s ).toString();
1688 if ( name.isEmpty() )
1689 name = node->name();
1690 if ( nodeLayer->customProperty( u"showFeatureCount"_s, 0 ).toInt() )
1691 {
1692 if ( vlayer && vlayer->featureCount() >= 0 )
1693 {
1694 name += u" [%1]"_s.arg( vlayer->featureCount() );
1695 node->setCustomProperty( u"cached_name"_s, name );
1696 return name;
1697 }
1698 }
1699 node->setCustomProperty( u"cached_name"_s, name );
1700 return name;
1701 }
1702 return QgsLayerTreeModel::data( index, role );
1703}
1704
1705Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1706{
1707 // make the legend nodes selectable even if they are not by default
1708 if ( index2legendNode( index ) )
1709 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1710
1712}
1713
1714QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1715{
1716 if ( !mLegend.contains( nodeLayer ) )
1717 return QList<QgsLayerTreeModelLegendNode *>();
1718
1719 const LayerLegendData &data = mLegend[nodeLayer];
1720 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1721 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1722 lst.prepend( data.embeddedNodeInParent );
1723 return lst;
1724}
1725
1727{
1728 node->removeCustomProperty( u"cached_name"_s );
1729}
1730
1731void QgsLegendModel::forceRefresh()
1732{
1733 emit refreshLegend();
1734}
@ Millimeters
Millimeters.
Definition qgis.h:5361
LegendComponent
Component of legends which can be styled.
Definition qgis.h:4719
@ Symbol
Symbol icon (excluding label).
Definition qgis.h:4725
@ Group
Legend group title.
Definition qgis.h:4723
@ Subgroup
Legend subgroup title.
Definition qgis.h:4724
@ Title
Legend title.
Definition qgis.h:4722
@ SymbolLabel
Symbol label (excluding icon).
Definition qgis.h:4726
LegendSyncMode
Legend synchronization mode.
Definition qgis.h:4737
@ VisibleLayers
Synchronize to map layers. The legend will include layers which are included in the linked map only.
Definition qgis.h:4739
@ AllProjectLayers
Synchronize to all project layers.
Definition qgis.h:4738
@ Manual
No automatic synchronization of legend layers. The legend will be manually populated.
Definition qgis.h:4740
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
Definition qgis.h:4680
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
Definition qgis.h:6421
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4699
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
Definition qgis.h:1642
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition qgis.h:2859
@ SynchronousLegendGraphics
Query legend graphics synchronously.
Definition qgis.h:5409
@ UseAdvancedEffects
Enable advanced effects such as blend modes.
Definition qgis.h:5398
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:1506
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:7176
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7504
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7157
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
#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.