QGIS API Documentation  3.2.0-Bonn (bc43194)
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 "qgsvectorlayer.h"
25 
26 #include <QPainter>
27 
28 
29 
31  : mLegendModel( legendModel )
32  , mSettings( settings )
33 {
34 }
35 
37 {
38  return paintAndDetermineSize( nullptr );
39 }
40 
41 void QgsLegendRenderer::drawLegend( QPainter *painter )
42 {
43  paintAndDetermineSize( painter );
44 }
45 
46 
47 QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter )
48 {
49  QSizeF size( 0, 0 );
50  QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
51  if ( !rootGroup ) return size;
52 
53  QList<Atom> atomList = createAtomList( rootGroup, mSettings.splitLayer() );
54 
55  setColumns( atomList );
56 
57  qreal maxColumnWidth = 0;
58  if ( mSettings.equalColumnWidth() )
59  {
60  Q_FOREACH ( const Atom &atom, atomList )
61  {
62  maxColumnWidth = std::max( atom.size.width(), maxColumnWidth );
63  }
64  }
65 
66  //calculate size of title
67  QSizeF titleSize = drawTitle();
68  //add title margin to size of title text
69  titleSize.rwidth() += mSettings.boxSpace() * 2.0;
70  double columnTop = mSettings.boxSpace() + titleSize.height() + mSettings.style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom );
71 
72  QPointF point( mSettings.boxSpace(), columnTop );
73  bool firstInColumn = true;
74  double columnMaxHeight = 0;
75  qreal columnWidth = 0;
76  int column = 0;
77  Q_FOREACH ( const Atom &atom, atomList )
78  {
79  if ( atom.column > column )
80  {
81  // Switch to next column
82  if ( mSettings.equalColumnWidth() )
83  {
84  point.rx() += mSettings.columnSpace() + maxColumnWidth;
85  }
86  else
87  {
88  point.rx() += mSettings.columnSpace() + columnWidth;
89  }
90  point.ry() = columnTop;
91  columnWidth = 0;
92  column++;
93  firstInColumn = true;
94  }
95  if ( !firstInColumn )
96  {
97  point.ry() += spaceAboveAtom( atom );
98  }
99 
100  QSizeF atomSize = drawAtom( atom, painter, point );
101  columnWidth = std::max( atomSize.width(), columnWidth );
102 
103  point.ry() += atom.size.height();
104  columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight );
105 
106  firstInColumn = false;
107  }
108  point.rx() += columnWidth + mSettings.boxSpace();
109 
110  size.rheight() = columnTop + columnMaxHeight + mSettings.boxSpace();
111  size.rwidth() = point.x();
112  if ( !mSettings.title().isEmpty() )
113  {
114  size.rwidth() = std::max( titleSize.width(), size.width() );
115  }
116 
117  // override the size if it was set by the user
118  if ( mLegendSize.isValid() )
119  {
120  qreal w = std::max( size.width(), mLegendSize.width() );
121  qreal h = std::max( size.height(), mLegendSize.height() );
122  size = QSizeF( w, h );
123  }
124 
125  // Now we have set the correct total item width and can draw the title centered
126  if ( !mSettings.title().isEmpty() )
127  {
128  if ( mSettings.titleAlignment() == Qt::AlignLeft )
129  {
130  point.rx() = mSettings.boxSpace();
131  }
132  else if ( mSettings.titleAlignment() == Qt::AlignHCenter )
133  {
134  point.rx() = size.width() / 2;
135  }
136  else
137  {
138  point.rx() = size.width() - mSettings.boxSpace();
139  }
140  point.ry() = mSettings.boxSpace();
141  drawTitle( painter, point, mSettings.titleAlignment(), size.width() );
142  }
143 
144  return size;
145 }
146 
147 
148 QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList( QgsLayerTreeGroup *parentGroup, bool splitLayer )
149 {
150  QList<Atom> atoms;
151 
152  if ( !parentGroup ) return atoms;
153 
154  Q_FOREACH ( QgsLayerTreeNode *node, parentGroup->children() )
155  {
156  if ( QgsLayerTree::isGroup( node ) )
157  {
158  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
159 
160  // Group subitems
161  QList<Atom> groupAtoms = createAtomList( nodeGroup, splitLayer );
162  bool hasSubItems = !groupAtoms.empty();
163 
164  if ( nodeLegendStyle( nodeGroup ) != QgsLegendStyle::Hidden )
165  {
166  Nucleon nucleon;
167  nucleon.item = node;
168  nucleon.size = drawGroupTitle( nodeGroup );
169 
170  if ( !groupAtoms.isEmpty() )
171  {
172  // Add internal space between this group title and the next nucleon
173  groupAtoms[0].size.rheight() += spaceAboveAtom( groupAtoms[0] );
174  // Prepend this group title to the first atom
175  groupAtoms[0].nucleons.prepend( nucleon );
176  groupAtoms[0].size.rheight() += nucleon.size.height();
177  groupAtoms[0].size.rwidth() = std::max( nucleon.size.width(), groupAtoms[0].size.width() );
178  }
179  else
180  {
181  // no subitems, append new atom
182  Atom atom;
183  atom.nucleons.append( nucleon );
184  atom.size.rwidth() += nucleon.size.width();
185  atom.size.rheight() += nucleon.size.height();
186  atom.size.rwidth() = std::max( nucleon.size.width(), atom.size.width() );
187  groupAtoms.append( atom );
188  }
189  }
190 
191  if ( hasSubItems ) //leave away groups without content
192  {
193  atoms.append( groupAtoms );
194  }
195 
196  }
197  else if ( QgsLayerTree::isLayer( node ) )
198  {
199  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
200 
201  Atom atom;
202 
203  if ( nodeLegendStyle( nodeLayer ) != QgsLegendStyle::Hidden )
204  {
205  Nucleon nucleon;
206  nucleon.item = node;
207  nucleon.size = drawLayerTitle( nodeLayer );
208  atom.nucleons.append( nucleon );
209  atom.size.rwidth() = nucleon.size.width();
210  atom.size.rheight() = nucleon.size.height();
211  }
212 
213  QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
214 
215  // workaround for the issue that "filtering by map" does not remove layer nodes that have no symbols present
216  // on the map. We explicitly skip such layers here. In future ideally that should be handled directly
217  // in the layer tree model
218  if ( legendNodes.isEmpty() && mLegendModel->legendFilterMapSettings() )
219  continue;
220 
221  QList<Atom> layerAtoms;
222 
223  for ( int j = 0; j < legendNodes.count(); j++ )
224  {
225  QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
226 
227  Nucleon symbolNucleon = drawSymbolItem( legendNode );
228 
229  if ( !mSettings.splitLayer() || j == 0 )
230  {
231  // append to layer atom
232  // the width is not correct at this moment, we must align all symbol labels
233  atom.size.rwidth() = std::max( symbolNucleon.size.width(), atom.size.width() );
234  // Add symbol space only if there is already title or another item above
235  if ( !atom.nucleons.isEmpty() )
236  {
237  // TODO: for now we keep Symbol and SymbolLabel Top margin in sync
238  atom.size.rheight() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
239  }
240  atom.size.rheight() += symbolNucleon.size.height();
241  atom.nucleons.append( symbolNucleon );
242  }
243  else
244  {
245  Atom symbolAtom;
246  symbolAtom.nucleons.append( symbolNucleon );
247  symbolAtom.size.rwidth() = symbolNucleon.size.width();
248  symbolAtom.size.rheight() = symbolNucleon.size.height();
249  layerAtoms.append( symbolAtom );
250  }
251  }
252  layerAtoms.prepend( atom );
253  atoms.append( layerAtoms );
254  }
255  }
256 
257  return atoms;
258 }
259 
260 
261 void QgsLegendRenderer::setColumns( QList<Atom> &atomList )
262 {
263  if ( mSettings.columnCount() == 0 ) return;
264 
265  // Divide atoms to columns
266  double totalHeight = 0;
267  qreal maxAtomHeight = 0;
268  Q_FOREACH ( const Atom &atom, atomList )
269  {
270  totalHeight += spaceAboveAtom( atom );
271  totalHeight += atom.size.height();
272  maxAtomHeight = std::max( atom.size.height(), maxAtomHeight );
273  }
274 
275  // We know height of each atom and we have to split them into columns
276  // minimizing max column height. It is sort of bin packing problem, NP-hard.
277  // We are using simple heuristic, brute fore appeared to be to slow,
278  // the number of combinations is N = n!/(k!*(n-k)!) where n = atomsCount-1
279  // and k = columnsCount-1
280  double maxColumnHeight = 0;
281  int currentColumn = 0;
282  int currentColumnAtomCount = 0; // number of atoms in current column
283  double currentColumnHeight = 0;
284  double closedColumnsHeight = 0;
285 
286  for ( int i = 0; i < atomList.size(); i++ )
287  {
288  // Recalc average height for remaining columns including current
289  double avgColumnHeight = ( totalHeight - closedColumnsHeight ) / ( mSettings.columnCount() - currentColumn );
290 
291  Atom atom = atomList.at( i );
292  double currentHeight = currentColumnHeight;
293  if ( currentColumnAtomCount > 0 )
294  currentHeight += spaceAboveAtom( atom );
295  currentHeight += atom.size.height();
296 
297  bool canCreateNewColumn = ( currentColumnAtomCount > 0 ) // do not leave empty column
298  && ( currentColumn < mSettings.columnCount() - 1 ); // must not exceed max number of columns
299 
300  bool shouldCreateNewColumn = ( currentHeight - avgColumnHeight ) > atom.size.height() / 2 // center of current atom is over average height
301  && currentColumnAtomCount > 0 // do not leave empty column
302  && currentHeight > maxAtomHeight // no sense to make smaller columns than max atom height
303  && currentHeight > maxColumnHeight; // no sense to make smaller columns than max column already created
304 
305  // also should create a new column if the number of items left < number of columns left
306  // in this case we should spread the remaining items out over the remaining columns
307  shouldCreateNewColumn |= ( atomList.size() - i < mSettings.columnCount() - currentColumn );
308 
309  if ( canCreateNewColumn && shouldCreateNewColumn )
310  {
311  // New column
312  currentColumn++;
313  currentColumnAtomCount = 0;
314  closedColumnsHeight += currentColumnHeight;
315  currentColumnHeight = atom.size.height();
316  }
317  else
318  {
319  currentColumnHeight = currentHeight;
320  }
321  atomList[i].column = currentColumn;
322  currentColumnAtomCount++;
323  maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
324  }
325 
326  // Align labels of symbols for each layr/column to the same labelXOffset
327  QMap<QString, qreal> maxSymbolWidth;
328  for ( int i = 0; i < atomList.size(); i++ )
329  {
330  Atom &atom = atomList[i];
331  for ( int j = 0; j < atom.nucleons.size(); j++ )
332  {
333  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( atom.nucleons.at( j ).item ) )
334  {
335  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
336  maxSymbolWidth[key] = std::max( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] );
337  }
338  }
339  }
340  for ( int i = 0; i < atomList.size(); i++ )
341  {
342  Atom &atom = atomList[i];
343  for ( int j = 0; j < atom.nucleons.size(); j++ )
344  {
345  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( atom.nucleons.at( j ).item ) )
346  {
347  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
348  double space = mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right ) +
350  atom.nucleons[j].labelXOffset = maxSymbolWidth[key] + space;
351  atom.nucleons[j].size.rwidth() = maxSymbolWidth[key] + space + atom.nucleons.at( j ).labelSize.width();
352  }
353  }
354  }
355 }
356 
357 
358 QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::AlignmentFlag halignment, double legendWidth )
359 {
360  QSizeF size( 0, 0 );
361  if ( mSettings.title().isEmpty() )
362  {
363  return size;
364  }
365 
366  QStringList lines = mSettings.splitStringForWrapping( mSettings.title() );
367  double y = point.y();
368 
369  if ( painter )
370  {
371  painter->setPen( mSettings.fontColor() );
372  }
373 
374  //calculate width and left pos of rectangle to draw text into
375  double textBoxWidth;
376  double textBoxLeft;
377  switch ( halignment )
378  {
379  case Qt::AlignHCenter:
380  textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.boxSpace() ) * 2.0;
381  textBoxLeft = point.x() - textBoxWidth / 2.;
382  break;
383  case Qt::AlignRight:
384  textBoxLeft = mSettings.boxSpace();
385  textBoxWidth = point.x() - mSettings.boxSpace();
386  break;
387  case Qt::AlignLeft:
388  default:
389  textBoxLeft = point.x();
390  textBoxWidth = legendWidth - point.x() - mSettings.boxSpace();
391  break;
392  }
393 
394  QFont titleFont = mSettings.style( QgsLegendStyle::Title ).font();
395 
396  for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
397  {
398  //last word is not drawn if rectangle width is exactly text width, so add 1
399  //TODO - correctly calculate size of italicized text, since QFontMetrics does not
400  qreal width = mSettings.textWidthMillimeters( titleFont, *titlePart ) + 1;
401  qreal height = mSettings.fontAscentMillimeters( titleFont ) + mSettings.fontDescentMillimeters( titleFont );
402 
403  QRectF r( textBoxLeft, y, textBoxWidth, height );
404 
405  if ( painter )
406  {
407  mSettings.drawText( painter, r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
408  }
409 
410  //update max width of title
411  size.rwidth() = std::max( width, size.rwidth() );
412 
413  y += height;
414  if ( titlePart != ( lines.end() - 1 ) )
415  {
416  y += mSettings.lineSpacing();
417  }
418  }
419  size.rheight() = y - point.y();
420 
421  return size;
422 }
423 
424 
425 double QgsLegendRenderer::spaceAboveAtom( const Atom &atom )
426 {
427  if ( atom.nucleons.isEmpty() ) return 0;
428 
429  Nucleon nucleon = atom.nucleons.first();
430 
431  if ( QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
432  {
433  return mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Top );
434  }
435  else if ( QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
436  {
437  return mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Top );
438  }
439  else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
440  {
441  // TODO: use Symbol or SymbolLabel Top margin
443  }
444 
445  return 0;
446 }
447 
448 
449 // Draw atom and expand its size (using actual nucleons labelXOffset)
450 QSizeF QgsLegendRenderer::drawAtom( const Atom &atom, QPainter *painter, QPointF point )
451 {
452  bool first = true;
453  QSizeF size = QSizeF( atom.size );
454  Q_FOREACH ( const Nucleon &nucleon, atom.nucleons )
455  {
456  if ( QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
457  {
458  QgsLegendStyle::Style s = nodeLegendStyle( groupItem );
459  if ( s != QgsLegendStyle::Hidden )
460  {
461  if ( !first )
462  {
463  point.ry() += mSettings.style( s ).margin( QgsLegendStyle::Top );
464  }
465  drawGroupTitle( groupItem, painter, point );
466  }
467  }
468  else if ( QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
469  {
470  QgsLegendStyle::Style s = nodeLegendStyle( layerItem );
471  if ( s != QgsLegendStyle::Hidden )
472  {
473  if ( !first )
474  {
475  point.ry() += mSettings.style( s ).margin( QgsLegendStyle::Top );
476  }
477  drawLayerTitle( layerItem, painter, point );
478  }
479  }
480  else if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
481  {
482  if ( !first )
483  {
484  point.ry() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
485  }
486 
487  Nucleon symbolNucleon = drawSymbolItem( legendNode, painter, point, nucleon.labelXOffset );
488  // expand width, it may be wider because of labelXOffset
489  size.rwidth() = std::max( symbolNucleon.size.width(), size.width() );
490  }
491  point.ry() += nucleon.size.height();
492  first = false;
493  }
494  return size;
495 }
496 
497 
498 QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QPainter *painter, QPointF point, double labelXOffset )
499 {
501  ctx.painter = painter;
502  ctx.point = point;
503  ctx.labelXOffset = labelXOffset;
504 
505  QgsLayerTreeModelLegendNode::ItemMetrics im = symbolItem->draw( mSettings, painter ? &ctx : nullptr );
506 
507  Nucleon nucleon;
508  nucleon.item = symbolItem;
509  nucleon.symbolSize = im.symbolSize;
510  nucleon.labelSize = im.labelSize;
511  //QgsDebugMsg( QString( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() ));
512  double width = std::max( static_cast< double >( im.symbolSize.width() ), labelXOffset ) + im.labelSize.width();
513  double height = std::max( im.symbolSize.height(), im.labelSize.height() );
514  nucleon.size = QSizeF( width, height );
515  return nucleon;
516 }
517 
518 
519 QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QPainter *painter, QPointF point )
520 {
521  QSizeF size( 0, 0 );
522  QModelIndex idx = mLegendModel->node2index( nodeLayer );
523 
524  //Let the user omit the layer title item by having an empty layer title string
525  if ( mLegendModel->data( idx, Qt::DisplayRole ).toString().isEmpty() ) return size;
526 
527  double y = point.y();
528 
529  if ( painter ) painter->setPen( mSettings.fontColor() );
530 
531  QFont layerFont = mSettings.style( nodeLegendStyle( nodeLayer ) ).font();
532 
533  QStringList lines = mSettings.splitStringForWrapping( mLegendModel->data( idx, Qt::DisplayRole ).toString() );
534  for ( QStringList::Iterator layerItemPart = lines.begin(); layerItemPart != lines.end(); ++layerItemPart )
535  {
536  y += mSettings.fontAscentMillimeters( layerFont );
537  if ( painter ) mSettings.drawText( painter, point.x(), y, *layerItemPart, layerFont );
538  qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart );
539  size.rwidth() = std::max( width, size.width() );
540  if ( layerItemPart != ( lines.end() - 1 ) )
541  {
542  y += mSettings.lineSpacing();
543  }
544  }
545  size.rheight() = y - point.y();
546 
547  return size;
548 }
549 
550 
551 QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QPainter *painter, QPointF point )
552 {
553  QSizeF size( 0, 0 );
554  QModelIndex idx = mLegendModel->node2index( nodeGroup );
555 
556  double y = point.y();
557 
558  if ( painter ) painter->setPen( mSettings.fontColor() );
559 
560  QFont groupFont = mSettings.style( nodeLegendStyle( nodeGroup ) ).font();
561 
562  QStringList lines = mSettings.splitStringForWrapping( mLegendModel->data( idx, Qt::DisplayRole ).toString() );
563  for ( QStringList::Iterator groupPart = lines.begin(); groupPart != lines.end(); ++groupPart )
564  {
565  y += mSettings.fontAscentMillimeters( groupFont );
566  if ( painter ) mSettings.drawText( painter, point.x(), y, *groupPart, groupFont );
567  qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart );
568  size.rwidth() = std::max( width, size.width() );
569  if ( groupPart != ( lines.end() - 1 ) )
570  {
571  y += mSettings.lineSpacing();
572  }
573  }
574  size.rheight() = y - point.y();
575  return size;
576 }
577 
578 
579 
581 {
582  QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
583  if ( style == QLatin1String( "hidden" ) )
584  return QgsLegendStyle::Hidden;
585  else if ( style == QLatin1String( "group" ) )
586  return QgsLegendStyle::Group;
587  else if ( style == QLatin1String( "subgroup" ) )
589 
590  // use a default otherwise
591  if ( QgsLayerTree::isGroup( node ) )
592  return QgsLegendStyle::Group;
593  else if ( QgsLayerTree::isLayer( node ) )
594  {
595  if ( model->legendNodeEmbeddedInParent( QgsLayerTree::toLayer( node ) ) )
596  return QgsLegendStyle::Hidden;
598  }
599 
600  return QgsLegendStyle::Undefined; // should not happen, only if corrupted project file
601 }
602 
604 {
605  return nodeLegendStyle( node, mLegendModel );
606 }
607 
609 {
610  QString str;
611  switch ( style )
612  {
614  str = QStringLiteral( "hidden" );
615  break;
617  str = QStringLiteral( "group" );
618  break;
620  str = QStringLiteral( "subgroup" );
621  break;
622  default:
623  break; // nothing
624  }
625 
626  if ( !str.isEmpty() )
627  node->setCustomProperty( QStringLiteral( "legend/title-style" ), str );
628  else
629  node->removeCustomProperty( QStringLiteral( "legend/title-style" ) );
630 }
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Layer tree group node serves as a container for layers and further groups.
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
int columnCount() const
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
void drawLegend(QPainter *painter)
Draw the legend with given painter.
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
double margin(Side side)
QFont font() const
The font for this style.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Should not happen, only if corrupted project file.
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...
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Construct legend renderer. The ownership of legend model does not change.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
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...
Symbol without label.
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.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
double boxSpace() const
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
QSizeF minimumSize()
Run the layout algorithm and determine the size required for legend.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or null if none is enabled) ...
Special style, item is hidden including margins around.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non-null pointer.
QPointF point
Top-left corner of the legend item.
bool equalColumnWidth() const
double columnSpace() const
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
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...
double lineSpacing() const
QColor fontColor() const
double labelXOffset
offset from the left side where label should start
bool splitLayer() const
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
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...
Layer tree node points to a map layer.
QString title() const