QGIS API Documentation  2.17.0-Master (0497e4a)
qgscomposertable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposertable.cpp
3  --------------------
4  begin : January 2010
5  copyright : (C) 2010 by Marco Hugentobler
6  email : marco at hugis dot net
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 "qgscomposertable.h"
19 #include "qgscomposertablecolumn.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgscomposerutils.h"
22 #include "qgsfontutils.h"
23 #include <QPainter>
24 #include <QSettings>
25 
26 
28  : QgsComposerItem( composition )
29  , mLineTextDistance( 1.0 )
30  , mHeaderFontColor( Qt::black )
31  , mContentFontColor( Qt::black )
32  , mHeaderHAlignment( FollowColumn )
33  , mShowGrid( true )
34  , mGridStrokeWidth( 0.5 )
35  , mGridColor( QColor( 0, 0, 0 ) )
36 {
37  //get default composer font from settings
38  QSettings settings;
39  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
40  if ( !defaultFontString.isEmpty() )
41  {
42  mHeaderFont.setFamily( defaultFontString );
43  mContentFont.setFamily( defaultFontString );
44  }
45 }
46 
48 {
49  qDeleteAll( mColumns );
50  mColumns.clear();
51 }
52 
54 {
56  mAttributeMaps.clear();
57 
58  //getFeatureAttributes
60  {
61  return;
62  }
63 
64  //since attributes have changed, we also need to recalculate the column widths
65  //and size of table
67 }
68 
69 void QgsComposerTable::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
70 {
71  Q_UNUSED( itemStyle );
72  Q_UNUSED( pWidget );
73  if ( !painter )
74  {
75  return;
76  }
77  if ( !shouldDrawItem() )
78  {
79  return;
80  }
81 
84  {
85  //exporting composition, so force an attribute refresh
86  //we do this in case vector layer has changed via an external source (eg, another database user)
88  }
89 
90  drawBackground( painter );
91  painter->save();
92  //antialiasing on
93  painter->setRenderHint( QPainter::Antialiasing, true );
94 
95  painter->setPen( Qt::SolidLine );
96 
97  //now draw the text
98  double currentX = mGridStrokeWidth;
99  double currentY;
100 
102 
103  int col = 0;
104  double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mLineTextDistance;
105  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mLineTextDistance;
106  QRectF cell;
107  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
108  {
109  currentY = mGridStrokeWidth;
110  currentX += mLineTextDistance;
111 
112  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
113 
114  //calculate alignment of header
115  Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
116  switch ( mHeaderHAlignment )
117  {
118  case FollowColumn:
119  headerAlign = ( *columnIt )->hAlignment();
120  break;
121  case HeaderLeft:
122  headerAlign = Qt::AlignLeft;
123  break;
124  case HeaderCenter:
125  headerAlign = Qt::AlignHCenter;
126  break;
127  case HeaderRight:
128  headerAlign = Qt::AlignRight;
129  break;
130  }
131 
132  QgsComposerUtils::drawText( painter, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, Qt::TextDontClip );
133 
134  currentY += cellHeaderHeight;
135  currentY += mGridStrokeWidth;
136 
137  //draw the attribute values
139  for ( ; attIt != mAttributeMaps.constEnd(); ++attIt )
140  {
141  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight );
142 
143  const QgsAttributeMap &currentAttributeMap = *attIt;
144  QString str = currentAttributeMap[ col ].toString();
145  QgsComposerUtils::drawText( painter, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), ( *columnIt )->vAlignment(), Qt::TextDontClip );
146 
147  currentY += cellBodyHeight;
148  currentY += mGridStrokeWidth;
149  }
150 
151  currentX += mMaxColumnWidthMap[ col ];
152  currentX += mLineTextDistance;
153  currentX += mGridStrokeWidth;
154  col++;
155  }
156 
157  //and the borders
158  if ( mShowGrid )
159  {
160  QPen gridPen;
161  gridPen.setWidthF( mGridStrokeWidth );
162  gridPen.setColor( mGridColor );
163  gridPen.setJoinStyle( Qt::MiterJoin );
164  painter->setPen( gridPen );
165  drawHorizontalGridLines( painter, mAttributeMaps.size() );
167  }
168 
169  painter->restore();
170 
171  //draw frame and selection boxes if necessary
172  drawFrame( painter );
173  if ( isSelected() )
174  {
175  drawSelectionBoxes( painter );
176  }
177 }
178 
180 {
181  mLineTextDistance = d;
182  //since spacing has changed, we need to recalculate the table size
184 }
185 
187 {
188  mHeaderFont = f;
189  //since font attributes have changed, we need to recalculate the table size
191 }
192 
194 {
195  mHeaderFontColor = color;
196  repaint();
197 }
198 
200 {
201  mHeaderHAlignment = alignment;
202  repaint();
203 }
204 
206 {
207  mContentFont = f;
208  //since font attributes have changed, we need to recalculate the table size
210 }
211 
213 {
214  mContentFontColor = color;
215  repaint();
216 }
217 
219 {
220  mShowGrid = show;
221  //since grid spacing has changed, we need to recalculate the table size
223 }
224 
226 {
227  mGridStrokeWidth = w;
228  //since grid spacing has changed, we need to recalculate the table size
230 }
231 
233 {
234  //check how much space each column needs
236  {
237  return;
238  }
239  //adapt item frame to max width / height
241 
242  repaint();
243 }
244 
246 {
247  QMap<int, QString> headers;
248 
250  int col = 0;
251  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
252  {
253  headers.insert( col, ( *columnIt )->heading() );
254  col++;
255  }
256  return headers;
257 }
258 
260 {
261  //remove existing columns
262  qDeleteAll( mColumns );
263  mColumns.clear();
264 
265  mColumns.append( columns );
266 }
267 
269 {
270  elem.setAttribute( "lineTextDist", QString::number( mLineTextDistance ) );
271  elem.appendChild( QgsFontUtils::toXmlElement( mHeaderFont, doc, "headerFontProperties" ) );
273  elem.setAttribute( "headerHAlignment", QString::number( static_cast< int >( mHeaderHAlignment ) ) );
274  elem.appendChild( QgsFontUtils::toXmlElement( mContentFont, doc, "contentFontProperties" ) );
276  elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
278  elem.setAttribute( "showGrid", mShowGrid );
279 
280  //columns
281  QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
283  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
284  {
285  QDomElement columnElem = doc.createElement( "column" );
286  ( *columnIt )->writeXML( columnElem, doc );
287  displayColumnsElem.appendChild( columnElem );
288  }
289  elem.appendChild( displayColumnsElem );
290 
291  return _writeXML( elem, doc );
292 }
293 
294 bool QgsComposerTable::tableReadXML( const QDomElement& itemElem, const QDomDocument& doc )
295 {
296  if ( itemElem.isNull() )
297  {
298  return false;
299  }
300 
301  if ( !QgsFontUtils::setFromXmlChildNode( mHeaderFont, itemElem, "headerFontProperties" ) )
302  {
303  mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
304  }
305  mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
306  mHeaderHAlignment = QgsComposerTable::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
307  if ( !QgsFontUtils::setFromXmlChildNode( mContentFont, itemElem, "contentFontProperties" ) )
308  {
309  mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
310  }
311  mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
312  mLineTextDistance = itemElem.attribute( "lineTextDist", "1.0" ).toDouble();
313  mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
314  mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
315 
316  //grid color
317  if ( itemElem.hasAttribute( "gridColor" ) )
318  {
319  mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
320  }
321  else
322  {
323  //old style grid color
324  int gridRed = itemElem.attribute( "gridColorRed", "0" ).toInt();
325  int gridGreen = itemElem.attribute( "gridColorGreen", "0" ).toInt();
326  int gridBlue = itemElem.attribute( "gridColorBlue", "0" ).toInt();
327  int gridAlpha = itemElem.attribute( "gridColorAlpha", "255" ).toInt();
328  mGridColor = QColor( gridRed, gridGreen, gridBlue, gridAlpha );
329  }
330 
331  //restore column specifications
332  qDeleteAll( mColumns );
333  mColumns.clear();
334  QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
335  if ( !columnsList.isEmpty() )
336  {
337  QDomElement columnsElem = columnsList.at( 0 ).toElement();
338  QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
339  for ( int i = 0; i < columnEntryList.size(); ++i )
340  {
341  QDomElement columnElem = columnEntryList.at( i ).toElement();
343  column->readXML( columnElem );
344  mColumns.append( column );
345  }
346  }
347 
348  //restore general composer item properties
349  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
350  if ( !composerItemList.isEmpty() )
351  {
352  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
353  _readXML( composerItemElem, doc );
354  }
355  return true;
356 }
357 
359 {
360  maxWidthMap.clear();
362 
363  int col = 0;
364  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
365  {
366  maxWidthMap.insert( col, QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() ) );
367  col++;
368  }
369 
370  //go through all the attributes and adapt the max width values
371  QList<QgsAttributeMap>::const_iterator attIt = attributeMaps.constBegin();
372 
373  double currentAttributeTextWidth;
374 
375  for ( ; attIt != attributeMaps.constEnd(); ++attIt )
376  {
377  QgsAttributeMap::const_iterator attIt2 = attIt->constBegin();
378  for ( ; attIt2 != attIt->constEnd(); ++attIt2 )
379  {
380  currentAttributeTextWidth = QgsComposerUtils::textWidthMM( mContentFont, attIt2.value().toString() );
381  if ( currentAttributeTextWidth > maxWidthMap[ attIt2.key()] )
382  {
383  maxWidthMap[ attIt2.key()] = currentAttributeTextWidth;
384  }
385  }
386  }
387  return true;
388 }
389 
390 void QgsComposerTable::adaptItemFrame( const QMap<int, double>& maxWidthMap, const QList<QgsAttributeMap>& attributeMaps )
391 {
392  //calculate height
393  int n = attributeMaps.size();
394  double totalHeight = QgsComposerUtils::fontAscentMM( mHeaderFont )
396  + ( n + 1 ) * mLineTextDistance * 2
397  + ( n + 2 ) * mGridStrokeWidth;
398 
399  //adapt frame to total width
400  double totalWidth = 0;
401  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
402  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
403  {
404  totalWidth += maxColWidthIt.value();
405  }
406  totalWidth += ( 2 * maxWidthMap.size() * mLineTextDistance );
407  totalWidth += ( maxWidthMap.size() + 1 ) * mGridStrokeWidth;
408 
409  QRectF evaluatedRect = evalItemRect( QRectF( pos().x(), pos().y(), totalWidth, totalHeight ) );
410 
411  //update rect for data defined size and position
412  QgsComposerItem::setSceneRect( evaluatedRect );
413 }
414 
416 {
417  //horizontal lines
418  double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
419  double currentY = halfGridStrokeWidth;
420  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
421  currentY += mGridStrokeWidth;
423  for ( int i = 0; i < nAttributes; ++i )
424  {
425  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
426  currentY += mGridStrokeWidth;
428  }
429  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
430 }
431 
433 {
434  //vertical lines
435  double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
436  double currentX = halfGridStrokeWidth;
437  p->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, rect().height() - halfGridStrokeWidth ) );
438  currentX += mGridStrokeWidth;
439  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
440  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
441  {
442  currentX += ( maxColWidthIt.value() + 2 * mLineTextDistance );
443  p->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, rect().height() - halfGridStrokeWidth ) );
444  currentX += mGridStrokeWidth;
445  }
446 }
void clear()
QDomNodeList elementsByTagName(const QString &tagname) const
qreal x() const
qreal y() const
void setLineTextDistance(double d)
Sets the margin distance between cell borders and their contents.
void setContentFont(const QFont &f)
Sets the font used to draw text in table body cells.
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual bool calculateMaxColumnWidths(QMap< int, double > &maxWidthMap, const QList< QgsAttributeMap > &attributeMaps) const
Calculates the maximum width of text shown in columns.
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
bool tableWriteXML(QDomElement &itemElem, QDomDocument &doc) const
Writes common table properties to xml for storage.
HeaderHAlignment
Controls how headers are horizontally aligned in a table.
static QString encodeColor(const QColor &color)
void setGridStrokeWidth(double w)
Sets the width for grid lines in the table.
const_iterator constBegin() const
A item that forms part of a map composition.
void setHeaderFontColor(const QColor &color)
Sets the color used to draw header text in the table.
QRectF evalItemRect(const QRectF &newRect, const bool resizeOnly=false, const QgsExpressionContext *context=nullptr)
Evaluates an item&#39;s bounding rect to consider data defined position and size of item and reference po...
void setShowGrid(bool show)
Sets whether grid lines should be drawn in the table.
static void drawText(QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of composer specific issues (calculation ...
void save()
void setContentFontColor(const QColor &color)
Sets the color used to draw text in table body cells.
void setJoinStyle(Qt::PenJoinStyle style)
HeaderHAlignment mHeaderHAlignment
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QList< QgsComposerTableColumn * > * columns()
Returns a pointer to the list of QgsComposerTableColumns shown in the table.
void drawLine(const QLineF &line)
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
void clear()
double toDouble(bool *ok) const
int size() const
QgsComposition::PlotStyle plotStyle() const
virtual QMap< int, QString > headerLabels() const
Returns the text used in the column headers for the table.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QDomElement toElement() const
bool isEmpty() const
void drawHorizontalGridLines(QPainter *p, int nAttributes)
Draws the horizontal grid lines for the table.
QPointF pos() const
QString number(int n, int base)
void append(const T &value)
bool fromString(const QString &descrip)
bool hasAttribute(const QString &name) const
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
void setPen(const QColor &color)
void setAttribute(const QString &name, const QString &value)
bool isSelected() const
int toInt(bool *ok, int base) const
bool isEmpty() const
const_iterator constEnd() const
void setWidthF(qreal width)
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.
void repaint() override
Stores properties of a column in a QgsComposerTable.
virtual ~QgsComposerTable()
void setColor(const QColor &color)
Graphics scene for map printing.
virtual bool getFeatureAttributes(QList< QgsAttributeMap > &attributeMaps)
Fetches the text used for the rows of the table.
void setHeaderHAlignment(const HeaderHAlignment alignment)
Sets the horizontal alignment for table headers.
QList< QgsComposerTableColumn * > mColumns
void setColumns(const QList< QgsComposerTableColumn *> &columns)
Replaces the columns in the table with a specified list of QgsComposerTableColumns.
bool isNull() const
void restore()
static double textWidthMM(const QFont &font, const QString &text)
Calculate font width in millimeters for a string, including workarounds for QT font rendering issues...
QgsComposition * mComposition
virtual void refreshAttributes()
Refreshes the attributes shown in the table by querying the vector layer for new data.
QgsComposerTable(QgsComposition *composition)
void setHeaderFont(const QFont &f)
Sets the font used to draw header text in the table.
QVariant value(const QString &key, const QVariant &defaultValue) const
QList< QgsAttributeMap > mAttributeMaps
QString toString() const
bool tableReadXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads the table&#39;s common properties from xml.
virtual void adjustFrameToSize()
Adapts the size of the frame to match the content.
virtual void drawBackground(QPainter *p)
Draw background.
void setFamily(const QString &family)
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint.
double mLineTextDistance
Distance between table lines and text.
static QColor decodeColor(const QString &str)
iterator insert(const Key &key, const T &value)
virtual bool readXML(const QDomElement &columnElem)
Reads the column&#39;s properties from xml.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
int size() const
QMap< int, double > mMaxColumnWidthMap
void adaptItemFrame(const QMap< int, double > &maxWidthMap, const QList< QgsAttributeMap > &attributeMaps)
Adapts the size of the item frame to match the table&#39;s content.
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
QString toString() const
int size() const
QDomNode at(int index) const
QRectF rect() const
const T value(const Key &key) const
void drawVerticalGridLines(QPainter *p, const QMap< int, double > &maxWidthMap)
Draws the vertical grid lines for the table.