QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgslayouttable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayouttable.cpp
3  ------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgslayouttable.h"
19 #include "qgslayout.h"
20 #include "qgslayoututils.h"
21 #include "qgslayouttablecolumn.h"
22 #include "qgssymbollayerutils.h"
23 #include "qgslayoutframe.h"
24 #include "qgsfontutils.h"
25 #include "qgssettings.h"
27 
28 //
29 // QgsLayoutTableStyle
30 //
31 
32 bool QgsLayoutTableStyle::writeXml( QDomElement &styleElem, QDomDocument &doc ) const
33 {
34  Q_UNUSED( doc )
35  styleElem.setAttribute( QStringLiteral( "cellBackgroundColor" ), QgsSymbolLayerUtils::encodeColor( cellBackgroundColor ) );
36  styleElem.setAttribute( QStringLiteral( "enabled" ), enabled );
37  return true;
38 }
39 
40 bool QgsLayoutTableStyle::readXml( const QDomElement &styleElem )
41 {
42  cellBackgroundColor = QgsSymbolLayerUtils::decodeColor( styleElem.attribute( QStringLiteral( "cellBackgroundColor" ), QStringLiteral( "255,255,255,255" ) ) );
43  enabled = ( styleElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
44  return true;
45 }
46 
47 
48 //
49 // QgsLayoutTable
50 //
51 
53  : QgsLayoutMultiFrame( layout )
54 {
55  //get default composer font from settings
56  QgsSettings settings;
57  QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
58  if ( !defaultFontString.isEmpty() )
59  {
60  mHeaderFont.setFamily( defaultFontString );
61  mContentFont.setFamily( defaultFontString );
62  }
63 
64  initStyles();
65 }
66 
68 {
69  mColumns.clear();
70  mSortColumns.clear();
71 
72  qDeleteAll( mCellStyles );
73  mCellStyles.clear();
74 }
75 
76 bool QgsLayoutTable::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext & ) const
77 {
78  elem.setAttribute( QStringLiteral( "cellMargin" ), QString::number( mCellMargin ) );
79  elem.setAttribute( QStringLiteral( "emptyTableMode" ), QString::number( static_cast< int >( mEmptyTableMode ) ) );
80  elem.setAttribute( QStringLiteral( "emptyTableMessage" ), mEmptyTableMessage );
81  elem.setAttribute( QStringLiteral( "showEmptyRows" ), mShowEmptyRows );
82  elem.appendChild( QgsFontUtils::toXmlElement( mHeaderFont, doc, QStringLiteral( "headerFontProperties" ) ) );
83  elem.setAttribute( QStringLiteral( "headerFontColor" ), QgsSymbolLayerUtils::encodeColor( mHeaderFontColor ) );
84  elem.setAttribute( QStringLiteral( "headerHAlignment" ), QString::number( static_cast< int >( mHeaderHAlignment ) ) );
85  elem.setAttribute( QStringLiteral( "headerMode" ), QString::number( static_cast< int >( mHeaderMode ) ) );
86  elem.appendChild( QgsFontUtils::toXmlElement( mContentFont, doc, QStringLiteral( "contentFontProperties" ) ) );
87  elem.setAttribute( QStringLiteral( "contentFontColor" ), QgsSymbolLayerUtils::encodeColor( mContentFontColor ) );
88  elem.setAttribute( QStringLiteral( "gridStrokeWidth" ), QString::number( mGridStrokeWidth ) );
89  elem.setAttribute( QStringLiteral( "gridColor" ), QgsSymbolLayerUtils::encodeColor( mGridColor ) );
90  elem.setAttribute( QStringLiteral( "horizontalGrid" ), mHorizontalGrid );
91  elem.setAttribute( QStringLiteral( "verticalGrid" ), mVerticalGrid );
92  elem.setAttribute( QStringLiteral( "showGrid" ), mShowGrid );
93  elem.setAttribute( QStringLiteral( "backgroundColor" ), QgsSymbolLayerUtils::encodeColor( mBackgroundColor ) );
94  elem.setAttribute( QStringLiteral( "wrapBehavior" ), QString::number( static_cast< int >( mWrapBehavior ) ) );
95 
96  // display columns
97  QDomElement displayColumnsElem = doc.createElement( QStringLiteral( "displayColumns" ) );
98  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
99  {
100  QDomElement columnElem = doc.createElement( QStringLiteral( "column" ) );
101  column.writeXml( columnElem, doc );
102  displayColumnsElem.appendChild( columnElem );
103  }
104  elem.appendChild( displayColumnsElem );
105  // sort columns
106  QDomElement sortColumnsElem = doc.createElement( QStringLiteral( "sortColumns" ) );
107  for ( const QgsLayoutTableColumn &column : qgis::as_const( mSortColumns ) )
108  {
109  QDomElement columnElem = doc.createElement( QStringLiteral( "column" ) );
110  column.writeXml( columnElem, doc );
111  sortColumnsElem.appendChild( columnElem );
112  }
113  elem.appendChild( sortColumnsElem );
114 
115 
116  //cell styles
117  QDomElement stylesElem = doc.createElement( QStringLiteral( "cellStyles" ) );
118  QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
119  for ( ; it != mCellStyleNames.constEnd(); ++it )
120  {
121  QString styleName = it.value();
122  QDomElement styleElem = doc.createElement( styleName );
123  QgsLayoutTableStyle *style = mCellStyles.value( it.key() );
124  if ( style )
125  {
126  style->writeXml( styleElem, doc );
127  stylesElem.appendChild( styleElem );
128  }
129  }
130  elem.appendChild( stylesElem );
131  return true;
132 }
133 
134 bool QgsLayoutTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
135 {
136  mEmptyTableMode = QgsLayoutTable::EmptyTableMode( itemElem.attribute( QStringLiteral( "emptyTableMode" ), QStringLiteral( "0" ) ).toInt() );
137  mEmptyTableMessage = itemElem.attribute( QStringLiteral( "emptyTableMessage" ), tr( "No matching records" ) );
138  mShowEmptyRows = itemElem.attribute( QStringLiteral( "showEmptyRows" ), QStringLiteral( "0" ) ).toInt();
139  if ( !QgsFontUtils::setFromXmlChildNode( mHeaderFont, itemElem, QStringLiteral( "headerFontProperties" ) ) )
140  {
141  mHeaderFont.fromString( itemElem.attribute( QStringLiteral( "headerFont" ), QString() ) );
142  }
143  mHeaderFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "headerFontColor" ), QStringLiteral( "0,0,0,255" ) ) );
144  mHeaderHAlignment = QgsLayoutTable::HeaderHAlignment( itemElem.attribute( QStringLiteral( "headerHAlignment" ), QStringLiteral( "0" ) ).toInt() );
145  mHeaderMode = QgsLayoutTable::HeaderMode( itemElem.attribute( QStringLiteral( "headerMode" ), QStringLiteral( "0" ) ).toInt() );
146  if ( !QgsFontUtils::setFromXmlChildNode( mContentFont, itemElem, QStringLiteral( "contentFontProperties" ) ) )
147  {
148  mContentFont.fromString( itemElem.attribute( QStringLiteral( "contentFont" ), QString() ) );
149  }
150  mContentFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "contentFontColor" ), QStringLiteral( "0,0,0,255" ) ) );
151  mCellMargin = itemElem.attribute( QStringLiteral( "cellMargin" ), QStringLiteral( "1.0" ) ).toDouble();
152  mGridStrokeWidth = itemElem.attribute( QStringLiteral( "gridStrokeWidth" ), QStringLiteral( "0.5" ) ).toDouble();
153  mHorizontalGrid = itemElem.attribute( QStringLiteral( "horizontalGrid" ), QStringLiteral( "1" ) ).toInt();
154  mVerticalGrid = itemElem.attribute( QStringLiteral( "verticalGrid" ), QStringLiteral( "1" ) ).toInt();
155  mShowGrid = itemElem.attribute( QStringLiteral( "showGrid" ), QStringLiteral( "1" ) ).toInt();
156  mGridColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridColor" ), QStringLiteral( "0,0,0,255" ) ) );
157  mBackgroundColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "backgroundColor" ), QStringLiteral( "255,255,255,0" ) ) );
158  mWrapBehavior = QgsLayoutTable::WrapBehavior( itemElem.attribute( QStringLiteral( "wrapBehavior" ), QStringLiteral( "0" ) ).toInt() );
159 
160  //restore display column specifications
161  mColumns.clear();
162  QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral( "displayColumns" ) );
163  if ( !columnsList.isEmpty() )
164  {
165  QDomElement columnsElem = columnsList.at( 0 ).toElement();
166  QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
167  for ( int i = 0; i < columnEntryList.size(); ++i )
168  {
169  QDomElement columnElem = columnEntryList.at( i ).toElement();
170  QgsLayoutTableColumn column;
171  column.readXml( columnElem );
172  mColumns.append( column );
173  }
174  }
175  // sort columns
176  mSortColumns.clear();
177  QDomNodeList sortColumnsList = itemElem.elementsByTagName( QStringLiteral( "sortColumns" ) );
178  if ( !sortColumnsList.isEmpty() )
179  {
180  QDomElement columnsElem = sortColumnsList.at( 0 ).toElement();
181  QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
182  for ( int i = 0; i < columnEntryList.size(); ++i )
183  {
184  QDomElement columnElem = columnEntryList.at( i ).toElement();
185  QgsLayoutTableColumn column;
186  column.readXml( columnElem );
187  mSortColumns.append( column );
188  }
189  }
190  else
191  {
192  // backward compatibility for QGIS < 3.14
193  // copy the display columns if sortByRank > 0 and then, sort them by rank
195  std::copy_if( mColumns.begin(), mColumns.end(), std::back_inserter( mSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
196  std::sort( mSortColumns.begin(), mSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
198  }
199 
200  //restore cell styles
201  QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral( "cellStyles" ) );
202  if ( !stylesList.isEmpty() )
203  {
204  QDomElement stylesElem = stylesList.at( 0 ).toElement();
205 
206  QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
207  for ( ; it != mCellStyleNames.constEnd(); ++it )
208  {
209  QString styleName = it.value();
210  QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
211  if ( !styleList.isEmpty() )
212  {
213  QDomElement styleElem = styleList.at( 0 ).toElement();
214  QgsLayoutTableStyle *style = mCellStyles.value( it.key() );
215  if ( style )
216  style->readXml( styleElem );
217  }
218  }
219  }
220 
221  emit changed();
222  return true;
223 }
224 
226 {
227  return mTableSize;
228 }
229 
231 {
234 }
235 
236 int QgsLayoutTable::rowsVisible( double frameHeight, int firstRow, bool includeHeader, bool includeEmptyRows ) const
237 {
238  //calculate header height
239  double headerHeight = 0;
240  if ( includeHeader )
241  {
242  //frame has a header
244  }
245  else
246  {
247  //frame has no header text, just the stroke
248  headerHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
249  }
250 
251  //remaining height available for content rows
252  double contentHeight = frameHeight - headerHeight;
253 
254  double gridHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
255 
256  int currentRow = firstRow;
257  while ( contentHeight > 0 && currentRow <= mTableContents.count() )
258  {
259  double currentRowHeight = mMaxRowHeightMap.value( currentRow + 1 ) + gridHeight + 2 * mCellMargin;
260  contentHeight -= currentRowHeight;
261  currentRow++;
262  }
263 
264  if ( includeEmptyRows && contentHeight > 0 )
265  {
267  currentRow += std::max( std::floor( contentHeight / rowHeight ), 0.0 );
268  }
269 
270  return currentRow - firstRow - 1;
271 }
272 
273 int QgsLayoutTable::rowsVisible( int frameIndex, int firstRow, bool includeEmptyRows ) const
274 {
275  //get frame extent
276  if ( frameIndex >= frameCount() )
277  {
278  return 0;
279  }
280  QRectF frameExtent = frame( frameIndex )->extent();
281 
282  bool includeHeader = false;
285  {
286  includeHeader = true;
287  }
288  return rowsVisible( frameExtent.height(), firstRow, includeHeader, includeEmptyRows );
289 }
290 
291 QPair<int, int> QgsLayoutTable::rowRange( const int frameIndex ) const
292 {
293  //calculate row height
294  if ( frameIndex >= frameCount() )
295  {
296  //bad frame index
297  return qMakePair( 0, 0 );
298  }
299 
300  //loop through all previous frames to calculate how many rows are visible in each
301  //as the entire height of a frame may not be utilized for content rows
302  int rowsAlreadyShown = 0;
303  for ( int idx = 0; idx < frameIndex; ++idx )
304  {
305  rowsAlreadyShown += rowsVisible( idx, rowsAlreadyShown, false );
306  }
307 
308  //using zero based indexes
309  int firstVisible = std::min( rowsAlreadyShown, mTableContents.length() );
310  int possibleRowsVisible = rowsVisible( frameIndex, rowsAlreadyShown, false );
311  int lastVisible = std::min( firstVisible + possibleRowsVisible, mTableContents.length() );
312 
313  return qMakePair( firstVisible, lastVisible );
314 }
315 
316 void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &, const int frameIndex )
317 {
318  bool emptyTable = mTableContents.length() == 0;
319  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::HideTable )
320  {
321  //empty table set to hide table mode, so don't draw anything
322  return;
323  }
324 
325  if ( !mLayout->renderContext().isPreviewRender() )
326  {
327  //exporting composition, so force an attribute refresh
328  //we do this in case vector layer has changed via an external source (e.g., another database user)
330  }
331 
332  //calculate which rows to show in this frame
333  QPair< int, int > rowsToShow = rowRange( frameIndex );
334 
335  double gridSizeX = mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0;
336  double gridSizeY = mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0;
337  double cellHeaderHeight = mMaxRowHeightMap[0] + 2 * mCellMargin;//QgsLayoutUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin;
338  double cellBodyHeight = QgsLayoutUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin;
339  QRectF cell;
340 
341  //calculate whether a header is required
342  bool drawHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && frameIndex < 1 )
344  //calculate whether drawing table contents is required
345  bool drawContents = !( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage );
346 
347  int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
348  int numberEmptyRows = 0;
349  if ( drawContents && mShowEmptyRows )
350  {
351  numberRowsToDraw = rowsVisible( frameIndex, rowsToShow.first, true );
352  numberEmptyRows = numberRowsToDraw - rowsToShow.second + rowsToShow.first;
353  }
354  bool mergeCells = false;
355  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage )
356  {
357  //draw a merged row for the empty table message
358  numberRowsToDraw++;
359  rowsToShow.second++;
360  mergeCells = true;
361  }
362 
363  QPainter *p = context.renderContext().painter();
364  p->save();
365  // painter is scaled to dots, so scale back to layout units
366  p->scale( context.renderContext().scaleFactor(), context.renderContext().scaleFactor() );
367 
368  //draw the text
369  p->setPen( Qt::SolidLine );
370 
371  double currentX = gridSizeX;
372  double currentY = gridSizeY;
373  if ( drawHeader )
374  {
375  //draw the headers
376  int col = 0;
377  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
378  {
379  //draw background
380  p->save();
381  p->setPen( Qt::NoPen );
382  p->setBrush( backgroundColor( -1, col ) );
383  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellHeaderHeight ) );
384  p->restore();
385 
386  currentX += mCellMargin;
387 
388  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
389 
390  //calculate alignment of header
391  Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
392  switch ( mHeaderHAlignment )
393  {
394  case FollowColumn:
395  headerAlign = column.hAlignment();
396  break;
397  case HeaderLeft:
398  headerAlign = Qt::AlignLeft;
399  break;
400  case HeaderCenter:
401  headerAlign = Qt::AlignHCenter;
402  break;
403  case HeaderRight:
404  headerAlign = Qt::AlignRight;
405  break;
406  }
407 
408  // disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
409  // and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
410  QString str = column.heading();
411  Qt::TextFlag textFlag = static_cast< Qt::TextFlag >( Qt::TextDontClip );
412  if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( str, column.width(), mHeaderFont ) )
413  {
414  str = wrappedText( str, column.width(), mHeaderFont );
415  }
416  QgsLayoutUtils::drawText( p, cell, str, mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, textFlag );
417 
418  currentX += mMaxColumnWidthMap[ col ];
419  currentX += mCellMargin;
420  currentX += gridSizeX;
421  col++;
422  }
423 
424  currentY += cellHeaderHeight;
425  currentY += gridSizeY;
426  }
427 
428  //now draw the body cells
429  int rowsDrawn = 0;
430  if ( drawContents )
431  {
432  //draw the attribute values
433  for ( int row = rowsToShow.first; row < rowsToShow.second; ++row )
434  {
435  rowsDrawn++;
436  currentX = gridSizeX;
437  int col = 0;
438 
439  //calculate row height
440  double rowHeight = mMaxRowHeightMap[row + 1] + 2 * mCellMargin;
441 
442 
443  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
444  {
445  const QRectF fullCell( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, rowHeight );
446  //draw background
447  p->save();
448  p->setPen( Qt::NoPen );
449  p->setBrush( backgroundColor( row, col ) );
450  p->drawRect( fullCell );
451  p->restore();
452 
453  // currentY = gridSize;
454  currentX += mCellMargin;
455 
456  QVariant cellContents = mTableContents.at( row ).at( col );
457  QString str = cellContents.toString();
458 
459  // disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
460  // and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
461  Qt::TextFlag textFlag = static_cast< Qt::TextFlag >( Qt::TextDontClip );
462  if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( str, column.width(), mContentFont ) )
463  {
464  str = wrappedText( str, column.width(), mContentFont );
465  }
466 
467  p->save();
468  p->setClipRect( fullCell );
469  const QRectF textCell = QRectF( currentX, currentY + mCellMargin, mMaxColumnWidthMap[col], rowHeight - 2 * mCellMargin );
470 
471  const QgsConditionalStyle style = conditionalCellStyle( row, col );
472  QColor foreColor = mContentFontColor;
473  if ( style.textColor().isValid() )
474  foreColor = style.textColor();
475 
476  QgsLayoutUtils::drawText( p, textCell, str, mContentFont, foreColor, column.hAlignment(), column.vAlignment(), textFlag );
477  p->restore();
478 
479  currentX += mMaxColumnWidthMap[ col ];
480  currentX += mCellMargin;
481  currentX += gridSizeX;
482  col++;
483  }
484  currentY += rowHeight;
485  currentY += gridSizeY;
486  }
487  }
488 
489  if ( numberRowsToDraw > rowsDrawn )
490  {
491  p->save();
492  p->setPen( Qt::NoPen );
493 
494  //draw background of empty rows
495  for ( int row = rowsDrawn; row < numberRowsToDraw; ++row )
496  {
497  currentX = gridSizeX;
498  int col = 0;
499 
500  if ( mergeCells )
501  {
502  p->setBrush( backgroundColor( row + 10000, 0 ) );
503  p->drawRect( QRectF( gridSizeX, currentY, mTableSize.width() - 2 * gridSizeX, cellBodyHeight ) );
504  }
505  else
506  {
507  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
508  {
509  Q_UNUSED( column )
510 
511  //draw background
512 
513  //we use a bit of a hack here - since we don't want these extra blank rows to match the firstrow/lastrow rule, add 10000 to row number
514  p->setBrush( backgroundColor( row + 10000, col ) );
515  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellBodyHeight ) );
516 
517  // currentY = gridSize;
518  currentX += mMaxColumnWidthMap[ col ] + 2 * mCellMargin;
519  currentX += gridSizeX;
520  col++;
521  }
522  }
523  currentY += cellBodyHeight + gridSizeY;
524  }
525  p->restore();
526  }
527 
528  //and the borders
529  if ( mShowGrid )
530  {
531  QPen gridPen;
532  gridPen.setWidthF( mGridStrokeWidth );
533  gridPen.setColor( mGridColor );
534  gridPen.setJoinStyle( Qt::MiterJoin );
535  p->setPen( gridPen );
536  if ( mHorizontalGrid )
537  {
538  drawHorizontalGridLines( p, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader );
539  }
540  if ( mVerticalGrid )
541  {
542  drawVerticalGridLines( p, mMaxColumnWidthMap, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader, mergeCells );
543  }
544  }
545 
546  //special case - no records and table is set to ShowMessage mode
547  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage )
548  {
549  double messageX = gridSizeX + mCellMargin;
550  double messageY = gridSizeY + ( drawHeader ? cellHeaderHeight + gridSizeY : 0 );
551  cell = QRectF( messageX, messageY, mTableSize.width() - messageX, cellBodyHeight );
552  QgsLayoutUtils::drawText( p, cell, mEmptyTableMessage, mContentFont, mContentFontColor, Qt::AlignHCenter, Qt::AlignVCenter, static_cast< Qt::TextFlag >( 0 ) );
553  }
554 
555  p->restore();
556 
557 }
558 
559 void QgsLayoutTable::setCellMargin( const double margin )
560 {
561  if ( qgsDoubleNear( margin, mCellMargin ) )
562  {
563  return;
564  }
565 
566  mCellMargin = margin;
567 
568  //since spacing has changed, we need to recalculate the table size
570 
571  emit changed();
572 }
573 
575 {
576  if ( mode == mEmptyTableMode )
577  {
578  return;
579  }
580 
581  mEmptyTableMode = mode;
582 
583  //since appearance has changed, we need to recalculate the table size
585 
586  emit changed();
587 }
588 
589 void QgsLayoutTable::setEmptyTableMessage( const QString &message )
590 {
591  if ( message == mEmptyTableMessage )
592  {
593  return;
594  }
595 
596  mEmptyTableMessage = message;
597 
598  //since message has changed, we need to recalculate the table size
600 
601  emit changed();
602 }
603 
604 void QgsLayoutTable::setShowEmptyRows( const bool showEmpty )
605 {
606  if ( showEmpty == mShowEmptyRows )
607  {
608  return;
609  }
610 
611  mShowEmptyRows = showEmpty;
612  update();
613  emit changed();
614 }
615 
616 void QgsLayoutTable::setHeaderFont( const QFont &font )
617 {
618  if ( font == mHeaderFont )
619  {
620  return;
621  }
622 
623  mHeaderFont = font;
624  //since font attributes have changed, we need to recalculate the table size
626 
627  emit changed();
628 }
629 
630 void QgsLayoutTable::setHeaderFontColor( const QColor &color )
631 {
632  if ( color == mHeaderFontColor )
633  {
634  return;
635  }
636 
637  mHeaderFontColor = color;
638  update();
639 
640  emit changed();
641 }
642 
644 {
645  if ( alignment == mHeaderHAlignment )
646  {
647  return;
648  }
649 
650  mHeaderHAlignment = alignment;
651  update();
652 
653  emit changed();
654 }
655 
657 {
658  if ( mode == mHeaderMode )
659  {
660  return;
661  }
662 
663  mHeaderMode = mode;
665 
666  emit changed();
667 }
668 
669 void QgsLayoutTable::setContentFont( const QFont &font )
670 {
671  if ( font == mContentFont )
672  {
673  return;
674  }
675 
676  mContentFont = font;
677  //since font attributes have changed, we need to recalculate the table size
679 
680  emit changed();
681 }
682 
683 void QgsLayoutTable::setContentFontColor( const QColor &color )
684 {
685  if ( color == mContentFontColor )
686  {
687  return;
688  }
689 
690  mContentFontColor = color;
691  update();
692 
693  emit changed();
694 }
695 
696 void QgsLayoutTable::setShowGrid( const bool showGrid )
697 {
698  if ( showGrid == mShowGrid )
699  {
700  return;
701  }
702 
704  //since grid spacing has changed, we need to recalculate the table size
706 
707  emit changed();
708 }
709 
710 void QgsLayoutTable::setGridStrokeWidth( const double width )
711 {
712  if ( qgsDoubleNear( width, mGridStrokeWidth ) )
713  {
714  return;
715  }
716 
717  mGridStrokeWidth = width;
718  //since grid spacing has changed, we need to recalculate the table size
720 
721  emit changed();
722 }
723 
724 void QgsLayoutTable::setGridColor( const QColor &color )
725 {
726  if ( color == mGridColor )
727  {
728  return;
729  }
730 
731  mGridColor = color;
732  update();
733 
734  emit changed();
735 }
736 
737 void QgsLayoutTable::setHorizontalGrid( const bool horizontalGrid )
738 {
740  {
741  return;
742  }
743 
745  //since grid spacing has changed, we need to recalculate the table size
747 
748  emit changed();
749 }
750 
751 void QgsLayoutTable::setVerticalGrid( const bool verticalGrid )
752 {
753  if ( verticalGrid == mVerticalGrid )
754  {
755  return;
756  }
757 
759  //since grid spacing has changed, we need to recalculate the table size
761 
762  emit changed();
763 }
764 
765 void QgsLayoutTable::setBackgroundColor( const QColor &color )
766 {
767  if ( color == mBackgroundColor )
768  {
769  return;
770  }
771 
772  mBackgroundColor = color;
773  update();
774 
775  emit changed();
776 }
777 
779 {
780  if ( behavior == mWrapBehavior )
781  {
782  return;
783  }
784 
785  mWrapBehavior = behavior;
787 
788  emit changed();
789 }
790 
792 {
793  //remove existing columns
794  mColumns = columns;
795 
796  // backward compatibility
797  // test if sorting is provided with the columns and call setSortColumns in such case
798  QgsLayoutTableSortColumns newSortColumns;
800  std::copy_if( mColumns.begin(), mColumns.end(), std::back_inserter( newSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
801  if ( !newSortColumns.isEmpty() )
802  {
803  std::sort( newSortColumns.begin(), newSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
804  setSortColumns( newSortColumns );
805  }
807 }
808 
810 {
812 }
813 
815 {
816  if ( mCellStyles.contains( group ) )
817  delete mCellStyles.take( group );
818 
819  mCellStyles.insert( group, new QgsLayoutTableStyle( style ) );
820 }
821 
823 {
824  if ( !mCellStyles.contains( group ) )
825  return nullptr;
826 
827  return mCellStyles.value( group );
828 }
829 
830 QMap<int, QString> QgsLayoutTable::headerLabels() const
831 {
832  QMap<int, QString> headers;
833 
834  int i = 0;
835  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
836  {
837  headers.insert( i, col.heading() );
838  i++;
839  }
840  return headers;
841 }
842 
844 {
845  return QgsConditionalStyle();
846 }
847 
848 QSizeF QgsLayoutTable::fixedFrameSize( const int frameIndex ) const
849 {
850  Q_UNUSED( frameIndex )
851  return QSizeF( mTableSize.width(), 0 );
852 }
853 
854 QSizeF QgsLayoutTable::minFrameSize( const int frameIndex ) const
855 {
856  double height = 0;
859  {
860  //header required, force frame to be high enough for header
862  }
863  return QSizeF( 0, height );
864 }
865 
867 {
868  mMaxColumnWidthMap.clear();
869  mMaxRowHeightMap.clear();
870  mTableContents.clear();
871 
872  //get new contents
874  {
875  return;
876  }
877 }
878 
880 {
881  mTableSize = QSizeF( totalWidth(), totalHeight() );
883 }
884 
885 void QgsLayoutTable::initStyles()
886 {
887  mCellStyles.insert( OddColumns, new QgsLayoutTableStyle() );
889  mCellStyles.insert( OddRows, new QgsLayoutTableStyle() );
890  mCellStyles.insert( EvenRows, new QgsLayoutTableStyle() );
892  mCellStyles.insert( LastColumn, new QgsLayoutTableStyle() );
893  mCellStyles.insert( HeaderRow, new QgsLayoutTableStyle() );
894  mCellStyles.insert( FirstRow, new QgsLayoutTableStyle() );
895  mCellStyles.insert( LastRow, new QgsLayoutTableStyle() );
896 
897  mCellStyleNames.insert( OddColumns, QStringLiteral( "oddColumns" ) );
898  mCellStyleNames.insert( EvenColumns, QStringLiteral( "evenColumns" ) );
899  mCellStyleNames.insert( OddRows, QStringLiteral( "oddRows" ) );
900  mCellStyleNames.insert( EvenRows, QStringLiteral( "evenRows" ) );
901  mCellStyleNames.insert( FirstColumn, QStringLiteral( "firstColumn" ) );
902  mCellStyleNames.insert( LastColumn, QStringLiteral( "lastColumn" ) );
903  mCellStyleNames.insert( HeaderRow, QStringLiteral( "headerRow" ) );
904  mCellStyleNames.insert( FirstRow, QStringLiteral( "firstRow" ) );
905  mCellStyleNames.insert( LastRow, QStringLiteral( "lastRow" ) );
906 }
907 
909 {
910  mMaxColumnWidthMap.clear();
911 
912  //total number of cells (rows + 1 for header)
913  int cols = mColumns.count();
914  int cells = cols * ( mTableContents.count() + 1 );
915  QVector< double > widths( cells );
916 
917  double currentCellTextWidth;
918 
919  //first, go through all the column headers and calculate the sizes
920  int i = 0;
921  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
922  {
923  if ( col.width() > 0 )
924  {
925  //column has manually specified width
926  widths[i] = col.width();
927  }
929  {
930  //column width set to automatic, so check content size
931  QStringList multiLineSplit = col.heading().split( '\n' );
932  currentCellTextWidth = 0;
933  const auto constMultiLineSplit = multiLineSplit;
934  for ( const QString &line : constMultiLineSplit )
935  {
936  currentCellTextWidth = std::max( currentCellTextWidth, QgsLayoutUtils::textWidthMM( mHeaderFont, line ) );
937  }
938  widths[i] = currentCellTextWidth;
939  }
940  else
941  {
942  widths[i] = 0.0;
943  }
944  i++;
945  }
946 
947  //next, go through all the table contents and calculate the sizes
948  QgsLayoutTableContents::const_iterator rowIt = mTableContents.constBegin();
949  int row = 1;
950  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
951  {
952  QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
953  int col = 0;
954  for ( ; colIt != rowIt->constEnd(); ++colIt )
955  {
956  if ( mColumns.at( col ).width() <= 0 )
957  {
958  //column width set to automatic, so check content size
959  QStringList multiLineSplit = ( *colIt ).toString().split( '\n' );
960  currentCellTextWidth = 0;
961  const auto constMultiLineSplit = multiLineSplit;
962  for ( const QString &line : constMultiLineSplit )
963  {
964  currentCellTextWidth = std::max( currentCellTextWidth, QgsLayoutUtils::textWidthMM( mContentFont, line ) );
965  }
966  widths[ row * cols + col ] = currentCellTextWidth;
967  }
968  else
969  {
970  widths[ row * cols + col ] = 0;
971  }
972 
973  col++;
974  }
975  row++;
976  }
977 
978  //calculate maximum
979  for ( int col = 0; col < cols; ++col )
980  {
981  double maxColWidth = 0;
982  for ( int row = 0; row < mTableContents.count() + 1; ++row )
983  {
984  maxColWidth = std::max( widths[ row * cols + col ], maxColWidth );
985  }
986  mMaxColumnWidthMap.insert( col, maxColWidth );
987  }
988 
989  return true;
990 }
991 
993 {
994  mMaxRowHeightMap.clear();
995 
996  //total number of cells (rows + 1 for header)
997  int cols = mColumns.count();
998  int cells = cols * ( mTableContents.count() + 1 );
999  QVector< double > heights( cells );
1000 
1001  //first, go through all the column headers and calculate the sizes
1002  int i = 0;
1003  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
1004  {
1005  //height
1007  {
1008  heights[i] = 0;
1009  }
1010  else if ( textRequiresWrapping( col.heading(), mColumns.at( i ).width(), mHeaderFont ) )
1011  {
1012  //contents too wide for cell, need to wrap
1013  heights[i] = QgsLayoutUtils::textHeightMM( mHeaderFont, wrappedText( col.heading(), mColumns.at( i ).width(), mHeaderFont ) );
1014  }
1015  else
1016  {
1017  heights[i] = QgsLayoutUtils::textHeightMM( mHeaderFont, col.heading() );
1018  }
1019  i++;
1020  }
1021 
1022  //next, go through all the table contents and calculate the sizes
1023  QgsLayoutTableContents::const_iterator rowIt = mTableContents.constBegin();
1024  int row = 1;
1025  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
1026  {
1027  QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
1028  int i = 0;
1029  for ( ; colIt != rowIt->constEnd(); ++colIt )
1030  {
1031  if ( textRequiresWrapping( ( *colIt ).toString(), mColumns.at( i ).width(), mContentFont ) )
1032  {
1033  //contents too wide for cell, need to wrap
1034  heights[ row * cols + i ] = QgsLayoutUtils::textHeightMM( mContentFont, wrappedText( ( *colIt ).toString(), mColumns.at( i ).width(), mContentFont ) );
1035  }
1036  else
1037  {
1038  heights[ row * cols + i ] = QgsLayoutUtils::textHeightMM( mContentFont, ( *colIt ).toString() );
1039  }
1040 
1041  i++;
1042  }
1043  row++;
1044  }
1045 
1046  //calculate maximum
1047  for ( int row = 0; row < mTableContents.count() + 1; ++row )
1048  {
1049  double maxRowHeight = 0;
1050  for ( int col = 0; col < cols; ++col )
1051  {
1052  maxRowHeight = std::max( heights[ row * cols + col ], maxRowHeight );
1053  }
1054  mMaxRowHeightMap.insert( row, maxRowHeight );
1055  }
1056 
1057  return true;
1058 }
1059 
1061 {
1062  //check how much space each column needs
1063  if ( !calculateMaxColumnWidths() )
1064  {
1065  return 0;
1066  }
1067 
1068  //adapt frame to total width
1069  double totalWidth = 0;
1070  QMap<int, double>::const_iterator maxColWidthIt = mMaxColumnWidthMap.constBegin();
1071  for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
1072  {
1073  totalWidth += maxColWidthIt.value();
1074  }
1075  totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
1076  totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1077 
1078  return totalWidth;
1079 }
1080 
1082 {
1083  //check how much space each row needs
1084  if ( !calculateMaxRowHeights() )
1085  {
1086  return 0;
1087  }
1088 
1089  double height = 0;
1090 
1091  //loop through all existing frames to calculate how many rows are visible in each
1092  //as the entire height of a frame may not be utilized for content rows
1093  int rowsAlreadyShown = 0;
1094  int numberExistingFrames = frameCount();
1095  int rowsVisibleInLastFrame = 0;
1096  double heightOfLastFrame = 0;
1097  for ( int idx = 0; idx < numberExistingFrames; ++idx )
1098  {
1099  bool hasHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && idx == 0 )
1101  heightOfLastFrame = frame( idx )->rect().height();
1102  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1103  rowsAlreadyShown += rowsVisibleInLastFrame;
1104  height += heightOfLastFrame;
1105  if ( rowsAlreadyShown >= mTableContents.length() )
1106  {
1107  //shown entire contents of table, nothing remaining
1108  return height;
1109  }
1110  }
1111 
1112  //calculate how many rows left to show
1113  int remainingRows = mTableContents.length() - rowsAlreadyShown;
1114 
1115  if ( remainingRows <= 0 )
1116  {
1117  //no remaining rows
1118  return height;
1119  }
1120 
1122  {
1123  QgsLayoutItemPage *page = mLayout->pageCollection()->page( mLayout->pageCollection()->pageCount() - 1 );
1124  if ( page )
1125  heightOfLastFrame = page->sizeWithUnits().height();
1126  }
1127 
1128  bool hasHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && numberExistingFrames < 1 )
1130 
1131  int numberFramesMissing = 0;
1132  while ( remainingRows > 0 )
1133  {
1134  numberFramesMissing++;
1135 
1136  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1137  if ( rowsVisibleInLastFrame < 1 )
1138  {
1139  //if no rows are visible in the last frame, calculation of missing frames
1140  //is impossible. So just return total height of existing frames
1141  return height;
1142  }
1143 
1144  rowsAlreadyShown += rowsVisibleInLastFrame;
1145  remainingRows = mTableContents.length() - rowsAlreadyShown;
1146  }
1147 
1148  //rows remain unshown -- how many extra frames would we need to complete the table?
1149  //assume all added frames are same size as final frame
1150  height += heightOfLastFrame * numberFramesMissing;
1151  return height;
1152 }
1153 
1154 void QgsLayoutTable::drawHorizontalGridLines( QPainter *painter, int firstRow, int lastRow, bool drawHeaderLines ) const
1155 {
1156  //horizontal lines
1157  if ( lastRow - firstRow < 1 && !drawHeaderLines )
1158  {
1159  return;
1160  }
1161 
1162  double cellBodyHeight = QgsLayoutUtils::fontAscentMM( mContentFont );
1163  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
1164  double currentY = 0;
1165  currentY = halfGridStrokeWidth;
1166  if ( drawHeaderLines )
1167  {
1168  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1169  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1170  currentY += mMaxRowHeightMap[0] + 2 * mCellMargin;
1171  }
1172  for ( int row = firstRow; row < lastRow; ++row )
1173  {
1174  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1175  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1176  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeight;
1177  currentY += ( rowHeight + 2 * mCellMargin );
1178  }
1179  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1180 }
1181 
1182 bool QgsLayoutTable::textRequiresWrapping( const QString &text, double columnWidth, const QFont &font ) const
1183 {
1184  if ( qgsDoubleNear( columnWidth, 0.0 ) || mWrapBehavior != WrapText )
1185  return false;
1186 
1187  QStringList multiLineSplit = text.split( '\n' );
1188  double currentTextWidth = 0;
1189  const auto constMultiLineSplit = multiLineSplit;
1190  for ( const QString &line : constMultiLineSplit )
1191  {
1192  currentTextWidth = std::max( currentTextWidth, QgsLayoutUtils::textWidthMM( font, line ) );
1193  }
1194 
1195  return ( currentTextWidth > columnWidth );
1196 }
1197 
1198 QString QgsLayoutTable::wrappedText( const QString &value, double columnWidth, const QFont &font ) const
1199 {
1200  QStringList lines = value.split( '\n' );
1201  QStringList outLines;
1202  const auto constLines = lines;
1203  for ( const QString &line : constLines )
1204  {
1205  if ( textRequiresWrapping( line, columnWidth, font ) )
1206  {
1207  //first step is to identify words which must be on their own line (too long to fit)
1208  QStringList words = line.split( ' ' );
1209  QStringList linesToProcess;
1210  QString wordsInCurrentLine;
1211  const auto constWords = words;
1212  for ( const QString &word : constWords )
1213  {
1214  if ( textRequiresWrapping( word, columnWidth, font ) )
1215  {
1216  //too long to fit
1217  if ( !wordsInCurrentLine.isEmpty() )
1218  linesToProcess << wordsInCurrentLine;
1219  wordsInCurrentLine.clear();
1220  linesToProcess << word;
1221  }
1222  else
1223  {
1224  if ( !wordsInCurrentLine.isEmpty() )
1225  wordsInCurrentLine.append( ' ' );
1226  wordsInCurrentLine.append( word );
1227  }
1228  }
1229  if ( !wordsInCurrentLine.isEmpty() )
1230  linesToProcess << wordsInCurrentLine;
1231 
1232  const auto constLinesToProcess = linesToProcess;
1233  for ( const QString &line : constLinesToProcess )
1234  {
1235  QString remainingText = line;
1236  int lastPos = remainingText.lastIndexOf( ' ' );
1237  while ( lastPos > -1 )
1238  {
1239  //check if remaining text is short enough to go in one line
1240  if ( !textRequiresWrapping( remainingText, columnWidth, font ) )
1241  {
1242  break;
1243  }
1244 
1245  if ( !textRequiresWrapping( remainingText.left( lastPos ), columnWidth, font ) )
1246  {
1247  outLines << remainingText.left( lastPos );
1248  remainingText = remainingText.mid( lastPos + 1 );
1249  lastPos = 0;
1250  }
1251  lastPos = remainingText.lastIndexOf( ' ', lastPos - 1 );
1252  }
1253  outLines << remainingText;
1254  }
1255  }
1256  else
1257  {
1258  outLines << line;
1259  }
1260  }
1261 
1262  return outLines.join( QStringLiteral( "\n" ) );
1263 }
1264 
1265 QColor QgsLayoutTable::backgroundColor( int row, int column ) const
1266 {
1267  QColor color = mBackgroundColor;
1268  if ( QgsLayoutTableStyle *style = mCellStyles.value( OddColumns ) )
1269  if ( style->enabled && column % 2 == 0 )
1270  color = style->cellBackgroundColor;
1271  if ( QgsLayoutTableStyle *style = mCellStyles.value( EvenColumns ) )
1272  if ( style->enabled && column % 2 == 1 )
1273  color = style->cellBackgroundColor;
1274  if ( QgsLayoutTableStyle *style = mCellStyles.value( OddRows ) )
1275  if ( style->enabled && row % 2 == 0 )
1276  color = style->cellBackgroundColor;
1277  if ( QgsLayoutTableStyle *style = mCellStyles.value( EvenRows ) )
1278  if ( style->enabled && row % 2 == 1 )
1279  color = style->cellBackgroundColor;
1280  if ( QgsLayoutTableStyle *style = mCellStyles.value( FirstColumn ) )
1281  if ( style->enabled && column == 0 )
1282  color = style->cellBackgroundColor;
1283  if ( QgsLayoutTableStyle *style = mCellStyles.value( LastColumn ) )
1284  if ( style->enabled && column == mColumns.count() - 1 )
1285  color = style->cellBackgroundColor;
1286  if ( QgsLayoutTableStyle *style = mCellStyles.value( HeaderRow ) )
1287  if ( style->enabled && row == -1 )
1288  color = style->cellBackgroundColor;
1289  if ( QgsLayoutTableStyle *style = mCellStyles.value( FirstRow ) )
1290  if ( style->enabled && row == 0 )
1291  color = style->cellBackgroundColor;
1292  if ( QgsLayoutTableStyle *style = mCellStyles.value( LastRow ) )
1293  if ( style->enabled && row == mTableContents.count() - 1 )
1294  color = style->cellBackgroundColor;
1295 
1296  if ( row >= 0 )
1297  {
1298  QgsConditionalStyle conditionalStyle = conditionalCellStyle( row, column );
1299  if ( conditionalStyle.backgroundColor().isValid() )
1300  color = conditionalStyle.backgroundColor();
1301  }
1302 
1303  return color;
1304 }
1305 
1306 void QgsLayoutTable::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells ) const
1307 {
1308  //vertical lines
1309  if ( lastRow - firstRow < 1 && !hasHeader )
1310  {
1311  return;
1312  }
1313 
1314  //calculate height of table within frame
1315  double tableHeight = 0;
1316  if ( hasHeader )
1317  {
1318  tableHeight += ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2 + mMaxRowHeightMap[0];
1319  }
1320  tableHeight += ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
1321  double headerHeight = tableHeight;
1322 
1323  double cellBodyHeight = QgsLayoutUtils::fontAscentMM( mContentFont );
1324  for ( int row = firstRow; row < lastRow; ++row )
1325  {
1326  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeight;
1327  tableHeight += rowHeight + ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2;
1328  }
1329 
1330  double halfGridStrokeWidth = ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 ) / 2.0;
1331  double currentX = halfGridStrokeWidth;
1332  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1333  currentX += ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1334  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
1335  int col = 1;
1336  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
1337  {
1338  currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
1339  if ( col == maxWidthMap.size() || !mergeCells )
1340  {
1341  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1342  }
1343  else if ( hasHeader )
1344  {
1345  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
1346  }
1347 
1348  currentX += ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1349  col++;
1350  }
1351 }
1352 
1354 {
1356 
1357  //force recalculation of frame rects, so that they are set to the correct
1358  //fixed and minimum frame sizes
1360 }
1361 
1363 {
1364  return ( contents.indexOf( row ) >= 0 );
1365 }
1366 
QgsLayoutMultiFrame::frame
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
Definition: qgslayoutmultiframe.cpp:469
QgsLayoutTable::mWrapBehavior
WrapBehavior mWrapBehavior
Definition: qgslayouttable.h:591
QgsLayoutTable::setHeaderMode
void setHeaderMode(HeaderMode mode)
Sets the display mode for headers in the table.
Definition: qgslayouttable.cpp:656
QgsLayoutTableStyle::cellBackgroundColor
QColor cellBackgroundColor
Cell background color.
Definition: qgslayouttable.h:86
QgsLayoutTable::showGrid
bool showGrid() const
Returns whether grid lines are drawn in the table.
Definition: qgslayouttable.h:337
QgsLayoutTable::backgroundColor
QColor backgroundColor() const
Returns the color used for the background of the table.
Definition: qgslayouttable.h:421
QgsSymbolLayerUtils::encodeColor
static QString encodeColor(const QColor &color)
Definition: qgssymbollayerutils.cpp:52
QgsLayoutUtils::textHeightMM
static double textHeightMM(const QFont &font, const QString &text, double multiLineHeight=1.0)
Calculate a font height in millimeters for a text string, including workarounds for QT font rendering...
Definition: qgslayoututils.cpp:228
QgsLayoutTable::render
void render(QgsLayoutItemRenderContext &context, const QRectF &renderExtent, int frameIndex) override
Renders a portion of the multiframe's content into a render context.
Definition: qgslayouttable.cpp:316
QgsLayoutTableColumns
QVector< QgsLayoutTableColumn > QgsLayoutTableColumns
Definition: qgslayouttable.h:57
QgsLayoutTable::setHeaderHAlignment
void setHeaderHAlignment(HeaderHAlignment alignment)
Sets the horizontal alignment for table headers.
Definition: qgslayouttable.cpp:643
QgsLayoutTable::HeaderHAlignment
HeaderHAlignment
Controls how headers are horizontally aligned in a table.
Definition: qgslayouttable.h:120
QgsLayoutItemPage
Item representing the paper in a layout.
Definition: qgslayoutitempage.h:54
QgsLayoutTable::mTableContents
QgsLayoutTableContents mTableContents
Contents to show in table.
Definition: qgslayouttable.h:581
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsLayoutTableContents
QVector< QgsLayoutTableRow > QgsLayoutTableContents
Definition: qgslayouttable.h:46
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsLayoutTable::mMaxColumnWidthMap
QMap< int, double > mMaxColumnWidthMap
Map of maximum width for each column.
Definition: qgslayouttable.h:584
QgsLayoutTable::drawHorizontalGridLines
void drawHorizontalGridLines(QPainter *painter, int firstRow, int lastRow, bool drawHeaderLines) const
Draws the horizontal grid lines for the table.
Definition: qgslayouttable.cpp:1154
QgsLayoutTable::mVerticalGrid
bool mVerticalGrid
True if grid should be shown.
Definition: qgslayouttable.h:569
QgsLayoutTable::CellStyleGroup
CellStyleGroup
Row or column groups for cell styling.
Definition: qgslayouttable.h:160
QgsLayoutTable::~QgsLayoutTable
~QgsLayoutTable() override
Definition: qgslayouttable.cpp:67
QgsLayoutTable::cellStyle
const QgsLayoutTableStyle * cellStyle(CellStyleGroup group) const
Returns the cell style for a cell group.
Definition: qgslayouttable.cpp:822
QgsLayoutTable::sortColumns
QgsLayoutTableSortColumns & sortColumns()
Returns a reference to the list of QgsLayoutTableSortColumns shown in the table.
Definition: qgslayouttable.h:455
QgsLayoutTableStyle::writeXml
bool writeXml(QDomElement &styleElem, QDomDocument &doc) const
Writes the style's properties to XML for storage.
Definition: qgslayouttable.cpp:32
qgssymbollayerutils.h
QgsLayoutTable::columns
QgsLayoutTableColumns & columns()
Returns a reference to the list of QgsLayoutTableColumns shown in the table.
Definition: qgslayouttable.h:441
QgsLayoutItemRenderContext
Definition: qgslayoutitem.h:44
QgsLayoutTable::mEmptyTableMode
EmptyTableMode mEmptyTableMode
Behavior for empty tables.
Definition: qgslayouttable.h:530
QgsLayoutTable::setGridColor
void setGridColor(const QColor &color)
Sets the color used for grid lines in the table.
Definition: qgslayouttable.cpp:724
QgsLayoutTable::mHeaderHAlignment
HeaderHAlignment mHeaderHAlignment
Alignment for table headers.
Definition: qgslayouttable.h:545
QgsLayoutTable::mEmptyTableMessage
QString mEmptyTableMessage
String to show in empty tables.
Definition: qgslayouttable.h:533
QgsLayoutTable::setBackgroundColor
void setBackgroundColor(const QColor &color)
Sets the color used for background of table.
Definition: qgslayouttable.cpp:765
QgsLayoutTable::mContentFont
QFont mContentFont
Table contents font.
Definition: qgslayouttable.h:551
QgsLayoutTable::rowRange
QPair< int, int > rowRange(int frameIndex) const
Calculates a range of rows which should be visible in a given frame.
Definition: qgslayouttable.cpp:291
QgsLayoutTable::OddColumns
@ OddColumns
Style odd numbered columns.
Definition: qgslayouttable.h:162
QgsLayoutTable::setGridStrokeWidth
void setGridStrokeWidth(double width)
Sets the width in mm for grid lines in the table.
Definition: qgslayouttable.cpp:710
QgsSettings
Definition: qgssettings.h:61
QgsLayoutTable::HideTable
@ HideTable
Hides entire table if empty.
Definition: qgslayouttable.h:144
QgsLayoutTable::FirstRow
@ FirstRow
Style first row only.
Definition: qgslayouttable.h:169
QgsLayoutTable::FirstColumn
@ FirstColumn
Style first column only.
Definition: qgslayouttable.h:166
QgsLayoutTable::writePropertiesToElement
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
Definition: qgslayouttable.cpp:76
QgsLayoutMultiFrame
Definition: qgslayoutmultiframe.h:48
qgsfontutils.h
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:317
QgsLayoutTable::setEmptyTableBehavior
void setEmptyTableBehavior(EmptyTableMode mode)
Sets the behavior mode for empty tables with no content rows.
Definition: qgslayouttable.cpp:574
QgsLayoutObject::changed
void changed()
Emitted when the object's properties change.
QgsSymbolLayerUtils::decodeColor
static QColor decodeColor(const QString &str)
Definition: qgssymbollayerutils.cpp:57
QgsLayoutTable::setShowGrid
void setShowGrid(bool showGrid)
Sets whether grid lines should be drawn in the table.
Definition: qgslayouttable.cpp:696
QgsConditionalStyle
Definition: qgsconditionalstyle.h:112
QgsLayoutItem::sizeWithUnits
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
Definition: qgslayoutitem.h:668
QgsLayoutTable::calculateMaxColumnWidths
virtual bool calculateMaxColumnWidths()
Calculates the maximum width of text shown in columns.
Definition: qgslayouttable.cpp:908
QgsLayoutTable::mShowGrid
bool mShowGrid
True if grid should be shown.
Definition: qgslayouttable.h:557
QgsLayoutTableColumn::readXml
bool readXml(const QDomElement &columnElem)
Reads the column's properties from xml.
Definition: qgslayouttablecolumn.cpp:50
QgsLayoutTableSortColumns
QVector< QgsLayoutTableColumn > QgsLayoutTableSortColumns
Definition: qgslayouttable.h:64
QgsLayoutMultiFrame::ExtendToNextPage
@ ExtendToNextPage
Creates new full page frames on the following page(s) until the entire multiframe content is visible.
Definition: qgslayoutmultiframe.h:104
QgsLayoutTable::mCellMargin
double mCellMargin
Margin between cell borders and cell text.
Definition: qgslayouttable.h:527
qgslayoutframe.h
QgsLayoutTable::setHeaderFont
void setHeaderFont(const QFont &font)
Sets the font used to draw header text in the table.
Definition: qgslayouttable.cpp:616
qgslayoututils.h
QgsLayoutTable::mSortColumns
QgsLayoutTableSortColumns mSortColumns
Columns to sort the table.
Definition: qgslayouttable.h:578
QgsLayoutTable::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
Definition: qgslayouttable.cpp:134
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:752
QgsLayoutTable::fixedFrameSize
QSizeF fixedFrameSize(int frameIndex=-1) const override
Returns the fixed size for a frame, if desired.
Definition: qgslayouttable.cpp:848
QgsLayoutTable::HeaderRow
@ HeaderRow
Style header row.
Definition: qgslayouttable.h:168
QgsLayoutTable::FirstFrame
@ FirstFrame
Header shown on first frame only.
Definition: qgslayouttable.h:133
QgsLayoutTable::QgsLayoutTable
QgsLayoutTable(QgsLayout *layout)
Constructor for QgsLayoutTable, belonging to the specified layout.
Definition: qgslayouttable.cpp:52
QgsLayoutMultiFrame::update
void update()
Forces a redraw of all child frames.
Definition: qgslayoutmultiframe.cpp:445
QgsLayoutTable::mGridColor
QColor mGridColor
Color for grid lines.
Definition: qgslayouttable.h:563
QgsLayoutTable::ShowMessage
@ ShowMessage
Shows preset message instead of table contents.
Definition: qgslayouttable.h:145
qgslayouttable.h
QgsLayoutTable::minFrameSize
QSizeF minFrameSize(int frameIndex=-1) const override
Returns the minimum size for a frames, if desired.
Definition: qgslayouttable.cpp:854
QgsLayoutTable::contents
QgsLayoutTableContents & contents()
Returns the current contents of the table.
Definition: qgslayouttable.h:502
QgsLayoutTable::WrapText
@ WrapText
Text which doesn't fit inside the cell is wrapped. Note that this only applies to text in columns wit...
Definition: qgslayouttable.h:154
QgsLayoutTable::verticalGrid
bool verticalGrid() const
Returns whether the grid's vertical lines are drawn in the table.
Definition: qgslayouttable.h:407
QgsLayoutTable::setVerticalGrid
void setVerticalGrid(bool verticalGrid)
Sets whether the grid's vertical lines should be drawn in the table.
Definition: qgslayouttable.cpp:751
QgsLayoutTable::mContentFontColor
QColor mContentFontColor
Table contents font color.
Definition: qgslayouttable.h:554
QgsLayoutTable::rowsVisible
int rowsVisible(double frameHeight, int firstRow, bool includeHeader, bool includeEmptyRows) const
Calculates how many content rows would be visible within a frame of the specified height.
Definition: qgslayouttable.cpp:236
QgsLayoutTable::totalHeight
double totalHeight()
Returns total height of table contents.
Definition: qgslayouttable.cpp:1081
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsLayoutTable::setCellMargin
void setCellMargin(double margin)
Sets the margin distance in mm between cell borders and their contents.
Definition: qgslayouttable.cpp:559
QgsConditionalStyle::backgroundColor
QColor backgroundColor() const
The background color for style.
Definition: qgsconditionalstyle.h:216
QgsLayoutTable::OddRows
@ OddRows
Style odd numbered rows.
Definition: qgslayouttable.h:164
QgsLayoutTable::setHorizontalGrid
void setHorizontalGrid(bool horizontalGrid)
Sets whether the grid's horizontal lines should be drawn in the table.
Definition: qgslayouttable.cpp:737
QgsLayoutTable::TruncateText
@ TruncateText
Text which doesn't fit inside the cell is truncated.
Definition: qgslayouttable.h:153
QgsLayoutFrame::extent
QRectF extent() const
Returns the visible portion of the multi frame's content which is shown in this frame,...
Definition: qgslayoutframe.h:76
QgsLayoutTable::totalSize
QSizeF totalSize() const override
Returns the total size of the multiframe's content, in layout units.
Definition: qgslayouttable.cpp:225
QgsLayoutTable::LastRow
@ LastRow
Style last row only.
Definition: qgslayouttable.h:170
QgsLayoutTable::mGridStrokeWidth
double mGridStrokeWidth
Width of grid lines.
Definition: qgslayouttable.h:560
QgsLayoutTable::NoHeaders
@ NoHeaders
No headers shown for table.
Definition: qgslayouttable.h:135
QgsLayoutTable::getTableContents
virtual bool getTableContents(QgsLayoutTableContents &contents)=0
Fetches the contents used for the cells in the table.
QgsLayoutTable::mTableSize
QSizeF mTableSize
Definition: qgslayouttable.h:589
QgsLayoutMultiFrame::mResizeMode
ResizeMode mResizeMode
Definition: qgslayoutmultiframe.h:410
QgsLayoutTable::HeaderRight
@ HeaderRight
Align headers right.
Definition: qgslayouttable.h:125
QgsLayoutTable::HeaderMode
HeaderMode
Controls where headers are shown in the table.
Definition: qgslayouttable.h:131
QgsLayoutTable::mHeaderFontColor
QColor mHeaderFontColor
Header font color.
Definition: qgslayouttable.h:542
QgsLayoutTable::WrapBehavior
WrapBehavior
Controls how long strings in the table are handled.
Definition: qgslayouttable.h:151
QgsLayoutTable::refreshAttributes
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
Definition: qgslayouttable.cpp:866
QgsLayoutTable::recalculateFrameSizes
void recalculateFrameSizes() override
Definition: qgslayouttable.cpp:879
qgslayout.h
QgsLayoutTable::EvenRows
@ EvenRows
Style even numbered rows.
Definition: qgslayouttable.h:165
QgsLayoutTable::EmptyTableMode
EmptyTableMode
Controls how empty tables are displayed.
Definition: qgslayouttable.h:141
QgsLayoutMultiFrame::recalculateFrameRects
void recalculateFrameRects()
Forces a recalculation of all the associated frame's scene rectangles.
Definition: qgslayoutmultiframe.cpp:227
QgsLayoutTable::recalculateTableSize
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
Definition: qgslayouttable.cpp:1353
QgsLayoutTable::HeaderCenter
@ HeaderCenter
Align headers to center.
Definition: qgslayouttable.h:124
QgsLayoutMultiFrame::refresh
void refresh() override
Refreshes the multiframe, causing a recalculation of any property overrides.
Definition: qgslayoutmultiframe.cpp:338
QgsLayoutSize::height
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
QgsLayoutTable::calculateMaxRowHeights
virtual bool calculateMaxRowHeights()
Calculates the maximum height of text shown in rows.
Definition: qgslayouttable.cpp:992
QgsLayoutTable::setSortColumns
void setSortColumns(const QgsLayoutTableSortColumns &sortColumns)
Replaces the sorting columns in the table with a specified list of QgsLayoutTableSortColumns.
Definition: qgslayouttable.cpp:809
QgsLayoutTable::setShowEmptyRows
void setShowEmptyRows(bool showEmpty)
Sets whether empty rows should be drawn.
Definition: qgslayouttable.cpp:604
QgsLayoutTable::setHeaderFontColor
void setHeaderFontColor(const QColor &color)
Sets the color used to draw header text in the table.
Definition: qgslayouttable.cpp:630
QgsLayoutTable::headerLabels
virtual QMap< int, QString > headerLabels() const
Returns the text used in the column headers for the table.
Definition: qgslayouttable.cpp:830
QgsFontUtils::setFromXmlChildNode
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
Definition: qgsfontutils.cpp:348
QgsLayoutItemRenderContext::renderContext
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
QgsLayoutTableStyle::enabled
bool enabled
Whether the styling option is enabled.
Definition: qgslayouttable.h:83
QgsLayoutTableStyle
Styling option for a layout table cell.
Definition: qgslayouttable.h:75
QgsLayoutTable::setColumns
void setColumns(const QgsLayoutTableColumns &columns)
Replaces the columns in the table with a specified list of QgsLayoutTableColumns.
Definition: qgslayouttable.cpp:791
QgsFontUtils::toXmlElement
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
Definition: qgsfontutils.cpp:324
QgsLayoutTable::LastColumn
@ LastColumn
Style last column only.
Definition: qgslayouttable.h:167
QgsLayoutTable::mCellStyles
QMap< CellStyleGroup, QgsLayoutTableStyle * > mCellStyles
Definition: qgslayouttable.h:593
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:335
QgsLayoutUtils::drawText
static void drawText(QPainter *painter, QPointF position, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of layout specific issues (calculation to...
Definition: qgslayoututils.cpp:244
QgsLayoutTable::horizontalGrid
bool horizontalGrid() const
Returns whether the grid's horizontal lines are drawn in the table.
Definition: qgslayouttable.h:388
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
QgsLayoutTable::drawVerticalGridLines
void drawVerticalGridLines(QPainter *painter, const QMap< int, double > &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells=false) const
Draws the vertical grid lines for the table.
Definition: qgslayouttable.cpp:1306
QgsConditionalStyle::textColor
QColor textColor() const
The text color set for style.
Definition: qgsconditionalstyle.h:203
QgsLayoutTable::setWrapBehavior
void setWrapBehavior(WrapBehavior behavior)
Sets the wrap behavior for the table, which controls how text within cells is automatically wrapped.
Definition: qgslayouttable.cpp:778
QgsLayoutTable::setCellStyle
void setCellStyle(CellStyleGroup group, const QgsLayoutTableStyle &style)
Sets the cell style for a cell group.
Definition: qgslayouttable.cpp:814
qgslayouttablecolumn.h
QgsLayoutTable::setContentFont
void setContentFont(const QFont &font)
Sets the font used to draw text in table body cells.
Definition: qgslayouttable.cpp:669
qgssettings.h
QgsLayoutTable::contentsContainsRow
bool contentsContainsRow(const QgsLayoutTableContents &contents, const QgsLayoutTableRow &row) const
Checks whether a table contents contains a given row.
Definition: qgslayouttable.cpp:1362
QgsLayoutUtils::fontAscentMM
static double fontAscentMM(const QFont &font)
Calculates a font ascent in millimeters, including workarounds for QT font rendering issues.
Definition: qgslayoututils.cpp:181
qgslayoutpagecollection.h
QgsLayoutTable::mMaxRowHeightMap
QMap< int, double > mMaxRowHeightMap
Map of maximum height for each row.
Definition: qgslayouttable.h:587
QgsLayoutMultiFrame::recalculateFrameSizes
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of its component frames.
Definition: qgslayoutmultiframe.cpp:96
QgsLayoutTableStyle::readXml
bool readXml(const QDomElement &styleElem)
Reads the style's properties from XML.
Definition: qgslayouttable.cpp:40
QgsLayoutTableRow
QVector< QVariant > QgsLayoutTableRow
Definition: qgslayouttable.h:29
QgsLayoutTable::conditionalCellStyle
virtual QgsConditionalStyle conditionalCellStyle(int row, int column) const
Returns the conditional style to use for the cell at row, column.
Definition: qgslayouttable.cpp:843
QgsLayoutTable::mColumns
QgsLayoutTableColumns mColumns
Columns to show in table.
Definition: qgslayouttable.h:575
QgsLayoutTableColumn
Definition: qgslayouttablecolumn.h:36
QgsLayoutTable::FollowColumn
@ FollowColumn
Header uses the same alignment as the column.
Definition: qgslayouttable.h:122
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:174
QgsLayoutTable::mBackgroundColor
QColor mBackgroundColor
Color for table background.
Definition: qgslayouttable.h:572
QgsLayoutTable::setContentFontColor
void setContentFontColor(const QColor &color)
Sets the color used to draw text in table body cells.
Definition: qgslayouttable.cpp:683
QgsLayoutTable::mHeaderMode
HeaderMode mHeaderMode
Header display mode.
Definition: qgslayouttable.h:548
QgsLayoutMultiFrame::frameIndex
int frameIndex(QgsLayoutFrame *frame) const
Returns the index of a frame within the multiframe.
Definition: qgslayoutmultiframe.cpp:478
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:751
QgsLayoutTable::HeaderLeft
@ HeaderLeft
Align headers left.
Definition: qgslayouttable.h:123
QgsLayoutTable::setEmptyTableMessage
void setEmptyTableMessage(const QString &message)
Sets the message for empty tables with no content rows.
Definition: qgslayouttable.cpp:589
QgsLayoutTable::totalWidth
double totalWidth()
Returns total width of table contents.
Definition: qgslayouttable.cpp:1060
QgsLayoutTable::AllFrames
@ AllFrames
Headers shown on all frames.
Definition: qgslayouttable.h:134
QgsLayoutTable::mHorizontalGrid
bool mHorizontalGrid
True if grid should be shown.
Definition: qgslayouttable.h:566
QgsLayoutTable::refresh
void refresh() override
Definition: qgslayouttable.cpp:230
QgsLayoutTable::mHeaderFont
QFont mHeaderFont
Header font.
Definition: qgslayouttable.h:539
QgsLayoutMultiFrame::frameCount
int frameCount() const
Returns the number of frames associated with this multiframe.
Definition: qgslayoutmultiframe.h:265
QgsSettings::Gui
@ Gui
Definition: qgssettings.h:71
QgsLayoutTable::EvenColumns
@ EvenColumns
Style even numbered columns.
Definition: qgslayouttable.h:163
QgsLayoutTable::mShowEmptyRows
bool mShowEmptyRows
True if empty rows should be shown in the table.
Definition: qgslayouttable.h:536
QgsLayoutUtils::textWidthMM
static double textWidthMM(const QFont &font, const QString &text)
Calculate a font width in millimeters for a text string, including workarounds for QT font rendering ...
Definition: qgslayoututils.cpp:219