Fix incorrect annotation scaling when exporting layouts

Previously, annotation size and position always used pixel units. This
did not work well when exporting layouts, resulting in tiny annotations
(it also caused issues when moving projects between hidpi/non hidpi
displays).

Instead, use millimeters for annotation size and position so that the
appearance is consistent across displays and works correctly in layout
exports.

Add lots of unit tests covering this.

Fixes #18373
This commit is contained in:
Nyall Dawson 2019-04-16 16:50:20 +10:00
parent 070de69e30
commit 38ef62e1cc
43 changed files with 455 additions and 142 deletions

View File

@ -143,34 +143,80 @@ the relative percentage for the position compared to the map width and height.
.. seealso:: :py:func:`relativePosition`
%End
void setFrameOffsetFromReferencePoint( QPointF offset );
void setFrameOffsetFromReferencePoint( QPointF offset ) /Deprecated/;
%Docstring
Sets the annotation's frame's offset from the mapPosition() reference point.
Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
.. seealso:: :py:func:`frameOffsetFromReferencePoint`
.. deprecated:: use setFrameOffsetFromReferencePointMm() instead
%End
QPointF frameOffsetFromReferencePoint() const;
QPointF frameOffsetFromReferencePoint() const /Deprecated/;
%Docstring
Returns the annotation's frame's offset from the mapPosition() reference point.
Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
.. seealso:: :py:func:`setFrameOffsetFromReferencePoint`
.. deprecated:: use frameOffsetFromReferencePointMm() instead
%End
void setFrameSize( QSizeF size );
void setFrameOffsetFromReferencePointMm( QPointF offset );
%Docstring
Sets the size of the annotation's frame (the main area in which
Sets the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
.. seealso:: :py:func:`frameOffsetFromReferencePointMm`
.. versionadded:: 3.4.8
%End
QPointF frameOffsetFromReferencePointMm() const;
%Docstring
Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
.. seealso:: :py:func:`setFrameOffsetFromReferencePointMm`
.. versionadded:: 3.4.8
%End
void setFrameSize( QSizeF size ) /Deprecated/;
%Docstring
Sets the size (in pixels) of the annotation's frame (the main area in which
the annotation's content is drawn).
.. seealso:: :py:func:`frameSize`
.. deprecated:: use setFrameSizeMm() instead
%End
QSizeF frameSize() const;
QSizeF frameSize() const /Deprecated/;
%Docstring
Returns the size of the annotation's frame (the main area in which
Returns the size (in pixels) of the annotation's frame (the main area in which
the annotation's content is drawn).
.. seealso:: :py:func:`setFrameSize`
.. deprecated:: use frameSizeMm() instead
%End
void setFrameSizeMm( QSizeF size );
%Docstring
Sets the size (in millimeters) of the annotation's frame (the main area in which
the annotation's content is drawn).
.. seealso:: :py:func:`frameSizeMm`
.. versionadded:: 3.4.8
%End
QSizeF frameSizeMm() const;
%Docstring
Returns the size (in millimeters) of the annotation's frame (the main area in which
the annotation's content is drawn).
.. seealso:: :py:func:`setFrameSizeMm`
.. versionadded:: 3.4.8
%End
void setContentsMargin( const QgsMargins &margins );

View File

