Skip to content

Commit fa0f0bd

Browse files
committedMay 15, 2014
[FEATURE][composer] Add option for controlling placement of rendered images inside a picture item (eg, top left, bottom right, etc)
1 parent 061941e commit fa0f0bd

File tree

14 files changed

+353
-39
lines changed

14 files changed

+353
-39
lines changed
 

‎python/core/composer/qgscomposerpicture.sip

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,22 @@ class QgsComposerPicture: QgsComposerItem
117117
*/
118118
ResizeMode resizeMode() const;
119119

120+
/**Sets the picture's anchor point, which controls how it is placed
121+
* within the picture item's frame.
122+
* @param anchor anchor point for picture
123+
* @note added in 2.3
124+
* @see pictureAnchor
125+
*/
126+
void setPictureAnchor( QgsComposerItem::ItemPositionMode anchor );
127+
128+
/**Returns the picture's current anchor, which controls how it is placed
129+
* within the picture item's frame.
130+
* @returns anchor point for picture
131+
* @note added in 2.3
132+
* @see setPictureAnchor
133+
*/
134+
ItemPositionMode pictureAnchor() const;
135+
120136
/**Returns whether the picture item is using an expression for the image source.
121137
* @returns true if the picture is using an expression for the source, false if
122138
* it is using a single static file path for the source.

‎src/app/composer/qgscomposerpicturewidget.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,29 @@ void QgsComposerPictureWidget::on_mResizeModeComboBox_currentIndexChanged( int i
245245

246246
//disable picture rotation for non-zoom modes
247247
mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom );
248+
249+
//disable anchor point control for certain zoom modes
250+
if ( mPicture->resizeMode() == QgsComposerPicture::Zoom ||
251+
mPicture->resizeMode() == QgsComposerPicture::Clip )
252+
{
253+
mAnchorPointComboBox->setEnabled( true );
254+
}
255+
else
256+
{
257+
mAnchorPointComboBox->setEnabled( false );
258+
}
259+
}
260+
261+
void QgsComposerPictureWidget::on_mAnchorPointComboBox_currentIndexChanged( int index )
262+
{
263+
if ( !mPicture )
264+
{
265+
return;
266+
}
267+
268+
mPicture->beginCommand( tr( "Picture placement changed" ) );
269+
mPicture->setPictureAnchor(( QgsComposerItem::ItemPositionMode )index );
270+
mPicture->endCommand();
248271
}
249272

250273
void QgsComposerPictureWidget::on_mRadioPath_clicked()
@@ -422,12 +445,12 @@ void QgsComposerPictureWidget::setGuiElementValues()
422445
mComposerMapComboBox->blockSignals( true );
423446
mRotationFromComposerMapCheckBox->blockSignals( true );
424447
mResizeModeComboBox->blockSignals( true );
448+
mAnchorPointComboBox->blockSignals( true );
425449
mRadioPath->blockSignals( true );
426450
mRadioExpression->blockSignals( true );
427451
mPictureExpressionLineEdit->blockSignals( true );
428452

429453
mPictureLineEdit->setText( mPicture->pictureFile() );
430-
// QRectF pictureRect = mPicture->rect();
431454
mPictureRotationSpinBox->setValue( mPicture->pictureRotation() );
432455

433456
refreshMapComboBox();
@@ -455,6 +478,18 @@ void QgsComposerPictureWidget::setGuiElementValues()
455478
//disable picture rotation for non-zoom modes
456479
mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom );
457480

481+
mAnchorPointComboBox->setCurrentIndex(( int )mPicture->pictureAnchor() );
482+
//disable anchor point control for certain zoom modes
483+
if ( mPicture->resizeMode() == QgsComposerPicture::Zoom ||
484+
mPicture->resizeMode() == QgsComposerPicture::Clip )
485+
{
486+
mAnchorPointComboBox->setEnabled( true );
487+
}
488+
else
489+
{
490+
mAnchorPointComboBox->setEnabled( false );
491+
}
492+
458493
mRadioPath->setChecked( !( mPicture->usePictureExpression() ) );
459494
mRadioExpression->setChecked( mPicture->usePictureExpression() );
460495
mPictureLineEdit->setEnabled( !( mPicture->usePictureExpression() ) );
@@ -469,6 +504,7 @@ void QgsComposerPictureWidget::setGuiElementValues()
469504
mPictureLineEdit->blockSignals( false );
470505
mComposerMapComboBox->blockSignals( false );
471506
mResizeModeComboBox->blockSignals( false );
507+
mAnchorPointComboBox->blockSignals( false );
472508
mRadioPath->blockSignals( false );
473509
mRadioExpression->blockSignals( false );
474510
mPictureExpressionLineEdit->blockSignals( false );

‎src/app/composer/qgscomposerpicturewidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi
3030
Q_OBJECT
3131

3232
public:
33+
3334
QgsComposerPictureWidget( QgsComposerPicture* picture );
3435
~QgsComposerPictureWidget();
3536

@@ -47,6 +48,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi
4748
void on_mRotationFromComposerMapCheckBox_stateChanged( int state );
4849
void on_mComposerMapComboBox_activated( const QString & text );
4950
void on_mResizeModeComboBox_currentIndexChanged( int index );
51+
void on_mAnchorPointComboBox_currentIndexChanged( int index );
5052
void on_mRadioPath_clicked();
5153
void on_mRadioExpression_clicked();
5254
void setPictureExpression();

‎src/core/composer/qgscomposerpicture.cpp

Lines changed: 142 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition ) :
3838
mPictureRotation( 0 ),
3939
mRotationMap( 0 ),
4040
mResizeMode( QgsComposerPicture::Zoom ),
41+
mPictureAnchor( UpperLeft ),
4142
mPictureExpr( 0 )
4243
{
4344
mPictureWidth = rect().width();
@@ -50,6 +51,7 @@ QgsComposerPicture::QgsComposerPicture() : QgsComposerItem( 0 ),
5051
mPictureRotation( 0 ),
5152
mRotationMap( 0 ),
5253
mResizeMode( QgsComposerPicture::Zoom ),
54+
mPictureAnchor( UpperLeft ),
5355
mPictureExpr( 0 )
5456
{
5557
mPictureHeight = rect().height();
@@ -90,40 +92,99 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
9092

9193
//int newDpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
9294

95+
//picture resizing
9396
if ( mMode != Unknown )
9497
{
9598
double boundRectWidthMM;
9699
double boundRectHeightMM;
97-
double imageRectWidthMM;
98-
double imageRectHeightMM;
100+
QRect imageRect;
99101
if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
100102
{
101103
boundRectWidthMM = mPictureWidth;
102104
boundRectHeightMM = mPictureHeight;
103-
imageRectWidthMM = mImage.width();
104-
imageRectHeightMM = mImage.height();
105+
imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
105106
}
106107
else if ( mResizeMode == QgsComposerPicture::Stretch )
107108
{
108109
boundRectWidthMM = rect().width();
109110
boundRectHeightMM = rect().height();
110-
imageRectWidthMM = mImage.width();
111-
imageRectHeightMM = mImage.height();
111+
imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
112+
}
113+
else if ( mResizeMode == QgsComposerPicture::Clip )
114+
{
115+
boundRectWidthMM = rect().width();
116+
boundRectHeightMM = rect().height();
117+
int imageRectWidthPixels = mImage.width();
118+
int imageRectHeightPixels = mImage.height();
119+
imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM ,
120+
QSize( imageRectWidthPixels, imageRectHeightPixels ) );
112121
}
113122
else
114123
{
115124
boundRectWidthMM = rect().width();
116125
boundRectHeightMM = rect().height();
117-
imageRectWidthMM = rect().width() * mComposition->printResolution() / 25.4;
118-
imageRectHeightMM = rect().height() * mComposition->printResolution() / 25.4;
126+
imageRect = QRect( 0, 0, rect().width() * mComposition->printResolution() / 25.4,
127+
rect().height() * mComposition->printResolution() / 25.4 );
119128
}
120129
painter->save();
121130

131+
//zoom mode - calculate anchor point and rotation
122132
if ( mResizeMode == Zoom )
123133
{
124-
painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
125-
painter->rotate( mPictureRotation );
126-
painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
134+
//TODO - allow placement modes with rotation set. for now, setting a rotation
135+
//always places picture in center of frame
136+
if ( mPictureRotation != 0 )
137+
{
138+
painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
139+
painter->rotate( mPictureRotation );
140+
painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
141+
}
142+
else
143+
{
144+
//shift painter to edge/middle of frame depending on placement
145+
double diffX = rect().width() - boundRectWidthMM;
146+
double diffY = rect().height() - boundRectHeightMM;
147+
148+
double dX = 0;
149+
double dY = 0;
150+
switch ( mPictureAnchor )
151+
{
152+
case UpperLeft:
153+
case MiddleLeft:
154+
case LowerLeft:
155+
//nothing to do
156+
break;
157+
case UpperMiddle:
158+
case Middle:
159+
case LowerMiddle:
160+
dX = diffX / 2.0;
161+
break;
162+
case UpperRight:
163+
case MiddleRight:
164+
case LowerRight:
165+
dX = diffX;
166+
break;
167+
}
168+
switch ( mPictureAnchor )
169+
{
170+
case UpperLeft:
171+
case UpperMiddle:
172+
case UpperRight:
173+
//nothing to do
174+
break;
175+
case MiddleLeft:
176+
case Middle:
177+
case MiddleRight:
178+
dY = diffY / 2.0;
179+
break;
180+
case LowerLeft:
181+
case LowerMiddle:
182+
case LowerRight:
183+
dY = diffY;
184+
break;
185+
}
186+
painter->translate( dX, dY );
187+
}
127188
}
128189

129190
if ( mMode == SVG )
@@ -132,7 +193,7 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
132193
}
133194
else if ( mMode == RASTER )
134195
{
135-
painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, imageRectWidthMM, imageRectHeightMM ) );
196+
painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
136197
}
137198

138199
painter->restore();
@@ -146,6 +207,64 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
146207
}
147208
}
148209

210+
QRect QgsComposerPicture::clippedImageRect( double &boundRectWidthMM, double &boundRectHeightMM, QSize imageRectPixels )
211+
{
212+
int boundRectWidthPixels = boundRectWidthMM * mComposition->printResolution() / 25.4;
213+
int boundRectHeightPixels = boundRectHeightMM * mComposition->printResolution() / 25.4;
214+
215+
//update boundRectWidth/Height so that they exactly match pixel bounds
216+
boundRectWidthMM = boundRectWidthPixels * 25.4 / mComposition->printResolution();
217+
boundRectHeightMM = boundRectHeightPixels * 25.4 / mComposition->printResolution();
218+
219+
//calculate part of image which fits in bounds
220+
int leftClip = 0;
221+
int topClip = 0;
222+
223+
//calculate left crop
224+
switch ( mPictureAnchor )
225+
{
226+
case UpperLeft:
227+
case MiddleLeft:
228+
case LowerLeft:
229+
leftClip = 0;
230+
break;
231+
case UpperMiddle:
232+
case Middle:
233+
case LowerMiddle:
234+
leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
235+
break;
236+
case UpperRight:
237+
case MiddleRight:
238+
case LowerRight:
239+
leftClip = imageRectPixels.width() - boundRectWidthPixels;
240+
break;
241+
}
242+
243+
//calculate top crop
244+
switch ( mPictureAnchor )
245+
{
246+
case UpperLeft:
247+
case UpperMiddle:
248+
case UpperRight:
249+
topClip = 0;
250+
break;
251+
case MiddleLeft:
252+
case Middle:
253+
case MiddleRight:
254+
topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
255+
break;
256+
case LowerLeft:
257+
case LowerMiddle:
258+
case LowerRight:
259+
topClip = imageRectPixels.height() - boundRectHeightPixels;
260+
break;
261+
}
262+
263+
264+
return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
265+
266+
}
267+
149268
void QgsComposerPicture::setPictureFile( const QString& path )
150269
{
151270
mSourceFile.setFileName( path );
@@ -525,6 +644,8 @@ bool QgsComposerPicture::writeXML( QDomElement& elem, QDomDocument & doc ) const
525644
composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
526645
composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
527646
composerPictureElem.setAttribute( "resizeMode", QString::number(( int )mResizeMode ) );
647+
composerPictureElem.setAttribute( "anchorPoint", QString::number(( int )mPictureAnchor ) );
648+
528649
if ( mUseSourceExpression )
529650
{
530651
composerPictureElem.setAttribute( "useExpression", "true" );
@@ -561,6 +682,7 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
561682
mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
562683
mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
563684
mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
685+
mPictureAnchor = QgsComposerItem::ItemPositionMode( itemElem.attribute( "anchorPoint", QString( QgsComposerItem::UpperLeft ) ).toInt() );
564686

565687
QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
566688
if ( composerItemList.size() > 0 )
@@ -594,8 +716,6 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
594716
QString fileName = QgsProject::instance()->readPath( itemElem.attribute( "file" ) );
595717
mSourceFile.setFileName( fileName );
596718

597-
refreshPicture();
598-
599719
//picture rotation
600720
if ( itemElem.attribute( "pictureRotation", "0" ).toDouble() != 0 )
601721
{
@@ -619,6 +739,8 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
619739
QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
620740
}
621741

742+
refreshPicture();
743+
622744
emit itemChanged();
623745
return true;
624746
}
@@ -635,6 +757,12 @@ int QgsComposerPicture::rotationMap() const
635757
}
636758
}
637759

760+
void QgsComposerPicture::setPictureAnchor( QgsComposerItem::ItemPositionMode anchor )
761+
{
762+
mPictureAnchor = anchor;
763+
update();
764+
}
765+
638766
bool QgsComposerPicture::imageSizeConsideringRotation( double& width, double& height ) const
639767
{
640768
//kept for api compatibility with QGIS 2.0 - use mPictureRotation

0 commit comments

Comments
 (0)
Please sign in to comment.