[FEATURE][composer] Implement different resize modes for composer picture items, including zoom, stretch, clip, frame to image size (fix #7886). (Sponsored by City of Uster, Switzerland)

This commit is contained in:
Nyall Dawson 2014-04-28 19:28:53 +10:00
parent 8ab3ce4c75
commit 23b660ec74
5 changed files with 190 additions and 28 deletions

View File

@ -198,6 +198,21 @@ void QgsComposerPictureWidget::on_mRemoveDirectoryButton_clicked()
s.setValue( "/Composer/PictureWidgetDirectories", userDirList ); s.setValue( "/Composer/PictureWidgetDirectories", userDirList );
} }
void QgsComposerPictureWidget::on_mResizeModeComboBox_currentIndexChanged( int index )
{
if ( !mPicture )
{
return;
}
mPicture->beginCommand( tr( "Picture resize mode changed" ) );
mPicture->setResizeMode(( QgsComposerPicture::ResizeMode )index );
mPicture->endCommand();
//disable picture rotation for non-zoom modes
mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom );
}
void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged( int state ) void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged( int state )
{ {
if ( !mPicture ) if ( !mPicture )
@ -326,6 +341,7 @@ void QgsComposerPictureWidget::setGuiElementValues()
mPictureLineEdit->blockSignals( true ); mPictureLineEdit->blockSignals( true );
mComposerMapComboBox->blockSignals( true ); mComposerMapComboBox->blockSignals( true );
mRotationFromComposerMapCheckBox->blockSignals( true ); mRotationFromComposerMapCheckBox->blockSignals( true );
mResizeModeComboBox->blockSignals( true );
mPictureLineEdit->setText( mPicture->pictureFile() ); mPictureLineEdit->setText( mPicture->pictureFile() );
// QRectF pictureRect = mPicture->rect(); // QRectF pictureRect = mPicture->rect();
@ -352,11 +368,15 @@ void QgsComposerPictureWidget::setGuiElementValues()
mComposerMapComboBox->setEnabled( false ); mComposerMapComboBox->setEnabled( false );
} }
mResizeModeComboBox->setCurrentIndex(( int )mPicture->resizeMode() );
//disable picture rotation for non-zoom modes
mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom );
mRotationFromComposerMapCheckBox->blockSignals( false ); mRotationFromComposerMapCheckBox->blockSignals( false );
mPictureRotationSpinBox->blockSignals( false ); mPictureRotationSpinBox->blockSignals( false );
mPictureLineEdit->blockSignals( false ); mPictureLineEdit->blockSignals( false );
mComposerMapComboBox->blockSignals( false ); mComposerMapComboBox->blockSignals( false );
mResizeModeComboBox->blockSignals( false );
} }
} }

View File

@ -45,6 +45,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi
void on_mRemoveDirectoryButton_clicked(); void on_mRemoveDirectoryButton_clicked();
void on_mRotationFromComposerMapCheckBox_stateChanged( int state ); void on_mRotationFromComposerMapCheckBox_stateChanged( int state );
void on_mComposerMapComboBox_activated( const QString & text ); void on_mComposerMapComboBox_activated( const QString & text );
void on_mResizeModeComboBox_currentIndexChanged( int index );
protected: protected:
void showEvent( QShowEvent * event ); void showEvent( QShowEvent * event );

View File

