Skip to content

Commit 0fd99d2

Browse files
committed
[FEATURE][composer] Data defined item rotation. Funded by Canton of Neuchâtel, Switzerland
1 parent a3c8289 commit 0fd99d2

File tree

7 files changed

+160
-48
lines changed

7 files changed

+160
-48
lines changed

python/core/composer/qgscomposeritem.sip

+18-3
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ class QgsComposerItem : QObject, QGraphicsRectItem
163163
MapYMax /*< map extent y maximum */
164164
};
165165

166+
/** Specifies whether the value returned by a function should be the original, user
167+
* set value, or the current evaluated value for the property. This may differ if
168+
* a property has a data defined expression active.
169+
*/
170+
enum PropertyValueType
171+
{
172+
EvaluatedValue = 0, /*< return the current evaluated value for the property */
173+
OriginalValue /*< return the original, user set value */
174+
};
175+
166176
/**Constructor
167177
@param composition parent composition
168178
@param manageZValue true if the z-Value of this object should be managed by mComposition*/
@@ -458,9 +468,14 @@ class QgsComposerItem : QObject, QGraphicsRectItem
458468
@note this method was added in version 1.2*/
459469
bool positionLock() const;
460470

461-
/**Returns the rotation for the composer item
462-
@note this method was added in version 2.1*/
463-
double itemRotation() const;
471+
/**Returns the current rotation for the composer item.
472+
* @returns rotation for composer item
473+
* @param valueType controls whether the returned value is the user specified rotation,
474+
* or the current evaluated rotation (which may be affected by data driven rotation
475+
* settings).
476+
* @note this method was added in version 2.1
477+
*/
478+
double itemRotation( PropertyValueType valueType = EvaluatedValue ) const;
464479

