mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Start porting label item
This commit is contained in:
parent
87d497eb2b
commit
83f177c1d2
@ -189,6 +189,7 @@ Get font color
|
||||
%End
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshExpressionContext();
|
||||
|
||||
|
||||
|
@ -168,7 +168,6 @@
|
||||
%Include layout/qgslayoutsize.sip
|
||||
%Include layout/qgslayoutsnapper.sip
|
||||
%Include layout/qgslayoutundocommand.sip
|
||||
%Include layout/qgslayoutundostack.sip
|
||||
%Include layout/qgslayoututils.sip
|
||||
%Include metadata/qgslayermetadata.sip
|
||||
%Include metadata/qgslayermetadatavalidator.sip
|
||||
@ -408,6 +407,7 @@
|
||||
%Include layout/qgslayoutguidecollection.sip
|
||||
%Include layout/qgslayoutitem.sip
|
||||
%Include layout/qgslayoutitemgroup.sip
|
||||
%Include layout/qgslayoutitemlabel.sip
|
||||
%Include layout/qgslayoutitemmap.sip
|
||||
%Include layout/qgslayoutitemmapgrid.sip
|
||||
%Include layout/qgslayoutitemmapitem.sip
|
||||
@ -422,6 +422,7 @@
|
||||
%Include layout/qgslayoutmodel.sip
|
||||
%Include layout/qgslayoutpagecollection.sip
|
||||
%Include layout/qgslayoutobject.sip
|
||||
%Include layout/qgslayoutundostack.sip
|
||||
%Include symbology/qgscptcityarchive.sip
|
||||
%Include symbology/qgssvgcache.sip
|
||||
%Include symbology/qgsstyle.sip
|
||||
|
224
python/core/layout/qgslayoutitemlabel.sip
Normal file
224
python/core/layout/qgslayoutitemlabel.sip
Normal file
@ -0,0 +1,224 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/layout/qgslayoutitemlabel.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsLayoutItemLabel: QgsLayoutItem
|
||||
{
|
||||
%Docstring
|
||||
A layout item subclass for text labels.
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslayoutitemlabel.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
enum Mode
|
||||
{
|
||||
ModeFont,
|
||||
ModeHtml,
|
||||
};
|
||||
|
||||
QgsLayoutItemLabel( QgsLayout *layout );
|
||||
%Docstring
|
||||
Constructor for QgsLayoutItemLabel, with the specified parent ``layout``.
|
||||
%End
|
||||
|
||||
static QgsLayoutItemLabel *create( QgsLayout *layout ) /Factory/;
|
||||
%Docstring
|
||||
Returns a new label item for the specified ``layout``.
|
||||
|
||||
The caller takes responsibility for deleting the returned object.
|
||||
:rtype: QgsLayoutItemLabel
|
||||
%End
|
||||
|
||||
|
||||
virtual int type() const;
|
||||
|
||||
virtual QString stringType() const;
|
||||
|
||||
virtual QString displayName() const;
|
||||
|
||||
void adjustSizeToText();
|
||||
%Docstring
|
||||
Resizes the item so that the label's text fits to the item. Keeps the top left point stationary.
|
||||
%End
|
||||
|
||||
QString text();
|
||||
%Docstring
|
||||
Returns the label's preset text.
|
||||
.. seealso:: currentText()
|
||||
.. seealso:: setText()
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
void setText( const QString &text );
|
||||
%Docstring
|
||||
Sets the label's preset ``text``.
|
||||
.. seealso:: text()
|
||||
%End
|
||||
|
||||
QString currentText() const;
|
||||
%Docstring
|
||||
Returns the text as it appears on the label (with evaluated expressions
|
||||
and other dynamic content).
|
||||
.. seealso:: text()
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
Mode mode() const;
|
||||
%Docstring
|
||||
Returns the label's current mode.
|
||||
.. seealso:: setMode()
|
||||
:rtype: Mode
|
||||
%End
|
||||
|
||||
void setMode( Mode mode );
|
||||
%Docstring
|
||||
Sets the label's current ``mode``, allowing the label
|
||||
to switch between font based and HTML based rendering.
|
||||
.. seealso:: mode()
|
||||
%End
|
||||
|
||||
QFont font() const;
|
||||
%Docstring
|
||||
Returns the label's current font.
|
||||
.. seealso:: setFont()
|
||||
:rtype: QFont
|
||||
%End
|
||||
|
||||
void setFont( const QFont &font );
|
||||
%Docstring
|
||||
Sets the label's current ``font``.
|
||||
.. seealso:: font()
|
||||
%End
|
||||
|
||||
Qt::AlignmentFlag vAlign() const;
|
||||
%Docstring
|
||||
Returns for the vertical alignment of the label.
|
||||
.. seealso:: setVAlign()
|
||||
.. seealso:: hAlign()
|
||||
:rtype: Qt.AlignmentFlag
|
||||
%End
|
||||
|
||||
Qt::AlignmentFlag hAlign() const;
|
||||
%Docstring
|
||||
Returns the horizontal alignment of the label.
|
||||
.. seealso:: vAlign()
|
||||
.. seealso:: setHAlign()
|
||||
:rtype: Qt.AlignmentFlag
|
||||
%End
|
||||
|
||||
void setHAlign( Qt::AlignmentFlag alignment );
|
||||
%Docstring
|
||||
Sets the horizontal ``alignment`` of the label.
|
||||
.. seealso:: hAlign()
|
||||
.. seealso:: setVAlign()
|
||||
%End
|
||||
|
||||
void setVAlign( Qt::AlignmentFlag alignment );
|
||||
%Docstring
|
||||
Sets for the vertical ``alignment`` of the label.
|
||||
.. seealso:: vAlign()
|
||||
.. seealso:: setHAlign()
|
||||
%End
|
||||
|
||||
double marginX() const;
|
||||
%Docstring
|
||||
Returns the horizontal margin between the edge of the frame and the label
|
||||
contents, in layout units.
|
||||
.. seealso:: setMargin()
|
||||
.. seealso:: marginY()
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
double marginY() const;
|
||||
%Docstring
|
||||
Returns the vertical margin between the edge of the frame and the label
|
||||
contents, in layout units.
|
||||
.. seealso:: setMargin()
|
||||
.. seealso:: marginX()
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
void setMargin( double margin );
|
||||
%Docstring
|
||||
Sets the ``margin`` between the edge of the frame and the label contents.
|
||||
This method sets both the horizontal and vertical margins to the same
|
||||
value. The margins can be individually controlled using the setMarginX()
|
||||
and setMarginY() methods.
|
||||
|
||||
Margins are set using the current layout units.
|
||||
|
||||
.. seealso:: setMarginX()
|
||||
.. seealso:: setMarginY()
|
||||
%End
|
||||
|
||||
void setMarginX( double margin );
|
||||
%Docstring
|
||||
Sets the horizontal ``margin`` between the edge of the frame and the label
|
||||
contents, in layout units.
|
||||
.. seealso:: setMargin()
|
||||
.. seealso:: setMarginY()
|
||||
%End
|
||||
|
||||
void setMarginY( double margin );
|
||||
%Docstring
|
||||
Sets the vertical ``margin`` between the edge of the frame and the label
|
||||
contents, in layout units.
|
||||
.. seealso:: setMargin()
|
||||
.. seealso:: setMarginX()
|
||||
%End
|
||||
|
||||
void setFontColor( const QColor &color );
|
||||
%Docstring
|
||||
Sets the label font ``color``.
|
||||
.. seealso:: fontColor()
|
||||
%End
|
||||
|
||||
QColor fontColor() const;
|
||||
%Docstring
|
||||
Returns the label font color.
|
||||
.. seealso:: setFontColor()
|
||||
:rtype: QColor
|
||||
%End
|
||||
|
||||
virtual QRectF boundingRect() const;
|
||||
|
||||
|
||||
virtual void setFrameEnabled( const bool drawFrame );
|
||||
|
||||
|
||||
virtual void setFrameStrokeWidth( const QgsLayoutMeasurement &strokeWidth );
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
|
||||
protected:
|
||||
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );
|
||||
|
||||
virtual bool writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/layout/qgslayoutitemlabel.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -104,6 +104,7 @@ class QgsLayoutItemRegistry : QObject
|
||||
LayoutPage,
|
||||
LayoutMap,
|
||||
LayoutPicture,
|
||||
LayoutLabel,
|
||||
LayoutShape,
|
||||
LayoutPolygon,
|
||||
LayoutPolyline,
|
||||
|
@ -369,6 +369,7 @@ SET(QGIS_CORE_SRCS
|
||||
layout/qgslayoutitem.cpp
|
||||
layout/qgslayoutitemgroup.cpp
|
||||
layout/qgslayoutitemgroupundocommand.cpp
|
||||
layout/qgslayoutitemlabel.cpp
|
||||
layout/qgslayoutitemmap.cpp
|
||||
layout/qgslayoutitemmapgrid.cpp
|
||||
layout/qgslayoutitemmapitem.cpp
|
||||
@ -728,6 +729,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
layout/qgslayoutitem.h
|
||||
layout/qgslayoutitemgroup.h
|
||||
layout/qgslayoutitemgroupundocommand.h
|
||||
layout/qgslayoutitemlabel.h
|
||||
layout/qgslayoutitemmap.h
|
||||
layout/qgslayoutitemmapgrid.h
|
||||
layout/qgslayoutitemmapitem.h
|
||||
|
@ -170,6 +170,7 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
|
||||
virtual void setFrameStrokeWidth( const double strokeWidth ) override;
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshExpressionContext();
|
||||
|
||||
|
||||
|
603
src/core/layout/qgslayoutitemlabel.cpp
Normal file
603
src/core/layout/qgslayoutitemlabel.cpp
Normal file
@ -0,0 +1,603 @@
|
||||
/***************************************************************************
|
||||
qgslayoutitemlabel.cpp
|
||||
-------------------
|
||||
begin : October 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslayoutitemlabel.h"
|
||||
#include "qgslayoutitemregistry.h"
|
||||
#include "qgslayout.h"
|
||||
#include "qgslayoututils.h"
|
||||
#include "qgslayoutmodel.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsnetworkaccessmanager.h"
|
||||
#include "qgscomposermodel.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsdistancearea.h"
|
||||
#include "qgsfontutils.h"
|
||||
#include "qgsexpressioncontext.h"
|
||||
#include "qgsmapsettings.h"
|
||||
#include "qgscomposermap.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#include "qgswebview.h"
|
||||
#include "qgswebframe.h"
|
||||
#include "qgswebpage.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDate>
|
||||
#include <QDomElement>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QEventLoop>
|
||||
|
||||
QgsLayoutItemLabel::QgsLayoutItemLabel( QgsLayout *layout )
|
||||
: QgsLayoutItem( layout )
|
||||
{
|
||||
mDistanceArea.reset( new QgsDistanceArea() );
|
||||
mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
|
||||
|
||||
//get default composer font from settings
|
||||
QgsSettings settings;
|
||||
QString defaultFontString = settings.value( QStringLiteral( "Composer/defaultFont" ) ).toString();
|
||||
if ( !defaultFontString.isEmpty() )
|
||||
{
|
||||
mFont.setFamily( defaultFontString );
|
||||
}
|
||||
|
||||
//default to a 10 point font size
|
||||
mFont.setPointSizeF( 10 );
|
||||
|
||||
//default to no background
|
||||
setBackgroundEnabled( false );
|
||||
|
||||
//a label added while atlas preview is enabled needs to have the expression context set,
|
||||
//otherwise fields in the label aren't correctly evaluated until atlas preview feature changes (#9457)
|
||||
refreshExpressionContext();
|
||||
|
||||
if ( mLayout )
|
||||
{
|
||||
#if 0 //TODO
|
||||
//connect to atlas feature changes
|
||||
//to update the expression context
|
||||
connect( &mLayout->atlasComposition(), &QgsAtlasComposition::featureChanged, this, &QgsLayoutItemLabel::refreshExpressionContext );
|
||||
#endif
|
||||
}
|
||||
|
||||
mWebPage.reset( new QgsWebPage( this ) );
|
||||
mWebPage->setIdentifier( tr( "Layout label item" ) );
|
||||
mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
|
||||
|
||||
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
|
||||
QPalette palette = mWebPage->palette();
|
||||
palette.setBrush( QPalette::Base, Qt::transparent );
|
||||
mWebPage->setPalette( palette );
|
||||
|
||||
mWebPage->mainFrame()->setZoomFactor( 10.0 );
|
||||
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
|
||||
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
|
||||
|
||||
connect( mWebPage.get(), &QWebPage::loadFinished, this, &QgsLayoutItemLabel::loadingHtmlFinished );
|
||||
}
|
||||
|
||||
QgsLayoutItemLabel *QgsLayoutItemLabel::create( QgsLayout *layout )
|
||||
{
|
||||
return new QgsLayoutItemLabel( layout );
|
||||
}
|
||||
|
||||
int QgsLayoutItemLabel::type() const
|
||||
{
|
||||
return QgsLayoutItemRegistry::LayoutLabel;
|
||||
}
|
||||
|
||||
QString QgsLayoutItemLabel::stringType() const
|
||||
{
|
||||
return QStringLiteral( "ItemLabel" );
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * )
|
||||
{
|
||||
QPainter *painter = context.painter();
|
||||
painter->save();
|
||||
|
||||
// painter is scaled to dots, so scale back to layout units
|
||||
painter->scale( context.scaleFactor(), context.scaleFactor() );
|
||||
|
||||
double penWidth = hasFrame() ? ( pen().widthF() / 2.0 ) : 0;
|
||||
double xPenAdjust = mMarginX < 0 ? -penWidth : penWidth;
|
||||
double yPenAdjust = mMarginY < 0 ? -penWidth : penWidth;
|
||||
QRectF painterRect( xPenAdjust + mMarginX, yPenAdjust + mMarginY, rect().width() - 2 * xPenAdjust - 2 * mMarginX, rect().height() - 2 * yPenAdjust - 2 * mMarginY );
|
||||
|
||||
switch ( mMode )
|
||||
{
|
||||
case ModeHtml:
|
||||
{
|
||||
if ( mFirstRender )
|
||||
{
|
||||
contentChanged();
|
||||
mFirstRender = false;
|
||||
}
|
||||
painter->scale( 1.0 / mHtmlUnitsToLayoutUnits / 10.0, 1.0 / mHtmlUnitsToLayoutUnits / 10.0 );
|
||||
mWebPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToLayoutUnits * 10.0, painterRect.height() * mHtmlUnitsToLayoutUnits * 10.0 ) );
|
||||
mWebPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );
|
||||
mWebPage->mainFrame()->render( painter );
|
||||
break;
|
||||
}
|
||||
|
||||
case ModeFont:
|
||||
{
|
||||
const QString textToDraw = currentText();
|
||||
painter->setFont( mFont );
|
||||
QgsLayoutUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::contentChanged()
|
||||
{
|
||||
switch ( mMode )
|
||||
{
|
||||
case ModeHtml:
|
||||
{
|
||||
const QString textToDraw = currentText();
|
||||
|
||||
//mHtmlLoaded tracks whether the QWebPage has completed loading
|
||||
//its html contents, set it initially to false. The loadingHtmlFinished slot will
|
||||
//set this to true after html is loaded.
|
||||
mHtmlLoaded = false;
|
||||
|
||||
const QUrl baseUrl = QUrl::fromLocalFile( mLayout->project()->fileInfo().absoluteFilePath() );
|
||||
mWebPage->mainFrame()->setHtml( textToDraw, baseUrl );
|
||||
|
||||
//For very basic html labels with no external assets, the html load will already be
|
||||
//complete before we even get a chance to start the QEventLoop. Make sure we check
|
||||
//this before starting the loop
|
||||
if ( !mHtmlLoaded )
|
||||
{
|
||||
//Setup event loop and timeout for rendering html
|
||||
QEventLoop loop;
|
||||
|
||||
//Connect timeout and webpage loadFinished signals to loop
|
||||
connect( mWebPage.get(), &QWebPage::loadFinished, &loop, &QEventLoop::quit );
|
||||
|
||||
// Start a 20 second timeout in case html loading will never complete
|
||||
QTimer timeoutTimer;
|
||||
timeoutTimer.setSingleShot( true );
|
||||
connect( &timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit );
|
||||
timeoutTimer.start( 20000 );
|
||||
|
||||
// Pause until html is loaded
|
||||
loop.exec( QEventLoop::ExcludeUserInputEvents );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ModeFont:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::loadingHtmlFinished( bool result )
|
||||
{
|
||||
Q_UNUSED( result );
|
||||
mHtmlLoaded = true;
|
||||
}
|
||||
|
||||
double QgsLayoutItemLabel::htmlUnitsToLayoutUnits()
|
||||
{
|
||||
if ( !mLayout )
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
//TODO : fix this more precisely so that the label's default text size is the same with or without "display as html"
|
||||
return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->context().dpi() / 72.0, QgsUnitTypes::LayoutMillimeters ) ); //webkit seems to assume a standard dpi of 72
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setText( const QString &text )
|
||||
{
|
||||
mText = text;
|
||||
emit changed();
|
||||
|
||||
contentChanged();
|
||||
|
||||
if ( mLayout && id().isEmpty() && mMode != ModeHtml )
|
||||
{
|
||||
//notify the model that the display name has changed
|
||||
mLayout->itemsModel()->updateItemDisplayName( this );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setMode( Mode mode )
|
||||
{
|
||||
if ( mode == mMode )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mMode = mode;
|
||||
contentChanged();
|
||||
|
||||
if ( mLayout && id().isEmpty() )
|
||||
{
|
||||
//notify the model that the display name has changed
|
||||
mLayout->itemsModel()->updateItemDisplayName( this );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::refreshExpressionContext()
|
||||
{
|
||||
if ( !mLayout )
|
||||
return;
|
||||
|
||||
QgsVectorLayer *layer = nullptr;
|
||||
#if 0 //TODO
|
||||
if ( mComposition->atlasComposition().enabled() )
|
||||
{
|
||||
layer = mComposition->atlasComposition().coverageLayer();
|
||||
}
|
||||
#endif
|
||||
|
||||
//setup distance area conversion
|
||||
if ( layer )
|
||||
{
|
||||
mDistanceArea->setSourceCrs( layer->crs() );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0 //TODO
|
||||
//set to composition's reference map's crs
|
||||
QgsLayoutItemMap *referenceMap = mComposition->referenceMap();
|
||||
if ( referenceMap )
|
||||
mDistanceArea->setSourceCrs( referenceMap->crs() );
|
||||
#endif
|
||||
}
|
||||
mDistanceArea->setEllipsoid( mLayout->project()->ellipsoid() );
|
||||
contentChanged();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QString QgsLayoutItemLabel::currentText() const
|
||||
{
|
||||
QString displayText = mText;
|
||||
replaceDateText( displayText );
|
||||
|
||||
QgsExpressionContext context = createExpressionContext();
|
||||
|
||||
return QgsExpression::replaceExpressionText( displayText, &context, mDistanceArea.get() );
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::replaceDateText( QString &text ) const
|
||||
{
|
||||
QString constant = QStringLiteral( "$CURRENT_DATE" );
|
||||
int currentDatePos = text.indexOf( constant );
|
||||
if ( currentDatePos != -1 )
|
||||
{
|
||||
//check if there is a bracket just after $CURRENT_DATE
|
||||
QString formatText;
|
||||
int openingBracketPos = text.indexOf( '(', currentDatePos );
|
||||
int closingBracketPos = text.indexOf( ')', openingBracketPos + 1 );
|
||||
if ( openingBracketPos != -1 &&
|
||||
closingBracketPos != -1 &&
|
||||
( closingBracketPos - openingBracketPos ) > 1 &&
|
||||
openingBracketPos == currentDatePos + constant.size() )
|
||||
{
|
||||
formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
|
||||
text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
|
||||
}
|
||||
else //no bracket
|
||||
{
|
||||
text.replace( QLatin1String( "$CURRENT_DATE" ), QDate::currentDate().toString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setFont( const QFont &f )
|
||||
{
|
||||
mFont = f;
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setMargin( const double m )
|
||||
{
|
||||
mMarginX = m;
|
||||
mMarginY = m;
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setMarginX( const double margin )
|
||||
{
|
||||
mMarginX = margin;
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setMarginY( const double margin )
|
||||
{
|
||||
mMarginY = margin;
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::adjustSizeToText()
|
||||
{
|
||||
double textWidth = QgsLayoutUtils::textWidthMM( mFont, currentText() );
|
||||
double fontHeight = QgsLayoutUtils::fontHeightMM( mFont );
|
||||
|
||||
double penWidth = hasFrame() ? ( pen().widthF() / 2.0 ) : 0;
|
||||
|
||||
double width = textWidth + 2 * mMarginX + 2 * penWidth + 1;
|
||||
double height = fontHeight + 2 * mMarginY + 2 * penWidth;
|
||||
|
||||
//keep alignment point constant
|
||||
double xShift = 0;
|
||||
double yShift = 0;
|
||||
|
||||
QSizeF newSize = mLayout->convertToLayoutUnits( QgsLayoutSize( width, height, QgsUnitTypes::LayoutMillimeters ) );
|
||||
itemShiftAdjustSize( newSize.width(), newSize.height(), xShift, yShift );
|
||||
|
||||
//update rect for data defined size and position
|
||||
attemptSetSceneRect( QRectF( pos().x() + xShift, pos().y() + yShift, width, height ) );
|
||||
}
|
||||
|
||||
QFont QgsLayoutItemLabel::font() const
|
||||
{
|
||||
return mFont;
|
||||
}
|
||||
|
||||
bool QgsLayoutItemLabel::writePropertiesToElement( QDomElement &composerLabelElem, QDomDocument &doc, const QgsReadWriteContext & ) const
|
||||
{
|
||||
composerLabelElem.setAttribute( QStringLiteral( "htmlState" ), static_cast< int >( mMode ) );
|
||||
|
||||
composerLabelElem.setAttribute( QStringLiteral( "labelText" ), mText );
|
||||
composerLabelElem.setAttribute( QStringLiteral( "marginX" ), QString::number( mMarginX ) );
|
||||
composerLabelElem.setAttribute( QStringLiteral( "marginY" ), QString::number( mMarginY ) );
|
||||
composerLabelElem.setAttribute( QStringLiteral( "halign" ), mHAlignment );
|
||||
composerLabelElem.setAttribute( QStringLiteral( "valign" ), mVAlignment );
|
||||
|
||||
//font
|
||||
QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "LabelFont" ) );
|
||||
composerLabelElem.appendChild( labelFontElem );
|
||||
|
||||
//font color
|
||||
QDomElement fontColorElem = doc.createElement( QStringLiteral( "FontColor" ) );
|
||||
fontColorElem.setAttribute( QStringLiteral( "red" ), mFontColor.red() );
|
||||
fontColorElem.setAttribute( QStringLiteral( "green" ), mFontColor.green() );
|
||||
fontColorElem.setAttribute( QStringLiteral( "blue" ), mFontColor.blue() );
|
||||
composerLabelElem.appendChild( fontColorElem );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsLayoutItemLabel::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
|
||||
{
|
||||
//restore label specific properties
|
||||
|
||||
//text
|
||||
mText = itemElem.attribute( QStringLiteral( "labelText" ) );
|
||||
|
||||
//html state
|
||||
mMode = static_cast< Mode >( itemElem.attribute( QStringLiteral( "htmlState" ) ).toInt() );
|
||||
|
||||
//margin
|
||||
bool marginXOk = false;
|
||||
bool marginYOk = false;
|
||||
mMarginX = itemElem.attribute( QStringLiteral( "marginX" ) ).toDouble( &marginXOk );
|
||||
mMarginY = itemElem.attribute( QStringLiteral( "marginY" ) ).toDouble( &marginYOk );
|
||||
if ( !marginXOk || !marginYOk )
|
||||
{
|
||||
//upgrade old projects where margins where stored in a single attribute
|
||||
double margin = itemElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "1.0" ) ).toDouble();
|
||||
mMarginX = margin;
|
||||
mMarginY = margin;
|
||||
}
|
||||
|
||||
//Horizontal alignment
|
||||
mHAlignment = static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "halign" ) ).toInt() );
|
||||
|
||||
//Vertical alignment
|
||||
mVAlignment = static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "valign" ) ).toInt() );
|
||||
|
||||
//font
|
||||
QgsFontUtils::setFromXmlChildNode( mFont, itemElem, QStringLiteral( "LabelFont" ) );
|
||||
|
||||
//font color
|
||||
QDomNodeList fontColorList = itemElem.elementsByTagName( QStringLiteral( "FontColor" ) );
|
||||
if ( !fontColorList.isEmpty() )
|
||||
{
|
||||
QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
|
||||
int red = fontColorElem.attribute( QStringLiteral( "red" ), QStringLiteral( "0" ) ).toInt();
|
||||
int green = fontColorElem.attribute( QStringLiteral( "green" ), QStringLiteral( "0" ) ).toInt();
|
||||
int blue = fontColorElem.attribute( QStringLiteral( "blue" ), QStringLiteral( "0" ) ).toInt();
|
||||
mFontColor = QColor( red, green, blue );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFontColor = QColor( 0, 0, 0 );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QgsLayoutItemLabel::displayName() const
|
||||
{
|
||||
if ( !id().isEmpty() )
|
||||
{
|
||||
return id();
|
||||
}
|
||||
|
||||
switch ( mMode )
|
||||
{
|
||||
case ModeHtml:
|
||||
return tr( "<HTML Label>" );
|
||||
|
||||
case ModeFont:
|
||||
{
|
||||
|
||||
//if no id, default to portion of label text
|
||||
QString text = mText;
|
||||
if ( text.isEmpty() )
|
||||
{
|
||||
return tr( "<Label>" );
|
||||
}
|
||||
if ( text.length() > 25 )
|
||||
{
|
||||
return QString( tr( "%1..." ) ).arg( text.left( 25 ).simplified() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return text.simplified();
|
||||
}
|
||||
}
|
||||
}
|
||||
return QString(); // no warnings
|
||||
}
|
||||
|
||||
QRectF QgsLayoutItemLabel::boundingRect() const
|
||||
{
|
||||
QRectF rectangle = rect();
|
||||
double penWidth = hasFrame() ? ( pen().widthF() / 2.0 ) : 0;
|
||||
rectangle.adjust( -penWidth, -penWidth, penWidth, penWidth );
|
||||
|
||||
if ( mMarginX < 0 )
|
||||
{
|
||||
rectangle.adjust( mMarginX, 0, -mMarginX, 0 );
|
||||
}
|
||||
if ( mMarginY < 0 )
|
||||
{
|
||||
rectangle.adjust( 0, mMarginY, 0, -mMarginY );
|
||||
}
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setFrameEnabled( const bool drawFrame )
|
||||
{
|
||||
QgsLayoutItem::setFrameEnabled( drawFrame );
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::setFrameStrokeWidth( const QgsLayoutMeasurement &strokeWidth )
|
||||
{
|
||||
QgsLayoutItem::setFrameStrokeWidth( strokeWidth );
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::refresh()
|
||||
{
|
||||
contentChanged();
|
||||
}
|
||||
|
||||
void QgsLayoutItemLabel::itemShiftAdjustSize( double newWidth, double newHeight, double &xShift, double &yShift ) const
|
||||
{
|
||||
//keep alignment point constant
|
||||
double currentWidth = rect().width();
|
||||
double currentHeight = rect().height();
|
||||
xShift = 0;
|
||||
yShift = 0;
|
||||
|
||||
double r = rotation();
|
||||
if ( r >= 0 && r < 90 )
|
||||
{
|
||||
if ( mHAlignment == Qt::AlignHCenter )
|
||||
{
|
||||
xShift = - ( newWidth - currentWidth ) / 2.0;
|
||||
}
|
||||
else if ( mHAlignment == Qt::AlignRight )
|
||||
{
|
||||
xShift = - ( newWidth - currentWidth );
|
||||
}
|
||||
if ( mVAlignment == Qt::AlignVCenter )
|
||||
{
|
||||
yShift = -( newHeight - currentHeight ) / 2.0;
|
||||
}
|
||||
else if ( mVAlignment == Qt::AlignBottom )
|
||||
{
|
||||
yShift = - ( newHeight - currentHeight );
|
||||
}
|
||||
}
|
||||
if ( r >= 90 && r < 180 )
|
||||
{
|
||||
if ( mHAlignment == Qt::AlignHCenter )
|
||||
{
|
||||
yShift = -( newHeight - currentHeight ) / 2.0;
|
||||
}
|
||||
else if ( mHAlignment == Qt::AlignRight )
|
||||
{
|
||||
yShift = -( newHeight - currentHeight );
|
||||
}
|
||||
if ( mVAlignment == Qt::AlignTop )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth );
|
||||
}
|
||||
else if ( mVAlignment == Qt::AlignVCenter )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth / 2.0 );
|
||||
}
|
||||
}
|
||||
else if ( r >= 180 && r < 270 )
|
||||
{
|
||||
if ( mHAlignment == Qt::AlignHCenter )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth ) / 2.0;
|
||||
}
|
||||
else if ( mHAlignment == Qt::AlignLeft )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth );
|
||||
}
|
||||
if ( mVAlignment == Qt::AlignVCenter )
|
||||
{
|
||||
yShift = ( newHeight - currentHeight ) / 2.0;
|
||||
}
|
||||
else if ( mVAlignment == Qt::AlignTop )
|
||||
{
|
||||
yShift = ( newHeight - currentHeight );
|
||||
}
|
||||
}
|
||||
else if ( r >= 270 && r < 360 )
|
||||
{
|
||||
if ( mHAlignment == Qt::AlignHCenter )
|
||||
{
|
||||
yShift = -( newHeight - currentHeight ) / 2.0;
|
||||
}
|
||||
else if ( mHAlignment == Qt::AlignLeft )
|
||||
{
|
||||
yShift = -( newHeight - currentHeight );
|
||||
}
|
||||
if ( mVAlignment == Qt::AlignBottom )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth );
|
||||
}
|
||||
else if ( mVAlignment == Qt::AlignVCenter )
|
||||
{
|
||||
xShift = -( newWidth - currentWidth / 2.0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QUrl QgsLayoutItemLabel::createStylesheetUrl() const
|
||||
{
|
||||
QString stylesheet;
|
||||
stylesheet += QStringLiteral( "body { margin: %1 %2;" ).arg( std::max( mMarginY * mHtmlUnitsToLayoutUnits, 0.0 ) ).arg( std::max( mMarginX * mHtmlUnitsToLayoutUnits, 0.0 ) );
|
||||
stylesheet += QgsFontUtils::asCSS( mFont, 0.352778 * mHtmlUnitsToLayoutUnits );
|
||||
stylesheet += QStringLiteral( "color: %1;" ).arg( mFontColor.name() );
|
||||
stylesheet += QStringLiteral( "text-align: %1; }" ).arg( mHAlignment == Qt::AlignLeft ? QStringLiteral( "left" ) : mHAlignment == Qt::AlignRight ? QStringLiteral( "right" ) : mHAlignment == Qt::AlignHCenter ? QStringLiteral( "center" ) : QStringLiteral( "justify" ) );
|
||||
|
||||
QByteArray ba;
|
||||
ba.append( stylesheet.toUtf8() );
|
||||
QUrl cssFileURL = QUrl( "data:text/css;charset=utf-8;base64," + ba.toBase64() );
|
||||
|
||||
return cssFileURL;
|
||||
}
|
271
src/core/layout/qgslayoutitemlabel.h
Normal file
271
src/core/layout/qgslayoutitemlabel.h
Normal file
@ -0,0 +1,271 @@
|
||||
/***************************************************************************
|
||||
qgslayoutitemlabel.h
|
||||
-------------------
|
||||
begin : October 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSLAYOUTITEMLABEL_H
|
||||
#define QGSLAYOUTITEMLABEL_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgslayoutitem.h"
|
||||
#include "qgswebpage.h"
|
||||
#include <QFont>
|
||||
|
||||
class QgsVectorLayer;
|
||||
class QgsFeature;
|
||||
class QgsDistanceArea;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* A layout item subclass for text labels.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsLayoutItemLabel: public QgsLayoutItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
//! Label modes
|
||||
enum Mode
|
||||
{
|
||||
ModeFont, //!< Label displays text rendered using a single font
|
||||
ModeHtml, //!< Label displays rendered HTML content
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for QgsLayoutItemLabel, with the specified parent \a layout.
|
||||
*/
|
||||
QgsLayoutItemLabel( QgsLayout *layout );
|
||||
|
||||
/**
|
||||
* Returns a new label item for the specified \a layout.
|
||||
*
|
||||
* The caller takes responsibility for deleting the returned object.
|
||||
*/
|
||||
static QgsLayoutItemLabel *create( QgsLayout *layout ) SIP_FACTORY;
|
||||
|
||||
|
||||
int type() const override;
|
||||
QString stringType() const override;
|
||||
//Overridden to contain part of label's text
|
||||
virtual QString displayName() const override;
|
||||
|
||||
/**
|
||||
* Resizes the item so that the label's text fits to the item. Keeps the top left point stationary.
|
||||
*/
|
||||
void adjustSizeToText();
|
||||
|
||||
/**
|
||||
* Returns the label's preset text.
|
||||
* \see currentText()
|
||||
* \see setText()
|
||||
*/
|
||||
QString text() { return mText; }
|
||||
|
||||
/**
|
||||
* Sets the label's preset \a text.
|
||||
* \see text()
|
||||
*/
|
||||
void setText( const QString &text );
|
||||
|
||||
/**
|
||||
* Returns the text as it appears on the label (with evaluated expressions
|
||||
* and other dynamic content).
|
||||
* \see text()
|
||||
*/
|
||||
QString currentText() const;
|
||||
|
||||
/**
|
||||
* Returns the label's current mode.
|
||||
* \see setMode()
|
||||
*/
|
||||
Mode mode() const { return mMode; }
|
||||
|
||||
/**
|
||||
* Sets the label's current \a mode, allowing the label
|
||||
* to switch between font based and HTML based rendering.
|
||||
* \see mode()
|
||||
*/
|
||||
void setMode( Mode mode );
|
||||
|
||||
/**
|
||||
* Returns the label's current font.
|
||||
* \see setFont()
|
||||
*/
|
||||
QFont font() const;
|
||||
|
||||
/**
|
||||
* Sets the label's current \a font.
|
||||
* \see font()
|
||||
*/
|
||||
void setFont( const QFont &font );
|
||||
|
||||
/**
|
||||
* Returns for the vertical alignment of the label.
|
||||
* \see setVAlign()
|
||||
* \see hAlign()
|
||||
*/
|
||||
Qt::AlignmentFlag vAlign() const { return mVAlignment; }
|
||||
|
||||
/**
|
||||
* Returns the horizontal alignment of the label.
|
||||
* \see vAlign()
|
||||
* \see setHAlign()
|
||||
*/
|
||||
Qt::AlignmentFlag hAlign() const { return mHAlignment; }
|
||||
|
||||
/**
|
||||
* Sets the horizontal \a alignment of the label.
|
||||
* \see hAlign()
|
||||
* \see setVAlign()
|
||||
*/
|
||||
void setHAlign( Qt::AlignmentFlag alignment ) { mHAlignment = alignment; }
|
||||
|
||||
/**
|
||||
* Sets for the vertical \a alignment of the label.
|
||||
* \see vAlign()
|
||||
* \see setHAlign()
|
||||
*/
|
||||
void setVAlign( Qt::AlignmentFlag alignment ) { mVAlignment = alignment; }
|
||||
|
||||
/**
|
||||
* Returns the horizontal margin between the edge of the frame and the label
|
||||
* contents, in layout units.
|
||||
* \see setMargin()
|
||||
* \see marginY()
|
||||
*/
|
||||
double marginX() const { return mMarginX; }
|
||||
|
||||
/**
|
||||
* Returns the vertical margin between the edge of the frame and the label
|
||||
* contents, in layout units.
|
||||
* \see setMargin()
|
||||
* \see marginX()
|
||||
*/
|
||||
double marginY() const { return mMarginY; }
|
||||
|
||||
/**
|
||||
* Sets the \a margin between the edge of the frame and the label contents.
|
||||
* This method sets both the horizontal and vertical margins to the same
|
||||
* value. The margins can be individually controlled using the setMarginX()
|
||||
* and setMarginY() methods.
|
||||
*
|
||||
* Margins are set using the current layout units.
|
||||
|
||||
* \see setMarginX()
|
||||
* \see setMarginY()
|
||||
*/
|
||||
void setMargin( double margin );
|
||||
|
||||
/**
|
||||
* Sets the horizontal \a margin between the edge of the frame and the label
|
||||
* contents, in layout units.
|
||||
* \see setMargin()
|
||||
* \see setMarginY()
|
||||
*/
|
||||
void setMarginX( double margin );
|
||||
|
||||
/**
|
||||
* Sets the vertical \a margin between the edge of the frame and the label
|
||||
* contents, in layout units.
|
||||
* \see setMargin()
|
||||
* \see setMarginX()
|
||||
*/
|
||||
void setMarginY( double margin );
|
||||
|
||||
/**
|
||||
* Sets the label font \a color.
|
||||
* \see fontColor()
|
||||
*/
|
||||
void setFontColor( const QColor &color ) { mFontColor = color; }
|
||||
|
||||
/**
|
||||
* Returns the label font color.
|
||||
* \see setFontColor()
|
||||
*/
|
||||
QColor fontColor() const { return mFontColor; }
|
||||
|
||||
// In case of negative margins, the bounding rect may be larger than the
|
||||
// label's frame
|
||||
QRectF boundingRect() const override;
|
||||
|
||||
// Reimplemented to call prepareGeometryChange after toggling frame
|
||||
void setFrameEnabled( const bool drawFrame ) override;
|
||||
|
||||
// Reimplemented to call prepareGeometryChange after changing stroke width
|
||||
void setFrameStrokeWidth( const QgsLayoutMeasurement &strokeWidth ) override;
|
||||
|
||||
public slots:
|
||||
|
||||
void refresh() override;
|
||||
|
||||
protected:
|
||||
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
|
||||
bool writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const override;
|
||||
bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context ) override;
|
||||
|
||||
private slots:
|
||||
|
||||
//! Track when QWebPage has finished loading its html contents
|
||||
void loadingHtmlFinished( bool );
|
||||
|
||||
void refreshExpressionContext();
|
||||
|
||||
private:
|
||||
bool mFirstRender = true;
|
||||
|
||||
// Text
|
||||
QString mText;
|
||||
|
||||
Mode mMode = ModeFont;
|
||||
double mHtmlUnitsToLayoutUnits = 1.0;
|
||||
double htmlUnitsToLayoutUnits(); //calculate scale factor
|
||||
bool mHtmlLoaded = false;
|
||||
|
||||
//! Helper function to calculate x/y shift for adjustSizeToText() depending on rotation, current size and alignment
|
||||
void itemShiftAdjustSize( double newWidth, double newHeight, double &xShift, double &yShift ) const;
|
||||
|
||||
//! Called when the content is changed to handle HTML loading
|
||||
void contentChanged();
|
||||
|
||||
//! Font
|
||||
QFont mFont;
|
||||
|
||||
//! Horizontal margin between contents and frame (in mm)
|
||||
double mMarginX = 0.0;
|
||||
//! Vertical margin between contents and frame (in mm)
|
||||
double mMarginY = 0.0;
|
||||
|
||||
//! Font color
|
||||
QColor mFontColor = QColor( 0, 0, 0 );
|
||||
|
||||
//! Horizontal Alignment
|
||||
Qt::AlignmentFlag mHAlignment = Qt::AlignJustify;
|
||||
|
||||
//! Vertical Alignment
|
||||
Qt::AlignmentFlag mVAlignment = Qt::AlignTop;
|
||||
|
||||
//! Replaces replace '$CURRENT_DATE<(FORMAT)>' with the current date (e.g. $CURRENT_DATE(d 'June' yyyy)
|
||||
void replaceDateText( QString &text ) const;
|
||||
|
||||
//! Creates an encoded stylesheet url using the current font and label appearance settings
|
||||
QUrl createStylesheetUrl() const;
|
||||
|
||||
std::unique_ptr< QgsDistanceArea > mDistanceArea;
|
||||
|
||||
std::unique_ptr< QgsWebPage > mWebPage;
|
||||
};
|
||||
|
||||
#endif //QGSLAYOUTITEMLABEL_H
|
@ -17,6 +17,7 @@
|
||||
#include "qgslayoutitemregistry.h"
|
||||
#include "qgslayoutitemshape.h"
|
||||
#include "qgslayoutitemmap.h"
|
||||
#include "qgslayoutitemlabel.h"
|
||||
#include "qgslayoutitempolygon.h"
|
||||
#include "qgslayoutitempolyline.h"
|
||||
#include "qgslayoutitempage.h"
|
||||
@ -52,6 +53,7 @@ bool QgsLayoutItemRegistry::populate()
|
||||
addLayoutItemType( new QgsLayoutItemMetadata( LayoutPage, QStringLiteral( "Page" ), QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileNew.svg" ) ), QgsLayoutItemPage::create ) );
|
||||
addLayoutItemType( new QgsLayoutItemMetadata( LayoutMap, QStringLiteral( "Map" ), QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddMap.svg" ) ), QgsLayoutItemMap::create ) );
|
||||
addLayoutItemType( new QgsLayoutItemMetadata( LayoutPicture, QStringLiteral( "Picture" ), QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddImage.svg" ) ), QgsLayoutItemPicture::create ) );
|
||||
addLayoutItemType( new QgsLayoutItemMetadata( LayoutLabel, QStringLiteral( "Label" ), QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddLabel.svg" ) ), QgsLayoutItemLabel::create ) );
|
||||
addLayoutItemType( new QgsLayoutItemMetadata( LayoutShape, QStringLiteral( "Shape" ), QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicRectangle.svg" ) ), []( QgsLayout * layout )
|
||||
{
|
||||
QgsLayoutItemShape *shape = new QgsLayoutItemShape( layout );
|
||||
|
@ -186,6 +186,7 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject
|
||||
LayoutPage, //!< Page items
|
||||
LayoutMap, //!< Map item
|
||||
LayoutPicture, //!< Picture item
|
||||
LayoutLabel, //!< Label item
|
||||
LayoutShape, //!< Shape item
|
||||
LayoutPolygon, //!< Polygon shape item
|
||||
LayoutPolyline, //!< Polyline shape item
|
||||
|
@ -135,6 +135,7 @@ SET(TESTS
|
||||
testqgslayoutcontext.cpp
|
||||
testqgslayoutitem.cpp
|
||||
testqgslayoutitemgroup.cpp
|
||||
testqgslayoutlabel.cpp
|
||||
testqgslayoutmap.cpp
|
||||
testqgslayoutmapgrid.cpp
|
||||
testqgslayoutmapoverview.cpp
|
||||
|
291
tests/src/core/testqgslayoutlabel.cpp
Normal file
291
tests/src/core/testqgslayoutlabel.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
/***************************************************************************
|
||||
testqgslayoutlabel.cpp
|
||||
----------------------
|
||||
begin : October 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslayout.h"
|
||||
#include "qgslayoutitemlabel.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsmultirenderchecker.h"
|
||||
#include "qgsfontutils.h"
|
||||
#include "qgsproject.h"
|
||||
|
||||
#include <QObject>
|
||||
#include "qgstest.h"
|
||||
|
||||
class TestQgsLayoutLabel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestQgsLayoutLabel() = default;
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init();// will be called before each testfunction is executed.
|
||||
void cleanup();// will be called after every testfunction.
|
||||
|
||||
// test simple expression evaluation
|
||||
void evaluation();
|
||||
// test expression evaluation when a feature is set
|
||||
void feature_evaluation();
|
||||
// test page expressions
|
||||
void page_evaluation();
|
||||
void marginMethods(); //tests getting/setting margins
|
||||
void render();
|
||||
void renderAsHtml();
|
||||
void renderAsHtmlRelative();
|
||||
|
||||
private:
|
||||
QgsVectorLayer *mVectorLayer = nullptr;
|
||||
QString mReport;
|
||||
};
|
||||
|
||||
void TestQgsLayoutLabel::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
|
||||
//create maplayers from testdata and add to layer registry
|
||||
QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + '/' + "france_parts.shp" );
|
||||
mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(),
|
||||
vectorFileInfo.completeBaseName(),
|
||||
QStringLiteral( "ogr" ) );
|
||||
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::cleanupTestCase()
|
||||
{
|
||||
QString myReportFile = QDir::tempPath() + "/qgistest.html";
|
||||
QFile myFile( myReportFile );
|
||||
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
|
||||
{
|
||||
QTextStream myQTextStream( &myFile );
|
||||
myQTextStream << mReport;
|
||||
myFile.close();
|
||||
}
|
||||
|
||||
delete mVectorLayer;
|
||||
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::init()
|
||||
{
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::evaluation()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
|
||||
#if 0 //TODO
|
||||
l.atlasComposition().setCoverageLayer( mVectorLayer );
|
||||
#endif
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
qWarning() << "composer label font: " << label->font().toString() << " exactMatch:" << label->font().exactMatch();
|
||||
|
||||
|
||||
{
|
||||
// $CURRENT_DATE evaluation
|
||||
QString expected = "__" + QDate::currentDate().toString() + "__";
|
||||
label->setText( QStringLiteral( "__$CURRENT_DATE__" ) );
|
||||
QString evaluated = label->currentText();
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
{
|
||||
// $CURRENT_DATE() evaluation
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QString expected = "__" + now.toString( QStringLiteral( "dd" ) ) + "(ok)__";
|
||||
label->setText( QStringLiteral( "__$CURRENT_DATE(dd)(ok)__" ) );
|
||||
QString evaluated = label->currentText();
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
{
|
||||
// $CURRENT_DATE() evaluation (inside an expression)
|
||||
QDate now = QDate::currentDate();
|
||||
int dd = now.day();
|
||||
|
||||
QString expected = "__" + QStringLiteral( "%1" ).arg( dd + 1 ) + "(ok)__";
|
||||
label->setText( QStringLiteral( "__[%$CURRENT_DATE(dd) + 1%](ok)__" ) );
|
||||
QString evaluated = label->currentText();
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
{
|
||||
// expression evaluation (without feature)
|
||||
QString expected = QStringLiteral( "__[NAME_1]42__" );
|
||||
label->setText( QStringLiteral( "__[%\"NAME_1\"%][%21*2%]__" ) );
|
||||
QString evaluated = label->currentText();
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::feature_evaluation()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
#if 0 //TODO
|
||||
l.atlasComposition().setCoverageLayer( mVectorLayer );
|
||||
#endif
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
#if 0 //TODO
|
||||
l.atlasComposition().setEnabled( true );
|
||||
l.setAtlasMode( QgsComposition::ExportAtlas );
|
||||
l.atlasComposition().updateFeatures();
|
||||
l.atlasComposition().prepareForFeature( 0 );
|
||||
|
||||
{
|
||||
// evaluation with a feature
|
||||
label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) );
|
||||
QString evaluated = label->displayText();
|
||||
QString expected = QStringLiteral( "Basse-Normandie_ok" );
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
mComposition->atlasComposition().prepareForFeature( 1 );
|
||||
{
|
||||
// evaluation with a feature
|
||||
label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) );
|
||||
QString evaluated = label->displayText();
|
||||
QString expected = QStringLiteral( "Bretagne_ok" );
|
||||
QCOMPARE( evaluated, expected );
|
||||
}
|
||||
mComposition->atlasComposition().setEnabled( false );
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::page_evaluation()
|
||||
{
|
||||
#if 0 //TODO
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
mComposition->atlasComposition().setCoverageLayer( mVectorLayer );
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
mComposition->setNumPages( 2 );
|
||||
{
|
||||
label->setText( QStringLiteral( "[%@layout_page||'/'||@layout_numpages%]" ) );
|
||||
QString evaluated = label->displayText();
|
||||
QString expected = QStringLiteral( "1/2" );
|
||||
QCOMPARE( evaluated, expected );
|
||||
|
||||
// move to the second page and re-evaluate
|
||||
label->setItemPosition( 0, 320 );
|
||||
QCOMPARE( label->displayText(), QString( "2/2" ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::marginMethods()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
//test setting margins separately
|
||||
label->setMarginX( 3.0 );
|
||||
label->setMarginY( 4.0 );
|
||||
QCOMPARE( label->marginX(), 3.0 );
|
||||
QCOMPARE( label->marginY(), 4.0 );
|
||||
//test setting margins together
|
||||
label->setMargin( 5.0 );
|
||||
QCOMPARE( label->marginX(), 5.0 );
|
||||
QCOMPARE( label->marginY(), 5.0 );
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::render()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
label->setText( QStringLiteral( "test label" ) );
|
||||
label->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) );
|
||||
label->attemptMove( QgsLayoutPoint( 70, 70 ) );
|
||||
label->adjustSizeToText();
|
||||
|
||||
QgsLayoutChecker checker( QStringLiteral( "composerlabel_render" ), &l );
|
||||
checker.setControlPathPrefix( QStringLiteral( "composer_label" ) );
|
||||
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::renderAsHtml()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
label->setFontColor( QColor( 200, 40, 60 ) );
|
||||
label->setText( QStringLiteral( "test <i>html</i>" ) );
|
||||
label->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) );
|
||||
label->setPos( 70, 70 );
|
||||
label->adjustSizeToText();
|
||||
label->setMode( QgsLayoutItemLabel::ModeHtml );
|
||||
label->update();
|
||||
|
||||
QgsLayoutChecker checker( QStringLiteral( "composerlabel_renderhtml" ), &l );
|
||||
checker.setControlPathPrefix( QStringLiteral( "composer_label" ) );
|
||||
QVERIFY( checker.testLayout( mReport, 0, 10 ) );
|
||||
}
|
||||
|
||||
void TestQgsLayoutLabel::renderAsHtmlRelative()
|
||||
{
|
||||
QgsLayout l( QgsProject::instance() );
|
||||
l.initializeDefaults();
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
label->setMargin( 1 );
|
||||
l.addLayoutItem( label );
|
||||
|
||||
QgsProject::instance()->setFileName( QStringLiteral( TEST_DATA_DIR ) + QDir::separator() + "test.qgs" );
|
||||
label->setFontColor( QColor( 200, 40, 60 ) );
|
||||
label->setText( QStringLiteral( "test <img src=\"small_sample_image.png\" />" ) );
|
||||
label->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) );
|
||||
label->setPos( 70, 70 );
|
||||
label->adjustSizeToText();
|
||||
label->setMode( QgsLayoutItemLabel::ModeHtml );
|
||||
label->update();
|
||||
|
||||
QgsLayoutChecker checker( QStringLiteral( "composerlabel_renderhtmlrelative" ), &l );
|
||||
checker.setControlPathPrefix( QStringLiteral( "composer_label" ) );
|
||||
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsLayoutLabel )
|
||||
#include "testqgslayoutlabel.moc"
|
@ -86,6 +86,7 @@ ADD_PYTHON_TEST(PyQgsLayoutGridSettings test_qgslayoutgridsettings.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutGuide test_qgslayoutguides.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutItem test_qgslayoutitem.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutItemPropertiesDialog test_qgslayoutitempropertiesdialog.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutLabel test_qgslayoutlabel.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMap test_qgslayoutmap.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMapGrid test_qgslayoutmapgrid.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutPicture test_qgslayoutpicture.py)
|
||||
|
94
tests/src/python/test_qgslayoutlabel.py
Normal file
94
tests/src/python/test_qgslayoutlabel.py
Normal file
@ -0,0 +1,94 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsLayoutItemLabel.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = '(C) 2017 by Nyall Dawson'
|
||||
__date__ = '23/10/2017'
|
||||
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.PyQt.QtCore import QFileInfo, QDate, QDateTime
|
||||
from qgis.core import QgsVectorLayer, QgsLayout, QgsLayoutItemLabel, QgsProject
|
||||
from utilities import unitTestDataPath
|
||||
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class TestQgsLayoutItemLabel(unittest.TestCase):
|
||||
|
||||
def testCase(self):
|
||||
TEST_DATA_DIR = unitTestDataPath()
|
||||
vectorFileInfo = QFileInfo(TEST_DATA_DIR + "/france_parts.shp")
|
||||
mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
|
||||
|
||||
QgsProject.instance().addMapLayers([mVectorLayer])
|
||||
|
||||
layout = QgsLayout(QgsProject.instance())
|
||||
layout.initializeDefaults()
|
||||
|
||||
label = QgsLayoutItemLabel(layout)
|
||||
layout.addLayoutItem(label)
|
||||
|
||||
self.evaluation_test(layout, label)
|
||||
self.feature_evaluation_test(layout, label, mVectorLayer)
|
||||
self.page_evaluation_test(layout, label, mVectorLayer)
|
||||
|
||||
def evaluation_test(self, layout, label):
|
||||
# $CURRENT_DATE evaluation
|
||||
label.setText("__$CURRENT_DATE__")
|
||||
assert label.currentText() == ("__" + QDate.currentDate().toString() + "__")
|
||||
|
||||
# $CURRENT_DATE() evaluation
|
||||
label.setText("__$CURRENT_DATE(dd)(ok)__")
|
||||
expected = "__" + QDateTime.currentDateTime().toString("dd") + "(ok)__"
|
||||
assert label.currentText() == expected
|
||||
|
||||
# $CURRENT_DATE() evaluation (inside an expression)
|
||||
label.setText("__[%$CURRENT_DATE(dd) + 1%](ok)__")
|
||||
dd = QDate.currentDate().day()
|
||||
expected = "__%d(ok)__" % (dd + 1)
|
||||
assert label.currentText() == expected
|
||||
|
||||
# expression evaluation (without associated feature)
|
||||
label.setText("__[%\"NAME_1\"%][%21*2%]__")
|
||||
assert label.currentText() == "__[NAME_1]42__"
|
||||
|
||||
def feature_evaluation_test(self, layout, label, mVectorLayer):
|
||||
pass
|
||||
# TODO
|
||||
#atlas = layout.atlasComposition()
|
||||
#atlas.setCoverageLayer(mVectorLayer)
|
||||
#atlas.setEnabled(True)
|
||||
#layout.setAtlasMode(QgsComposition.ExportAtlas)
|
||||
|
||||
#label.setText("[%\"NAME_1\"||'_ok'%]")
|
||||
#atlas.beginRender()
|
||||
#atlas.prepareForFeature(0)
|
||||
#assert label.currentText() == "Basse-Normandie_ok"
|
||||
|
||||
#atlas.prepareForFeature(1)
|
||||
#assert label.currentText() == "Bretagne_ok"
|
||||
|
||||
def page_evaluation_test(self, layout, label, mVectorLayer):
|
||||
pass
|
||||
# TODO
|
||||
#layout.setNumPages(2)
|
||||
#label.setText("[%@layout_page||'/'||@layout_numpages%]")
|
||||
#assert label.currentText() == "1/2"
|
||||
|
||||
# move the the second page and re-evaluate
|
||||
#label.setItemPosition(0, 320)
|
||||
#assert label.currentText() == "2/2"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Binary file not shown.
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Loading…
x
Reference in New Issue
Block a user