QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 <QDomDocument>
34 #include <QDomElement>
35 #include <QPainter>
36 
38  : QgsLayoutItem( layout )
39  , mLegendModel( new QgsLegendModel( layout->project()->layerTreeRoot() ) )
40 {
41 #if 0 //no longer required?
42  connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
43 #endif
44 
45  mTitle = mSettings.title();
46 
47  // Connect to the main layertreeroot.
48  // It serves in "auto update mode" as a medium between the main app legend and this one
49  connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
50 }
51 
53 {
54  return new QgsLayoutItemLegend( layout );
55 }
56 
58 {
60 }
61 
63 {
64  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
65 }
66 
67 QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
68 {
70 }
71 
72 void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
73 {
74  if ( !painter )
75  return;
76 
77  if ( mFilterAskedForUpdate )
78  {
79  mFilterAskedForUpdate = false;
80  doUpdateFilterByMap();
81  }
82 
83  int dpi = painter->device()->logicalDpiX();
84  double dotsPerMM = dpi / 25.4;
85 
86  if ( mLayout )
87  {
89  mSettings.setDpi( dpi );
90  }
91  if ( mMap && mLayout )
92  {
93  mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() );
94 
95  // use a temporary QgsMapSettings to find out real map scale
96  QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
97  QgsRectangle mapExtent = mMap->extent();
98 
99  QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
100  mSettings.setMapScale( ms.scale() );
101  }
102  mInitialMapScaleCalculated = true;
103 
104  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
105  legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
106 
107  //adjust box if width or height is too small
108  if ( mSizeToContents )
109  {
110  QSizeF size = legendRenderer.minimumSize();
111  if ( mForceResize )
112  {
113  mForceResize = false;
114 
115  //set new rect, respecting position mode and data defined size/position
116  QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
117  attemptResize( newSize );
118  }
119  else if ( size.height() > rect().height() || size.width() > rect().width() )
120  {
121  //need to resize box
122  QSizeF targetSize = rect().size();
123  if ( size.height() > targetSize.height() )
124  targetSize.setHeight( size.height() );
125  if ( size.width() > targetSize.width() )
126  targetSize.setWidth( size.width() );
127 
128  QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
129  //set new rect, respecting position mode and data defined size/position
130  attemptResize( newSize );
131  }
132  }
133  QgsLayoutItem::paint( painter, itemStyle, pWidget );
134 }
135 
137 {
138  if ( !mMapUuid.isEmpty() )
139  {
140  setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
141  }
142 }
143 
145 {
147  onAtlasFeature();
148 }
149 
151 {
152  QPainter *painter = context.renderContext().painter();
153  painter->save();
154 
155  // painter is scaled to dots, so scale back to layout units
156  painter->scale( context.renderContext().scaleFactor(), context.renderContext().scaleFactor() );
157 
158  painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
159 
160  if ( !mSizeToContents )
161  {
162  // set a clip region to crop out parts of legend which don't fit
163  QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
164  painter->setClipRect( thisPaintRect );
165  }
166 
167  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
168  legendRenderer.setLegendSize( mSizeToContents ? QSize() : rect().size() );
169 
170  legendRenderer.drawLegend( painter );
171 
172  painter->restore();
173 }
174 
176 {
177  if ( !mSizeToContents )
178  return;
179 
180  if ( !mInitialMapScaleCalculated )
181  {
182  // this is messy - but until we have painted the item we have no knowledge of the current DPI
183  // and so cannot correctly calculate the map scale. This results in incorrect size calculations
184  // for marker symbols with size in map units, causing the legends to initially expand to huge
185  // sizes if we attempt to calculate the box size first.
186  return;
187  }
188 
189  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
190  QSizeF size = legendRenderer.minimumSize();
191  QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
192  if ( size.isValid() )
193  {
194  QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
195  //set new rect, respecting position mode and data defined size/position
196  attemptResize( newSize );
197  }
198 }
199 
201 {
202  mSizeToContents = enabled;
203 }
204 
206 {
207  return mSizeToContents;
208 }
209 
210 void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
211 {
212  mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
213 
214  mCustomLayerTree.reset( rootGroup );
215 }
216 
218 {
219  if ( autoUpdate == autoUpdateModel() )
220  return;
221 
222  setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
223  adjustBoxSize();
224  updateFilterByMap( false );
225 }
226 
227 void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString & )
228 {
229  if ( autoUpdateModel() )
230  {
231  // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
232  // we must then call updateItem to reflect the changes
233  updateFilterByMap( false );
234  }
235 }
236 
238 {
239  return !mCustomLayerTree;
240 }
241 
243 {
244  mLegendFilterByMap = enabled;
245  updateFilterByMap( false );
246 }
247 
248 void QgsLayoutItemLegend::setTitle( const QString &t )
249 {
250  mTitle = t;
251  mSettings.setTitle( t );
252 
253  if ( mLayout && id().isEmpty() )
254  {
255  //notify the model that the display name has changed
256  mLayout->itemsModel()->updateItemDisplayName( this );
257  }
258 }
260 {
261  return mTitle;
262 }
263 
264 Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
265 {
266  return mSettings.titleAlignment();
267 }
268 
269 void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
270 {
271  mSettings.setTitleAlignment( alignment );
272 }
273 
275 {
276  return mSettings.rstyle( s );
277 }
278 
280 {
281  return mSettings.style( s );
282 }
283 
285 {
286  mSettings.setStyle( s, style );
287 }
288 
290 {
291  return mSettings.style( s ).font();
292 }
293 
295 {
296  rstyle( s ).setFont( f );
297 }
298 
300 {
301  rstyle( s ).setMargin( margin );
302 }
303 
305 {
306  rstyle( s ).setMargin( side, margin );
307 }
308 
310 {
311  return mSettings.lineSpacing();
312 }
313 
315 {
316  mSettings.setLineSpacing( spacing );
317 }
318 
320 {
321  return mSettings.boxSpace();
322 }
323 
325 {
326  mSettings.setBoxSpace( s );
327 }
328 
330 {
331  return mSettings.columnSpace();
332 }
333 
335 {
336  mSettings.setColumnSpace( s );
337 }
338 
340 {
341  return mSettings.fontColor();
342 }
343 
345 {
346  mSettings.setFontColor( c );
347 }
348 
350 {
351  return mSettings.symbolSize().width();
352 }
353 
355 {
356  mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
357 }
358 
360 {
361  return mSettings.symbolSize().height();
362 }
363 
365 {
366  mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
367 }
368 
370 {
371  return mSettings.wmsLegendSize().width();
372 }
373 
375 {
376  mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
377 }
378 
380 {
381  return mSettings.wmsLegendSize().height();
382 }
384 {
385  mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
386 }
387 
388 void QgsLayoutItemLegend::setWrapString( const QString &t )
389 {
390  mSettings.setWrapChar( t );
391 }
392 
394 {
395  return mSettings.wrapChar();
396 }
397 
399 {
400  return mColumnCount;
401 }
402 
404 {
405  mColumnCount = c;
406  mSettings.setColumnCount( c );
407 }
408 
410 {
411  return mSettings.splitLayer();
412 }
413 
415 {
416  mSettings.setSplitLayer( s );
417 }
418 
420 {
421  return mSettings.equalColumnWidth();
422 }
423 
425 {
426  mSettings.setEqualColumnWidth( s );
427 }
428 
430 {
431  return mSettings.drawRasterStroke();
432 }
433 
435 {
436  mSettings.setDrawRasterStroke( enabled );
437 }
438 
440 {
441  return mSettings.rasterStrokeColor();
442 }
443 
444 void QgsLayoutItemLegend::setRasterStrokeColor( const QColor &color )
445 {
446  mSettings.setRasterStrokeColor( color );
447 }
448 
450 {
451  return mSettings.rasterStrokeWidth();
452 }
453 
455 {
456  mSettings.setRasterStrokeWidth( width );
457 }
458 
459 
461 {
462  adjustBoxSize();
463  updateFilterByMap( false );
464 }
465 
466 bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
467 {
468 
469  //write general properties
470  legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
471  legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
472  legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
473  legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
474  legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
475 
476  legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
477  legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
478 
479  legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
480  legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
481  legendElem.setAttribute( QStringLiteral( "lineSpacing" ), QString::number( mSettings.lineSpacing() ) );
482 
483  legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
484  legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterStrokeColor() ) );
485  legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
486 
487  legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
488  legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
489  legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
490  legendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
491 
492  legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
493 
494  if ( mMap )
495  {
496  legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
497  }
498 
499  QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
500  legendElem.appendChild( legendStyles );
501 
502  style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc );
503  style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc );
504  style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc );
505  style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc );
506  style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc );
507 
508  if ( mCustomLayerTree )
509  {
510  // if not using auto-update - store the custom layer tree
511  mCustomLayerTree->writeXml( legendElem, context );
512  }
513 
514  if ( mLegendFilterByMap )
515  {
516  legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
517  }
518  legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
519 
520  return true;
521 }
522 
523 bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
524 {
525  //read general properties
526  mTitle = itemElem.attribute( QStringLiteral( "title" ) );
527  mSettings.setTitle( mTitle );
528  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
529  {
530  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
531  }
532  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
533  if ( colCount < 1 ) colCount = 1;
534  mColumnCount = colCount;
535  mSettings.setColumnCount( mColumnCount );
536  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
537  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
538 
539  QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
540  if ( !stylesNodeList.isEmpty() )
541  {
542  QDomNode stylesNode = stylesNodeList.at( 0 );
543  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
544  {
545  QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
547  style.readXml( styleElem, doc );
548  QString name = styleElem.attribute( QStringLiteral( "name" ) );
550  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
551  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
552  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
553  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
554  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
555  else continue;
556  setStyle( s, style );
557  }
558  }
559 
560  //font color
561  QColor fontClr;
562  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
563  mSettings.setFontColor( fontClr );
564 
565  //spaces
566  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
567  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
568 
569  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
570  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
571  mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
572 
573  mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
574  mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
575  mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
576 
577  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
578 
579  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
580 
581  // map
582  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
583 
584  mMapUuid.clear();
585  if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
586  {
587  mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
588  }
589  // disconnect current map
590  setupMapConnections( mMap, false );
591  mMap = nullptr;
592 
593  mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
594 
595  // QGIS >= 2.6
596  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
597  if ( layerTreeElem.isNull() )
598  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
599 
600  if ( !layerTreeElem.isNull() )
601  {
602  std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
603  if ( mLayout )
604  tree->resolveReferences( mLayout->project(), true );
605  setCustomLayerTree( tree.release() );
606  }
607  else
608  setCustomLayerTree( nullptr );
609 
610  return true;
611 }
612 
614 {
615  if ( !id().isEmpty() )
616  {
617  return id();
618  }
619 
620  //if no id, default to portion of title text
621  QString text = mSettings.title();
622  if ( text.isEmpty() )
623  {
624  return tr( "<Legend>" );
625  }
626  if ( text.length() > 25 )
627  {
628  return tr( "%1…" ).arg( text.left( 25 ) );
629  }
630  else
631  {
632  return text;
633  }
634 }
635 
636 
637 void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
638 {
639  if ( !map )
640  return;
641 
642  if ( !connectSlots )
643  {
644  disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
645  disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
646  disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
647  disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
648  }
649  else
650  {
651  connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
652  connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
653  connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
654  connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
655  }
656 }
657 
659 {
660  if ( mMap )
661  {
662  setupMapConnections( mMap, false );
663  }
664 
665  mMap = map;
666 
667  if ( mMap )
668  {
669  setupMapConnections( mMap, true );
670  }
671 
673 }
674 
675 void QgsLayoutItemLegend::invalidateCurrentMap()
676 {
677  setLinkedMap( nullptr );
678 }
679 
681 {
683 
684  bool forceUpdate = false;
685  //updates data defined properties and redraws item to match
686  if ( property == QgsLayoutObject::LegendTitle || property == QgsLayoutObject::AllProperties )
687  {
688  bool ok = false;
689  QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::LegendTitle, context, mTitle, &ok );
690  if ( ok )
691  {
692  mSettings.setTitle( t );
693  forceUpdate = true;
694  }
695  }
697  {
698  bool ok = false;
699  int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::LegendColumnCount, context, mColumnCount, &ok );
700  if ( ok && cols >= 0 )
701  {
702  mSettings.setColumnCount( cols );
703  forceUpdate = true;
704  }
705  }
706  if ( forceUpdate )
707  {
708  adjustBoxSize();
709  update();
710  }
711 
713 }
714 
715 
716 void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
717 {
718  updateFilterByMap( true );
719 }
720 
721 void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
722 {
723  if ( !mMap )
724  return;
725 
726  // map's style has been changed, so make sure to update the legend here
727  if ( mLegendFilterByMap )
728  {
729  // legend is being filtered by map, so we need to re run the hit test too
730  // as the style overrides may also have affected the visible symbols
731  updateFilterByMap( false );
732  }
733  else
734  {
735  mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
736 
737  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mLegendModel->rootGroup()->findLayers() )
738  mLegendModel->refreshLayerLegend( nodeLayer );
739  }
740 
741  adjustBoxSize();
742  updateFilterByMap( false );
743 }
744 
746 {
747  // ask for update
748  // the actual update will take place before the redraw.
749  // This is to avoid multiple calls to the filter
750  mFilterAskedForUpdate = true;
751 
752  if ( redraw )
753  update();
754 }
755 
756 void QgsLayoutItemLegend::doUpdateFilterByMap()
757 {
758  if ( mMap )
759  mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
760  else
761  mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
762 
763 
764  bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
765 
766  if ( mMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
767  {
768  int dpi = mLayout->renderContext().dpi();
769 
770  QgsRectangle requestRectangle = mMap->requestedExtent();
771 
772  QSizeF size( requestRectangle.width(), requestRectangle.height() );
773  size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() * dpi / 25.4;
774 
775  QgsMapSettings ms = mMap->mapSettings( requestRectangle, size, dpi, true );
776 
777  QgsGeometry filterPolygon;
778  if ( mInAtlas )
779  {
780  filterPolygon = mLayout->reportContext().currentGeometry( mMap->crs() );
781  }
782  mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true );
783  }
784  else
785  mLegendModel->setLegendFilterByMap( nullptr );
786 
787  mForceResize = true;
788 }
789 
791 {
792  mFilterOutAtlas = doFilter;
793 }
794 
796 {
797  return mFilterOutAtlas;
798 }
799 
800 void QgsLayoutItemLegend::onAtlasFeature()
801 {
802  if ( !mLayout->reportContext().feature().isValid() )
803  return;
804  mInAtlas = mFilterOutAtlas;
806 }
807 
808 void QgsLayoutItemLegend::onAtlasEnded()
809 {
810  mInAtlas = false;
812 }
813 
814 // -------------------------------------------------------------------------
816 #include "qgsvectorlayer.h"
817 
818 QgsLegendModel::QgsLegendModel( QgsLayerTree *rootNode, QObject *parent )
819  : QgsLayerTreeModel( rootNode, parent )
820 {
823 }
824 
825 QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
826 {
827  // handle custom layer node labels
828  if ( QgsLayerTreeNode *node = index2node( index ) )
829  {
830  if ( QgsLayerTree::isLayer( node ) && ( role == Qt::DisplayRole || role == Qt::EditRole ) && !node->customProperty( QStringLiteral( "legend/title-label" ) ).isNull() )
831  {
832  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
833  QString name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
834  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
835  {
836  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
837  if ( vlayer && vlayer->featureCount() >= 0 )
838  name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
839  }
840  return name;
841  }
842  }
843 
844  return QgsLayerTreeModel::data( index, role );
845 }
846 
847 Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
848 {
849  // make the legend nodes selectable even if they are not by default
850  if ( index2legendNode( index ) )
851  return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
852 
853  return QgsLayerTreeModel::flags( index );
854 }
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
QString wrapChar() const
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setWrapChar(const QString &t)
The class is used as a container of context for various read/write operations on other objects...
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setLegendSize(QSizeF s)
Sets the preferred resulting legend size.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setFontColor(const QColor &color)
Sets the legend font color.
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
A rectangle specified with double values.
Definition: qgsrectangle.h:40
void setTitle(const QString &title)
Sets the legend title.
double boxSpace() const
void setEqualColumnWidth(bool s)
Item model implementation based on layer tree model for layout legend.
int columnCount() const
Returns the legend column count.
void setBoxSpace(double s)
void setMmPerMapUnit(double mmPerMapUnit)
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
double lineSpacing() const
Base class for graphical items within a QgsLayout.
void setSymbolWidth(double width)
Sets the legend symbol width.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings) const
Returns map settings that will be used for drawing of the map.
double scale() const
Returns the calculated map scale.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
double wmsLegendHeight() const
Returns the WMS legend height.
double lineSpacing() const
Returns the spacing in-between lines in layout units.
double rasterStrokeWidth() const
Returns the stroke width (in layout units) for the stroke drawn around raster symbol items...
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...
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...
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
QgsMapLayer * layer() const
Returns the map layer associated with this node.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
double wmsLegendWidth() const
Returns the WMS legend width.
int type() const override
QMap< QString, QString > layerStyleOverrides() const
Returns stored overrides of styles for layers.
void setSymbolSize(QSizeF s)
void extentChanged()
Is emitted when the map&#39;s extent changes.
QgsRectangle requestedExtent() const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
bool splitLayer() const
Returns whether the legend items from a single layer can be split over multiple columns.
double columnSpace() const
Flags flags() const
Returns OR-ed combination of model flags.
Composer legend components style.
void updateFilterByMap(bool redraw=true)
Updates the legend content when filtered by map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void setStyle(QgsLegendStyle::Style component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
void setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map...
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns legend style.
QString title() const
Returns the legend title.
bool equalColumnWidth() const
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
Allow check boxes for legend nodes (if supported by layer&#39;s legend)
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
bool autoUpdateModel() const
Returns whether the legend content should auto update to reflect changes in the project&#39;s layer tree...
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
The QgsMapSettings class contains configuration for rendering of the map.
static QString encodeColor(const QColor &color)
QColor fontColor() const
QString displayName() const override
Gets item display name.
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
The QgsLayerTreeModel class is model implementation for Qt item views framework.
void adjustBoxSize()
Sets the legend&#39;s item bounds to fit the whole legend content.
Layout graphical items for displaying a map.
void setFont(const QFont &font)
The font for this style.
QgsPropertyCollection mDataDefinedProperties
bool legendFilterOutAtlas() const
Returns whether to filter out legend elements outside of the current atlas feature.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item&#39;s content.
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
double symbolWidth() const
Returns the legend symbol width.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setMapScale(double scale)
Sets the legend map scale.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgslayoutitem.h:71
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
QVariant data(const QModelIndex &index, int role) const override
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable legend style.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
double columnSpace() const
Returns the legend column spacing.
QPointer< QgsLayout > mLayout
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
Symbol without label.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct the model based on the given layer tree.
void setTitle(const QString &t)
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
This class is a base class for nodes in a layer tree.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
virtual QString uuid() const
Returns the item identification string.
void setMargin(Side side, double margin)
QgsLayoutItemLegend(QgsLayout *layout)
Constructor for QgsLayoutItemLegend, with the specified parent layout.
void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
void setDpi(int dpi)
Side
Margin side.
void setFontColor(const QColor &c)
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
void setColumnSpace(double s)
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
void updateLegend()
Updates the model and all legend entries.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
QSizeF minimumSize()
Run the layout algorithm and determine the size required for legend.
QColor fontColor() const
Returns the legend font color.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc) const
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void setStyleFont(QgsLegendStyle::Style component, const QFont &font)
Sets the style font for a legend component.
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
void setAutoUpdateModel(bool autoUpdate)
Sets whether the legend content should auto update to reflect changes in the project&#39;s layer tree...
void setColumnCount(int count)
Sets the legend column count.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:43
void setLineSpacing(double s)
double symbolHeight() const
Returns the legend symbol height.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void readXml(const QDomElement &elem, const QDomDocument &doc)
static QgsLayoutItemLegend * create(QgsLayout *layout)
Returns a new legend item for the specified layout.
void setWmsLegendSize(QSizeF s)
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable style.
QgsRectangle extent() const
Returns the current map extent.
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items...
void setUseAdvancedEffects(bool use)
QFont styleFont(QgsLegendStyle::Style component) const
Returns the font settings for a legend component.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
QPainter * painter()
Returns the destination QPainter for the render operation.
bool splitLayer() const
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
Enable advanced effects such as blend modes.
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
void setStyleMargin(QgsLegendStyle::Style component, double margin)
Set the margin for a legend component.
void setColumnCount(int c)
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
QString wrapString() const
Returns the legend text wrapping string.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QString title() const
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
QString id() const
Returns the item&#39;s ID name.
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed...
void setWmsLegendWidth(double width)
Sets the WMS legend width.
QFont font() const
The font for this style.
Item overrides the default layout item painting method.
const QgsLayout * layout() const
Returns the layout the object is attached to.
double boxSpace() const
Returns the legend box space.
A layout item subclass for map legends.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
QSizeF symbolSize() const
void setSymbolHeight(double height)
Sets the legend symbol height.
QSizeF wmsLegendSize() const
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
void setBoxSpace(double space)
Sets the legend box space.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
void changed()
Emitted when the object&#39;s properties change.
QgsLayoutItem::Flags itemFlags() const override
Returns the item&#39;s flags, which indicate how the item behaves.
Represents a vector layer which manages a vector based data sets.
DataDefinedProperty
Data defined properties for different item types.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
QIcon icon() const override
Returns the item&#39;s icon.
Allow reordering with drag&#39;n&#39;drop.
bool equalColumnWidth() const
Returns whether column widths should be equalized.
void setSplitLayer(bool s)
static QColor decodeColor(const QString &str)
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
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...
Layer tree node points to a map layer.
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
All properties for item.