@ -28,12 +28,13 @@
QgsComposerPicture::QgsComposerPicture( QgsComposition *composition ) QgsComposerPicture::QgsComposerPicture( QgsComposition *composition )
: QgsComposerItem( composition ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ) : QgsComposerItem( composition ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ),
mResizeMode( QgsComposerPicture::Zoom )
{ {
mPictureWidth = rect().width(); mPictureWidth = rect().width();
} }
QgsComposerPicture::QgsComposerPicture(): QgsComposerItem( 0 ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ) QgsComposerPicture::QgsComposerPicture(): QgsComposerItem( 0 ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ), mResizeMode( QgsComposerPicture::Zoom )
{ {
mPictureHeight = rect().height(); mPictureHeight = rect().height();
} }
@ -58,13 +59,39 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
if ( mMode != Unknown ) if ( mMode != Unknown )
{ {
double boundRectWidthMM = mPictureWidth; double boundRectWidthMM;
double boundRectHeightMM = mPictureHeight; double boundRectHeightMM;
double imageRectWidthMM;
double imageRectHeightMM;
if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
{
boundRectWidthMM = mPictureWidth;
boundRectHeightMM = mPictureHeight;
imageRectWidthMM = mImage.width();
imageRectHeightMM = mImage.height();
}
else if ( mResizeMode == QgsComposerPicture::Stretch )
{
boundRectWidthMM = rect().width();
boundRectHeightMM = rect().height();
imageRectWidthMM = mImage.width();
imageRectHeightMM = mImage.height();
}
else
{
boundRectWidthMM = rect().width();
boundRectHeightMM = rect().height();
imageRectWidthMM = rect().width() * mComposition->printResolution() / 25.4;
imageRectHeightMM = rect().height() * mComposition->printResolution() / 25.4;
}
painter->save(); painter->save();
if ( mResizeMode == Zoom )
{
painter->translate( rect().width() / 2.0, rect().height() / 2.0 ); painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
painter->rotate( mPictureRotation ); painter->rotate( mPictureRotation );
painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 ); painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
}
if ( mMode == SVG ) if ( mMode == SVG )
{ {
@ -72,7 +99,7 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte
} }
else if ( mMode == RASTER ) else if ( mMode == RASTER )
{ {
painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) ); painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, imageRectWidthMM, imageRectHeightMM ) );
} }
painter->restore(); painter->restore();
@ -203,15 +230,54 @@ QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeig
#endif //0 #endif //0
void QgsComposerPicture::setSceneRect( const QRectF& rectangle ) void QgsComposerPicture::setSceneRect( const QRectF& rectangle )
{
QSizeF currentPictureSize = pictureSize();
if ( mResizeMode == QgsComposerPicture::Clip )
{ {
QgsComposerItem::setSceneRect( rectangle ); QgsComposerItem::setSceneRect( rectangle );
mPictureWidth = rectangle.width();
mPictureHeight = rectangle.height();
return;
}
QRectF newRect = rectangle;
if ( mResizeMode == ZoomResizeFrame && !rect().isEmpty() )
{
//if width has changed less than height, then fix width and set height correspondingly
//else, do the opposite
if ( qAbs( rect().width() - rectangle.width() ) <
qAbs( rect().height() - rectangle.height() ) )
{
newRect.setHeight( currentPictureSize.height() * newRect.width() / currentPictureSize.width() );
}
else
{
newRect.setWidth( currentPictureSize.width() * newRect.height() / currentPictureSize.height() );
}
}
else if ( mResizeMode == FrameToImageSize )
{
newRect.setWidth( currentPictureSize.width() * 25.4 / mComposition->printResolution() );
newRect.setHeight( currentPictureSize.height() * 25.4 / mComposition->printResolution() );
}
//find largest scaling of picture with this rotation which fits in item //find largest scaling of picture with this rotation which fits in item
QSizeF currentPictureSize = pictureSize(); if ( mResizeMode == Zoom )
QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rectangle, mPictureRotation ); {
QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), newRect, mPictureRotation );
mPictureWidth = rotatedImageRect.width(); mPictureWidth = rotatedImageRect.width();
mPictureHeight = rotatedImageRect.height(); mPictureHeight = rotatedImageRect.height();
}
else
{
mPictureWidth = newRect.width();
mPictureHeight = newRect.height();
}
QgsComposerItem::setSceneRect( newRect );
emit itemChanged(); emit itemChanged();
} }
@ -225,13 +291,16 @@ void QgsComposerPicture::setPictureRotation( double r )
{ {
mPictureRotation = r; mPictureRotation = r;
if ( mResizeMode == Zoom )
{
//find largest scaling of picture with this rotation which fits in item //find largest scaling of picture with this rotation which fits in item
QSizeF currentPictureSize = pictureSize(); QSizeF currentPictureSize = pictureSize();
QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation ); QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation );
mPictureWidth = rotatedImageRect.width(); mPictureWidth = rotatedImageRect.width();
mPictureHeight = rotatedImageRect.height(); mPictureHeight = rotatedImageRect.height();
update(); update();
}
emit pictureRotationChanged( mPictureRotation ); emit pictureRotationChanged( mPictureRotation );
} }
@ -264,6 +333,18 @@ void QgsComposerPicture::setRotationMap( int composerMapId )
emit pictureRotationChanged( mPictureRotation ); emit pictureRotationChanged( mPictureRotation );
} }
void QgsComposerPicture::setResizeMode( QgsComposerPicture::ResizeMode mode )
{
mResizeMode = mode;
if ( mode == QgsComposerPicture::ZoomResizeFrame || mode == QgsComposerPicture::FrameToImageSize
|| ( mode == QgsComposerPicture::Zoom && mPictureRotation != 0 ) )
{
//call set scene rect to force item to resize to fit picture
setSceneRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
}
update();
}
QString QgsComposerPicture::pictureFile() const QString QgsComposerPicture::pictureFile() const
{ {
return mSourceFile.fileName(); return mSourceFile.fileName();
@ -279,6 +360,7 @@ bool QgsComposerPicture::writeXML( QDomElement& elem, QDomDocument & doc ) const
composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourceFile.fileName() ) ); composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourceFile.fileName() ) );
composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) ); composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) ); composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
composerPictureElem.setAttribute( "resizeMode", QString::number(( int )mResizeMode ) );
//rotation //rotation
composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) ); composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) );
@ -305,6 +387,7 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen
mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble(); mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble(); mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" ); QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
if ( composerItemList.size() > 0 ) if ( composerItemList.size() > 0 )

