Skip to content

Commit e771b05

Browse files
committed
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
1 parent 4aaa523 commit e771b05

File tree

43 files changed

+456
-143
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+456
-143
lines changed

python/core/auto_generated/annotations/qgsannotation.sip.in

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,34 +143,80 @@ the relative percentage for the position compared to the map width and height.
143143
.. seealso:: :py:func:`relativePosition`
144144
%End
145145

146-
void setFrameOffsetFromReferencePoint( QPointF offset );
146+
void setFrameOffsetFromReferencePoint( QPointF offset ) /Deprecated/;
147147
%Docstring
148-
Sets the annotation's frame's offset from the mapPosition() reference point.
148+
Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
149149

150150
.. seealso:: :py:func:`frameOffsetFromReferencePoint`
151+
152+
.. deprecated:: use setFrameOffsetFromReferencePointMm() instead
151153
%End
152154

153-
QPointF frameOffsetFromReferencePoint() const;
155+
QPointF frameOffsetFromReferencePoint() const /Deprecated/;
154156
%Docstring
155-
Returns the annotation's frame's offset from the mapPosition() reference point.
157+
Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
156158

157159
.. seealso:: :py:func:`setFrameOffsetFromReferencePoint`
160+
161+
.. deprecated:: use frameOffsetFromReferencePointMm() instead
162+
%End
163+
164+
void setFrameOffsetFromReferencePointMm( QPointF offset );
165+
%Docstring
166+
Sets the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
167+
168+
.. seealso:: :py:func:`frameOffsetFromReferencePointMm`
169+
170+
.. versionadded:: 3.4.8
158171
%End
159172

160-
void setFrameSize( QSizeF size );
173+
QPointF frameOffsetFromReferencePointMm() const;
161174
%Docstring
162-
Sets the size of the annotation's frame (the main area in which
175+
Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
176+
177+
.. seealso:: :py:func:`setFrameOffsetFromReferencePointMm`
178+
179+
.. versionadded:: 3.4.8
180+
%End
181+
182+
void setFrameSize( QSizeF size ) /Deprecated/;
183+
%Docstring
184+
Sets the size (in pixels) of the annotation's frame (the main area in which
163185
the annotation's content is drawn).
164186

165187
.. seealso:: :py:func:`frameSize`
188+
189+
.. deprecated:: use setFrameSizeMm() instead
166190
%End
167191

168-
QSizeF frameSize() const;
192+
QSizeF frameSize() const /Deprecated/;
169193
%Docstring
170-
Returns the size of the annotation's frame (the main area in which
194+
Returns the size (in pixels) of the annotation's frame (the main area in which
171195
the annotation's content is drawn).
172196

173197
.. seealso:: :py:func:`setFrameSize`
198+
199+
.. deprecated:: use frameSizeMm() instead
200+
%End
201+
202+
void setFrameSizeMm( QSizeF size );
203+
%Docstring
204+
Sets the size (in millimeters) of the annotation's frame (the main area in which
205+
the annotation's content is drawn).
206+
207+
.. seealso:: :py:func:`frameSizeMm`
208+
209+
.. versionadded:: 3.4.8
210+
%End
211+
212+
QSizeF frameSizeMm() const;
213+
%Docstring
214+
Returns the size (in millimeters) of the annotation's frame (the main area in which
215+
the annotation's content is drawn).
216+
217+
.. seealso:: :py:func:`setFrameSizeMm`
218+
219+
.. versionadded:: 3.4.8
174220
%End
175221

176222
void setContentsMargin( const QgsMargins &margins );

src/app/qgsmaptoolannotation.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,19 @@ QDialog *QgsMapToolAnnotation::createItemEditor( QgsMapCanvasAnnotationItem *ite
5050

5151
QgsAnnotation *annotation = item->annotation();
5252

53-
QgsTextAnnotation *tItem = dynamic_cast<QgsTextAnnotation *>( annotation );
54-
if ( tItem )
53+
if ( qobject_cast<QgsTextAnnotation *>( annotation ) )
5554
{
5655
return new QgsTextAnnotationDialog( item );
5756
}
58-
59-
QgsFormAnnotation *fItem = dynamic_cast<QgsFormAnnotation *>( annotation );
60-
if ( fItem )
57+
else if ( qobject_cast<QgsFormAnnotation *>( annotation ) )
6158
{
6259
return new QgsFormAnnotationDialog( item );
6360
}
64-
65-
QgsHtmlAnnotation *hItem = dynamic_cast<QgsHtmlAnnotation *>( annotation );
66-
if ( hItem )
61+
else if ( qobject_cast<QgsHtmlAnnotation *>( annotation ) )
6762
{
6863
return new QgsHtmlAnnotationDialog( item );
6964
}
70-
71-
QgsSvgAnnotation *sItem = dynamic_cast<QgsSvgAnnotation *>( annotation );
72-
if ( sItem )
65+
else if ( qobject_cast<QgsSvgAnnotation *>( annotation ) )
7366
{
7467
return new QgsSvgAnnotationDialog( item );
7568
}
@@ -124,7 +117,7 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e )
124117
annotation->setMapPositionCrs( mCanvas->mapSettings().destinationCrs() );
125118
annotation->setRelativePosition( QPointF( e->pos().x() / mCanvas->width(),
126119
e->pos().y() / mCanvas->height() ) );
127-
annotation->setFrameSize( QSizeF( 200, 100 ) );
120+
annotation->setFrameSizeMm( QSizeF( 50, 25 ) );
128121

