Skip to content
Permalink
Browse files
[FEATURE][composer] Data defined item rotation. Funded by Canton of N…
…euchâtel, Switzerland
  • Loading branch information
nyalldawson committed Jul 5, 2014
1 parent a3c8289 commit 0fd99d2820387dec293beea288212c6307b25c10
@@ -163,6 +163,16 @@ class QgsComposerItem : QObject, QGraphicsRectItem
MapYMax /*< map extent y maximum */
};

/** Specifies whether the value returned by a function should be the original, user
* set value, or the current evaluated value for the property. This may differ if
* a property has a data defined expression active.
*/
enum PropertyValueType
{
EvaluatedValue = 0, /*< return the current evaluated value for the property */
OriginalValue /*< return the original, user set value */
};

/**Constructor
@param composition parent composition
@param manageZValue true if the z-Value of this object should be managed by mComposition*/
@@ -458,9 +468,14 @@ class QgsComposerItem : QObject, QGraphicsRectItem
@note this method was added in version 1.2*/
bool positionLock() const;

/**Returns the rotation for the composer item
@note this method was added in version 2.1*/
double itemRotation() const;
/**Returns the current rotation for the composer item.
* @returns rotation for composer item
* @param valueType controls whether the returned value is the user specified rotation,
* or the current evaluated rotation (which may be affected by data driven rotation
* settings).
* @note this method was added in version 2.1
*/
double itemRotation( PropertyValueType valueType = EvaluatedValue ) const;

/**Returns the rotation for the composer item
* @deprecated Use itemRotation()
@@ -470,8 +470,9 @@ class QgsComposition : QGraphicsScene

/**Forces items in the composition to refresh. For instance, this causes maps to redraw
* and rebuild cached images, html items to reload their source url, and attribute tables
* to refresh their contents.
@note added in version 2.3*/
* to refresh their contents. Calling this also triggers a recalculation of all data defined
* attributes within the composition.
* @note added in version 2.3*/
void refreshItems();

