QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgslegendrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslegendrenderer.cpp
3  --------------------------------------
4  Date : July 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgslegendrenderer.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreemodel.h"
21 #include "qgslegendstyle.h"
22 #include "qgsmaplayerlegend.h"
23 #include "qgssymbol.h"
24 #include "qgsrendercontext.h"
25 #include "qgsvectorlayer.h"
27 
28 #include <QJsonObject>
29 #include <QPainter>
30 
31 
32 
34  : mLegendModel( legendModel )
35  , mSettings( settings )
36 {
37 }
38 
40 {
41  std::unique_ptr< QgsRenderContext > tmpContext;
42 
43  if ( !renderContext )
44  {
45  // QGIS 4.0 - make render context mandatory
47  tmpContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( nullptr ) ) );
48  tmpContext->setRendererScale( mSettings.mapScale() );
49  tmpContext->setMapToPixel( QgsMapToPixel( 1 / ( mSettings.mmPerMapUnit() * tmpContext->scaleFactor() ) ) );
50  renderContext = tmpContext.get();
52  }
53 
54  QgsScopedRenderContextPainterSwap nullPainterSwap( *renderContext, nullptr );
55  return paintAndDetermineSize( *renderContext );
56 }
57 
58 void QgsLegendRenderer::drawLegend( QPainter *painter )
59 {
62  QgsScopedRenderContextScaleToMm scaleToMm( context );
63 
64  context.setRendererScale( mSettings.mapScale() );
65  context.setMapToPixel( QgsMapToPixel( 1 / ( mSettings.mmPerMapUnit() * context.scaleFactor() ) ) );
67 
68  paintAndDetermineSize( context );
69 }
70 
72 {
73  QJsonObject json;
74 
75  QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
76  if ( !rootGroup )
77  return json;
78 
79  json = exportLegendToJson( context, rootGroup );
80  json[QStringLiteral( "title" )] = mSettings.title();
81  return json;
82 }
83 
84 QJsonObject QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup )
85 {
86  QJsonObject json;
87  QJsonArray nodes;
88  const QList<QgsLayerTreeNode *> childNodes = nodeGroup->children();
89  for ( QgsLayerTreeNode *node : childNodes )
90  {
91  if ( QgsLayerTree::isGroup( node ) )
92  {
93  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
94  const QModelIndex idx = mLegendModel->node2index( nodeGroup );
95  const QString text = mLegendModel->data( idx, Qt::DisplayRole ).toString();
96 
97  QJsonObject group = exportLegendToJson( context, nodeGroup );
98  group[ QStringLiteral( "type" ) ] = QStringLiteral( "group" );
99  group[ QStringLiteral( "title" ) ] = text;
100  nodes.append( group );
101  }
102  else if ( QgsLayerTree::isLayer( node ) )
103  {
104  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
105 
106  QString text;
107  if ( nodeLegendStyle( nodeLayer ) != QgsLegendStyle::Hidden )
108  {
109  const QModelIndex idx = mLegendModel->node2index( nodeLayer );
110  text = mLegendModel->data( idx, Qt::DisplayRole ).toString();
111  }
112 
113  QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
114 
115  if ( legendNodes.isEmpty() && mLegendModel->legendFilterMapSettings() )
116  continue;
117 
118  if ( legendNodes.count() == 1 )
119  {
120  QJsonObject group = legendNodes.at( 0 )->exportToJson( mSettings, context );
121  group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
122  nodes.append( group );
123  }
124  else if ( legendNodes.count() > 1 )
125  {
126  QJsonObject group;
127  group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
128  group[ QStringLiteral( "title" ) ] = text;
129 
130  QJsonArray symbols;
131  for ( int j = 0; j < legendNodes.count(); j++ )
132  {
133  QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
134  QJsonObject symbol = legendNode->exportToJson( mSettings, context );
135  symbols.append( symbol );
136  }
137  group[ QStringLiteral( "symbols" ) ] = symbols;
138 
139  nodes.append( group );
140  }
141  }
142  }
143 
144  json[QStringLiteral( "nodes" )] = nodes;
145  return json;
146 }
147 
148 QSizeF QgsLegendRenderer::paintAndDetermineSize( QgsRenderContext &context )
149 {
150  QSizeF size( 0, 0 );
151  QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
152  if ( !rootGroup )
153  return size;
154 
155  // temporarily remove painter from context -- we don't need to actually draw anything yet. But we DO need
156  // to send the full render context so that an expression context is available during the size calculation
157  QgsScopedRenderContextPainterSwap noPainter( context, nullptr );
158 
159  QList<LegendComponentGroup> componentGroups = createComponentGroupList( rootGroup, context );
160 
161  const int columnCount = setColumns( componentGroups );
162 
163  QMap< int, double > maxColumnWidths;
164  qreal maxEqualColumnWidth = 0;
165  // another iteration -- this one is required to calculate the maximum item width for each
166  // column. Unfortunately, we can't trust the component group widths at this stage, as they are minimal widths
167  // only. When actually rendering a symbol node, the text is aligned according to the WIDEST
168  // symbol in a column. So that means we can't possibly determine the exact size of legend components
169  // until now. BUUUUUUUUUUUUT. Because everything sucks, we can't even start the actual render of items
170  // at the same time we calculate this -- legend items REQUIRE the REAL width of the columns in order to
171  // correctly align right or center-aligned symbols/text. Bah -- A triple iteration it is!
172  for ( const LegendComponentGroup &group : std::as_const( componentGroups ) )
173  {
174  const QSizeF actualSize = drawGroup( group, context, ColumnContext() );
175  maxEqualColumnWidth = std::max( actualSize.width(), maxEqualColumnWidth );
176  maxColumnWidths[ group.column ] = std::max( actualSize.width(), maxColumnWidths.value( group.column, 0 ) );
177  }
178 
179  if ( columnCount == 1 )
180  {
181  // single column - use the full available width
182  maxEqualColumnWidth = std::max( maxEqualColumnWidth, mLegendSize.width() - 2 * mSettings.boxSpace() );
183  maxColumnWidths[ 0 ] = maxEqualColumnWidth;
184  }
185 
186  //calculate size of title
187  QSizeF titleSize = drawTitle( context, 0 );
188  //add title margin to size of title text
189  titleSize.rwidth() += mSettings.boxSpace() * 2.0;
190  double columnTop = mSettings.boxSpace() + titleSize.height() + mSettings.style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom );
191 
192  noPainter.reset();
193 
194  bool firstInColumn = true;
195  double columnMaxHeight = 0;
196  qreal columnWidth = 0;
197  int column = -1;
198  ColumnContext columnContext;
199  columnContext.left = mSettings.boxSpace();
200  columnContext.right = std::max( mLegendSize.width() - mSettings.boxSpace(), mSettings.boxSpace() );
201  double currentY = columnTop;
202 
203  for ( const LegendComponentGroup &group : std::as_const( componentGroups ) )
204  {
205  if ( group.column > column )
206  {
207  // Switch to next column
208  columnContext.left = group.column > 0 ? columnContext.right + mSettings.columnSpace() : mSettings.boxSpace();
209  columnWidth = mSettings.equalColumnWidth() ? maxEqualColumnWidth : maxColumnWidths.value( group.column );
210  columnContext.right = columnContext.left + columnWidth;
211  currentY = columnTop;
212  column++;
213  firstInColumn = true;
214  }
215  if ( !firstInColumn )
216  {
217  currentY += spaceAboveGroup( group );
218  }
219 
220  drawGroup( group, context, columnContext, currentY );
221 
222  currentY += group.size.height();
223  columnMaxHeight = std::max( currentY - columnTop, columnMaxHeight );
224 
225  firstInColumn = false;
226  }
227  const double totalWidth = columnContext.right + mSettings.boxSpace();
228 
229  size.rheight() = columnTop + columnMaxHeight + mSettings.boxSpace();
230  size.rwidth() = totalWidth;
231  if ( !mSettings.title().isEmpty() )
232  {
233  size.rwidth() = std::max( titleSize.width(), size.width() );
234  }
235 
236  // override the size if it was set by the user
237  if ( mLegendSize.isValid() )
238  {
239  qreal w = std::max( size.width(), mLegendSize.width() );
240  qreal h = std::max( size.height(), mLegendSize.height() );
241  size = QSizeF( w, h );
242  }
243 
244  // Now we have set the correct total item width and can draw the title centered
245  if ( !mSettings.title().isEmpty() )
246  {
247  drawTitle( context, mSettings.boxSpace(), mSettings.titleAlignment(), size.width() );
248  }
249 
250  return size;
251 }
252 
253 void QgsLegendRenderer::widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, const double legendWidth, double &textBoxWidth, double &textBoxLeft ) const
254 {
255  switch ( halignment )
256  {
257  default:
258  textBoxLeft = mSettings.boxSpace();
259  textBoxWidth = legendWidth - 2 * mSettings.boxSpace();
260  break;
261 
262  case Qt::AlignHCenter:
263  {
264  // not sure on this logic, I just moved it -- don't blame me for it being totally obscure!
265  const double centerX = legendWidth / 2;
266  textBoxWidth = ( std::min( static_cast< double >( centerX ), legendWidth - centerX ) - mSettings.boxSpace() ) * 2.0;
267  textBoxLeft = centerX - textBoxWidth / 2.;
268  break;
269  }
270  }
271 }
272 
273 QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context, double indent )
274 {
275  QList<LegendComponentGroup> componentGroups;
276 
277  if ( !parentGroup )
278  return componentGroups;
279 
280  const QList<QgsLayerTreeNode *> childNodes = parentGroup->children();
281  for ( QgsLayerTreeNode *node : childNodes )
282  {
283  if ( QgsLayerTree::isGroup( node ) )
284  {
285  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
286  QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
287  // Update the required indent for the group/subgroup items, starting from the indent accumulated from parent groups
288  double newIndent = indent;
289  if ( style == QLatin1String( "subgroup" ) )
290  {
291  newIndent += mSettings.style( QgsLegendStyle::Subgroup ).indent( );
292  }
293  else
294  {
295  newIndent += mSettings.style( QgsLegendStyle::Group ).indent( );
296  }
297 
298  // Group subitems
299  QList<LegendComponentGroup> subgroups = createComponentGroupList( nodeGroup, context, newIndent );
300 
301  bool hasSubItems = !subgroups.empty();
302 
303  if ( nodeLegendStyle( nodeGroup ) != QgsLegendStyle::Hidden )
304  {
305  LegendComponent component;
306  component.item = node;
307  component.indent = newIndent;
308  component.size = drawGroupTitle( nodeGroup, context );
309 
310  if ( !subgroups.isEmpty() )
311  {
312  // Add internal space between this group title and the next component
313  subgroups[0].size.rheight() += spaceAboveGroup( subgroups[0] );
314  // Prepend this group title to the first group
315  subgroups[0].components.prepend( component );
316  subgroups[0].size.rheight() += component.size.height();
317  subgroups[0].size.rwidth() = std::max( component.size.width(), subgroups[0].size.width() );
318  if ( nodeGroup->customProperty( QStringLiteral( "legend/column-break" ) ).toInt() )
319  subgroups[0].placeColumnBreakBeforeGroup = true;
320  }
321  else
322  {
323  // no subitems, create new group
324  LegendComponentGroup group;
325  group.placeColumnBreakBeforeGroup = nodeGroup->customProperty( QStringLiteral( "legend/column-break" ) ).toInt();
326  group.components.append( component );
327  group.size.rwidth() += component.size.width();
328  group.size.rheight() += component.size.height();
329  group.size.rwidth() = std::max( component.size.width(), group.size.width() );
330  subgroups.append( group );
331  }
332  }
333 
334  if ( hasSubItems ) //leave away groups without content
335  {
336  componentGroups.append( subgroups );
337  }
338 
339  }
340  else if ( QgsLayerTree::isLayer( node ) )
341  {
342  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
343 
344  bool allowColumnSplit = false;
345  switch ( nodeLayer->legendSplitBehavior() )
346  {
348  allowColumnSplit = mSettings.splitLayer();
349  break;
351  allowColumnSplit = true;
352  break;
354  allowColumnSplit = false;
355  break;
356  }
357 
358  LegendComponentGroup group;
359  group.placeColumnBreakBeforeGroup = nodeLayer->customProperty( QStringLiteral( "legend/column-break" ) ).toInt();
360 
361  if ( nodeLegendStyle( nodeLayer ) != QgsLegendStyle::Hidden )
362  {
363  LegendComponent component;
364  component.item = node;
365  component.size = drawLayerTitle( nodeLayer, context );
366  component.indent = indent;
367  group.components.append( component );
368  group.size.rwidth() = component.size.width();
369  group.size.rheight() = component.size.height();
370  }
371 
372  QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
373 
374  // workaround for the issue that "filtering by map" does not remove layer nodes that have no symbols present
375  // on the map. We explicitly skip such layers here. In future ideally that should be handled directly
376  // in the layer tree model
377  if ( legendNodes.isEmpty() && mLegendModel->legendFilterMapSettings() )
378  continue;
379 
380  QList<LegendComponentGroup> layerGroups;
381  layerGroups.reserve( legendNodes.count() );
382 
383  bool groupIsLayerGroup = true;
384 
385  for ( int j = 0; j < legendNodes.count(); j++ )
386  {
387  QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
388 
389  LegendComponent symbolComponent = drawSymbolItem( legendNode, context, ColumnContext(), 0 );
390 
391  const bool forceBreak = legendNode->columnBreak();
392 
393  if ( !allowColumnSplit || j == 0 )
394  {
395  if ( forceBreak )
396  {
397  if ( groupIsLayerGroup )
398  layerGroups.prepend( group );
399  else
400  layerGroups.append( group );
401 
402  group = LegendComponentGroup();
403  group.placeColumnBreakBeforeGroup = true;
404  groupIsLayerGroup = false;
405  }
406 
407  // append to layer group
408  // the width is not correct at this moment, we must align all symbol labels
409  group.size.rwidth() = std::max( symbolComponent.size.width(), group.size.width() );
410  // Add symbol space only if there is already title or another item above
411  if ( !group.components.isEmpty() )
412  {
413  // TODO: for now we keep Symbol and SymbolLabel Top margin in sync
414  group.size.rheight() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
415  }
416  group.size.rheight() += symbolComponent.size.height();
417  symbolComponent.indent = indent;
418  group.components.append( symbolComponent );
419  }
420  else
421  {
422  if ( group.size.height() > 0 )
423  {
424  if ( groupIsLayerGroup )
425  layerGroups.prepend( group );
426  else
427  layerGroups.append( group );
428  group = LegendComponentGroup();
429  groupIsLayerGroup = false;
430  }
431  LegendComponentGroup symbolGroup;
432  symbolGroup.placeColumnBreakBeforeGroup = forceBreak;
433  symbolComponent.indent = indent;
434  symbolGroup.components.append( symbolComponent );
435  symbolGroup.size.rwidth() = symbolComponent.size.width();
436  symbolGroup.size.rheight() = symbolComponent.size.height();
437  layerGroups.append( symbolGroup );
438  }
439  }
440  if ( group.size.height() > 0 )
441  {
442  if ( groupIsLayerGroup )
443  layerGroups.prepend( group );
444  else
445  layerGroups.append( group );
446  }
447  componentGroups.append( layerGroups );
448  }
449  }
450 
451  return componentGroups;
452 }
453 
454 
455 int QgsLegendRenderer::setColumns( QList<LegendComponentGroup> &componentGroups )
456 {
457  // Divide groups to columns
458  double totalHeight = 0;
459  qreal maxGroupHeight = 0;
460  int forcedColumnBreaks = 0;
461  double totalSpaceAboveGroups = 0;
462  for ( const LegendComponentGroup &group : std::as_const( componentGroups ) )
463  {
464  totalHeight += spaceAboveGroup( group );
465  totalSpaceAboveGroups += spaceAboveGroup( group );
466  totalHeight += group.size.height();
467  maxGroupHeight = std::max( group.size.height(), maxGroupHeight );
468 
469  if ( group.placeColumnBreakBeforeGroup )
470  forcedColumnBreaks++;
471  }
472  double averageGroupHeight = ( totalHeight - totalSpaceAboveGroups ) / componentGroups.size();
473 
474  if ( mSettings.columnCount() == 0 && forcedColumnBreaks == 0 )
475  return 0;
476 
477  // the target number of columns allowed is dictated by the number of forced column
478  // breaks OR the manually set column count (whichever is greater!)
479  const int targetNumberColumns = std::max( forcedColumnBreaks + 1, mSettings.columnCount() );
480  const int numberAutoPlacedBreaks = targetNumberColumns - forcedColumnBreaks - 1;
481 
482  // We know height of each group and we have to split them into columns
483  // minimizing max column height. It is sort of bin packing problem, NP-hard.
484  // We are using simple heuristic, brute fore appeared to be to slow,
485  // the number of combinations is N = n!/(k!*(n-k)!) where n = groupCount-1
486  // and k = columnsCount-1
487  double maxColumnHeight = 0;
488  int currentColumn = 0;
489  int currentColumnGroupCount = 0; // number of groups in current column
490  double currentColumnHeight = 0;
491  int autoPlacedBreaks = 0;
492 
493  // Calculate the expected average space between items
494  double averageSpaceAboveGroups = 0;
495  if ( componentGroups.size() > targetNumberColumns )
496  averageSpaceAboveGroups = totalSpaceAboveGroups / ( componentGroups.size() );
497  // Correct the totalHeight using the number of columns because the first item
498  // in each column does not get any space above it
499  totalHeight -= targetNumberColumns * averageSpaceAboveGroups;
500 
501  for ( int i = 0; i < componentGroups.size(); i++ )
502  {
503  LegendComponentGroup group = componentGroups.at( i );
504  double currentHeight = currentColumnHeight;
505  if ( currentColumnGroupCount > 0 )
506  currentHeight += spaceAboveGroup( group );
507  currentHeight += group.size.height();
508 
509  int numberRemainingGroups = componentGroups.size() - i;
510 
511  // Recalc average height for remaining columns including current
512  int numberRemainingColumns = numberAutoPlacedBreaks + 1 - autoPlacedBreaks;
513  double avgColumnHeight = ( currentHeight + numberRemainingGroups * averageGroupHeight + ( numberRemainingGroups - numberRemainingColumns - 1 ) * averageSpaceAboveGroups ) / numberRemainingColumns;
514  // Round up to the next full number of groups to put in one column
515  // This ensures that earlier columns contain more elements than later columns
516  int averageGroupsPerColumn = std::ceil( avgColumnHeight / ( averageGroupHeight + averageSpaceAboveGroups ) );
517  avgColumnHeight = averageGroupsPerColumn * ( averageGroupHeight + averageSpaceAboveGroups ) - averageSpaceAboveGroups;
518 
519  bool canCreateNewColumn = ( currentColumnGroupCount > 0 ) // do not leave empty column
520  && ( currentColumn < targetNumberColumns - 1 ) // must not exceed max number of columns
521  && ( autoPlacedBreaks < numberAutoPlacedBreaks );
522 
523  bool shouldCreateNewColumn = currentHeight > avgColumnHeight // current group height is greater than expected group height
524  && currentColumnGroupCount > 0 // do not leave empty column
525  && currentHeight > maxGroupHeight // no sense to make smaller columns than max group height
526  && currentHeight > maxColumnHeight; // no sense to make smaller columns than max column already created
527 
528  shouldCreateNewColumn |= group.placeColumnBreakBeforeGroup;
529  canCreateNewColumn |= group.placeColumnBreakBeforeGroup;
530 
531  // also should create a new column if the number of items left < number of columns left
532  // in this case we should spread the remaining items out over the remaining columns
533  shouldCreateNewColumn |= ( componentGroups.size() - i < targetNumberColumns - currentColumn );
534 
535  if ( canCreateNewColumn && shouldCreateNewColumn )
536  {
537  // New column
538  currentColumn++;
539  if ( !group.placeColumnBreakBeforeGroup )
540  autoPlacedBreaks++;
541  currentColumnGroupCount = 0;
542  currentColumnHeight = group.size.height();
543  }
544  else
545  {
546  currentColumnHeight = currentHeight;
547  }
548  componentGroups[i].column = currentColumn;
549  currentColumnGroupCount++;
550  maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
551  }
552 
553  // Align labels of symbols for each layer/column to the same labelXOffset
554  QMap<QString, qreal> maxSymbolWidth;
555  for ( int i = 0; i < componentGroups.size(); i++ )
556  {
557  LegendComponentGroup &group = componentGroups[i];
558  for ( int j = 0; j < group.components.size(); j++ )
559  {
560  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
561  {
562  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
563  maxSymbolWidth[key] = std::max( group.components.at( j ).symbolSize.width(), maxSymbolWidth[key] );
564  }
565  }
566  }
567  for ( int i = 0; i < componentGroups.size(); i++ )
568  {
569  LegendComponentGroup &group = componentGroups[i];
570  for ( int j = 0; j < group.components.size(); j++ )
571  {
572  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
573  {
574  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
575  double space = mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right ) +
577  group.components[j].labelXOffset = maxSymbolWidth[key] + space;
578  group.components[j].maxSiblingSymbolWidth = maxSymbolWidth[key];
579  group.components[j].size.rwidth() = maxSymbolWidth[key] + space + group.components.at( j ).labelSize.width();
580  }
581  }
582  }
583  return targetNumberColumns;
584 }
585 
586 QSizeF QgsLegendRenderer::drawTitle( QgsRenderContext &context, double top, Qt::AlignmentFlag halignment, double legendWidth )
587 {
588  QSizeF size( 0, 0 );
589  if ( mSettings.title().isEmpty() )
590  {
591  return size;
592  }
593 
594  QStringList lines = mSettings.splitStringForWrapping( mSettings.title() );
595  double y = top;
596 
597  if ( auto *lPainter = context.painter() )
598  {
599  lPainter->setPen( mSettings.fontColor() );
600  }
601 
602  //calculate width and left pos of rectangle to draw text into
603  double textBoxWidth;
604  double textBoxLeft;
605  widthAndOffsetForTitleText( halignment, legendWidth, textBoxWidth, textBoxLeft );
606 
607  QFont titleFont = mSettings.style( QgsLegendStyle::Title ).font();
608 
609  for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
610  {
611  //last word is not drawn if rectangle width is exactly text width, so add 1
612  //TODO - correctly calculate size of italicized text, since QFontMetrics does not
613  qreal width = mSettings.textWidthMillimeters( titleFont, *titlePart ) + 1;
614  qreal height = mSettings.fontAscentMillimeters( titleFont ) + mSettings.fontDescentMillimeters( titleFont );
615 
616  QRectF r( textBoxLeft, y, textBoxWidth, height );
617 
618  if ( context.painter() )
619  {
620  mSettings.drawText( context.painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
621  }
622 
623  //update max width of title
624  size.rwidth() = std::max( width, size.rwidth() );
625 
626  y += height;
627  if ( titlePart != ( lines.end() - 1 ) )
628  {
629  y += mSettings.lineSpacing();
630  }
631  }
632  size.rheight() = y - top;
633 
634  return size;
635 }
636 
637 
638 double QgsLegendRenderer::spaceAboveGroup( const LegendComponentGroup &group )
639 {
640  if ( group.components.isEmpty() ) return 0;
641 
642  LegendComponent component = group.components.first();
643 
644  if ( QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
645  {
646  return mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Top );
647  }
648  else if ( QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
649  {
650  return mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Top );
651  }
652  else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
653  {
654  // TODO: use Symbol or SymbolLabel Top margin
656  }
657 
658  return 0;
659 }
660 
661 QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRenderContext &context, ColumnContext columnContext, double top )
662 {
663  bool first = true;
664  QSizeF size = QSizeF( group.size );
665  double currentY = top;
666  for ( const LegendComponent &component : std::as_const( group.components ) )
667  {
668  if ( QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
669  {
670  QgsLegendStyle::Style s = nodeLegendStyle( groupItem );
671  if ( s != QgsLegendStyle::Hidden )
672  {
673  if ( !first )
674  {
675  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
676  }
677  QSizeF groupSize;
678  ColumnContext columnContextForItem = columnContext;
679  double indentWidth = component.indent;
680  if ( s == QgsLegendStyle::Subgroup )
681  {
682  // Remove indent - the subgroup items should be indented, not the subgroup title
683  indentWidth -= mSettings.style( QgsLegendStyle::Subgroup ).indent( );
684  }
685  else
686  {
687  // Remove indent - the group items should be indented, not the group title
688  indentWidth -= mSettings.style( QgsLegendStyle::Group ).indent( );
689  }
690  if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignLeft )
691  {
692  columnContextForItem.left += indentWidth;
693  }
694  if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignRight )
695  {
696  columnContextForItem.right -= indentWidth;
697  }
698  groupSize = drawGroupTitle( groupItem, context, columnContextForItem, currentY );
699  size.rwidth() = std::max( groupSize.width(), size.width() );
700  }
701  }
702  else if ( QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
703  {
704  QgsLegendStyle::Style s = nodeLegendStyle( layerItem );
705  if ( s != QgsLegendStyle::Hidden )
706  {
707  if ( !first )
708  {
709  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
710  }
711  QSizeF subGroupSize;
712 
713  ColumnContext columnContextForItem = columnContext;
714  double indentWidth = component.indent;
715  columnContextForItem.left += indentWidth;
716  subGroupSize = drawLayerTitle( layerItem, context, columnContextForItem, currentY );
717  size.rwidth() = std::max( subGroupSize.width(), size.width() );
718  }
719  }
720  else if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
721  {
722  if ( !first )
723  {
724  currentY += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
725  }
726 
727  ColumnContext columnContextForItem = columnContext;
728  double indentWidth = 0;
729  indentWidth = component.indent;
730  if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignLeft )
731  {
732  columnContextForItem.left += indentWidth;
733  }
734  if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignRight )
735  {
736  columnContextForItem.right -= indentWidth;
737  }
738 
739  LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContextForItem, currentY, component.maxSiblingSymbolWidth );
740  // expand width, it may be wider because of label offsets
741  size.rwidth() = std::max( symbolComponent.size.width() + indentWidth, size.width() );
742  }
743  currentY += component.size.height();
744  first = false;
745  }
746  return size;
747 }
748 
749 QgsLegendRenderer::LegendComponent QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QgsRenderContext &context, ColumnContext columnContext, double top, double maxSiblingSymbolWidth )
750 {
752  ctx.context = &context;
753 
754  // add a layer expression context scope
755  QgsExpressionContextScope *layerScope = nullptr;
756  if ( symbolItem->layerNode()->layer() )
757  {
758  layerScope = QgsExpressionContextUtils::layerScope( symbolItem->layerNode()->layer() );
759  context.expressionContext().appendScope( layerScope );
760  }
761 
762  ctx.painter = context.painter();
764  ctx.point = QPointF( columnContext.left, top );
765  ctx.labelXOffset = maxSiblingSymbolWidth;
767 
768  ctx.top = top;
769 
770  ctx.columnLeft = columnContext.left;
771  ctx.columnRight = columnContext.right;
772 
773  switch ( mSettings.symbolAlignment() )
774  {
775  case Qt::AlignLeft:
776  default:
778  break;
779 
780  case Qt::AlignRight:
782  break;
783  }
784 
785  ctx.maxSiblingSymbolWidth = maxSiblingSymbolWidth;
786 
787  if ( const QgsSymbolLegendNode *symbolNode = dynamic_cast< const QgsSymbolLegendNode * >( symbolItem ) )
788  ctx.patchShape = symbolNode->patchShape();
789 
790  ctx.patchSize = symbolItem->userPatchSize();
791 
792  QgsLayerTreeModelLegendNode::ItemMetrics im = symbolItem->draw( mSettings, &ctx );
793 
794  if ( layerScope )
795  delete context.expressionContext().popScope();
796 
797  LegendComponent component;
798  component.item = symbolItem;
799  component.symbolSize = im.symbolSize;
800  component.labelSize = im.labelSize;
801  //QgsDebugMsg( QStringLiteral( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() ));
802  // NOTE -- we hard code left/right margins below, because those are the only ones exposed for use currently.
803  // ideally we could (should?) expose all these margins as settings, and then adapt the below to respect the current symbol/text alignment
804  // and consider the correct margin sides...
805  double width = std::max( static_cast< double >( im.symbolSize.width() ), maxSiblingSymbolWidth )
809  + im.labelSize.width();
810 
811  double height = std::max( im.symbolSize.height(), im.labelSize.height() );
812  component.size = QSizeF( width, height );
813  return component;
814 }
815 
816 QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QgsRenderContext &context, ColumnContext columnContext, double top )
817 {
818  QSizeF size( 0, 0 );
819  QModelIndex idx = mLegendModel->node2index( nodeLayer );
820  QString titleString = mLegendModel->data( idx, Qt::DisplayRole ).toString();
821  //Let the user omit the layer title item by having an empty layer title string
822  if ( titleString.isEmpty() )
823  return size;
824 
825  double y = top;
826 
827  if ( auto *lPainter = context.painter() )
828  lPainter->setPen( mSettings.layerFontColor() );
829 
830  QFont layerFont = mSettings.style( nodeLegendStyle( nodeLayer ) ).font();
831 
832  QgsExpressionContextScope *layerScope = nullptr;
833  if ( nodeLayer->layer() )
834  {
835  layerScope = QgsExpressionContextUtils::layerScope( nodeLayer->layer() );
836  context.expressionContext().appendScope( layerScope );
837  }
838 
839  const QStringList lines = mSettings.evaluateItemText( titleString, context.expressionContext() );
840  int i = 0;
841 
842  const double sideMargin = mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Left );
843  for ( QStringList::ConstIterator layerItemPart = lines.constBegin(); layerItemPart != lines.constEnd(); ++layerItemPart )
844  {
845  y += mSettings.fontAscentMillimeters( layerFont );
846  if ( QPainter *destPainter = context.painter() )
847  {
848  double x = columnContext.left + sideMargin;
849  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() != Qt::AlignLeft )
850  {
851  const double labelWidth = mSettings.textWidthMillimeters( layerFont, *layerItemPart );
852  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignRight )
853  x = columnContext.right - labelWidth - sideMargin;
854  else if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter )
855  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
856  }
857  mSettings.drawText( destPainter, x, y, *layerItemPart, layerFont );
858  }
859  qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart ) + sideMargin *
860  ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
861  size.rwidth() = std::max( width, size.width() );
862  if ( layerItemPart != ( lines.end() - 1 ) )
863  {
864  y += mSettings.lineSpacing();
865  }
866  i++;
867  }
868  size.rheight() = y - top;
869  size.rheight() += mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Side::Bottom );
870 
871  if ( layerScope )
872  delete context.expressionContext().popScope();
873 
874  return size;
875 }
876 
877 QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QgsRenderContext &context, ColumnContext columnContext, double top )
878 {
879  QSizeF size( 0, 0 );
880  QModelIndex idx = mLegendModel->node2index( nodeGroup );
881 
882  double y = top;
883 
884  if ( auto *lPainter = context.painter() )
885  lPainter->setPen( mSettings.fontColor() );
886 
887  QFont groupFont = mSettings.style( nodeLegendStyle( nodeGroup ) ).font();
888 
889  const double sideMargin = mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Left );
890 
891  const QStringList lines = mSettings.evaluateItemText( mLegendModel->data( idx, Qt::DisplayRole ).toString(), context.expressionContext() );
892  for ( QStringList::ConstIterator groupPart = lines.constBegin(); groupPart != lines.constEnd(); ++groupPart )
893  {
894  y += mSettings.fontAscentMillimeters( groupFont );
895 
896  if ( QPainter *destPainter = context.painter() )
897  {
898  double x = columnContext.left + sideMargin;
899  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() != Qt::AlignLeft )
900  {
901  const double labelWidth = mSettings.textWidthMillimeters( groupFont, *groupPart );
902  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignRight )
903  x = columnContext.right - labelWidth - sideMargin;
904  else if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter )
905  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
906  }
907  mSettings.drawText( destPainter, x, y, *groupPart, groupFont );
908  }
909  qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart ) + sideMargin * ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
910  size.rwidth() = std::max( width, size.width() );
911  if ( groupPart != ( lines.end() - 1 ) )
912  {
913  y += mSettings.lineSpacing();
914  }
915  }
916  size.rheight() = y - top + mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Bottom );
917  return size;
918 }
919 
921 {
922  QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
923  if ( style == QLatin1String( "hidden" ) )
924  return QgsLegendStyle::Hidden;
925  else if ( style == QLatin1String( "group" ) )
926  return QgsLegendStyle::Group;
927  else if ( style == QLatin1String( "subgroup" ) )
929 
930  // use a default otherwise
931  if ( QgsLayerTree::isGroup( node ) )
932  return QgsLegendStyle::Group;
933  else if ( QgsLayerTree::isLayer( node ) )
934  {
935  if ( model->legendNodeEmbeddedInParent( QgsLayerTree::toLayer( node ) ) )
936  return QgsLegendStyle::Hidden;
938  }
939 
940  return QgsLegendStyle::Undefined; // should not happen, only if corrupted project file
941 }
942 
944 {
945  return nodeLegendStyle( node, mLegendModel );
946 }
947 
949 {
950  QString str;
951  switch ( style )
952  {
954  str = QStringLiteral( "hidden" );
955  break;
957  str = QStringLiteral( "group" );
958  break;
960  str = QStringLiteral( "subgroup" );
961  break;
962  default:
963  break; // nothing
964  }
965 
966  if ( !str.isEmpty() )
967  node->setCustomProperty( QStringLiteral( "legend/title-style" ), str );
968  else
969  node->removeCustomProperty( QStringLiteral( "legend/title-style" ) );
970 }
971 
973 {
974  paintAndDetermineSize( context );
975 }
976 
QgsLegendSettings::splitStringForWrapping
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
Definition: qgslegendsettings.cpp:99
qgsexpressioncontextutils.h
QgsLegendStyle::Symbol
@ Symbol
Symbol icon (excluding label)
Definition: qgslegendstyle.h:73
QgsLegendSettings::symbolAlignment
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
Definition: qgslegendsettings.h:291
QgsLegendSettings::title
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
Definition: qgslegendsettings.h:55
QgsLegendStyle::Style
Style
Component of legends which can be styled.
Definition: qgslegendstyle.h:53
QgsLegendRenderer::setNodeLegendStyle
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Definition: qgslegendrenderer.cpp:948
QgsLayerTreeNode
This class is a base class for nodes in a layer tree.
Definition: qgslayertreenode.h:75
QgsLegendSettings::columnCount
int columnCount() const
Returns the desired minimum number of columns to show in the legend.
Definition: qgslegendsettings.h:148
QgsLegendSettings::layerFontColor
QColor layerFontColor() const
Returns layer font color, defaults to fontColor()
Definition: qgslegendsettings.h:212
QgsLegendSettings::fontAscentMillimeters
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
Definition: qgslegendsettings.cpp:172
qgslayertreemodellegendnode.h
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:625
QgsLegendSettings::mmPerMapUnit
Q_DECL_DEPRECATED double mmPerMapUnit() const
Definition: qgslegendsettings.cpp:43
QgsLayerTreeModelLegendNode::ItemContext::columnRight
double columnRight
Right side of current legend column.
Definition: qgslayertreemodellegendnode.h:211
QgsExpressionContext::popScope
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
Definition: qgsexpressioncontext.cpp:504
QgsLegendSettings::textWidthMillimeters
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Definition: qgslegendsettings.cpp:158
QgsLegendRenderer::drawLegend
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
Definition: qgslegendrenderer.cpp:58
QgsLayerTreeModelLegendNode::ItemContext::patchSize
QSizeF patchSize
Symbol patch size to render for the node.
Definition: qgslayertreemodellegendnode.h:234
QgsLegendSettings::drawText
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
Definition: qgslegendsettings.cpp:118
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:334
QgsLayerTreeModelLegendNode::ItemContext::point
Q_DECL_DEPRECATED QPointF point
Top-left corner of the legend item.
Definition: qgslayertreemodellegendnode.h:183
QgsLegendSettings::mapScale
Q_DECL_DEPRECATED double mapScale() const
Returns the legend map scale.
Definition: qgslegendsettings.cpp:63
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsLegendSettings::style
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
Definition: qgslegendsettings.h:81
QgsLegendStyle::Bottom
@ Bottom
Bottom side.
Definition: qgslegendstyle.h:70
QgsLayerTreeModelLegendNode::exportToJson
QJsonObject exportToJson(const QgsLegendSettings &settings, const QgsRenderContext &context)
Entry point called from QgsLegendRenderer to do the rendering in a JSON object.
Definition: qgslayertreemodellegendnode.cpp:102
QgsLegendRenderer::nodeLegendStyle
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
Definition: qgslegendrenderer.cpp:920
QgsLayerTreeModelLegendNode::ItemContext::patchShape
QgsLegendPatchShape patchShape
The patch shape to render for the node.
Definition: qgslayertreemodellegendnode.h:225
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:88
QgsLayerTreeModel
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Definition: qgslayertreemodel.h:55
QgsLayerTreeModel::legendNodeEmbeddedInParent
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
Definition: qgslayertreemodel.cpp:1557
QgsLegendStyle::indent
double indent() const
Returns the indent (in mm) of a group or subgroup.
Definition: qgslegendstyle.h:143
QgsLegendStyle::SymbolLabel
@ SymbolLabel
Symbol label (excluding icon)
Definition: qgslegendstyle.h:74
QgsLayerTreeModelLegendNode::ItemContext::labelXOffset
Q_DECL_DEPRECATED double labelXOffset
Offset from the left side where label should start.
Definition: qgslayertreemodellegendnode.h:189
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:77
QgsLayerTreeModelLegendNode::draw
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
Definition: qgslayertreemodellegendnode.cpp:86
QgsLegendStyle::Title
@ Title
Legend title.
Definition: qgslegendstyle.h:70
QgsLegendRenderer::QgsLegendRenderer
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Constructor for QgsLegendRenderer.
Definition: qgslegendrenderer.cpp:33
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
QgsLayerTreeModelLegendNode::ItemMetrics::labelSize
QSizeF labelSize
Definition: qgslayertreemodellegendnode.h:240
QgsLayerTreeModel::node2index
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
Definition: qgslayertreemodel.cpp:429
QgsLayerTreeLayer::UseDefaultLegendSetting
@ UseDefaultLegendSetting
Inherit default legend column splitting setting.
Definition: qgslayertreelayer.h:199
QgsLegendStyle::Undefined
@ Undefined
Should not happen, only if corrupted project file.
Definition: qgslegendstyle.h:68
QgsLayerTreeModelLegendNode::userPatchSize
virtual QSizeF userPatchSize() const
Returns the user (overridden) size for the legend node.
Definition: qgslayertreemodellegendnode.cpp:69
QgsLegendStyle::alignment
Qt::Alignment alignment() const
Returns the alignment for the legend component.
Definition: qgslegendstyle.h:127
QgsLayerTreeLayer
Layer tree node points to a map layer.
Definition: qgslayertreelayer.h:43
QgsLegendSettings::boxSpace
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
Definition: qgslegendsettings.h:96
QgsLayerTreeModel::legendFilterMapSettings
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
Definition: qgslayertreemodel.h:252
QgsLayerTreeGroup
Layer tree group node serves as a container for layers and further groups.
Definition: qgslayertreegroup.h:40
qgsrendercontext.h
QgsLegendSettings
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
Definition: qgslegendsettings.h:38
QgsWms::legendModel
QgsLayerTreeModel * legendModel(const QgsWmsRenderContext &context, QgsLayerTree &tree)
Definition: qgswmsgetlegendgraphics.cpp:248
QgsLegendStyle::Right
@ Right
Right side.
Definition: qgslegendstyle.h:72
QgsLayerTreeLayer::AllowSplittingLegendNodesOverMultipleColumns
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:200
QgsLayerTreeModelLegendNode::ItemContext
Definition: qgslayertreemodellegendnode.h:168
QgsLegendSettings::titleAlignment
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
Definition: qgslegendsettings.h:61
QgsLayerTreeLayer::layer
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Definition: qgslayertreelayer.h:82
qgslayertree.h
QgsLayerTreeModelLegendNode::ItemMetrics::symbolSize
QSizeF symbolSize
Definition: qgslayertreemodellegendnode.h:239
QgsScopedRenderContextScaleToMm
Scoped object for temporary scaling of a QgsRenderContext for millimeter based rendering.
Definition: qgsrendercontext.h:1248
QgsLayerTreeModelLegendNode::columnBreak
virtual bool columnBreak() const
Returns whether a forced column break should occur before the node.
Definition: qgslayertreemodellegendnode.h:158
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:113
QgsLayerTreeModel::rootGroup
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
Definition: qgslayertreemodel.cpp:492
QgsLayerTreeLayer::PreventSplittingLegendNodesOverMultipleColumns
@ PreventSplittingLegendNodesOverMultipleColumns
Prevent splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:201
QgsWms::legendNode
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
Definition: qgswmsgetlegendgraphics.cpp:363
QgsLayerTreeModelLegendNode::ItemContext::maxSiblingSymbolWidth
double maxSiblingSymbolWidth
Largest symbol width, considering all other sibling legend components associated with the current com...
Definition: qgslayertreemodellegendnode.h:218
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:494
qgsvectorlayer.h
QgsScopedRenderContextPainterSwap
Scoped object for temporary replacement of a QgsRenderContext destination painter.
Definition: qgsrendercontext.h:1192
QgsLegendStyle::Left
@ Left
Left side.
Definition: qgslegendstyle.h:71
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:66
QgsLayerTreeModelLegendNode::ItemMetrics
Definition: qgslayertreemodellegendnode.h:237
qgslegendstyle.h
QgsLayerTreeModelLegendNode::layerNode
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
Definition: qgslayertreemodellegendnode.h:102
QgsMapToPixel
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:38
QgsLayerTreeNode::setCustomProperty
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.
Definition: qgslayertreenode.cpp:208
str
#define str(x)
Definition: qgis.cpp:37
QgsLegendStyle::font
QFont font() const
Returns the font used for rendering this legend component.
Definition: qgslegendstyle.h:81
QgsLayerTreeNode::customProperty
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.
Definition: qgslayertreenode.cpp:217
QgsRenderContext::fromQPainter
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
Definition: qgsrendercontext.cpp:143
QgsLayerTreeModelLegendNode::ItemContext::top
double top
Top y-position of legend item.
Definition: qgslayertreemodellegendnode.h:195
QgsLegendSettings::splitLayer
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
Definition: qgslegendsettings.h:165
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:121
qgsmaplayerlegend.h
QgsLegendSettings::equalColumnWidth
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
Definition: qgslegendsettings.h:181
QgsLayerTreeLayer::legendSplitBehavior
LegendNodesSplitBehavior legendSplitBehavior() const
Returns the column split behavior for the node.
Definition: qgslayertreelayer.h:212
qgslayertreemodel.h
QgsLayerTreeModelLegendNode::ItemContext::painter
QPainter * painter
Painter.
Definition: qgslayertreemodellegendnode.h:177
QgsLegendSettings::fontColor
QColor fontColor() const
Returns the font color used for legend items.
Definition: qgslegendsettings.h:197
QgsLegendSettings::lineSpacing
double lineSpacing() const
Returns the line spacing to use between lines of legend text.
Definition: qgslegendsettings.h:373
QgsLegendStyle::Hidden
@ Hidden
Special style, item is hidden including margins around.
Definition: qgslegendstyle.h:69
QgsLegendRenderer::minimumSize
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
Definition: qgslegendrenderer.cpp:39
QgsLegendSettings::fontDescentMillimeters
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
Definition: qgslegendsettings.cpp:179
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:112
QgsLayerTreeModel::layerLegendNodes
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
Definition: qgslayertreemodel.cpp:1572
QgsLayerTreeModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: qgslayertreemodel.cpp:162
QgsSymbolLegendNode
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Definition: qgslayertreemodellegendnode.h:358
QgsLayerTreeNode::removeCustomProperty
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:222
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsLegendStyle::Top
@ Top
Top side.
Definition: qgslegendstyle.h:69
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:56
qgslegendrenderer.h
QgsLegendSettings::columnSpace
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
Definition: qgslegendsettings.h:131
QgsLegendStyle::Group
@ Group
Legend group title.
Definition: qgslegendstyle.h:71
QgsLegendStyle::Subgroup
@ Subgroup
Legend subgroup title.
Definition: qgslegendstyle.h:72
qgssymbol.h
QgsLayerTreeModelLegendNode::ItemContext::columnLeft
double columnLeft
Left side of current legend column.
Definition: qgslayertreemodellegendnode.h:203
QgsLegendRenderer::exportLegendToJson
QJsonObject exportLegendToJson(const QgsRenderContext &context)
Renders the legend in a json object.
Definition: qgslegendrenderer.cpp:71
QgsLayerTreeModelLegendNode::ItemContext::context
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.
Definition: qgslayertreemodellegendnode.h:175
QgsLegendStyle::margin
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
Definition: qgslegendstyle.h:104
QgsLegendSettings::evaluateItemText
QStringList evaluateItemText(const QString &text, const QgsExpressionContext &context) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
Definition: qgslegendsettings.cpp:93
QgsLayerTreeModelLegendNode
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
Definition: qgslayertreemodellegendnode.h:49