QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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( QStringLiteral( "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.layerFontColor() );
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.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
double boxSpace() const
double lineSpacing() const
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non-null pointer.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
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...
int columnCount() const
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
void drawLegend(QPainter *painter)
Draw the legend with given painter.
double margin(Side side)
double columnSpace() const
Should not happen, only if corrupted project file.
bool equalColumnWidth() const
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Construct legend renderer. The ownership of legend model does not change.
QColor fontColor() const
The QgsLayerTreeModel class is model implementation for Qt item views framework.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
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.
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.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
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...
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or null if none is enabled) ...
QColor layerFontColor() const
Returns layer font color, defaults to fontColor()
Special style, item is hidden including margins around.
bool splitLayer() const
QPointF point
Top-left corner of the legend item.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QString title() const
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...
QFont font() const
The font for this style.
double labelXOffset
offset from the left side where label should start
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.
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...