QGIS API Documentation  2.4.0-Chugiak
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerlabel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerlabel.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : [email protected]
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 "qgscomposerlabel.h"
19 #include "qgscomposition.h"
20 #include "qgsexpression.h"
22 
23 #include <QCoreApplication>
24 #include <QDate>
25 #include <QDomElement>
26 #include <QPainter>
27 #include <QSettings>
28 #include <QTimer>
29 #include <QWebFrame>
30 #include <QWebPage>
31 #include <QEventLoop>
32 
34  QgsComposerItem( composition ), mHtmlState( 0 ), mHtmlUnitsToMM( 1.0 ),
35  mHtmlLoaded( false ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
36  mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ),
37  mExpressionFeature( 0 ), mExpressionLayer( 0 )
38 {
40 
41  //get default composer font from settings
42  QSettings settings;
43  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
44  if ( !defaultFontString.isEmpty() )
45  {
46  mFont.setFamily( defaultFontString );
47  }
48 
49  //default to a 10 point font size
50  mFont.setPointSizeF( 10 );
51 
53  {
54  //a label added while atlas preview is enabled needs to have the expression context set,
55  //otherwise fields in the label aren't correctly evaluated until atlas preview feature changes (#9457)
57  }
58 
59  //connect to atlas feature changes
60  //to update the expression context
61  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshExpressionContext() ) );
62 
63 }
64 
66 {
67 }
68 
69 void QgsComposerLabel::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 
78  drawBackground( painter );
79  painter->save();
80 
81  double penWidth = hasFrame() ? pen().widthF() : 0;
82  QRectF painterRect( penWidth + mMargin, penWidth + mMargin, rect().width() - 2 * penWidth - 2 * mMargin, rect().height() - 2 * penWidth - 2 * mMargin );
83 
84  QString textToDraw = displayText();
85 
86  if ( mHtmlState )
87  {
88  painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
89 
90  QWebPage *webPage = new QWebPage();
91  webPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
92 
93  //Setup event loop and timeout for rendering html
94  QEventLoop loop;
95  QTimer timeoutTimer;
96  timeoutTimer.setSingleShot( true );
97 
98  //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
99  QPalette palette = webPage->palette();
100  palette.setBrush( QPalette::Base, Qt::transparent );
101  webPage->setPalette( palette );
102  //webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
103 
104  webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
105  webPage->mainFrame()->setZoomFactor( 10.0 );
106  webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
107  webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
108 
109  // QGIS segfaults when rendering web page while in composer if html
110  // contains images. So if we are not printing the composition, then
111  // disable image loading
114  {
115  webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
116  }
117 
118  //Connect timeout and webpage loadFinished signals to loop
119  connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
120  connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
121 
122  //mHtmlLoaded tracks whether the QWebPage has completed loading
123  //its html contents, set it initially to false. The loadingHtmlFinished slot will
124  //set this to true after html is loaded.
125  mHtmlLoaded = false;
126  connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
127 
128  webPage->mainFrame()->setHtml( textToDraw );
129 
130  //For very basic html labels with no external assets, the html load will already be
131  //complete before we even get a chance to start the QEventLoop. Make sure we check
132  //this before starting the loop
133  if ( !mHtmlLoaded )
134  {
135  // Start a 20 second timeout in case html loading will never complete
136  timeoutTimer.start( 20000 );
137  // Pause until html is loaded
138  loop.exec();
139  }
140 
141  webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
142  }
143  else
144  {
145  painter->setPen( QPen( QColor( mFontColor ) ) );
146  painter->setFont( mFont );
147 
148  QFontMetricsF fontSize( mFont );
149 
150  //debug
151  //painter->setPen( QColor( Qt::red ) );
152  //painter->drawRect( painterRect );
153  drawText( painter, painterRect, textToDraw, mFont, mHAlignment, mVAlignment, Qt::TextWordWrap );
154  }
155 
156  painter->restore();
157 
158  drawFrame( painter );
159  if ( isSelected() )
160  {
161  drawSelectionBoxes( painter );
162  }
163 }
164 
165 /*Track when QWebPage has finished loading its html contents*/
167 {
168  Q_UNUSED( result );
169  mHtmlLoaded = true;
170 }
171 
173 {
174  if ( !mComposition )
175  {
176  return 1.0;
177  }
178 
179  //TODO : fix this more precisely so that the label's default text size is the same with or without "display as html"
180  return ( mComposition->printResolution() / 72.0 ); //webkit seems to assume a standard dpi of 72
181 }
182 
183 void QgsComposerLabel::setText( const QString& text )
184 {
185  mText = text;
186  emit itemChanged();
187 }
188 
189 void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions )
190 {
191  mExpressionFeature = feature;
192  mExpressionLayer = layer;
193  mSubstitutions = substitutions;
194  // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
195  update();
196 }
197 
199 {
200  QgsVectorLayer * vl = 0;
201  QgsFeature* feature = 0;
202 
204  {
206  }
208  {
210  }
211 
212  setExpressionContext( feature, vl );
213 }
214 
216 {
217  QString displayText = mText;
218  replaceDateText( displayText );
219  QMap<QString, QVariant> subs = mSubstitutions;
220  subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 );
222 }
223 
224 void QgsComposerLabel::replaceDateText( QString& text ) const
225 {
226  QString constant = "$CURRENT_DATE";
227  int currentDatePos = text.indexOf( constant );
228  if ( currentDatePos != -1 )
229  {
230  //check if there is a bracket just after $CURRENT_DATE
231  QString formatText;
232  int openingBracketPos = text.indexOf( "(", currentDatePos );
233  int closingBracketPos = text.indexOf( ")", openingBracketPos + 1 );
234  if ( openingBracketPos != -1 &&
235  closingBracketPos != -1 &&
236  ( closingBracketPos - openingBracketPos ) > 1 &&
237  openingBracketPos == currentDatePos + constant.size() )
238  {
239  formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
240  text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
241  }
242  else //no bracket
243  {
244  text.replace( "$CURRENT_DATE", QDate::currentDate().toString() );
245  }
246  }
247 }
248 
249 void QgsComposerLabel::setFont( const QFont& f )
250 {
251  mFont = f;
252 }
253 
255 {
256  double textWidth = textWidthMillimeters( mFont, displayText() );
257  double fontHeight = fontHeightMillimeters( mFont );
258 
259  double penWidth = hasFrame() ? pen().widthF() : 0;
260 
261  double width = textWidth + 2 * mMargin + 2 * penWidth + 1;
262  double height = fontHeight + 2 * mMargin + 2 * penWidth;
263 
264  //keep alignment point constant
265  double xShift = 0;
266  double yShift = 0;
267  itemShiftAdjustSize( width, height, xShift, yShift );
268 
269  setSceneRect( QRectF( pos().x() + xShift, pos().y() + yShift, width, height ) );
270 }
271 
273 {
274  return mFont;
275 }
276 
277 bool QgsComposerLabel::writeXML( QDomElement& elem, QDomDocument & doc ) const
278 {
279  QString alignment;
280 
281  if ( elem.isNull() )
282  {
283  return false;
284  }
285 
286  QDomElement composerLabelElem = doc.createElement( "ComposerLabel" );
287 
288  composerLabelElem.setAttribute( "htmlState", mHtmlState );
289 
290  composerLabelElem.setAttribute( "labelText", mText );
291  composerLabelElem.setAttribute( "margin", QString::number( mMargin ) );
292 
293  composerLabelElem.setAttribute( "halign", mHAlignment );
294  composerLabelElem.setAttribute( "valign", mVAlignment );
295 
296  //font
297  QDomElement labelFontElem = doc.createElement( "LabelFont" );
298  labelFontElem.setAttribute( "description", mFont.toString() );
299  composerLabelElem.appendChild( labelFontElem );
300 
301  //font color
302  QDomElement fontColorElem = doc.createElement( "FontColor" );
303  fontColorElem.setAttribute( "red", mFontColor.red() );
304  fontColorElem.setAttribute( "green", mFontColor.green() );
305  fontColorElem.setAttribute( "blue", mFontColor.blue() );
306  composerLabelElem.appendChild( fontColorElem );
307 
308  elem.appendChild( composerLabelElem );
309  return _writeXML( composerLabelElem, doc );
310 }
311 
312 bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument& doc )
313 {
314  QString alignment;
315 
316  if ( itemElem.isNull() )
317  {
318  return false;
319  }
320 
321  //restore label specific properties
322 
323  //text
324  mText = itemElem.attribute( "labelText" );
325 
326  //html state
327  mHtmlState = itemElem.attribute( "htmlState" ).toInt();
328 
329  //margin
330  mMargin = itemElem.attribute( "margin" ).toDouble();
331 
332  //Horizontal alignment
333  mHAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "halign" ).toInt() );
334 
335  //Vertical alignment
336  mVAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "valign" ).toInt() );
337 
338  //font
339  QDomNodeList labelFontList = itemElem.elementsByTagName( "LabelFont" );
340  if ( labelFontList.size() > 0 )
341  {
342  QDomElement labelFontElem = labelFontList.at( 0 ).toElement();
343  mFont.fromString( labelFontElem.attribute( "description" ) );
344  }
345 
346  //font color
347  QDomNodeList fontColorList = itemElem.elementsByTagName( "FontColor" );
348  if ( fontColorList.size() > 0 )
349  {
350  QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
351  int red = fontColorElem.attribute( "red", "0" ).toInt();
352  int green = fontColorElem.attribute( "green", "0" ).toInt();
353  int blue = fontColorElem.attribute( "blue", "0" ).toInt();
354  mFontColor = QColor( red, green, blue );
355  }
356  else
357  {
358  mFontColor = QColor( 0, 0, 0 );
359  }
360 
361  //restore general composer item properties
362  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
363  if ( composerItemList.size() > 0 )
364  {
365  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
366 
367  //rotation
368  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
369  {
370  //check for old (pre 2.1) rotation attribute
371  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
372  }
373 
374  _readXML( composerItemElem, doc );
375  }
376  emit itemChanged();
377  return true;
378 }
379 
380 void QgsComposerLabel::itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const
381 {
382  //keep alignment point constant
383  double currentWidth = rect().width();
384  double currentHeight = rect().height();
385  xShift = 0;
386  yShift = 0;
387 
388  if ( mItemRotation >= 0 && mItemRotation < 90 )
389  {
390  if ( mHAlignment == Qt::AlignHCenter )
391  {
392  xShift = - ( newWidth - currentWidth ) / 2.0;
393  }
394  else if ( mHAlignment == Qt::AlignRight )
395  {
396  xShift = - ( newWidth - currentWidth );
397  }
398  if ( mVAlignment == Qt::AlignVCenter )
399  {
400  yShift = -( newHeight - currentHeight ) / 2.0;
401  }
402  else if ( mVAlignment == Qt::AlignBottom )
403  {
404  yShift = - ( newHeight - currentHeight );
405  }
406  }
407  if ( mItemRotation >= 90 && mItemRotation < 180 )
408  {
409  if ( mHAlignment == Qt::AlignHCenter )
410  {
411  yShift = -( newHeight - currentHeight ) / 2.0;
412  }
413  else if ( mHAlignment == Qt::AlignRight )
414  {
415  yShift = -( newHeight - currentHeight );
416  }
417  if ( mVAlignment == Qt::AlignTop )
418  {
419  xShift = -( newWidth - currentWidth );
420  }
421  else if ( mVAlignment == Qt::AlignVCenter )
422  {
423  xShift = -( newWidth - currentWidth / 2.0 );
424  }
425  }
426  else if ( mItemRotation >= 180 && mItemRotation < 270 )
427  {
428  if ( mHAlignment == Qt::AlignHCenter )
429  {
430  xShift = -( newWidth - currentWidth ) / 2.0;
431  }
432  else if ( mHAlignment == Qt::AlignLeft )
433  {
434  xShift = -( newWidth - currentWidth );
435  }
436  if ( mVAlignment == Qt::AlignVCenter )
437  {
438  yShift = ( newHeight - currentHeight ) / 2.0;
439  }
440  else if ( mVAlignment == Qt::AlignTop )
441  {
442  yShift = ( newHeight - currentHeight );
443  }
444  }
445  else if ( mItemRotation >= 270 && mItemRotation < 360 )
446  {
447  if ( mHAlignment == Qt::AlignHCenter )
448  {
449  yShift = -( newHeight - currentHeight ) / 2.0;
450  }
451  else if ( mHAlignment == Qt::AlignLeft )
452  {
453  yShift = -( newHeight - currentHeight );
454  }
455  if ( mVAlignment == Qt::AlignBottom )
456  {
457  xShift = -( newWidth - currentWidth );
458  }
459  else if ( mVAlignment == Qt::AlignVCenter )
460  {
461  xShift = -( newWidth - currentWidth / 2.0 );
462  }
463  }
464 }
QgsComposition::AtlasMode atlasMode() const
Returns the current atlas mode of the composition.
QgsVectorLayer * mExpressionLayer
void replaceDateText(QString &text) const
Replaces replace '$CURRENT_DATE<(FORMAT)>' with the current date (e.g.
A item that forms part of a map composition.
bool enabled() const
Returns whether the atlas generation is enabled.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
void setFont(const QFont &f)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
void itemChanged()
Used e.g.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QgsFeature * mExpressionFeature
int itemPageNumber(const QgsComposerItem *) const
Returns on which page number (0-based) is displayed an item.
QFont font() const
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
void itemShiftAdjustSize(double newWidth, double newHeight, double &xShift, double &yShift) const
Helper function to calculate x/y shift for adjustSizeToText() depending on rotation, current size and alignment.
int printResolution() const
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint.
QMap< QString, QVariant > mSubstitutions
QgsComposerLabel(QgsComposition *composition)
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
Qt::AlignmentFlag mHAlignment
QgsComposition * mComposition
Graphics scene for map printing.
QgsFeature * currentFeature()
Returns the current atlas feature.
virtual void setItemRotation(double r, bool adjustPosition=false)
Sets the item rotation.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
Qt::AlignmentFlag mVAlignment
virtual void drawBackground(QPainter *p)
Draw background.
bool hasFrame() const
Whether this item has a frame or not.
double fontHeightMillimeters(const QFont &font) const
Returns the font height in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
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...
void setText(const QString &text)
QgsAtlasComposition & atlasComposition()
void setExpressionContext(QgsFeature *feature, QgsVectorLayer *layer, QMap< QString, QVariant > substitutions=(QMap< QString, QVariant >()))
Sets the current feature, the current layer and a list of local variable substitutions for evaluating...
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
QgsComposition::PlotStyle plotStyle() const
Represents a vector layer which manages a vector based data sets.
QString displayText() const
Returns the text as it appears on screen (with replaced data field)
double mItemRotation
Item rotation in degrees, clockwise.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom document
static QString replaceExpressionText(const QString &action, const QgsFeature *feat, QgsVectorLayer *layer, const QMap< QString, QVariant > *substitutionMap=0)
This function currently replaces each expression between [% and %] in the string with the result of i...
void loadingHtmlFinished(bool)
void adjustSizeToText()
resizes the widget such that the text fits to the item.
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element