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