View File

@ -31,6 +31,23 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ResizeMode
{
Zoom,
Stretch,
Clip,
ZoomResizeFrame,
FrameToImageSize
};
enum Mode //SVG or raster graphic format
{
SVG,
RASTER,
Unknown
};
QgsComposerPicture( QgsComposition *composition ); QgsComposerPicture( QgsComposition *composition );
~QgsComposerPicture(); ~QgsComposerPicture();
@ -76,6 +93,10 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
/**True if the rotation is taken from a map item*/ /**True if the rotation is taken from a map item*/
bool useRotationMap() const {return mRotationMap;} bool useRotationMap() const {return mRotationMap;}
/**Returns the resize mode used for drawing the picture within the composer item
*/
ResizeMode resizeMode() const { return mResizeMode;};
/**Calculates width and hight of the picture (in mm) such that it fits into the item frame with the given rotation /**Calculates width and hight of the picture (in mm) such that it fits into the item frame with the given rotation
* @deprecated Use bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height, double rotation ) * @deprecated Use bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height, double rotation )
* instead * instead
@ -92,6 +113,8 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
*/ */
Q_DECL_DEPRECATED void sizeChangedByRotation( double& width, double& height ); Q_DECL_DEPRECATED void sizeChangedByRotation( double& width, double& height );
Mode mode() const { return mMode; };
public slots: public slots:
/**Sets the picture rotation within the item bounds. This does not affect the item rectangle, /**Sets the picture rotation within the item bounds. This does not affect the item rectangle,
only the way the picture is drawn within the item. only the way the picture is drawn within the item.
@ -104,19 +127,16 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
@note this function was added in version 2.1*/ @note this function was added in version 2.1*/
virtual void setPictureRotation( double r ); virtual void setPictureRotation( double r );
/**Sets the resize mode used for drawing the picture within the item bounds
@note this function was added in version 2.1*/
virtual void setResizeMode( ResizeMode mode );
signals: signals:
/**Is emitted on picture rotation change*/ /**Is emitted on picture rotation change*/
void pictureRotationChanged( double newRotation ); void pictureRotationChanged( double newRotation );
private: private:
enum Mode //SVG or raster graphic format
{
SVG,
RASTER,
Unknown
};
//default constructor is forbidden //default constructor is forbidden
QgsComposerPicture(); QgsComposerPicture();
/**Calculates bounding rect for svg file (mSourcefile) such that aspect ratio is correct*/ /**Calculates bounding rect for svg file (mSourcefile) such that aspect ratio is correct*/
@ -142,6 +162,8 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
double mPictureWidth; double mPictureWidth;
/**Height of the picture (in mm)*/ /**Height of the picture (in mm)*/
double mPictureHeight; double mPictureHeight;
ResizeMode mResizeMode;
}; };
#endif #endif

View File

@ -105,6 +105,42 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Resize mode</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mResizeModeComboBox">
<item>
<property name="text">
<string>Zoom</string>
</property>
</item>
<item>
<property name="text">
<string>Stretch</string>
</property>
</item>
<item>
<property name="text">
<string>Clip</string>
</property>
</item>
<item>
<property name="text">
<string>Zoom and resize frame</string>
</property>
</item>
<item>
<property name="text">
<string>Resize frame to image size</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -211,7 +247,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox"> <widget class="QgsCollapsibleGroupBoxBasic" name="mRotationGroupBox">
<property name="title"> <property name="title">
<string>Image rotation</string> <string>Image rotation</string>
</property> </property>