Skip to content

Commit

Permalink
[FEATURE][composer] Add option for controlling placement of rendered …
Browse files Browse the repository at this point in the history
…images inside a picture item (eg, top left, bottom right, etc)
  • Loading branch information
nyalldawson committed May 15, 2014
1 parent 061941e commit fa0f0bd
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 39 deletions.
16 changes: 16 additions & 0 deletions python/core/composer/qgscomposerpicture.sip
Expand Up @@ -117,6 +117,22 @@ class QgsComposerPicture: QgsComposerItem
*/
ResizeMode resizeMode() const;

/**Sets the picture's anchor point, which controls how it is placed
* within the picture item's frame.
* @param anchor anchor point for picture
* @note added in 2.3
* @see pictureAnchor
*/
void setPictureAnchor( QgsComposerItem::ItemPositionMode anchor );

/**Returns the picture's current anchor, which controls how it is placed
* within the picture item's frame.
* @returns anchor point for picture
* @note added in 2.3
* @see setPictureAnchor
*/
ItemPositionMode pictureAnchor() const;

/**Returns whether the picture item is using an expression for the image source.
* @returns true if the picture is using an expression for the source, false if
* it is using a single static file path for the source.
Expand Down
38 changes: 37 additions & 1 deletion src/app/composer/qgscomposerpicturewidget.cpp
Expand Up @@ -245,6 +245,29 @@ void QgsComposerPictureWidget::on_mResizeModeComboBox_currentIndexChanged( int i

//disable picture rotation for non-zoom modes
mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom );

//disable anchor point control for certain zoom modes
if ( mPicture->resizeMode() == QgsComposerPicture::Zoom ||
mPicture->resizeMode() == QgsComposerPicture::Clip )
{
mAnchorPointComboBox->setEnabled( true );
}
else
{
mAnchorPointComboBox->setEnabled( false );
}
}

void QgsComposerPictureWidget::on_mAnchorPointComboBox_currentIndexChanged( int index )
{
if ( !mPicture )
{
return;
}

mPicture->beginCommand( tr( "Picture placement changed" ) );
mPicture->setPictureAnchor(( QgsComposerItem::ItemPositionMode )index );
mPicture->endCommand();
}

void QgsComposerPictureWidget::on_mRadioPath_clicked()
Expand Down Expand Up @@ -422,12 +445,12 @@ void QgsComposerPictureWidget::setGuiElementValues()
mComposerMapComboBox->blockSignals( true );
mRotationFromComposerMapCheckBox->blockSignals( true );
mResizeModeComboBox->blockSignals( true );
mAnchorPointComboBox->blockSignals( true );
mRadioPath->blockSignals( true );
mRadioExpression->blockSignals( true );
mPictureExpressionLineEdit->blockSignals( true );

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

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

mAnchorPointComboBox->setCurrentIndex(( int )mPicture->pictureAnchor() );
//disable anchor point control for certain zoom modes
if ( mPicture->resizeMode() == QgsComposerPicture::Zoom ||
mPicture->resizeMode() == QgsComposerPicture::Clip )
{
mAnchorPointComboBox->setEnabled( true );
}
else
{
mAnchorPointComboBox->setEnabled( false );
}

mRadioPath->setChecked( !( mPicture->usePictureExpression() ) );
mRadioExpression->setChecked( mPicture->usePictureExpression() );
mPictureLineEdit->setEnabled( !( mPicture->usePictureExpression() ) );
Expand All @@ -469,6 +504,7 @@ void QgsComposerPictureWidget::setGuiElementValues()
mPictureLineEdit->blockSignals( false );
mComposerMapComboBox->blockSignals( false );
mResizeModeComboBox->blockSignals( false );
mAnchorPointComboBox->blockSignals( false );
mRadioPath->blockSignals( false );
mRadioExpression->blockSignals( false );
mPictureExpressionLineEdit->blockSignals( false );
Expand Down
2 changes: 2 additions & 0 deletions src/app/composer/qgscomposerpicturewidget.h
Expand Up @@ -30,6 +30,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi
Q_OBJECT

public:

QgsComposerPictureWidget( QgsComposerPicture* picture );
~QgsComposerPictureWidget();

Expand All @@ -47,6 +48,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi
void on_mRotationFromComposerMapCheckBox_stateChanged( int state );
void on_mComposerMapComboBox_activated( const QString & text );
void on_mResizeModeComboBox_currentIndexChanged( int index );
void on_mAnchorPointComboBox_currentIndexChanged( int index );
void on_mRadioPath_clicked();
void on_mRadioExpression_clicked();
void setPictureExpression();
Expand Down
156 changes: 142 additions & 14 deletions src/core/composer/qgscomposerpicture.cpp
Expand Up @@ -38,6 +38,7 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition ) :
mPictureRotation( 0 ),
mRotationMap( 0 ),
mResizeMode( QgsComposerPicture::Zoom ),
mPictureAnchor( UpperLeft ),
mPictureExpr( 0 )
{
mPictureWidth = rect().width();
Expand All @@ -50,6 +51,7 @@ QgsComposerPicture::QgsComposerPicture() : QgsComposerItem( 0 ),
mPictureRotation( 0 ),
mRotationMap( 0 ),
mResizeMode( QgsComposerPicture::Zoom ),
mPictureAnchor( UpperLeft ),
mPictureExpr( 0 )
{
mPictureHeight = rect().height();
Expand Down Expand Up @@ -90,40 +92,99 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte

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

//picture resizing
if ( mMode != Unknown )
{
double boundRectWidthMM;
double boundRectHeightMM;
double imageRectWidthMM;
double imageRectHeightMM;
QRect imageRect;
if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
{
boundRectWidthMM = mPictureWidth;
boundRectHeightMM = mPictureHeight;
imageRectWidthMM = mImage.width();
imageRectHeightMM = mImage.height();
imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
}
else if ( mResizeMode == QgsComposerPicture::Stretch )
{
boundRectWidthMM = rect().width();
boundRectHeightMM = rect().height();
imageRectWidthMM = mImage.width();
imageRectHeightMM = mImage.height();
imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
}
else if ( mResizeMode == QgsComposerPicture::Clip )
{
boundRectWidthMM = rect().width();
boundRectHeightMM = rect().height();
int imageRectWidthPixels = mImage.width();
int imageRectHeightPixels = mImage.height();
imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM ,
QSize( imageRectWidthPixels, imageRectHeightPixels ) );
}
else
{
boundRectWidthMM = rect().width();
boundRectHeightMM = rect().height();
imageRectWidthMM = rect().width() * mComposition->printResolution() / 25.4;
imageRectHeightMM = rect().height() * mComposition->printResolution() / 25.4;
imageRect = QRect( 0, 0, rect().width() * mComposition->printResolution() / 25.4,
rect().height() * mComposition->printResolution() / 25.4 );
}
painter->save();

//zoom mode - calculate anchor point and rotation
if ( mResizeMode == Zoom )
{
painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
painter->rotate( mPictureRotation );
painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
//TODO - allow placement modes with rotation set. for now, setting a rotation
//always places picture in center of frame
if ( mPictureRotation != 0 )
{
painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
painter->rotate( mPictureRotation );
painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
}
else
{
//shift painter to edge/middle of frame depending on placement
double diffX = rect().width() - boundRectWidthMM;
double diffY = rect().height() - boundRectHeightMM;

double dX = 0;
double dY = 0;
switch ( mPictureAnchor )
{
case UpperLeft:
case MiddleLeft:
case LowerLeft:
//nothing to do
break;
case UpperMiddle:
case Middle:
case LowerMiddle:
dX = diffX / 2.0;
break;
case UpperRight:
case MiddleRight:
case LowerRight:
dX = diffX;
break;
}
switch ( mPictureAnchor )
{
case UpperLeft:
case UpperMiddle:
case UpperRight:
//nothing to do
break;
case MiddleLeft:
case Middle:
case MiddleRight:
dY = diffY / 2.0;
break;
case LowerLeft:
case LowerMiddle:
case LowerRight:
dY = diffY;
break;
}
painter->translate( dX, dY );
}
}

if ( mMode == SVG )
Expand All @@ -132,7 +193,7 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
}
else if ( mMode == RASTER )
{
painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, imageRectWidthMM, imageRectHeightMM ) );
painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
}

painter->restore();
Expand All @@ -146,6 +207,64 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
}
}

QRect QgsComposerPicture::clippedImageRect( double &boundRectWidthMM, double &boundRectHeightMM, QSize imageRectPixels )
{
int boundRectWidthPixels = boundRectWidthMM * mComposition->printResolution() / 25.4;
int boundRectHeightPixels = boundRectHeightMM * mComposition->printResolution() / 25.4;

//update boundRectWidth/Height so that they exactly match pixel bounds
boundRectWidthMM = boundRectWidthPixels * 25.4 / mComposition->printResolution();
boundRectHeightMM = boundRectHeightPixels * 25.4 / mComposition->printResolution();

//calculate part of image which fits in bounds
int leftClip = 0;
int topClip = 0;

//calculate left crop
switch ( mPictureAnchor )
{
case UpperLeft:
case MiddleLeft:
case LowerLeft:
leftClip = 0;
break;
case UpperMiddle:
case Middle:
case LowerMiddle:
leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
break;
case UpperRight:
case MiddleRight:
case LowerRight:
leftClip = imageRectPixels.width() - boundRectWidthPixels;
break;
}

//calculate top crop
switch ( mPictureAnchor )
{
case UpperLeft:
case UpperMiddle:
case UpperRight:
topClip = 0;
break;
case MiddleLeft:
case Middle:
case MiddleRight:
topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
break;
case LowerLeft:
case LowerMiddle:
case LowerRight:
topClip = imageRectPixels.height() - boundRectHeightPixels;
break;
}


return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );

}

void QgsComposerPicture::setPictureFile( const QString& path )
{
mSourceFile.setFileName( path );
Expand Down Expand Up @@ -525,6 +644,8 @@ bool QgsComposerPicture::writeXML( QDomElement& elem, QDomDocument & doc ) const
composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
composerPictureElem.setAttribute( "resizeMode", QString::number(( int )mResizeMode ) );
composerPictureElem.setAttribute( "anchorPoint", QString::number(( int )mPictureAnchor ) );

if ( mUseSourceExpression )
{
composerPictureElem.setAttribute( "useExpression", "true" );
Expand Down Expand Up @@ -561,6 +682,7 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
mPictureAnchor = QgsComposerItem::ItemPositionMode( itemElem.attribute( "anchorPoint", QString( QgsComposerItem::UpperLeft ) ).toInt() );

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

refreshPicture();

//picture rotation
if ( itemElem.attribute( "pictureRotation", "0" ).toDouble() != 0 )
{
Expand All @@ -619,6 +739,8 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
}

refreshPicture();

emit itemChanged();
return true;
}
Expand All @@ -635,6 +757,12 @@ int QgsComposerPicture::rotationMap() const
}
}

void QgsComposerPicture::setPictureAnchor( QgsComposerItem::ItemPositionMode anchor )
{
mPictureAnchor = anchor;
update();
}

bool QgsComposerPicture::imageSizeConsideringRotation( double& width, double& height ) const
{
//kept for api compatibility with QGIS 2.0 - use mPictureRotation
Expand Down

0 comments on commit fa0f0bd

Please sign in to comment.