/**Clears any selected items and sets an item as the current selection.
@@ -150,6 +150,10 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
}

//connect data defined buttons
connect( mItemRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mItemRotationSpinBox, SLOT( setDisabled( bool ) ) );

connect( mTransparencyDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mTransparencyDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mTransparencyDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mTransparencySlider, SLOT( setDisabled( bool ) ) );
@@ -494,7 +498,7 @@ void QgsComposerItemWidget::setValuesForGuiNonPositionElements()
mBlendModeCombo->setBlendMode( mItem->blendMode() );
mTransparencySlider->setValue( mItem->transparency() );
mTransparencySpnBx->setValue( mItem->transparency() );
mItemRotationSpinBox->setValue( mItem->itemRotation() );
mItemRotationSpinBox->setValue( mItem->itemRotation( QgsComposerItem::OriginalValue ) );

mBackgroundColorButton->blockSignals( false );
mFrameColorButton->blockSignals( false );
@@ -514,28 +518,37 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
QgsVectorLayer* vl = atlasCoverageLayer();

//block signals from data defined buttons
mItemRotationDDBtn->blockSignals( true );
mTransparencyDDBtn->blockSignals( true );
mBlendModeDDBtn->blockSignals( true );

//initialise buttons to use atlas coverage layer
mItemRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemRotation ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
mTransparencyDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::Transparency ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() );
mBlendModeDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::BlendMode ),
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );

//initial state of controls - disable related controls when dd buttons are active
mItemRotationSpinBox->setEnabled( !mItemRotationDDBtn->isActive() );
mTransparencySlider->setEnabled( !mTransparencyDDBtn->isActive() );
mTransparencySpnBx->setEnabled( !mTransparencyDDBtn->isActive() );
mBlendModeCombo->setEnabled( !mBlendModeDDBtn->isActive() );

//unblock signals from data defined buttons
mItemRotationDDBtn->blockSignals( false );
mTransparencyDDBtn->blockSignals( false );
mBlendModeDDBtn->blockSignals( false );
}

QgsComposerItem::DataDefinedProperty QgsComposerItemWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
{
if ( widget == mTransparencyDDBtn )
if ( widget == mItemRotationDDBtn )
{
return QgsComposerItem::ItemRotation;
}
else if ( widget == mTransparencyDDBtn )
{
return QgsComposerItem::Transparency;
}
@@ -61,6 +61,7 @@ QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue
, mItemPositionLocked( false )
, mLastValidViewScaleFactor( -1 )
, mItemRotation( 0 )
, mEvaluatedItemRotation( 0 )
, mBlendMode( QPainter::CompositionMode_SourceOver )
, mEffectsEnabled( true )
, mTransparency( 0 )
@@ -87,6 +88,7 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
, mItemPositionLocked( false )
, mLastValidViewScaleFactor( -1 )
, mItemRotation( 0 )
, mEvaluatedItemRotation( 0 )
, mBlendMode( QPainter::CompositionMode_SourceOver )
, mEffectsEnabled( true )
, mTransparency( 0 )
@@ -120,6 +122,7 @@ void QgsComposerItem::init( bool manageZValue )
setGraphicsEffect( mEffect );

// data defined strings
mDataDefinedNames.insert( ItemRotation, QString( "dataDefinedRotation" ) );
mDataDefinedNames.insert( Transparency, QString( "dataDefinedTransparency" ) );
mDataDefinedNames.insert( BlendMode, QString( "dataDefinedBlendMode" ) );

@@ -531,6 +534,11 @@ void QgsComposerItem::setPositionLock( bool lock )
mItemPositionLocked = lock;
}

double QgsComposerItem::itemRotation( PropertyValueType valueType ) const
{
return valueType == EvaluatedValue ? mEvaluatedItemRotation : mItemRotation;
}

void QgsComposerItem::move( double dx, double dy )
{
QRectF newSceneRect( pos().x() + dx, pos().y() + dy, rect().width(), rect().height() );
@@ -612,7 +620,7 @@ void QgsComposerItem::setItemPosition( double x, double y, double width, double
{
//adjust position to account for frame size

if ( mItemRotation == 0 )
if ( mEvaluatedItemRotation == 0 )
{
upperLeftX += estimatedFrameBleed();
upperLeftY += estimatedFrameBleed();
@@ -621,7 +629,7 @@ void QgsComposerItem::setItemPosition( double x, double y, double width, double
{
//adjust position for item rotation
QLineF lineToItemOrigin = QLineF( 0, 0, estimatedFrameBleed(), estimatedFrameBleed() );
lineToItemOrigin.setAngle( -45 - mItemRotation );
lineToItemOrigin.setAngle( -45 - mEvaluatedItemRotation );
upperLeftX += lineToItemOrigin.x2();
upperLeftY += lineToItemOrigin.y2();
}
@@ -935,39 +943,68 @@ void QgsComposerItem::setRotation( double r )

void QgsComposerItem::setItemRotation( double r, bool adjustPosition )
{
if ( r >= 360 )
{
mItemRotation = (( int )r ) % 360;
}
else
{
mItemRotation = r;
}

refreshRotation( true, adjustPosition );
}

void QgsComposerItem::refreshRotation( bool updateItem , bool adjustPosition )
{
double rotation = mItemRotation;

//data defined rotation set?
QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerItem::ItemRotation, exprVal ) )
{
bool ok;
double rotD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Rotation:%1" ).arg( rotD ) );
if ( ok )
{
rotation = rotD;
}
}

if ( adjustPosition )
{
//adjustPosition set, so shift the position of the item so that rotation occurs around item center
//create a line from the centrepoint of the rect() to its origin, in scene coordinates
QLineF refLine = QLineF( mapToScene( QPointF( rect().width() / 2.0, rect().height() / 2.0 ) ) , mapToScene( QPointF( 0 , 0 ) ) );
//rotate this line by the current rotation angle
refLine.setAngle( refLine.angle() - r + mItemRotation );
refLine.setAngle( refLine.angle() - rotation + mEvaluatedItemRotation );
//get new end point of line - this is the new item position
QPointF rotatedReferencePoint = refLine.p2();
setPos( rotatedReferencePoint );
emit sizeChanged();
}

if ( r > 360 )
{
mItemRotation = (( int )r ) % 360;
}
else
{
mItemRotation = r;
}

setTransformOriginPoint( 0, 0 );
QGraphicsItem::setRotation( mItemRotation );
QGraphicsItem::setRotation( rotation );

emit itemRotationChanged( r );
update();
mEvaluatedItemRotation = rotation;

emit itemRotationChanged( rotation );

//update bounds of scene, since rotation may affect this
mComposition->updateBounds();

if ( updateItem )
{
update();
}
}

bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
{
//kept for api compatibility with QGIS 2.0, use item rotation
return imageSizeConsideringRotation( width, height, mItemRotation );
return imageSizeConsideringRotation( width, height, mEvaluatedItemRotation );
}

QRectF QgsComposerItem::largestRotatedRectWithinBounds( QRectF originalRect, QRectF boundsRect, double rotation ) const
@@ -1108,7 +1145,7 @@ bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& heigh
bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
{
//kept for api compatibility with QGIS 2.0, use item rotation
return cornerPointOnRotatedAndScaledRect( x, y, width, height, mItemRotation );
return cornerPointOnRotatedAndScaledRect( x, y, width, height, mEvaluatedItemRotation );
}

bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height, double rotation ) const
@@ -1151,7 +1188,7 @@ bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, d
void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
{
//kept for api compatibility with QGIS 2.0, use item rotation
return sizeChangedByRotation( width, height, mItemRotation );
return sizeChangedByRotation( width, height, mEvaluatedItemRotation );
}

void QgsComposerItem::sizeChangedByRotation( double& width, double& height, double rotation )
@@ -1289,6 +1326,10 @@ void QgsComposerItem::repaint()
void QgsComposerItem::refreshDataDefinedProperty( QgsComposerItem::DataDefinedProperty property )
{
//update data defined properties and redraw item to match
if ( property == QgsComposerItem::ItemRotation || property == QgsComposerItem::AllProperties )
{
refreshRotation( false, true );
}
if ( property == QgsComposerItem::Transparency || property == QgsComposerItem::AllProperties )
{
refreshTransparency( false );
@@ -118,6 +118,16 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
MapYMax /*< map extent y maximum */
};

