QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgslayoutitemmanualtable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemmanualtable.cpp
3  ---------------------------
4  begin : January 2020
5  copyright : (C) 2020 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 
19 #include "qgsconditionalstyle.h"
20 #include "qgslayoutitemregistry.h"
21 #include "qgslayouttablecolumn.h"
22 #include "qgsnumericformat.h"
23 #include "qgsxmlutils.h"
25 
26 //
27 // QgsLayoutItemManualTable
28 //
29 
31  : QgsLayoutTable( layout )
32 {
35 }
36 
38 {
39 }
40 
42 {
44 }
45 
47 {
48  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
49 }
50 
52 {
53  return new QgsLayoutItemManualTable( layout );
54 }
55 
57 {
58  return tr( "<Table frame>" );
59 }
60 
62 {
63  contents.clear();
64 
65  QgsNumericFormatContext numericContext;
66 
68 
69  int rowNumber = 0;
70  for ( const QgsTableRow &row : qgis::as_const( mContents ) )
71  {
72  QgsLayoutTableRow currentRow;
73 
74  for ( int columnNumber = 0; columnNumber < mColumns.count(); ++columnNumber )
75  {
76  if ( columnNumber < row.count() )
77  {
78  QVariant cellContent = row.at( columnNumber ).content();
79 
80  if ( cellContent.canConvert< QgsProperty >() )
81  {
82  // expression based cell content, evaluate now
83  QgsExpressionContextScopePopper popper( context, scopeForCell( rowNumber, columnNumber ) );
84  cellContent = cellContent.value< QgsProperty >().value( context );
85  }
86 
87  if ( row.at( columnNumber ).numericFormat() )
88  currentRow << row.at( columnNumber ).numericFormat()->formatDouble( cellContent.toDouble(), numericContext );
89  else
90  currentRow << cellContent.toString();
91  }
92  else
93  {
94  currentRow << QString();
95  }
96  }
97  contents << currentRow;
98  rowNumber++;
99  }
100 
102  return true;
103 }
104 
106 {
107  if ( row < 0 || row >= mContents.size() )
108  return QgsConditionalStyle();
109 
110  const QgsTableRow &rowContents = mContents[ row ];
111  if ( column < 0 || column > rowContents.size() )
112  return QgsConditionalStyle();
113 
114  const QgsTableCell &c = rowContents[ column ];
116  if ( c.foregroundColor().isValid() )
117  res.setTextColor( c.foregroundColor() );
118  if ( c.backgroundColor().isValid() )
119  res.setBackgroundColor( c.backgroundColor() );
120 
121  return res;
122 }
123 
125 {
126  mContents = contents;
127 
128  refreshColumns();
130 }
131 
133 {
134  return mContents;
135 }
136 
137 void QgsLayoutItemManualTable::setRowHeights( const QList<double> &heights )
138 {
139  mRowHeights = heights;
140 
142 }
143 
144 void QgsLayoutItemManualTable::setColumnWidths( const QList<double> &widths )
145 {
146  mColumnWidths = widths;
147 
148  refreshColumns();
150 }
151 
153 {
154  return mIncludeHeader;
155 }
156 
158 {
159  mIncludeHeader = included;
160 
161  if ( !mIncludeHeader )
163  else
165  refreshColumns();
167 }
168 
170 {
171  return mHeaders;
172 }
173 
175 {
176  mHeaders.clear();
177 
178  mHeaders.append( headers );
179  refreshColumns();
181 }
182 
183 bool QgsLayoutItemManualTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
184 {
185  if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
186  return false;
187 
188  tableElem.setAttribute( QStringLiteral( "includeHeader" ), mIncludeHeader ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
189 
190  //headers
191  QDomElement headersElem = doc.createElement( QStringLiteral( "headers" ) );
192  for ( const QgsLayoutTableColumn &header : qgis::as_const( mHeaders ) )
193  {
194  QDomElement headerElem = doc.createElement( QStringLiteral( "header" ) );
195  header.writeXml( headerElem, doc );
196  headersElem.appendChild( headerElem );
197  }
198  tableElem.appendChild( headersElem );
199 
200  QDomElement contentsElement = doc.createElement( QStringLiteral( "contents" ) );
201  for ( const QgsTableRow &row : mContents )
202  {
203  QDomElement rowElement = doc.createElement( QStringLiteral( "row" ) );
204  for ( int i = 0; i < mColumns.count(); ++i )
205  {
206  if ( i < row.count() )
207  {
208  rowElement.appendChild( QgsXmlUtils::writeVariant( row.at( i ).properties( context ), doc ) );
209  }
210  }
211  contentsElement.appendChild( rowElement );
212  }
213  tableElem.appendChild( contentsElement );
214 
215  QDomElement rowHeightsElement = doc.createElement( QStringLiteral( "rowHeights" ) );
216  for ( double height : mRowHeights )
217  {
218  QDomElement rowElement = doc.createElement( QStringLiteral( "row" ) );
219  rowElement.setAttribute( QStringLiteral( "height" ), height );
220  rowHeightsElement.appendChild( rowElement );
221  }
222  tableElem.appendChild( rowHeightsElement );
223 
224  QDomElement columnWidthsElement = doc.createElement( QStringLiteral( "columnWidths" ) );
225  for ( double width : mColumnWidths )
226  {
227  QDomElement columnElement = doc.createElement( QStringLiteral( "column" ) );
228  columnElement.setAttribute( QStringLiteral( "width" ), width );
229  columnWidthsElement.appendChild( columnElement );
230  }
231  tableElem.appendChild( columnWidthsElement );
232 
233  return true;
234 }
235 
236 bool QgsLayoutItemManualTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
237 {
238  if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
239  return false;
240 
241  mIncludeHeader = itemElem.attribute( QStringLiteral( "includeHeader" ) ).toInt();
242  //restore header specifications
243  mHeaders.clear();
244  QDomNodeList headersList = itemElem.elementsByTagName( QStringLiteral( "headers" ) );
245  if ( !headersList.isEmpty() )
246  {
247  QDomElement headersElem = headersList.at( 0 ).toElement();
248  QDomNodeList headerEntryList = headersElem.elementsByTagName( QStringLiteral( "header" ) );
249  for ( int i = 0; i < headerEntryList.size(); ++i )
250  {
251  QDomElement headerElem = headerEntryList.at( i ).toElement();
252  QgsLayoutTableColumn header;
253  header.readXml( headerElem );
254  mHeaders.append( header );
255  }
256  }
257 
258  mRowHeights.clear();
259  const QDomNodeList rowHeightNodeList = itemElem.firstChildElement( QStringLiteral( "rowHeights" ) ).childNodes();
260  mRowHeights.reserve( rowHeightNodeList.size() );
261  for ( int r = 0; r < rowHeightNodeList.size(); ++r )
262  {
263  const QDomElement rowHeightElement = rowHeightNodeList.at( r ).toElement();
264  mRowHeights.append( rowHeightElement.attribute( QStringLiteral( "height" ) ).toDouble() );
265  }
266 
267  mColumnWidths.clear();
268  const QDomNodeList columnWidthNodeList = itemElem.firstChildElement( QStringLiteral( "columnWidths" ) ).childNodes();
269  mColumnWidths.reserve( columnWidthNodeList.size() );
270  for ( int r = 0; r < columnWidthNodeList.size(); ++r )
271  {
272  const QDomElement columnWidthElement = columnWidthNodeList.at( r ).toElement();
273  mColumnWidths.append( columnWidthElement.attribute( QStringLiteral( "width" ) ).toDouble() );
274  }
275 
276  QgsTableContents newContents;
277  const QDomElement contentsElement = itemElem.firstChildElement( QStringLiteral( "contents" ) );
278  const QDomNodeList rowNodeList = contentsElement.childNodes();
279  newContents.reserve( rowNodeList.size() );
280  for ( int r = 0; r < rowNodeList.size(); ++r )
281  {
282  QgsTableRow row;
283  const QDomElement rowElement = rowNodeList.at( r ).toElement();
284  const QDomNodeList cellNodeList = rowElement.childNodes();
285  row.reserve( cellNodeList.size() );
286  for ( int c = 0; c < cellNodeList.size(); ++c )
287  {
288  const QDomElement cellElement = cellNodeList.at( c ).toElement();
289  QgsTableCell newCell;
290  newCell.setProperties( QgsXmlUtils::readVariant( cellElement ).toMap(), context );
291  row << newCell;
292  }
293  newContents << row;
294  }
295  setTableContents( newContents );
296 
297  emit changed();
298  return true;
299 }
300 
302 {
304  return false;
305 
306  QMap<int, double> newHeights;
307  for ( auto it = mMaxRowHeightMap.constBegin(); it != mMaxRowHeightMap.constEnd(); ++it )
308  {
309  // first row in mMaxRowHeightMap corresponds to header, which we ignore here
310  const int row = it.key() - 1;
311  const double presetHeight = mRowHeights.value( row );
312  double thisRowHeight = it.value();
313  if ( presetHeight > 0 )
314  thisRowHeight = presetHeight;
315  newHeights.insert( row + 1, thisRowHeight );
316  }
317  mMaxRowHeightMap = newHeights;
318  return true;
319 }
320 
322 {
323 // if ( mHeaders.value( column ).)
324  return QgsLayoutTable::textFormatForHeader( column );
325 }
326 
328 {
329  if ( mContents.value( row ).value( column ).textFormat().isValid() )
330  return mContents.value( row ).value( column ).textFormat();
331 
332  return QgsLayoutTable::textFormatForCell( row, column );
333 }
334 
335 Qt::Alignment QgsLayoutItemManualTable::horizontalAlignmentForCell( int row, int column ) const
336 {
337  if ( row < mContents.size() && column < mContents.at( row ).size() )
338  return mContents.value( row ).value( column ).horizontalAlignment();
339 
340  return QgsLayoutTable::horizontalAlignmentForCell( row, column );
341 }
342 
343 Qt::Alignment QgsLayoutItemManualTable::verticalAlignmentForCell( int row, int column ) const
344 {
345  if ( row < mContents.size() && column < mContents.at( row ).size() )
346  return mContents.value( row ).value( column ).verticalAlignment();
347 
348  return QgsLayoutTable::verticalAlignmentForCell( row, column );
349 }
350 
351 void QgsLayoutItemManualTable::refreshColumns()
352 {
353  // refresh columns
355  if ( !mContents.empty() )
356  {
357  int colIndex = 0;
358  const QgsTableRow &firstRow = mContents[ 0 ];
359  columns.reserve( firstRow.size() );
360  for ( const QgsTableCell &cell : firstRow )
361  {
362  ( void )cell;
363  QgsLayoutTableColumn newCol( mHeaders.value( colIndex ).heading() );
364  newCol.setWidth( mColumnWidths.value( colIndex ) );
365  columns << newCol;
366  colIndex++;
367  }
368  }
369  setColumns( columns );
370 }
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Conditional styling for a rule.
void setTextColor(const QColor &value)
Set the text color for the style.
void setBackgroundColor(const QColor &value)
Set the background color for the style.
RAII class to pop scope from an expression context on destruction.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A layout table subclass that displays manually entered (and formatted) content.
bool calculateMaxRowHeights() override
Calculates the maximum height of text shown in rows.
QIcon icon() const override
Returns the item's icon.
void setColumnWidths(const QList< double > &widths)
Sets the list of column widths (in millimeters) to use when rendering the table.
Qt::Alignment horizontalAlignmentForCell(int row, int column) const override
Returns the horizontal alignment to use for the cell at the specified row and column.
QString displayName() const override
Returns the multiframe display name.
QgsConditionalStyle conditionalCellStyle(int row, int column) const override
Returns the conditional style to use for the cell at row, column.
Qt::Alignment verticalAlignmentForCell(int row, int column) const override
Returns the vertical alignment to use for the cell at the specified row and column.
static QgsLayoutItemManualTable * create(QgsLayout *layout)
Returns a new QgsLayoutItemManualTable for the specified parent layout.
void setIncludeTableHeader(bool included)
Sets whether the table includes a header row.
void setHeaders(const QgsLayoutTableColumns &headers)
Replaces the headers in the table with a specified list of QgsLayoutTableColumns.
QgsTextFormat textFormatForCell(int row, int column) const override
Returns the text format to use for the cell at the specified row and column.
void setTableContents(const QgsTableContents &contents)
Sets the contents of the table.
QgsTextFormat textFormatForHeader(int column) const override
Returns the text format to use for the header cell at the specified column.
int type() const override
Returns unique multiframe type id.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
void setRowHeights(const QList< double > &heights)
Sets the list of row heights (in millimeters) to use when rendering the table.
bool includeTableHeader() const
Returns true if the table includes a header row.
QgsLayoutTableColumns & headers()
Returns a reference to the list of headers shown in the table.
QgsLayoutItemManualTable(QgsLayout *layout)
Constructor for QgsLayoutItemManualTable, attached to the specified layout.
QgsTableContents tableContents() const
Returns the contents of the table.
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
bool getTableContents(QgsLayoutTableContents &contents) override
Fetches the contents used for the cells in the table.
@ LayoutManualTable
Manual (fixed) table.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
Stores properties of a column for a QgsLayoutTable.
bool readXml(const QDomElement &columnElem)
Reads the column's properties from xml.
A class to display a table in the print layout, and allow the table to span over multiple frames.
void setColumns(const QgsLayoutTableColumns &columns)
Replaces the columns in the table with a specified list of QgsLayoutTableColumns.
virtual Qt::Alignment horizontalAlignmentForCell(int row, int column) const
Returns the horizontal alignment to use for the cell at the specified row and column.
QgsLayoutTableColumns & columns()
Returns a reference to the list of QgsLayoutTableColumns shown in the table.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
virtual QgsTextFormat textFormatForHeader(int column) const
Returns the text format to use for the header cell at the specified column.
void setHeaderMode(HeaderMode mode)
Sets the display mode for headers in the table.
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
virtual QgsExpressionContextScope * scopeForCell(int row, int column) const
Creates a new QgsExpressionContextScope for the cell at row, column.
QgsLayoutTableContents & contents()
Returns the current contents of the table.
@ AllFrames
Headers shown on all frames.
@ NoHeaders
No headers shown for table.
QgsLayoutTableColumns mColumns
Columns to show in table.
virtual Qt::Alignment verticalAlignmentForCell(int row, int column) const
Returns the vertical alignment to use for the cell at the specified row and column.
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
virtual QgsTextFormat textFormatForCell(int row, int column) const
Returns the text format to use for the cell at the specified row and column.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
virtual bool calculateMaxRowHeights()
Calculates the maximum height of text shown in rows.
QMap< int, double > mMaxRowHeightMap
Map of maximum height for each row.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
A context for numeric formats.
A store for object properties.
Definition: qgsproperty.h:232
The class is used as a container of context for various read/write operations on other objects.
Encapsulates the contents and formatting of a single table cell.
Definition: qgstablecell.h:36
void setProperties(const QVariantMap &properties, const QgsReadWriteContext &context)
Sets the properties for the cell.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
QVector< QgsLayoutTableColumn > QgsLayoutTableColumns
List of column definitions for a QgsLayoutTable.
QVector< QgsLayoutTableRow > QgsLayoutTableContents
List of QgsLayoutTableRows, representing rows and column cell contents for a QgsLayoutTable.
QVector< QVariant > QgsLayoutTableRow
List of QVariants, representing a the contents of a single row in a QgsLayoutTable.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVector< QgsTableRow > QgsTableContents
A set of table rows.
Definition: qgstablecell.h:220
QVector< QgsTableCell > QgsTableRow
A row of table cells.
Definition: qgstablecell.h:211