129122
QgsProject::instance()->annotationManager()->addAnnotation( annotation );
130123

@@ -193,7 +186,11 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
193186
QPointF newCanvasPos = item->pos() + ( e->pos() - mLastMousePosition );
194187
if ( annotation->hasFixedMapPosition() )
195188
{
196-
annotation->setFrameOffsetFromReferencePoint( annotation->frameOffsetFromReferencePoint() + ( e->pos() - mLastMousePosition ) );
189+
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
190+
const double deltaX = pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
191+
const double deltaY = pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
192+
annotation->setFrameOffsetFromReferencePointMm( QPointF( annotation->frameOffsetFromReferencePointMm().x() + deltaX,
193+
annotation->frameOffsetFromReferencePointMm().y() + deltaY ) );
197194
annotation->setRelativePosition( QPointF( newCanvasPos.x() / mCanvas->width(),
198195
newCanvasPos.y() / mCanvas->height() ) );
199196
}
@@ -210,9 +207,12 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
210207
else if ( mCurrentMoveAction != QgsMapCanvasAnnotationItem::NoAction )
211208
{
212209
//handle the frame resize actions
213-
QSizeF size = annotation->frameSize();
214-
double xmin = annotation->frameOffsetFromReferencePoint().x();
215-
double ymin = annotation->frameOffsetFromReferencePoint().y();
210+
211+
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
212+
213+
QSizeF size = annotation->frameSizeMm();
214+
double xmin = annotation->frameOffsetFromReferencePointMm().x();
215+
double ymin = annotation->frameOffsetFromReferencePointMm().y();
216216
double xmax = xmin + size.width();
217217
double ymax = ymin + size.height();
218218
double relPosX = annotation->relativePosition().x();
@@ -222,27 +222,27 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
222222
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown ||
223223
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
224224
{
225-
xmax += e->pos().x() - mLastMousePosition.x();
225+
xmax += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
226226
}
227227
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeft ||
228228
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
229229
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp )
230230
{
231-
xmin += e->pos().x() - mLastMousePosition.x();
231+
xmin += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
232232
relPosX = ( relPosX * mCanvas->width() + e->pos().x() - mLastMousePosition.x() ) / static_cast<double>( mCanvas->width() );
233233
}
234234
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameUp ||
235235
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp ||
236236
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
237237
{
238-
ymin += e->pos().y() - mLastMousePosition.y();
238+
ymin += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
239239
relPosY = ( relPosY * mCanvas->height() + e->pos().y() - mLastMousePosition.y() ) / static_cast<double>( mCanvas->height() );
240240
}
241241
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameDown ||
242242
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
243243
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown )
244244
{
245-
ymax += e->pos().y() - mLastMousePosition.y();
245+
ymax += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
246246
}
247247

248248
//switch min / max if necessary
@@ -260,8 +260,8 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
260260
ymin = tmp;
261261
}
262262

263-
annotation->setFrameOffsetFromReferencePoint( QPointF( xmin, ymin ) );
264-
annotation->setFrameSize( QSizeF( xmax - xmin, ymax - ymin ) );
263+
annotation->setFrameOffsetFromReferencePointMm( QPointF( xmin, ymin ) );
264+
annotation->setFrameSizeMm( QSizeF( xmax - xmin, ymax - ymin ) );
265265
annotation->setRelativePosition( QPointF( relPosX, relPosY ) );
266266
item->update();
267267
QgsProject::instance()->setDirty( true );
@@ -351,7 +351,7 @@ void QgsMapToolAnnotation::toggleTextItemVisibilities()
351351
const auto constItemList = itemList;
352352
for ( QgsMapCanvasAnnotationItem *item : constItemList )
353353
{
354-
QgsTextAnnotation *textItem = dynamic_cast<QgsTextAnnotation *>( item->annotation() );
354+
QgsTextAnnotation *textItem = qobject_cast<QgsTextAnnotation *>( item->annotation() );
355355
if ( textItem )
356356
{
357357
textItem->setVisible( !textItem->isVisible() );

0 commit comments

Comments
 (0)