465480
/**Returns the rotation for the composer item
466481
* @deprecated Use itemRotation()

python/core/composer/qgscomposition.sip

+3-2
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,9 @@ class QgsComposition : QGraphicsScene
470470

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

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

src/app/composer/qgscomposeritemwidget.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
150150
}
151151

152152
//connect data defined buttons
153+
connect( mItemRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
154+
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
155+
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mItemRotationSpinBox, SLOT( setDisabled( bool ) ) );
156+
153157
connect( mTransparencyDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
154158
connect( mTransparencyDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
155159
connect( mTransparencyDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mTransparencySlider, SLOT( setDisabled( bool ) ) );
@@ -494,7 +498,7 @@ void QgsComposerItemWidget::setValuesForGuiNonPositionElements()
494498
mBlendModeCombo->setBlendMode( mItem->blendMode() );
495499
mTransparencySlider->setValue( mItem->transparency() );
496500
mTransparencySpnBx->setValue( mItem->transparency() );
497-
mItemRotationSpinBox->setValue( mItem->itemRotation() );
501+
mItemRotationSpinBox->setValue( mItem->itemRotation( QgsComposerItem::OriginalValue ) );
498502

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

516520
//block signals from data defined buttons
521+
mItemRotationDDBtn->blockSignals( true );
517522
mTransparencyDDBtn->blockSignals( true );
518523
mBlendModeDDBtn->blockSignals( true );
519524

520525
//initialise buttons to use atlas coverage layer
526+
mItemRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemRotation ),
527+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
521528
mTransparencyDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::Transparency ),
522529
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() );
523530
mBlendModeDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::BlendMode ),
524531
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );
525532

526533
//initial state of controls - disable related controls when dd buttons are active
534+
mItemRotationSpinBox->setEnabled( !mItemRotationDDBtn->isActive() );
527535
mTransparencySlider->setEnabled( !mTransparencyDDBtn->isActive() );
528536
mTransparencySpnBx->setEnabled( !mTransparencyDDBtn->isActive() );
529537
mBlendModeCombo->setEnabled( !mBlendModeDDBtn->isActive() );
530538

531539
//unblock signals from data defined buttons
540+
mItemRotationDDBtn->blockSignals( false );
532541
mTransparencyDDBtn->blockSignals( false );
533542
mBlendModeDDBtn->blockSignals( false );
534543
}
535544

536545
QgsComposerItem::DataDefinedProperty QgsComposerItemWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
537546
{
538-
if ( widget == mTransparencyDDBtn )
547+
if ( widget == mItemRotationDDBtn )
548+
{
549+
return QgsComposerItem::ItemRotation;
550+
}
551+
else if ( widget == mTransparencyDDBtn )
539552
{
540553
return QgsComposerItem::Transparency;
541554
}

src/core/composer/qgscomposeritem.cpp

+59-18
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue
6161
, mItemPositionLocked( false )
6262
, mLastValidViewScaleFactor( -1 )
6363
, mItemRotation( 0 )
64+
, mEvaluatedItemRotation( 0 )
6465
, mBlendMode( QPainter::CompositionMode_SourceOver )
6566
, mEffectsEnabled( true )
6667
, mTransparency( 0 )
@@ -87,6 +88,7 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
8788
, mItemPositionLocked( false )
8889
, mLastValidViewScaleFactor( -1 )
8990
, mItemRotation( 0 )
91+
, mEvaluatedItemRotation( 0 )
9092
, mBlendMode( QPainter::CompositionMode_SourceOver )
9193
, mEffectsEnabled( true )
9294
, mTransparency( 0 )
@@ -120,6 +122,7 @@ void QgsComposerItem::init( bool manageZValue )
120122
setGraphicsEffect( mEffect );
121123

122124
// data defined strings
125+
mDataDefinedNames.insert( ItemRotation, QString( "dataDefinedRotation" ) );
123126
mDataDefinedNames.insert( Transparency, QString( "dataDefinedTransparency" ) );
124127
mDataDefinedNames.insert( BlendMode, QString( "dataDefinedBlendMode" ) );
125128

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

537+
double QgsComposerItem::itemRotation( PropertyValueType valueType ) const
538+
{
539+
return valueType == EvaluatedValue ? mEvaluatedItemRotation : mItemRotation;
540+
}
541+
534542
void QgsComposerItem::move( double dx, double dy )
535543
{
536544
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
612620
{
613621
//adjust position to account for frame size
614622

615-
if ( mItemRotation == 0 )
623+
if ( mEvaluatedItemRotation == 0 )
616624
{
617625
upperLeftX += estimatedFrameBleed();
618626
upperLeftY += estimatedFrameBleed();
@@ -621,7 +629,7 @@ void QgsComposerItem::setItemPosition( double x, double y, double width, double
621629
{
622630
//adjust position for item rotation
623631
QLineF lineToItemOrigin = QLineF( 0, 0, estimatedFrameBleed(), estimatedFrameBleed() );
624-
lineToItemOrigin.setAngle( -45 - mItemRotation );
632+
lineToItemOrigin.setAngle( -45 - mEvaluatedItemRotation );
625633
upperLeftX += lineToItemOrigin.x2();
626634
upperLeftY += lineToItemOrigin.y2();
627635
}
@@ -935,39 +943,68 @@ void QgsComposerItem::setRotation( double r )
935943

936944
void QgsComposerItem::setItemRotation( double r, bool adjustPosition )
937945
{
946+
if ( r >= 360 )
947+
{
948+
mItemRotation = (( int )r ) % 360;
949+
}
950+
else
951+
{
952+
mItemRotation = r;
953+
}
954+
955+
refreshRotation( true, adjustPosition );
956+
}
957+
958+
void QgsComposerItem::refreshRotation( bool updateItem , bool adjustPosition )
959+
{
960+
double rotation = mItemRotation;
961+
962+
//data defined rotation set?
963+
QVariant exprVal;
964+
if ( dataDefinedEvaluate( QgsComposerItem::ItemRotation, exprVal ) )
965+
{
966+
bool ok;
967+
double rotD = exprVal.toDouble( &ok );
968+
QgsDebugMsg( QString( "exprVal Rotation:%1" ).arg( rotD ) );
969+
if ( ok )
970+
{
971+
rotation = rotD;
972+
}
973+
}
974+
938975
if ( adjustPosition )
939976
{
940977
//adjustPosition set, so shift the position of the item so that rotation occurs around item center
941978
//create a line from the centrepoint of the rect() to its origin, in scene coordinates
942979
QLineF refLine = QLineF( mapToScene( QPointF( rect().width() / 2.0, rect().height() / 2.0 ) ) , mapToScene( QPointF( 0 , 0 ) ) );
943980
//rotate this line by the current rotation angle
944-
refLine.setAngle( refLine.angle() - r + mItemRotation );
981+
refLine.setAngle( refLine.angle() - rotation + mEvaluatedItemRotation );
945982
//get new end point of line - this is the new item position
946983
QPointF rotatedReferencePoint = refLine.p2();
947984
setPos( rotatedReferencePoint );
948985
emit sizeChanged();
949986
}
950987

951-
if ( r > 360 )
952-
{
953-
mItemRotation = (( int )r ) % 360;
954-
}
955-
else
956-
{
957-
mItemRotation = r;
958-
}
959-
960988
setTransformOriginPoint( 0, 0 );
961-
QGraphicsItem::setRotation( mItemRotation );
989+
QGraphicsItem::setRotation( rotation );
962990

963-
emit itemRotationChanged( r );
964-
update();
991+
mEvaluatedItemRotation = rotation;
992+
993+
emit itemRotationChanged( rotation );
994+
995+
//update bounds of scene, since rotation may affect this
996+
mComposition->updateBounds();
997+
998+
if ( updateItem )
999+
{
1000+
update();
1001+
}
9651002
}
9661003

9671004
bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
9681005
{
9691006
//kept for api compatibility with QGIS 2.0, use item rotation
970-
return imageSizeConsideringRotation( width, height, mItemRotation );
1007+
return imageSizeConsideringRotation( width, height, mEvaluatedItemRotation );
9711008
}
9721009

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

11141151
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
11511188
void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
11521189
{
11531190
//kept for api compatibility with QGIS 2.0, use item rotation
1154-
return sizeChangedByRotation( width, height, mItemRotation );
1191+
return sizeChangedByRotation( width, height, mEvaluatedItemRotation );
11551192
}
11561193

11571194
void QgsComposerItem::sizeChangedByRotation( double& width, double& height, double rotation )
@@ -1289,6 +1326,10 @@ void QgsComposerItem::repaint()
12891326
void QgsComposerItem::refreshDataDefinedProperty( QgsComposerItem::DataDefinedProperty property )
12901327
{
12911328
//update data defined properties and redraw item to match
1329+
if ( property == QgsComposerItem::ItemRotation || property == QgsComposerItem::AllProperties )
1330+
{
1331+
refreshRotation( false, true );
1332+
}
12921333
if ( property == QgsComposerItem::Transparency || property == QgsComposerItem::AllProperties )
12931334
{
12941335
refreshTransparency( false );

src/core/composer/qgscomposeritem.h

+36-6
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
118118
MapYMax /*< map extent y maximum */
119119
};
120120

