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