@ -50,26 +50,19 @@ QDialog *QgsMapToolAnnotation::createItemEditor( QgsMapCanvasAnnotationItem *ite
QgsAnnotation *annotation = item->annotation();
QgsTextAnnotation *tItem = dynamic_cast<QgsTextAnnotation *>( annotation );
if ( tItem )
if ( qobject_cast<QgsTextAnnotation *>( annotation ) )
{
return new QgsTextAnnotationDialog( item );
}
QgsFormAnnotation *fItem = dynamic_cast<QgsFormAnnotation *>( annotation );
if ( fItem )
else if ( qobject_cast<QgsFormAnnotation *>( annotation ) )
{
return new QgsFormAnnotationDialog( item );
}
QgsHtmlAnnotation *hItem = dynamic_cast<QgsHtmlAnnotation *>( annotation );
if ( hItem )
else if ( qobject_cast<QgsHtmlAnnotation *>( annotation ) )
{
return new QgsHtmlAnnotationDialog( item );
}
QgsSvgAnnotation *sItem = dynamic_cast<QgsSvgAnnotation *>( annotation );
if ( sItem )
else if ( qobject_cast<QgsSvgAnnotation *>( annotation ) )
{
return new QgsSvgAnnotationDialog( item );
}
@ -124,7 +117,7 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e )
annotation->setMapPositionCrs( mCanvas->mapSettings().destinationCrs() );
annotation->setRelativePosition( QPointF( e->pos().x() / mCanvas->width(),
e->pos().y() / mCanvas->height() ) );
annotation->setFrameSize( QSizeF( 200, 100 ) );
annotation->setFrameSizeMm( QSizeF( 50, 25 ) );
QgsProject::instance()->annotationManager()->addAnnotation( annotation );
@ -193,7 +186,11 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
QPointF newCanvasPos = item->pos() + ( e->pos() - mLastMousePosition );
if ( annotation->hasFixedMapPosition() )
{
annotation->setFrameOffsetFromReferencePoint( annotation->frameOffsetFromReferencePoint() + ( e->pos() - mLastMousePosition ) );
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
const double deltaX = pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
const double deltaY = pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
annotation->setFrameOffsetFromReferencePointMm( QPointF( annotation->frameOffsetFromReferencePointMm().x() + deltaX,
annotation->frameOffsetFromReferencePointMm().y() + deltaY ) );
annotation->setRelativePosition( QPointF( newCanvasPos.x() / mCanvas->width(),
newCanvasPos.y() / mCanvas->height() ) );
}
@ -210,9 +207,12 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
else if ( mCurrentMoveAction != QgsMapCanvasAnnotationItem::NoAction )
{
//handle the frame resize actions
QSizeF size = annotation->frameSize();
double xmin = annotation->frameOffsetFromReferencePoint().x();
double ymin = annotation->frameOffsetFromReferencePoint().y();
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
QSizeF size = annotation->frameSizeMm();
double xmin = annotation->frameOffsetFromReferencePointMm().x();
double ymin = annotation->frameOffsetFromReferencePointMm().y();
double xmax = xmin + size.width();
double ymax = ymin + size.height();
double relPosX = annotation->relativePosition().x();
@ -222,27 +222,27 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
{
xmax += e->pos().x() - mLastMousePosition.x();
xmax += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
}
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeft ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp )
{
xmin += e->pos().x() - mLastMousePosition.x();
xmin += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
relPosX = ( relPosX * mCanvas->width() + e->pos().x() - mLastMousePosition.x() ) / static_cast<double>( mCanvas->width() );
}
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameUp ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
{
ymin += e->pos().y() - mLastMousePosition.y();
ymin += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
relPosY = ( relPosY * mCanvas->height() + e->pos().y() - mLastMousePosition.y() ) / static_cast<double>( mCanvas->height() );
}
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameDown ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown )
{
ymax += e->pos().y() - mLastMousePosition.y();
ymax += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
}
//switch min / max if necessary
@ -260,8 +260,8 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
ymin = tmp;
}
annotation->setFrameOffsetFromReferencePoint( QPointF( xmin, ymin ) );
annotation->setFrameSize( QSizeF( xmax - xmin, ymax - ymin ) );
annotation->setFrameOffsetFromReferencePointMm( QPointF( xmin, ymin ) );
annotation->setFrameSizeMm( QSizeF( xmax - xmin, ymax - ymin ) );
annotation->setRelativePosition( QPointF( relPosX, relPosY ) );
item->update();
QgsProject::instance()->setDirty( true );
@ -351,7 +351,7 @@ void QgsMapToolAnnotation::toggleTextItemVisibilities()
const auto constItemList = itemList;
for ( QgsMapCanvasAnnotationItem *item : constItemList )
{
QgsTextAnnotation *textItem = dynamic_cast<QgsTextAnnotation *>( item->annotation() );
QgsTextAnnotation *textItem = qobject_cast<QgsTextAnnotation *>( item->annotation() );
if ( textItem )
{
textItem->setVisible( !textItem->isVisible() );

View File

@ -19,9 +19,12 @@
#include "qgssymbollayerutils.h"
#include "qgsmaplayer.h"
#include "qgsproject.h"
#include "qgsgeometryutils.h"
#include <QPen>
#include <QPainter>
Q_GUI_EXPORT extern int qt_defaultDpiX();
QgsAnnotation::QgsAnnotation( QObject *parent )
: QObject( parent )
, mMarkerSymbol( new QgsMarkerSymbol() )
@ -74,14 +77,37 @@ void QgsAnnotation::setRelativePosition( QPointF position )
}
void QgsAnnotation::setFrameOffsetFromReferencePoint( QPointF offset )
{
// convert from offset in pixels at 96 dpi to mm
setFrameOffsetFromReferencePointMm( offset / 3.7795275 );
}
QPointF QgsAnnotation::frameOffsetFromReferencePoint() const
{
return mOffsetFromReferencePoint / 3.7795275;
}
void QgsAnnotation::setFrameOffsetFromReferencePointMm( QPointF offset )
{
mOffsetFromReferencePoint = offset;
updateBalloon();
emit moved();
emit appearanceChanged();
}
void QgsAnnotation::setFrameSize( QSizeF size )
{
// convert from size in pixels at 96 dpi to mm
setFrameSizeMm( size / 3.7795275 );
}
QSizeF QgsAnnotation::frameSize() const
{
return mFrameSize / 3.7795275;
}
void QgsAnnotation::setFrameSizeMm( QSizeF size )
{
QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
mFrameSize = frameSize;
@ -118,16 +144,20 @@ void QgsAnnotation::render( QgsRenderContext &context ) const
}
if ( mHasFixedMapPosition )
{
painter->translate( mOffsetFromReferencePoint.x() + context.convertToPainterUnits( mContentsMargins.left(), QgsUnitTypes::RenderMillimeters ),
mOffsetFromReferencePoint.y() + context.convertToPainterUnits( mContentsMargins.top(), QgsUnitTypes::RenderMillimeters ) );
painter->translate( context.convertToPainterUnits( mOffsetFromReferencePoint.x(), QgsUnitTypes::RenderMillimeters ) + context.convertToPainterUnits( mContentsMargins.left(), QgsUnitTypes::RenderMillimeters ),
context.convertToPainterUnits( mOffsetFromReferencePoint.y(), QgsUnitTypes::RenderMillimeters ) + context.convertToPainterUnits( mContentsMargins.top(), QgsUnitTypes::RenderMillimeters ) );
}
else
{
painter->translate( context.convertToPainterUnits( mContentsMargins.left(), QgsUnitTypes::RenderMillimeters ),
context.convertToPainterUnits( mContentsMargins.top(), QgsUnitTypes::RenderMillimeters ) );
}
QSizeF size( mFrameSize.width() - context.convertToPainterUnits( mContentsMargins.left() + mContentsMargins.right(), QgsUnitTypes::RenderMillimeters ),
mFrameSize.height() - context.convertToPainterUnits( mContentsMargins.top() + mContentsMargins.bottom(), QgsUnitTypes::RenderMillimeters ) );
QSizeF size( context.convertToPainterUnits( mFrameSize.width(), QgsUnitTypes::RenderMillimeters ) - context.convertToPainterUnits( mContentsMargins.left() + mContentsMargins.right(), QgsUnitTypes::RenderMillimeters ),
context.convertToPainterUnits( mFrameSize.height(), QgsUnitTypes::RenderMillimeters ) - context.convertToPainterUnits( mContentsMargins.top() + mContentsMargins.bottom(), QgsUnitTypes::RenderMillimeters ) );
// scale back from painter dpi to 96 dpi --
// double dotsPerMM = context.painter()->device()->logicalDpiX() / ( 25.4 * 3.78 );
// context.painter()->scale( dotsPerMM, dotsPerMM );
renderAnnotation( context, size );
painter->restore();
@ -168,10 +198,10 @@ void QgsAnnotation::updateBalloon()
//edge list
QList<QLineF> segmentList;
segmentList << segment( 0 );
segmentList << segment( 1 );
segmentList << segment( 2 );
segmentList << segment( 3 );
segmentList << segment( 0, nullptr );
segmentList << segment( 1, nullptr );
segmentList << segment( 2, nullptr );
segmentList << segment( 3, nullptr );
//find closest edge / closest edge point
double minEdgeDist = std::numeric_limits<double>::max();
@ -199,38 +229,56 @@ void QgsAnnotation::updateBalloon()
return;
}
//make that configurable for the item
double segmentPointWidth = 10;
mBalloonSegment = minEdgeIndex;
QPointF minEdgeEnd = minEdge.p2();
mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
if ( std::sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
if ( std::sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < mSegmentPointWidthMm )
{
mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
double x = 0;
double y = 0;
QgsGeometryUtils::pointOnLineWithDistance( minEdge.p2().x(), minEdge.p2().y(), minEdge.p1().x(), minEdge.p1().y(), mSegmentPointWidthMm, x, y );
mBalloonSegmentPoint1 = QPointF( x, y );
}
{
double x = 0;
double y = 0;
QgsGeometryUtils::pointOnLineWithDistance( mBalloonSegmentPoint1.x(), mBalloonSegmentPoint1.y(), minEdge.p2().x(), minEdge.p2().y(), mSegmentPointWidthMm, x, y );
mBalloonSegmentPoint2 = QPointF( x, y );
}
mBalloonSegmentPoint2 = pointOnLineWithDistance( mBalloonSegmentPoint1, minEdge.p2(), 10 );
}
QLineF QgsAnnotation::segment( int index ) const
QLineF QgsAnnotation::segment( int index, QgsRenderContext *context ) const
{
auto scaleSize = [context]( double size )->double
{
return context ? context->convertToPainterUnits( size, QgsUnitTypes::RenderMillimeters ) : size;
};
if ( mHasFixedMapPosition )
{
switch ( index )
{
case 0:
return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y(), mOffsetFromReferencePoint.x()
+ mFrameSize.width(), mOffsetFromReferencePoint.y() );
return QLineF( scaleSize( mOffsetFromReferencePoint.x() ),
scaleSize( mOffsetFromReferencePoint.y() ),
scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
scaleSize( mOffsetFromReferencePoint.y() ) );
case 1:
return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y(),
mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
return QLineF( scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
scaleSize( mOffsetFromReferencePoint.y() ),
scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ) );
case 2:
return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
return QLineF( scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ),
scaleSize( mOffsetFromReferencePoint.x() ),
scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ) );
case 3:
return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() );
return QLineF( scaleSize( mOffsetFromReferencePoint.x() ),
scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ),
scaleSize( mOffsetFromReferencePoint.x() ),
scaleSize( mOffsetFromReferencePoint.y() ) );
default:
return QLineF();
}
@ -240,15 +288,15 @@ QLineF QgsAnnotation::segment( int index ) const
switch ( index )
{
case 0:
return QLineF( 0, 0, mFrameSize.width(), 0 );
return QLineF( 0, 0, scaleSize( mFrameSize.width() ), 0 );
case 1:
return QLineF( mFrameSize.width(), 0,
mFrameSize.width(), mFrameSize.height() );
return QLineF( scaleSize( mFrameSize.width() ), 0,
scaleSize( mFrameSize.width() ), scaleSize( mFrameSize.height() ) );
case 2:
return QLineF( mFrameSize.width(), mFrameSize.height(),
0, mFrameSize.height() );
return QLineF( scaleSize( mFrameSize.width() ), scaleSize( mFrameSize.height() ),
0, scaleSize( mFrameSize.height() ) );
case 3:
return QLineF( 0, mFrameSize.height(),
return QLineF( 0, scaleSize( mFrameSize.height() ),
0, 0 );
default:
return QLineF();
@ -256,16 +304,6 @@ QLineF QgsAnnotation::segment( int index ) const
}
}
QPointF QgsAnnotation::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const
{
double dx = directionPoint.x() - startPoint.x();
double dy = directionPoint.y() - startPoint.y();
double length = std::sqrt( dx * dx + dy * dy );
double scaleFactor = distance / length;
return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
}
void QgsAnnotation::drawFrame( QgsRenderContext &context ) const
{
if ( !mFillSymbol )
@ -274,18 +312,22 @@ void QgsAnnotation::drawFrame( QgsRenderContext &context ) const
context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
QPolygonF poly;
poly.reserve( 9 + ( mHasFixedMapPosition ? 3 : 0 ) );
QList<QPolygonF> rings; //empty list
for ( int i = 0; i < 4; ++i )
{
QLineF currentSegment = segment( i );
poly << currentSegment.p1();
QLineF currentSegment = segment( i, &context );
poly << QPointF( currentSegment.p1().x(),
currentSegment.p1().y() );
if ( i == mBalloonSegment && mHasFixedMapPosition )
{
poly << mBalloonSegmentPoint1;
poly << QPointF( context.convertToPainterUnits( mBalloonSegmentPoint1.x(), QgsUnitTypes::RenderMillimeters ),
context.convertToPainterUnits( mBalloonSegmentPoint1.y(), QgsUnitTypes::RenderMillimeters ) );
poly << QPointF( 0, 0 );
poly << mBalloonSegmentPoint2;
poly << QPointF( context.convertToPainterUnits( mBalloonSegmentPoint2.x(), QgsUnitTypes::RenderMillimeters ),
context.convertToPainterUnits( mBalloonSegmentPoint2.y(), QgsUnitTypes::RenderMillimeters ) );
}
poly << currentSegment.p2();
poly << QPointF( currentSegment.p2().x(), currentSegment.p2().y() );
}
if ( poly.at( 0 ) != poly.at( poly.count() - 1 ) )
poly << poly.at( 0 );
@ -322,10 +364,10 @@ void QgsAnnotation::_writeXml( QDomElement &itemElem, QDomDocument &doc, const Q
annotationElem.setAttribute( QStringLiteral( "mapPosY" ), qgsDoubleToString( mMapPosition.y() ) );
if ( mMapPositionCrs.isValid() )
mMapPositionCrs.writeXml( annotationElem, doc );
annotationElem.setAttribute( QStringLiteral( "offsetX" ), qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
annotationElem.setAttribute( QStringLiteral( "offsetY" ), qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
annotationElem.setAttribute( QStringLiteral( "frameWidth" ), qgsDoubleToString( mFrameSize.width() ) );
annotationElem.setAttribute( QStringLiteral( "frameHeight" ), qgsDoubleToString( mFrameSize.height() ) );
annotationElem.setAttribute( QStringLiteral( "offsetXMM" ), qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
annotationElem.setAttribute( QStringLiteral( "offsetYMM" ), qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
annotationElem.setAttribute( QStringLiteral( "frameWidthMM" ), qgsDoubleToString( mFrameSize.width() ) );
annotationElem.setAttribute( QStringLiteral( "frameHeightMM" ), qgsDoubleToString( mFrameSize.height() ) );
annotationElem.setAttribute( QStringLiteral( "canvasPosX" ), qgsDoubleToString( mRelativePosition.x() ) );
annotationElem.setAttribute( QStringLiteral( "canvasPosY" ), qgsDoubleToString( mRelativePosition.y() ) );
annotationElem.setAttribute( QStringLiteral( "contentsMargin" ), mContentsMargins.toString() );
@ -379,10 +421,25 @@ void QgsAnnotation::_readXml( const QDomElement &annotationElem, const QgsReadWr
}
mContentsMargins = QgsMargins::fromString( annotationElem.attribute( QStringLiteral( "contentsMargin" ) ) );
mFrameSize.setWidth( annotationElem.attribute( QStringLiteral( "frameWidth" ), QStringLiteral( "50" ) ).toDouble() );
mFrameSize.setHeight( annotationElem.attribute( QStringLiteral( "frameHeight" ), QStringLiteral( "50" ) ).toDouble() );
mOffsetFromReferencePoint.setX( annotationElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble() );
mOffsetFromReferencePoint.setY( annotationElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble() );
const double dpiScale = 25.4 / qt_defaultDpiX();
if ( annotationElem.hasAttribute( QStringLiteral( "frameWidthMM" ) ) )
mFrameSize.setWidth( annotationElem.attribute( QStringLiteral( "frameWidthMM" ), QStringLiteral( "5" ) ).toDouble() );
else
mFrameSize.setWidth( dpiScale * annotationElem.attribute( QStringLiteral( "frameWidth" ), QStringLiteral( "50" ) ).toDouble() );
if ( annotationElem.hasAttribute( QStringLiteral( "frameHeightMM" ) ) )
mFrameSize.setHeight( annotationElem.attribute( QStringLiteral( "frameHeightMM" ), QStringLiteral( "3" ) ).toDouble() );
else
mFrameSize.setHeight( dpiScale * annotationElem.attribute( QStringLiteral( "frameHeight" ), QStringLiteral( "50" ) ).toDouble() );
if ( annotationElem.hasAttribute( QStringLiteral( "offsetXMM" ) ) )
mOffsetFromReferencePoint.setX( annotationElem.attribute( QStringLiteral( "offsetXMM" ), QStringLiteral( "0" ) ).toDouble() );
else
mOffsetFromReferencePoint.setX( dpiScale * annotationElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble() );
if ( annotationElem.hasAttribute( QStringLiteral( "offsetYMM" ) ) )
mOffsetFromReferencePoint.setY( annotationElem.attribute( QStringLiteral( "offsetYMM" ), QStringLiteral( "0" ) ).toDouble() );
else
mOffsetFromReferencePoint.setY( dpiScale * annotationElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble() );
mHasFixedMapPosition = annotationElem.attribute( QStringLiteral( "mapPositionFixed" ), QStringLiteral( "1" ) ).toInt();
mVisible = annotationElem.attribute( QStringLiteral( "visible" ), QStringLiteral( "1" ) ).toInt();
if ( annotationElem.hasAttribute( QStringLiteral( "mapLayer" ) ) )
@ -455,6 +512,7 @@ void QgsAnnotation::copyCommonProperties( QgsAnnotation *target ) const
target->mBalloonSegment = mBalloonSegment;
target->mBalloonSegmentPoint1 = mBalloonSegmentPoint1;
target->mBalloonSegmentPoint2 = mBalloonSegmentPoint2;
target->mSegmentPointWidthMm = mSegmentPointWidthMm;
target->mMapLayer = mMapLayer;
target->mFeature = mFeature;
}

View File

@ -157,30 +157,64 @@ class CORE_EXPORT QgsAnnotation : public QObject
void setRelativePosition( QPointF position );
/**
* Sets the annotation's frame's offset from the mapPosition() reference point.
* Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
* \see frameOffsetFromReferencePoint()
* \deprecated use setFrameOffsetFromReferencePointMm() instead
*/
void setFrameOffsetFromReferencePoint( QPointF offset );
Q_DECL_DEPRECATED void setFrameOffsetFromReferencePoint( QPointF offset ) SIP_DEPRECATED;
/**
* Returns the annotation's frame's offset from the mapPosition() reference point.
* Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
* \see setFrameOffsetFromReferencePoint()
* \deprecated use frameOffsetFromReferencePointMm() instead
*/
QPointF frameOffsetFromReferencePoint() const { return mOffsetFromReferencePoint; }
Q_DECL_DEPRECATED QPointF frameOffsetFromReferencePoint() const SIP_DEPRECATED;
/**
* Sets the size of the annotation's frame (the main area in which
* Sets the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
* \see frameOffsetFromReferencePointMm()
* \since QGIS 3.4.8
*/
void setFrameOffsetFromReferencePointMm( QPointF offset );
/**
* Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
* \see setFrameOffsetFromReferencePointMm()
* \since QGIS 3.4.8
*/
QPointF frameOffsetFromReferencePointMm() const { return mOffsetFromReferencePoint; }
/**
* Sets the size (in pixels) of the annotation's frame (the main area in which
* the annotation's content is drawn).
* \see frameSize()
* \deprecated use setFrameSizeMm() instead
*/
void setFrameSize( QSizeF size );
Q_DECL_DEPRECATED void setFrameSize( QSizeF size ) SIP_DEPRECATED;
/**
* Returns the size of the annotation's frame (the main area in which
* Returns the size (in pixels) of the annotation's frame (the main area in which
* the annotation's content is drawn).
* \see setFrameSize()
* \deprecated use frameSizeMm() instead
*/
QSizeF frameSize() const { return mFrameSize; }
Q_DECL_DEPRECATED QSizeF frameSize() const SIP_DEPRECATED;
/**
* Sets the size (in millimeters) of the annotation's frame (the main area in which
* the annotation's content is drawn).
* \see frameSizeMm()
* \since QGIS 3.4.8
*/
void setFrameSizeMm( QSizeF size );
/**
* Returns the size (in millimeters) of the annotation's frame (the main area in which
* the annotation's content is drawn).
* \see setFrameSizeMm()
* \since QGIS 3.4.8
*/
QSizeF frameSizeMm() const { return mFrameSize; }
/**
* Sets the margins (in millimeters) between the outside of the frame and the annotation
@ -332,10 +366,7 @@ class CORE_EXPORT QgsAnnotation : public QObject
void updateBalloon();
//! Gets the frame line (0 is the top line, 1 right, 2 bottom, 3 left)
QLineF segment( int index ) const;
//! Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point
QPointF pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const;
QLineF segment( int index, QgsRenderContext *context ) const;
//! Draws the annotation frame to a destination painter
void drawFrame( QgsRenderContext &context ) const;
@ -357,10 +388,10 @@ class CORE_EXPORT QgsAnnotation : public QObject
//! Relative position (for non-fixed items) (0-1)
QPointF mRelativePosition;
//! Describes the shift of the item content box to the reference point
QPointF mOffsetFromReferencePoint = QPointF( 50, -50 );
//! Describes the shift of the item content box to the reference point in mm
QPointF mOffsetFromReferencePoint = QPointF( 13, -13 );
//! Size of the frame (without balloon)
//! Size of the frame in mm (without balloon)
QSizeF mFrameSize;
//! Point symbol that is to be drawn at the map reference location
@ -374,10 +405,10 @@ class CORE_EXPORT QgsAnnotation : public QObject
//! Segment number where the connection to the map point is attached. -1 if no balloon needed (e.g. if point is contained in frame)
int mBalloonSegment = -1;
//! First segment point for drawing the connection (ccw direction)
//! First segment point for drawing the connection (ccw direction) (always in mm)
QPointF mBalloonSegmentPoint1;
//! Second segment point for drawing the balloon connection (ccw direction)
//! Second segment point for drawing the balloon connection (ccw direction) (always in mm)
QPointF mBalloonSegmentPoint2;
//! Associated layer (or NULLPTR if not attached to a layer)
@ -386,6 +417,9 @@ class CORE_EXPORT QgsAnnotation : public QObject
//! Associated feature, or invalid feature if no feature associated
QgsFeature mFeature;
//! Roughly 10 pixels at 96 dpi, to match earlier version rendering
double mSegmentPointWidthMm = 2.64;
};
#endif // QGSANNOTATION_H

View File

@ -83,8 +83,16 @@ void QgsHtmlAnnotation::renderAnnotation( QgsRenderContext &context, QSizeF size
return;
}
// scale painter back to 96 dpi, so layout prints match screen rendering
context.painter()->save();
const double scaleFactor = context.painter()->device()->logicalDpiX() / 96.0;
context.painter()->scale( scaleFactor, scaleFactor );
size /= scaleFactor;
mWebPage->setViewportSize( size.toSize() );
mWebPage->mainFrame()->render( context.painter() );
context.painter()->restore();
}
QSizeF QgsHtmlAnnotation::minimumFrameSize() const

View File

@ -1,10 +1,10 @@
/***************************************************************************
qgstextannotation.cpp
------------------------
begin : February 9, 2010
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at hugis dot net
***************************************************************************/
begin : February 9, 2010
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at hugis dot net
***************************************************************************/
/***************************************************************************
* *
@ -56,6 +56,12 @@ void QgsTextAnnotation::renderAnnotation( QgsRenderContext &context, QSizeF size
return;
}
// scale painter back to 96 dpi, so layout prints match screen rendering
context.painter()->save();
const double scaleFactor = context.painter()->device()->logicalDpiX() / 96.0;
context.painter()->scale( scaleFactor, scaleFactor );
size /= scaleFactor;
mDocument->setTextWidth( size.width() );
QRectF clipRect = QRectF( 0, 0, size.width(), size.height() );
@ -68,6 +74,8 @@ void QgsTextAnnotation::renderAnnotation( QgsRenderContext &context, QSizeF size
}
//draw text document
mDocument->drawContents( painter, clipRect );
painter->restore();
}
void QgsTextAnnotation::writeXml( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const

View File

@ -62,7 +62,8 @@ void QgsFormAnnotation::setDesignerForm( const QString &uiFile )
newFill->setColor( mDesignerWidget->palette().color( QPalette::Window ) );
setFillSymbol( newFill );
}
setFrameSize( preferredFrameSize() );
// convert from size in pixels at 96 dpi to mm
setFrameSizeMm( preferredFrameSize() / 3.7795275 );
}
emit appearanceChanged();
}
@ -112,10 +113,18 @@ void QgsFormAnnotation::renderAnnotation( QgsRenderContext &context, QSizeF size
if ( !mDesignerWidget )
return;
// scale painter back to 96 dpi, so that forms look good even in layout prints
context.painter()->save();
const double scaleFactor = context.painter()->device()->logicalDpiX() / 96.0;
context.painter()->scale( scaleFactor, scaleFactor );
size /= scaleFactor;
mDesignerWidget->setFixedSize( size.toSize() );
context.painter()->setBrush( Qt::NoBrush );
context.painter()->setPen( Qt::NoPen );
mDesignerWidget->render( context.painter(), QPoint( 0, 0 ) );
context.painter()->restore();
}
QSizeF QgsFormAnnotation::minimumFrameSize() const

View File

@ -94,9 +94,13 @@ void QgsMapCanvasAnnotationItem::updateBoundingRect()
double fillSymbolBleed = mAnnotation && mAnnotation->fillSymbol() ?
QgsSymbolLayerUtils::estimateMaxSymbolBleed( mAnnotation->fillSymbol(), rc ) : 0;
const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
if ( mAnnotation && !mAnnotation->hasFixedMapPosition() )
{
mBoundingRect = QRectF( - fillSymbolBleed, -fillSymbolBleed, mAnnotation->frameSize().width() + fillSymbolBleed * 2, mAnnotation->frameSize().height() + fillSymbolBleed * 2 );
mBoundingRect = QRectF( - fillSymbolBleed, -fillSymbolBleed,
mmToPixelScale * mAnnotation->frameSizeMm().width() + fillSymbolBleed * 2,
mmToPixelScale * mAnnotation->frameSizeMm().height() + fillSymbolBleed * 2 );
}
else
{
@ -106,9 +110,11 @@ void QgsMapCanvasAnnotationItem::updateBoundingRect()
halfSymbolSize = scaledSymbolSize() / 2.0;
}
QPointF offset = mAnnotation ? mAnnotation->frameOffsetFromReferencePoint() : QPointF( 0, 0 );
QPointF offset = mAnnotation ? QPointF( mAnnotation->frameOffsetFromReferencePointMm().x() * mmToPixelScale,
mAnnotation->frameOffsetFromReferencePointMm().y() * mmToPixelScale ) : QPointF( 0, 0 );
QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0.0, 0.0 );
QSizeF frameSize = mAnnotation ? QSizeF( mAnnotation->frameSizeMm().width() * mmToPixelScale,
mAnnotation->frameSizeMm().height() * mmToPixelScale ) : QSizeF( 0.0, 0.0 );
double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
@ -196,8 +202,10 @@ QgsMapCanvasAnnotationItem::MouseMoveAction QgsMapCanvasAnnotationItem::moveActi
return MoveMapPosition;
}
QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePoint() : QPointF( 0, 0 );
QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0, 0 );
const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
bool left, right, up, down;
left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;

View File

@ -26,14 +26,22 @@ from qgis.core import (QgsTextAnnotation,
QgsVectorLayer,
QgsFeature,
QgsMargins,
QgsFillSymbol)
QgsFillSymbol,
QgsProject,
QgsLayout,
QgsLayoutItemMap,
QgsPointXY)
from qgis.gui import QgsFormAnnotation
from qgis.PyQt.QtCore import (QDir,
QPointF,
QSizeF)
QSize,
QSizeF,
QRectF)
from qgis.PyQt.QtGui import (QColor,
QPainter,
QImage,
QTextDocument)
from qgslayoutchecker import QgsLayoutChecker
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
@ -57,8 +65,8 @@ class TestQgsAnnotation(unittest.TestCase):
a = QgsTextAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(300, 200))
a.setFrameOffsetFromReferencePoint(QPointF(40, 50))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
doc = QTextDocument()
doc.setHtml('<p style="font-family: arial; font-weight: bold; font-size: 40px;">test annotation</p>')
a.setDocument(doc)
@ -67,16 +75,28 @@ class TestQgsAnnotation(unittest.TestCase):
# check clone
clone = a.clone()
im = self.renderAnnotation(a, QPointF(20, 30))
im = self.renderAnnotation(clone, QPointF(20, 30))
self.assertTrue(self.imageCheck('text_annotation', 'text_annotation', im))
def testTextAnnotationInLayout(self):
""" test rendering a text annotation"""
a = QgsTextAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
doc = QTextDocument()
doc.setHtml('<p style="font-family: arial; font-weight: bold; font-size: 40px;">test annotation</p>')
a.setDocument(doc)
self.assertTrue(self.renderAnnotationInLayout('text_annotation_in_layout', a))
def testSvgAnnotation(self):
""" test rendering a svg annotation"""
a = QgsSvgAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(300, 200))
a.setFrameOffsetFromReferencePoint(QPointF(40, 50))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
svg = TEST_DATA_DIR + "/sample_svg.svg"
a.setFilePath(svg)
im = self.renderAnnotation(a, QPointF(20, 30))
@ -84,16 +104,27 @@ class TestQgsAnnotation(unittest.TestCase):
# check clone
clone = a.clone()
im = self.renderAnnotation(a, QPointF(20, 30))
im = self.renderAnnotation(clone, QPointF(20, 30))
self.assertTrue(self.imageCheck('svg_annotation', 'svg_annotation', im))
def testSvgAnnotationInLayout(self):
""" test rendering a svg annotation"""
a = QgsSvgAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
svg = TEST_DATA_DIR + "/sample_svg.svg"
a.setFilePath(svg)
self.assertTrue(self.renderAnnotationInLayout('svg_annotation_in_layout', a))
def testHtmlAnnotation(self):
""" test rendering a html annotation"""
a = QgsHtmlAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(400, 250))
a.setFrameOffsetFromReferencePoint(QPointF(70, 90))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(70 / 3.7795275, 90 / 3.7795275))
html = TEST_DATA_DIR + "/test_html.html"
a.setSourceFile(html)
im = self.renderAnnotation(a, QPointF(20, 30))
@ -101,9 +132,20 @@ class TestQgsAnnotation(unittest.TestCase):
# check clone
clone = a.clone()
im = self.renderAnnotation(a, QPointF(20, 30))
im = self.renderAnnotation(clone, QPointF(20, 30))
self.assertTrue(self.imageCheck('html_annotation', 'html_annotation', im))
def testHtmlAnnotationInLayout(self):
""" test rendering a svg annotation"""
a = QgsHtmlAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(70 / 3.7795275, 90 / 3.7795275))
html = TEST_DATA_DIR + "/test_html.html"
a.setSourceFile(html)
self.assertTrue(self.renderAnnotationInLayout('html_annotation_in_layout', a))
def testHtmlAnnotationWithFeature(self):
""" test rendering a html annotation with a feature"""
layer = QgsVectorLayer("Point?crs=EPSG:3111&field=station:string&field=suburb:string",
@ -112,8 +154,8 @@ class TestQgsAnnotation(unittest.TestCase):
a = QgsHtmlAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(400, 250))
a.setFrameOffsetFromReferencePoint(QPointF(70, 90))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(70 / 3.7795275, 90 / 3.7795275))
a.setMapLayer(layer)
html = TEST_DATA_DIR + "/test_html_feature.html"
a.setSourceFile(html)
@ -126,11 +168,39 @@ class TestQgsAnnotation(unittest.TestCase):
im = self.renderAnnotation(a, QPointF(20, 30))
self.assertTrue(self.imageCheck('html_feature', 'html_feature', im))
def testFormAnnotation(self):
""" test rendering a form annotation"""
a = QgsFormAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(70 / 3.7795275, 90 / 3.7795275))
ui = TEST_DATA_DIR + "/test_form.ui"
a.setDesignerForm(ui)
im = self.renderAnnotation(a, QPointF(20, 30))
self.assertTrue(self.imageCheck('form_annotation', 'form_annotation', im))
# check clone
clone = a.clone()
im = self.renderAnnotation(clone, QPointF(20, 30))
self.assertTrue(self.imageCheck('form_annotation', 'form_annotation', im))
def testFormAnnotationInLayout(self):
""" test rendering a form annotation"""
a = QgsFormAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.markerSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(70 / 3.7795275, 90 / 3.7795275))
ui = TEST_DATA_DIR + "/test_form.ui"
a.setDesignerForm(ui)
self.assertTrue(self.renderAnnotationInLayout('form_annotation_in_layout', a))
def testRelativePosition(self):
""" test rendering an annotation without map point"""
a = QgsHtmlAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(400, 250))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setHasFixedMapPosition(False)
html = TEST_DATA_DIR + "/test_html.html"
a.setSourceFile(html)
@ -141,7 +211,7 @@ class TestQgsAnnotation(unittest.TestCase):
""" test rendering an annotation with margins"""
a = QgsHtmlAnnotation()
a.fillSymbol().symbolLayer(0).setStrokeColor(QColor(0, 0, 0))
a.setFrameSize(QSizeF(400, 250))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setHasFixedMapPosition(False)
a.setContentsMargin(QgsMargins(15, 10, 30, 20))
html = TEST_DATA_DIR + "/test_html.html"
@ -152,7 +222,7 @@ class TestQgsAnnotation(unittest.TestCase):
def testFillSymbol(self):
""" test rendering an annotation with fill symbol"""
a = QgsTextAnnotation()
a.setFrameSize(QSizeF(400, 250))
a.setFrameSizeMm(QSizeF(400 / 3.7795275, 250 / 3.7795275))
a.setHasFixedMapPosition(False)
a.setFillSymbol(QgsFillSymbol.createSimple({'color': 'blue', 'width_border': '5', 'outline_color': 'black'}))
im = self.renderAnnotation(a, QPointF(20, 30))
@ -180,6 +250,30 @@ class TestQgsAnnotation(unittest.TestCase):
painter.end()
return image
def renderAnnotationInLayout(self, test_name, annotation):
pr = QgsProject()
l = QgsLayout(pr)
l.initializeDefaults()
map = QgsLayoutItemMap(l)
map.attemptSetSceneRect(QRectF(20, 20, 200, 100))
map.setFrameEnabled(True)
rectangle = QgsRectangle(0, 0, 18, 8)
map.setExtent(rectangle)
l.addLayoutItem(map)
annotation.setMapPosition(QgsPointXY(1, 7))
annotation.setHasFixedMapPosition(True)
pr.annotationManager().addAnnotation(annotation)
checker = QgsLayoutChecker(
test_name, l)
checker.dots_per_meter = 2 * 96 / 25.4 * 1000
checker.size = QSize(1122 * 2, 794 * 2)
checker.setControlPathPrefix("annotations")
result, message = checker.testLayout()
self.report += checker.report()
return result
def imageCheck(self, name, reference_image, image):
self.report += "<h2>Render {}</h2>\n".format(name)
temp_dir = QDir.tempPath() + '/'

View File

@ -38,8 +38,8 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase):
def testPosition(self):
""" test that map canvas annotation item syncs position correctly """
a = QgsTextAnnotation()
a.setFrameSize(QSizeF(300, 200))
a.setFrameOffsetFromReferencePoint(QPointF(40, 50))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
a.setMapPosition(QgsPointXY(12, 34))
a.setMapPositionCrs(QgsCoordinateReferenceSystem(4326))
@ -81,7 +81,7 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase):
def testSize(self):
""" test that map canvas annotation item size is correct """
a = QgsTextAnnotation()
a.setFrameSize(QSizeF(300, 200))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setHasFixedMapPosition(False)
a.setFillSymbol(QgsFillSymbol.createSimple({'color': 'blue', 'width_border': '0'}))
@ -94,6 +94,8 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase):
canvas.setExtent(QgsRectangle(10, 30, 20, 35))
i = QgsMapCanvasAnnotationItem(a, canvas)
# ugly, but Travis has different default DPI:
if 299 < i.boundingRect().width() < 301:
self.assertAlmostEqual(i.boundingRect().width(), 300, 1)
self.assertAlmostEqual(i.boundingRect().height(), 200, 1)
@ -104,6 +106,17 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase):
a.setFrameOffsetFromReferencePoint(QPointF(10, 20))
self.assertAlmostEqual(i.boundingRect().width(), 310, -1)
self.assertAlmostEqual(i.boundingRect().height(), 220, -1)
else:
self.assertAlmostEqual(i.boundingRect().width(), 312.5, 1)
self.assertAlmostEqual(i.boundingRect().height(), 208.33, 1)
a.setHasFixedMapPosition(True)
a.setFrameOffsetFromReferencePoint(QPointF(0, 0))
self.assertAlmostEqual(i.boundingRect().width(), 312.5, -1)
self.assertAlmostEqual(i.boundingRect().height(), 208.33, -1)
a.setFrameOffsetFromReferencePoint(QPointF(10, 20))
self.assertAlmostEqual(i.boundingRect().width(), 322.91, -1)
self.assertAlmostEqual(i.boundingRect().height(), 229.166, -1)
def testVisibility(self):
""" test that map canvas annotation item visibility follows layer"""
@ -123,8 +136,8 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase):
def testSettingFeature(self):
""" test that feature is set when item moves """
a = QgsTextAnnotation()
a.setFrameSize(QSizeF(300, 200))
a.setFrameOffsetFromReferencePoint(QPointF(40, 50))
a.setFrameSizeMm(QSizeF(300 / 3.7795275, 200 / 3.7795275))
a.setFrameOffsetFromReferencePointMm(QPointF(40 / 3.7795275, 50 / 3.7795275))
a.setHasFixedMapPosition(True)
a.setMapPosition(QgsPointXY(12, 34))
a.setMapPositionCrs(QgsCoordinateReferenceSystem(4326))

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

35
tests/testdata/test_form.ui vendored Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>