/** Specifies whether the value returned by a function should be the original, user
* set value, or the current evaluated value for the property. This may differ if
* a property has a data defined expression active.
*/
enum PropertyValueType
{
EvaluatedValue = 0, /*< return the current evaluated value for the property */
OriginalValue /*< return the original, user set value */
};

/**Constructor
@param composition parent composition
@param manageZValue true if the z-Value of this object should be managed by mComposition*/
@@ -139,7 +149,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
virtual void setSelected( bool s );

/** \brief Is selected */
virtual bool selected() const {return QGraphicsRectItem::isSelected();};
virtual bool selected() const { return QGraphicsRectItem::isSelected(); }

/** stores state in project */
virtual bool writeSettings();
@@ -414,15 +424,20 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
@note this method was added in version 1.2*/
bool positionLock() const { return mItemPositionLocked; }

/**Returns the rotation for the composer item
@note this method was added in version 2.1*/
double itemRotation() const { return mItemRotation; }
/**Returns the current rotation for the composer item.
* @returns rotation for composer item
* @param valueType controls whether the returned value is the user specified rotation,
* or the current evaluated rotation (which may be affected by data driven rotation
* settings).
* @note this method was added in version 2.1
*/
double itemRotation( PropertyValueType valueType = EvaluatedValue ) const;

/**Returns the rotation for the composer item
* @deprecated Use itemRotation()
* instead
*/
Q_DECL_DEPRECATED double rotation() const { return mItemRotation; }
Q_DECL_DEPRECATED double rotation() const { return mEvaluatedItemRotation; }

/**Updates item, with the possibility to do custom update for subclasses*/
virtual void updateItem() { QGraphicsRectItem::update(); }
@@ -541,6 +556,10 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem

/**Item rotation in degrees, clockwise*/
double mItemRotation;
/**Temporary evaluated item rotation in degrees, clockwise. Data defined rotation may mean
* this value differs from mItemRotation.
*/
double mEvaluatedItemRotation;

/**Composition blend mode for item*/
QPainter::CompositionMode mBlendMode;
@@ -678,13 +697,24 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
/**Map of current data defined properties*/
QMap< QgsComposerItem::DataDefinedProperty, QgsDataDefined* > mDataDefinedProperties;

/**Refresh item's rotation, considering data defined rotation setting
*@param updateItem set to false to prevent the item being automatically updated
*@param rotateAroundCenter set to true to rotate the item around its center rather
* than its origin
* @note this method was added in version 2.5
*/
void refreshRotation( bool updateItem = true, bool rotateAroundCenter = false );

/**Refresh item's transparency, considering data defined transparency
*@param updateItem set to false to prevent the item being automatically updated
* after the transparency is set
* @note this method was added in version 2.5
*/
void refreshTransparency( bool updateItem = true );

/**Refresh item's blend mode, considering data defined blend mode*/
/**Refresh item's blend mode, considering data defined blend mode
* @note this method was added in version 2.5
*/
void refreshBlendMode();

void init( bool manageZValue );
@@ -531,8 +531,9 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene

/**Forces items in the composition to refresh. For instance, this causes maps to redraw
* and rebuild cached images, html items to reload their source url, and attribute tables
* to refresh their contents.
@note added in version 2.3*/
* to refresh their contents. Calling this also triggers a recalculation of all data defined
* attributes within the composition.
* @note added in version 2.3*/
void refreshItems();

/**Clears any selected items and sets an item as the current selection.

0 comments on commit 0fd99d2

Please sign in to comment.