QGIS API Documentation  3.27.0-Master (e113457133)
qgslayoutitemlegend.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemlegend.cpp
3  -----------------------
4  begin : October 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include <limits>
18 
19 #include "qgslayoutitemlegend.h"
20 #include "qgslayoutitemregistry.h"
21 #include "qgslayoutitemmap.h"
22 #include "qgslayout.h"
23 #include "qgslayoutmodel.h"
24 #include "qgslayertree.h"
25 #include "qgslayertreemodel.h"
26 #include "qgslegendrenderer.h"
27 #include "qgslegendstyle.h"
28 #include "qgslogger.h"
29 #include "qgsmapsettings.h"
30 #include "qgsproject.h"
31 #include "qgssymbollayerutils.h"
32 #include "qgslayertreeutils.h"
33 #include "qgslayoututils.h"
34 #include "qgsmapthemecollection.h"
35 #include "qgsstyleentityvisitor.h"
36 #include <QDomDocument>
37 #include <QDomElement>
38 #include <QPainter>
39 #include "qgsexpressioncontext.h"
40 
42  : QgsLayoutItem( layout )
43  , mLegendModel( new QgsLegendModel( layout->project()->layerTreeRoot(), this ) )
44 {
45 #if 0 //no longer required?
46  connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
47 #endif
48 
49  mTitle = mSettings.title();
50 
51  // Connect to the main layertreeroot.
52  // It serves in "auto update mode" as a medium between the main app legend and this one
53  connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
54 
55  // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
56  connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [ = ]
57  {
58  invalidateCache();
59  update();
60  } );
61  connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [ = ]
62  {
63  // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
64  // causing an endless loop.
65 
66  // TODO -- the call to QgsLayoutItem::refresh() is probably NOT required!
67  QgsLayoutItem::refresh();
68 
69  // (this one is definitely required)
70  clearLegendCachedData();
71  } );
72 }
73 
75 {
76  return new QgsLayoutItemLegend( layout );
77 }
78 
80 {
82 }
83 
85 {
86  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
87 }
88 
89 QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
90 {
92 }
93 
94 void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
95 {
96  if ( !painter )
97  return;
98 
99  if ( mFilterAskedForUpdate )
100  {
101  mFilterAskedForUpdate = false;
102  doUpdateFilterByMap();
103  }
104 
105  const int dpi = painter->device()->logicalDpiX();
106  const double dotsPerMM = dpi / 25.4;
107 
108  if ( mLayout )
109  {
111  // no longer required, but left set for api stability
112  mSettings.setUseAdvancedEffects( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagUseAdvancedEffects );
113  mSettings.setDpi( dpi );
115  }
116  if ( mMap && mLayout )
117  {
119  // no longer required, but left set for api stability
120  mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() );
122 
123  // use a temporary QgsMapSettings to find out real map scale
124  const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
125  const QgsRectangle mapExtent = mMap->extent();
126 
127  const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
128 
129  // no longer required, but left set for api stability
131  mSettings.setMapScale( ms.scale() );
133  }
134  mInitialMapScaleCalculated = true;
135 
136  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
137  legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
138 
139  //adjust box if width or height is too small
140  if ( mSizeToContents )
141  {
142  QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
144 
145  const QSizeF size = legendRenderer.minimumSize( &context );
146  if ( mForceResize )
147  {
148  mForceResize = false;
149 
150  //set new rect, respecting position mode and data defined size/position
151  const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
152  attemptResize( newSize );
153  }
154  else if ( size.height() > rect().height() || size.width() > rect().width() )
155  {
156  //need to resize box
157  QSizeF targetSize = rect().size();
158  if ( size.height() > targetSize.height() )
159  targetSize.setHeight( size.height() );
160  if ( size.width() > targetSize.width() )
161  targetSize.setWidth( size.width() );
162 
163  const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
164  //set new rect, respecting position mode and data defined size/position
165  attemptResize( newSize );
166  }
167  }
168 
169  QgsLayoutItem::paint( painter, itemStyle, pWidget );
170 }
171 
173 {
174  if ( !mMapUuid.isEmpty() )
175  {
176  setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
177  }
178 }
179 
181 {
183  clearLegendCachedData();
184  onAtlasFeature();
185 }
186 
188 {
189  QPainter *painter = context.renderContext().painter();
190 
191  QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
193 
195 
196  const QgsScopedQPainterState painterState( painter );
197 
198  // painter is scaled to dots, so scale back to layout units
199  painter->scale( rc.scaleFactor(), rc.scaleFactor() );
200 
201  painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
202 
203  if ( !mSizeToContents )
204  {
205  // set a clip region to crop out parts of legend which don't fit
206  const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
207  painter->setClipRect( thisPaintRect );
208  }
209 
210  if ( mLayout )
211  {
212  // no longer required, but left for API compatibility
214  mSettings.setDpi( mLayout->renderContext().dpi() );
216  }
217 
218 
219 
220 
221  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
222  legendRenderer.setLegendSize( rect().size() );
223 
224  legendRenderer.drawLegend( rc );
225 }
226 
228 {
229  if ( !mSizeToContents )
230  return;
231 
232  if ( !mInitialMapScaleCalculated )
233  {
234  // this is messy - but until we have painted the item we have no knowledge of the current DPI
235  // and so cannot correctly calculate the map scale. This results in incorrect size calculations
236  // for marker symbols with size in map units, causing the legends to initially expand to huge
237  // sizes if we attempt to calculate the box size first.
238  return;
239  }
240 
241  QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
243 
244  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
245  const QSizeF size = legendRenderer.minimumSize( &context );
246  QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
247  if ( size.isValid() )
248  {
249  const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
250  //set new rect, respecting position mode and data defined size/position
251  attemptResize( newSize );
252  }
253 }
254 
256 {
257  mSizeToContents = enabled;
258 }
259 
261 {
262  return mSizeToContents;
263 }
264 
265 void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
266 {
267  mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
268 
269  mCustomLayerTree.reset( rootGroup );
270 }
271 
272 
274 {
275  if ( autoUpdate == autoUpdateModel() )
276  return;
277 
278  setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
279  adjustBoxSize();
280  updateFilterByMap( false );
281 }
282 
283 void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
284 {
285  if ( key == QLatin1String( "cached_name" ) )
286  return;
287 
288  if ( autoUpdateModel() )
289  {
290  // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
291  // we must then call updateItem to reflect the changes
292  updateFilterByMap( false );
293  }
294 }
295 
297 {
298  return !mCustomLayerTree;
299 }
300 
302 {
303  mLegendFilterByMap = enabled;
304  updateFilterByMap( false );
305 }
306 
307 void QgsLayoutItemLegend::setTitle( const QString &t )
308 {
309  mTitle = t;
310  mSettings.setTitle( t );
311 
312  if ( mLayout && id().isEmpty() )
313  {
314  //notify the model that the display name has changed
315  mLayout->itemsModel()->updateItemDisplayName( this );
316  }
317 }
319 {
320  return mTitle;
321 }
322 
323 Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
324 {
325  return mSettings.titleAlignment();
326 }
327 
328 void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
329 {
330  mSettings.setTitleAlignment( alignment );
331 }
332 
334 {
335  return mSettings.rstyle( s );
336 }
337 
339 {
340  return mSettings.style( s );
341 }
342 
344 {
345  mSettings.setStyle( s, style );
346 }
347 
349 {
350  return mSettings.style( s ).font();
351 }
352 
354 {
355  rstyle( s ).setFont( f );
356 }
357 
359 {
360  rstyle( s ).setMargin( margin );
361 }
362 
364 {
365  rstyle( s ).setMargin( side, margin );
366 }
367 
369 {
370  return mSettings.lineSpacing();
371 }
372 
374 {
375  mSettings.setLineSpacing( spacing );
376 }
377 
379 {
380  return mSettings.boxSpace();
381 }
382 
384 {
385  mSettings.setBoxSpace( s );
386 }
387 
389 {
390  return mSettings.columnSpace();
391 }
392 
394 {
395  mSettings.setColumnSpace( s );
396 }
397 
399 {
400  return mSettings.fontColor();
401 }
402 
404 {
405  mSettings.setFontColor( c );
406 }
407 
409 {
410  return mSettings.symbolSize().width();
411 }
412 
414 {
415  mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
416 }
417 
419 {
420  return mSettings.maximumSymbolSize();
421 }
422 
424 {
425  mSettings.setMaximumSymbolSize( size );
426 }
427 
429 {
430  return mSettings.minimumSymbolSize();
431 }
432 
434 {
435  mSettings.setMinimumSymbolSize( size );
436 }
437 
438 void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
439 {
440  mSettings.setSymbolAlignment( alignment );
441 }
442 
443 Qt::AlignmentFlag QgsLayoutItemLegend::symbolAlignment() const
444 {
445  return mSettings.symbolAlignment();
446 }
447 
449 {
450  return mSettings.symbolSize().height();
451 }
452 
454 {
455  mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
456 }
457 
459 {
460  return mSettings.wmsLegendSize().width();
461 }
462 
464 {
465  mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
466 }
467 
469 {
470  return mSettings.wmsLegendSize().height();
471 }
473 {
474  mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
475 }
476 
477 void QgsLayoutItemLegend::setWrapString( const QString &t )
478 {
479  mSettings.setWrapChar( t );
480 }
481 
483 {
484  return mSettings.wrapChar();
485 }
486 
488 {
489  return mColumnCount;
490 }
491 
493 {
494  mColumnCount = c;
495  mSettings.setColumnCount( c );
496 }
497 
499 {
500  return mSettings.splitLayer();
501 }
502 
504 {
505  mSettings.setSplitLayer( s );
506 }
507 
509 {
510  return mSettings.equalColumnWidth();
511 }
512 
514 {
515  mSettings.setEqualColumnWidth( s );
516 }
517 
519 {
520  return mSettings.drawRasterStroke();
521 }
522 
524 {
525  mSettings.setDrawRasterStroke( enabled );
526 }
527 
529 {
530  return mSettings.rasterStrokeColor();
531 }
532 
533 void QgsLayoutItemLegend::setRasterStrokeColor( const QColor &color )
534 {
535  mSettings.setRasterStrokeColor( color );
536 }
537 
539 {
540  return mSettings.rasterStrokeWidth();
541 }
542 
544 {
545  mSettings.setRasterStrokeWidth( width );
546 }
547 
548 
550 {
551  adjustBoxSize();
552  updateFilterByMap( false );
553 }
554 
555 bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
556 {
557 
558  //write general properties
559  legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
560  legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
561  legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
562  legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
563  legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
564 
565  legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
566  legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
567 
568  legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
569  legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
570  legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
571  legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
572 
573  legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
574 
575  legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
576  legendElem.setAttribute( QStringLiteral( "lineSpacing" ), QString::number( mSettings.lineSpacing() ) );
577 
578  legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
579  legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterStrokeColor() ) );
580  legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
581 
582  legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
583  legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
584  legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
585  legendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
586 
587  legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
588 
589  if ( mMap )
590  {
591  legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
592  }
593 
594  QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
595  legendElem.appendChild( legendStyles );
596 
597  style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc );
598  style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc );
599  style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc );
600  style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc );
601  style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc );
602 
603  if ( mCustomLayerTree )
604  {
605  // if not using auto-update - store the custom layer tree
606  mCustomLayerTree->writeXml( legendElem, context );
607  }
608 
609  if ( mLegendFilterByMap )
610  {
611  legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
612  }
613  legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
614 
615  return true;
616 }
617 
618 bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
619 {
620  //read general properties
621  mTitle = itemElem.attribute( QStringLiteral( "title" ) );
622  mSettings.setTitle( mTitle );
623  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
624  {
625  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
626  }
627  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
628  if ( colCount < 1 ) colCount = 1;
629  mColumnCount = colCount;
630  mSettings.setColumnCount( mColumnCount );
631  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
632  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
633 
634  const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
635  if ( !stylesNodeList.isEmpty() )
636  {
637  const QDomNode stylesNode = stylesNodeList.at( 0 );
638  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
639  {
640  const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
642  style.readXml( styleElem, doc, context );
643  const QString name = styleElem.attribute( QStringLiteral( "name" ) );
645  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
646  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
647  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
648  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
649  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
650  else continue;
651  setStyle( s, style );
652  }
653  }
654 
655  //font color
656  QColor fontClr;
657  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
658  mSettings.setFontColor( fontClr );
659 
660  //spaces
661  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
662  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
663 
664  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
665  mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
666 
667  mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
668  mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
669 
670  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
671  mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
672 
673  mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
674  mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
675  mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
676 
677  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
678 
679  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
680 
681  // map
682  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
683 
684  mMapUuid.clear();
685  if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
686  {
687  mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
688  }
689  // disconnect current map
690  setupMapConnections( mMap, false );
691  mMap = nullptr;
692 
693  mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
694 
695  // QGIS >= 2.6
696  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
697  if ( layerTreeElem.isNull() )
698  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
699 
700  if ( !layerTreeElem.isNull() )
701  {
702  std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
703  if ( mLayout )
704  tree->resolveReferences( mLayout->project(), true );
705  setCustomLayerTree( tree.release() );
706  }
707  else
708  setCustomLayerTree( nullptr );
709 
710  return true;
711 }
712 
714 {
715  if ( !id().isEmpty() )
716  {
717  return id();
718  }
719 
720  //if no id, default to portion of title text
721  QString text = mSettings.title();
722  if ( text.isEmpty() )
723  {
724  return tr( "<Legend>" );
725  }
726  if ( text.length() > 25 )
727  {
728  return tr( "%1…" ).arg( text.left( 25 ) );
729  }
730  else
731  {
732  return text;
733  }
734 }
735 
736 
737 void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
738 {
739  if ( !map )
740  return;
741 
742  if ( !connectSlots )
743  {
744  disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
745  disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
746  disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
747  disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
748  disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
749  disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
750  }
751  else
752  {
753  connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
754  connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
755  connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
756  connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
757  connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
758  connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
759  }
760 }
761 
763 {
764  if ( mMap )
765  {
766  setupMapConnections( mMap, false );
767  }
768 
769  mMap = map;
770 
771  if ( mMap )
772  {
773  setupMapConnections( mMap, true );
774  mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
775  }
776 
778 }
779 
780 void QgsLayoutItemLegend::invalidateCurrentMap()
781 {
782  setLinkedMap( nullptr );
783 }
784 
786 {
788 
789  bool forceUpdate = false;
790  //updates data defined properties and redraws item to match
791  if ( property == QgsLayoutObject::LegendTitle || property == QgsLayoutObject::AllProperties )
792  {
793  bool ok = false;
794  const QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::LegendTitle, context, mTitle, &ok );
795  if ( ok )
796  {
797  mSettings.setTitle( t );
798  forceUpdate = true;
799  }
800  }
802  {
803  bool ok = false;
804  const int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::LegendColumnCount, context, mColumnCount, &ok );
805  if ( ok && cols >= 0 )
806  {
807  mSettings.setColumnCount( cols );
808  forceUpdate = true;
809  }
810  }
811  if ( forceUpdate )
812  {
813  adjustBoxSize();
814  update();
815  }
816 
818 }
819 
820 
821 void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
822 {
823  updateFilterByMap( true );
824 }
825 
826 void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
827 {
828  mLegendModel->setLayerStyleOverrides( overrides );
829  const QList< QgsLayerTreeLayer * > layers = mLegendModel->rootGroup()->findLayers();
830  for ( QgsLayerTreeLayer *nodeLayer : layers )
831  mLegendModel->refreshLayerLegend( nodeLayer );
832 
833 }
834 
835 void QgsLayoutItemLegend::clearLegendCachedData()
836 {
837  std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
838  clearNodeCache = [&]( QgsLayerTreeNode * node )
839  {
840  mLegendModel->clearCachedData( node );
841  if ( QgsLayerTree::isGroup( node ) )
842  {
843  QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
844  const QList< QgsLayerTreeNode * > children = group->children();
845  for ( QgsLayerTreeNode *child : children )
846  {
847  clearNodeCache( child );
848  }
849  }
850  };
851 
852  clearNodeCache( mLegendModel->rootGroup() );
853 }
854 
855 void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
856 {
857  if ( !mMap )
858  return;
859 
860  // map's style has been changed, so make sure to update the legend here
861  if ( mLegendFilterByMap )
862  {
863  // legend is being filtered by map, so we need to re run the hit test too
864  // as the style overrides may also have affected the visible symbols
865  updateFilterByMap( false );
866  }
867  else
868  {
869  setModelStyleOverrides( mMap->layerStyleOverrides() );
870  }
871 
872  adjustBoxSize();
873 
874  updateFilterByMap( false );
875 }
876 
877 void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
878 {
879  if ( mThemeName == theme )
880  return;
881 
882  mThemeName = theme;
883 
884  // map's theme has been changed, so make sure to update the legend here
885  if ( mLegendFilterByMap )
886  {
887  // legend is being filtered by map, so we need to re run the hit test too
888  // as the style overrides may also have affected the visible symbols
889  updateFilterByMap( false );
890  }
891  else
892  {
893  if ( mThemeName.isEmpty() )
894  {
895  setModelStyleOverrides( QMap<QString, QString>() );
896  }
897  else
898  {
899  // get style overrides for theme
900  const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
901  setModelStyleOverrides( overrides );
902  }
903  }
904 
905  adjustBoxSize();
906 
908 }
909 
911 {
912  // ask for update
913  // the actual update will take place before the redraw.
914  // This is to avoid multiple calls to the filter
915  mFilterAskedForUpdate = true;
916 
917  if ( redraw )
918  update();
919 }
920 
921 void QgsLayoutItemLegend::doUpdateFilterByMap()
922 {
923  if ( mMap )
924  {
925  if ( !mThemeName.isEmpty() )
926  {
927  // get style overrides for theme
928  const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
929  mLegendModel->setLayerStyleOverrides( overrides );
930  }
931  else
932  {
933  mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
934  }
935  }
936  else
937  mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
938 
939 
940  const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
941 
942  if ( mMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
943  {
944  const double dpi = mLayout->renderContext().dpi();
945 
946  const QgsRectangle requestRectangle = mMap->requestedExtent();
947 
948  QSizeF size( requestRectangle.width(), requestRectangle.height() );
949  size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() * dpi / 25.4;
950 
951  const QgsMapSettings ms = mMap->mapSettings( requestRectangle, size, dpi, true );
952 
953  QgsGeometry filterPolygon;
954  if ( mInAtlas )
955  {
956  filterPolygon = mLayout->reportContext().currentGeometry( mMap->crs() );
957  }
958  mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true );
959  }
960  else
961  mLegendModel->setLegendFilterByMap( nullptr );
962 
963  clearLegendCachedData();
964  mForceResize = true;
965 }
966 
968 {
969  return mThemeName;
970 }
971 
973 {
974  mFilterOutAtlas = doFilter;
975 }
976 
978 {
979  return mFilterOutAtlas;
980 }
981 
982 void QgsLayoutItemLegend::onAtlasFeature()
983 {
984  if ( !mLayout || !mLayout->reportContext().feature().isValid() )
985  return;
986  mInAtlas = mFilterOutAtlas;
988 }
989 
990 void QgsLayoutItemLegend::onAtlasEnded()
991 {
992  mInAtlas = false;
994 }
995 
997 {
999 
1000  // We only want the last scope from the map's expression context, as this contains
1001  // the map specific variables. We don't want the rest of the map's context, because that
1002  // will contain duplicate global, project, layout, etc scopes.
1003  if ( mMap )
1004  context.appendScope( mMap->createExpressionContext().popScope() );
1005 
1006  QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1007 
1008  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
1009  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
1010  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
1011  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
1012  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
1013  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
1014 
1015  context.appendScope( scope );
1016  return context;
1017 }
1018 
1020 {
1021  return MustPlaceInOwnLayer;
1022 }
1023 
1025 {
1026  std::function<bool( QgsLayerTreeGroup *group ) >visit;
1027 
1028  visit = [ =, &visit]( QgsLayerTreeGroup * group ) -> bool
1029  {
1030  const QList<QgsLayerTreeNode *> childNodes = group->children();
1031  for ( QgsLayerTreeNode *node : childNodes )
1032  {
1033  if ( QgsLayerTree::isGroup( node ) )
1034  {
1035  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1036  if ( !visit( nodeGroup ) )
1037  return false;
1038  }
1039  else if ( QgsLayerTree::isLayer( node ) )
1040  {
1041  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1042  if ( !nodeLayer->patchShape().isNull() )
1043  {
1044  QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1045  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1046  return false;
1047  }
1048  const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1049  for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1050  {
1051  if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1052  {
1053  if ( !symbolNode->patchShape().isNull() )
1054  {
1055  QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1056  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1057  return false;
1058  }
1059  }
1060  }
1061  }
1062  }
1063  return true;
1064  };
1065  return visit( mLegendModel->rootGroup( ) );
1066 }
1067 
1068 
1069 // -------------------------------------------------------------------------
1071 #include "qgsvectorlayer.h"
1072 #include "qgsmaplayerlegend.h"
1073 
1075  : QgsLayerTreeModel( rootNode, parent )
1076  , mLayoutLegend( layout )
1077 {
1080  connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1081 }
1082 
1084  : QgsLayerTreeModel( rootNode )
1085  , mLayoutLegend( layout )
1086 {
1089  connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1090 }
1091 
1092 QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1093 {
1094  // handle custom layer node labels
1095 
1096  QgsLayerTreeNode *node = index2node( index );
1097  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1098  if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1099  {
1100  QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
1101  if ( !name.isEmpty() )
1102  return name;
1103 
1104  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1105 
1106  //finding the first label that is stored
1107  name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1108  if ( name.isEmpty() )
1109  name = nodeLayer->name();
1110  if ( name.isEmpty() )
1111  name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1112  if ( name.isEmpty() )
1113  name = node->name();
1114  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
1115  {
1116  if ( vlayer && vlayer->featureCount() >= 0 )
1117  {
1118  name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
1119  node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1120  return name;
1121  }
1122  }
1123 
1124  const bool evaluate = ( vlayer && !nodeLayer->labelExpression().isEmpty() ) || name.contains( "[%" );
1125  if ( evaluate )
1126  {
1127  QgsExpressionContext expressionContext;
1128  if ( vlayer )
1129  {
1130  connect( vlayer, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsLegendModel::forceRefresh, Qt::UniqueConnection );
1131  // counting is done here to ensure that a valid vector layer needs to be evaluated, count is used to validate previous count or update the count if invalidated
1132  vlayer->countSymbolFeatures();
1133  }
1134 
1135  if ( mLayoutLegend )
1136  expressionContext = mLayoutLegend->createExpressionContext();
1137 
1138  const QList<QgsLayerTreeModelLegendNode *> legendnodes = layerLegendNodes( nodeLayer, false );
1139  if ( legendnodes.count() > 1 ) // evaluate all existing legend nodes but leave the name for the legend evaluator
1140  {
1141  for ( QgsLayerTreeModelLegendNode *treenode : legendnodes )
1142  {
1143  if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( treenode ) )
1144  symnode->evaluateLabel( expressionContext );
1145  }
1146  }
1147  else if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( legendnodes.first() ) )
1148  name = symnode->evaluateLabel( expressionContext );
1149  }
1150  node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1151  return name;
1152  }
1153  return QgsLayerTreeModel::data( index, role );
1154 }
1155 
1156 Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1157 {
1158  // make the legend nodes selectable even if they are not by default
1159  if ( index2legendNode( index ) )
1160  return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1161 
1162  return QgsLayerTreeModel::flags( index );
1163 }
1164 
1165 QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1166 {
1167  if ( !mLegend.contains( nodeLayer ) )
1168  return QList<QgsLayerTreeModelLegendNode *>();
1169 
1170  const LayerLegendData &data = mLegend[nodeLayer];
1171  QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1172  if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1173  lst.prepend( data.embeddedNodeInParent );
1174  return lst;
1175 }
1176 
1178 {
1179  node->removeCustomProperty( QStringLiteral( "cached_name" ) );
1180 }
1181 
1182 void QgsLegendModel::forceRefresh()
1183 {
1184  emit refreshLegend();
1185 }
1186 
1187 
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
Layer tree group node serves as a container for layers and further groups.
Layer tree node points to a map layer.
QString labelExpression() const
Returns the expression member of the LayerTreeNode.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Flags flags() const
Returns OR-ed combination of model flags.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
This class is a base class for nodes in a layer tree.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
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.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
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.
Definition: qgslayertree.h:33
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
A layout item subclass for map legends.
bool autoUpdateModel() const
Returns whether the legend content should auto update to reflect changes in the project's layer tree.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setStyleMargin(QgsLegendStyle::Style component, double margin)
Set the margin for a legend component.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QString title() const
Returns the legend title.
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
void adjustBoxSize()
Sets the legend's item bounds to fit the whole legend content.
double wmsLegendWidth() const
Returns the WMS legend width.
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setBoxSpace(double space)
Sets the legend box space.
bool splitLayer() const
Returns whether the legend items from a single layer can be split over multiple columns.
double symbolHeight() const
Returns the legend symbol height.
void updateFilterByMap(bool redraw=true)
Updates the legend content when filtered by map.
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
QFont styleFont(QgsLegendStyle::Style component) const
Returns the font settings for a legend component.
static QgsLayoutItemLegend * create(QgsLayout *layout)
Returns a new legend item for the specified layout.
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
void setSymbolWidth(double width)
Sets the legend symbol width.
QString wrapString() const
Returns the legend text wrapping string.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
void updateLegend()
Updates the model and all legend entries.
QgsLayoutItemLegend(QgsLayout *layout)
Constructor for QgsLayoutItemLegend, with the specified parent layout.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable legend style.
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.
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.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
double symbolWidth() const
Returns the legend symbol width.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
int type() const override
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool legendFilterOutAtlas() const
Returns whether to filter out legend elements outside of the current atlas feature.
void setStyle(QgsLegendStyle::Style component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
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 setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map.
void setSymbolHeight(double height)
Sets the legend symbol height.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setAutoUpdateModel(bool autoUpdate)
Sets whether the legend content should auto update to reflect changes in the project's layer tree.
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
QIcon icon() const override
Returns the item's icon.
double wmsLegendHeight() const
Returns the WMS legend height.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in layout units) for the stroke drawn around raster symbol items.
double lineSpacing() const
Returns the spacing in-between lines in layout units.
void setStyleFont(QgsLegendStyle::Style component, const QFont &font)
Sets the style font for a legend component.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns legend style.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
QString themeName() const
Returns the name of the theme currently linked to the legend.
void setColumnCount(int count)
Sets the legend column count.
bool equalColumnWidth() const
Returns whether column widths should be equalized.
int columnCount() const
Returns the legend column count.
bool legendFilterByMapEnabled() const
Find out whether legend items are filtered to show just the ones visible in the associated map.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings) const
Returns map settings that will be used for drawing of the map.
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed...
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QMap< QString, QString > layerStyleOverrides() const
Returns stored overrides of styles for layers.
QgsRectangle requestedExtent() const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
QgsRectangle extent() const
Returns the current map extent.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
Base class for graphical items within a QgsLayout.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item's content.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
@ FlagOverridesPaint
Item overrides the default layout item painting method.
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.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
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.
@ LegendTitle
Legend title.
@ AllProperties
All properties for item.
@ LegendColumnCount
Legend column count.
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:41
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
Item model implementation based on layer tree model for layout legend.
void clearCachedData(QgsLayerTreeNode *node) const
Clears any previously cached data for the specified node.
void refreshLegend()
Emitted to refresh the legend.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr, QgsLayoutItemLegend *layout=nullptr)
Construct the model based on the given layer tree.
QVariant data(const QModelIndex &index, int role) const override
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false) const
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
void setLegendSize(QSizeF s)
Sets the preferred resulting legend size.
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QString wrapChar() const
Returns the string used as a wrapping character.
void setFontColor(const QColor &c)
Sets the font color used for legend items.
void setWrapChar(const QString &t)
Sets a string to use as a wrapping character.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
Sets the style for a legend component.
void setColumnSpace(double s)
Sets the margin space between adjacent columns (in millimeters).
Q_DECL_DEPRECATED void setMapScale(double scale)
Sets the legend map scale.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
void setTitle(const QString &t)
Sets the title for the legend, which will be rendered above all legend items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
QSizeF wmsLegendSize() const
Returns the size (in millimeters) of WMS legend graphics shown in the legend.
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items.
void setLineSpacing(double s)
Sets the line spacing to use between lines of legend text.
void setColumnCount(int c)
Sets the desired minimum number of columns to show in the legend.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
Q_DECL_DEPRECATED void setMmPerMapUnit(double mmPerMapUnit)
Q_DECL_DEPRECATED void setDpi(int dpi)
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns modifiable reference to the style for a legend component.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
QSizeF symbolSize() const
Returns the default symbol size (in millimeters) used for legend items.
QColor fontColor() const
Returns the font color used for legend items.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED void setUseAdvancedEffects(bool use)
void setSplitLayer(bool s)
Sets whether layer components can be split over multiple columns.
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
void setEqualColumnWidth(bool s)
Sets whether all columns should have equal widths.
void setBoxSpace(double s)
Sets the legend box space (in millimeters), which is the empty margin around the inside of the legend...
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
double lineSpacing() const
Returns the line spacing to use between lines of legend text.
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
void setSymbolSize(QSizeF s)
Sets the default symbol size (in millimeters) used for legend items.
void setWmsLegendSize(QSizeF s)
Sets the desired size (in millimeters) of WMS legend graphics shown in the legend.
Contains detailed styling information relating to how a layout legend should be rendered.
QFont font() const
Returns the font used for rendering this legend component.
void setMargin(Side side, double margin)
Sets the margin (in mm) for the specified side of the component.
Side
Margin sides.
void readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads the component's style definition from an XML element.
Style
Component of legends which can be styled.
@ Group
Legend group title.
@ Symbol
Symbol icon (excluding label)
@ Subgroup
Legend subgroup title.
@ Title
Legend title.
@ SymbolLabel
Symbol label (excluding icon)
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.
The QgsMapSettings class contains configuration for rendering of the map.
double scale() const
Returns the calculated map scale.
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A legend patch shape entity for QgsStyle databases.
Definition: qgsstyle.h:1465
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
@ LayoutMillimeters
Millimeters.
Definition: qgsunittypes.h:183
Represents a vector layer which manages a vector based data sets.
QgsVectorLayerFeatureCounter * countSymbolFeatures(bool storeSymbolFids=false)
Count features for symbols.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void symbolFeatureCountMapChanged()
Emitted when the feature count for symbols on this layer has been recalculated.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2815
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2814
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
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.