121+
/** Specifies whether the value returned by a function should be the original, user
122+
* set value, or the current evaluated value for the property. This may differ if
123+
* a property has a data defined expression active.
124+
*/
125+
enum PropertyValueType
126+
{
127+
EvaluatedValue = 0, /*< return the current evaluated value for the property */
128+
OriginalValue /*< return the original, user set value */
129+
};
130+
121131
/**Constructor
122132
@param composition parent composition
123133
@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
139149
virtual void setSelected( bool s );
140150

141151
/** \brief Is selected */
142-
virtual bool selected() const {return QGraphicsRectItem::isSelected();};
152+
virtual bool selected() const { return QGraphicsRectItem::isSelected(); }
143153

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

417-
/**Returns the rotation for the composer item
418-
@note this method was added in version 2.1*/
419-
double itemRotation() const { return mItemRotation; }
427+
/**Returns the current rotation for the composer item.
428+
* @returns rotation for composer item
429+
* @param valueType controls whether the returned value is the user specified rotation,
430+
* or the current evaluated rotation (which may be affected by data driven rotation
431+
* settings).
432+
* @note this method was added in version 2.1
433+
*/
434+
double itemRotation( PropertyValueType valueType = EvaluatedValue ) const;
420435

421436
/**Returns the rotation for the composer item
422437
* @deprecated Use itemRotation()
423438
* instead
424439
*/
425-
Q_DECL_DEPRECATED double rotation() const { return mItemRotation; }
440+
Q_DECL_DEPRECATED double rotation() const { return mEvaluatedItemRotation; }
426441

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

542557
/**Item rotation in degrees, clockwise*/
543558
double mItemRotation;
559+
/**Temporary evaluated item rotation in degrees, clockwise. Data defined rotation may mean
560+
* this value differs from mItemRotation.
561+
*/
562+
double mEvaluatedItemRotation;
544563

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

700+
/**Refresh item's rotation, considering data defined rotation setting
701+
*@param updateItem set to false to prevent the item being automatically updated
702+
*@param rotateAroundCenter set to true to rotate the item around its center rather
703+
* than its origin
704+
* @note this method was added in version 2.5
705+
*/
706+
void refreshRotation( bool updateItem = true, bool rotateAroundCenter = false );
707+
681708
/**Refresh item's transparency, considering data defined transparency
682709
*@param updateItem set to false to prevent the item being automatically updated
683710
* after the transparency is set
711+
* @note this method was added in version 2.5
684712
*/
685713
void refreshTransparency( bool updateItem = true );
686714

687-
/**Refresh item's blend mode, considering data defined blend mode*/
715+
/**Refresh item's blend mode, considering data defined blend mode
716+
* @note this method was added in version 2.5
717+
*/
688718
void refreshBlendMode();
689719

690720
void init( bool manageZValue );

src/core/composer/qgscomposition.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -531,8 +531,9 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
531531

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

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

0 commit comments

Comments
 (0)