Skip to content

Commit da97684

Browse files
committed
[FEATURE][composer] Data defined item size and position. Funded by Canton of Neuchâtel, Switzerland
1 parent 7604324 commit da97684

File tree

4 files changed

+227
-25
lines changed

4 files changed

+227
-25
lines changed

src/app/composer/qgscomposeritemwidget.cpp

+55-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,23 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
150150
}
151151

152152
//connect data defined buttons
153+
connect( mXPositionDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
154+
connect( mXPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
155+
connect( mXPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXLineEdit, SLOT( setDisabled( bool ) ) );
156+
157+
connect( mYPositionDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
158+
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
159+
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYLineEdit, SLOT( setDisabled( bool ) ) );
160+
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mPageSpinBox, SLOT( setDisabled( bool ) ) );
161+
162+
connect( mWidthDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
163+
connect( mWidthDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
164+
connect( mWidthDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mWidthLineEdit, SLOT( setDisabled( bool ) ) );
165+
166+
connect( mHeightDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
167+
connect( mHeightDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
168+
connect( mHeightDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mHeightLineEdit, SLOT( setDisabled( bool ) ) );
169+
153170
connect( mItemRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
154171
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
155172
connect( mItemRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mItemRotationSpinBox, SLOT( setDisabled( bool ) ) );
@@ -518,11 +535,23 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
518535
QgsVectorLayer* vl = atlasCoverageLayer();
519536

520537
//block signals from data defined buttons
538+
mXPositionDDBtn->blockSignals( true );
539+
mYPositionDDBtn->blockSignals( true );
540+
mWidthDDBtn->blockSignals( true );
541+
mHeightDDBtn->blockSignals( true );
521542
mItemRotationDDBtn->blockSignals( true );
522543
mTransparencyDDBtn->blockSignals( true );
523544
mBlendModeDDBtn->blockSignals( true );
524545

525546
//initialise buttons to use atlas coverage layer
547+
mXPositionDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::PositionX ),
548+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
549+
mYPositionDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::PositionY ),
550+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
551+
mWidthDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemWidth ),
552+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
553+
mHeightDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemHeight ),
554+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
526555
mItemRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemRotation ),
527556
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
528557
mTransparencyDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::Transparency ),
@@ -531,20 +560,45 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
531560
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );
532561

533562
//initial state of controls - disable related controls when dd buttons are active
563+
mXLineEdit->setEnabled( !mXPositionDDBtn->isActive() );
564+
mYLineEdit->setEnabled( !mYPositionDDBtn->isActive() );
565+
mPageSpinBox->setEnabled( !mYPositionDDBtn->isActive() );
566+
mWidthLineEdit->setEnabled( !mWidthDDBtn->isActive() );
567+
mHeightLineEdit->setEnabled( !mHeightDDBtn->isActive() );
534568
mItemRotationSpinBox->setEnabled( !mItemRotationDDBtn->isActive() );
535569
mTransparencySlider->setEnabled( !mTransparencyDDBtn->isActive() );
536570
mTransparencySpnBx->setEnabled( !mTransparencyDDBtn->isActive() );
537571
mBlendModeCombo->setEnabled( !mBlendModeDDBtn->isActive() );
538572

539573
//unblock signals from data defined buttons
574+
mXPositionDDBtn->blockSignals( false );
575+
mYPositionDDBtn->blockSignals( false );
576+
mWidthDDBtn->blockSignals( false );
577+
mHeightDDBtn->blockSignals( false );
540578
mItemRotationDDBtn->blockSignals( false );
541579
mTransparencyDDBtn->blockSignals( false );
542580
mBlendModeDDBtn->blockSignals( false );
543581
}
544582

545583
QgsComposerItem::DataDefinedProperty QgsComposerItemWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
546584
{
547-
if ( widget == mItemRotationDDBtn )
585+
if ( widget == mXPositionDDBtn )
586+
{
587+
return QgsComposerItem::PositionX;
588+
}
589+
else if ( widget == mYPositionDDBtn )
590+
{
591+
return QgsComposerItem::PositionY;
592+
}
593+
else if ( widget == mWidthDDBtn )
594+
{
595+
return QgsComposerItem::ItemWidth;
596+
}
597+
else if ( widget == mHeightDDBtn )
598+
{
599+
return QgsComposerItem::ItemHeight;
600+
}
601+
else if ( widget == mItemRotationDDBtn )
548602
{
549603
return QgsComposerItem::ItemRotation;
550604
}

src/core/composer/qgscomposeritem.cpp

+124-7
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ void QgsComposerItem::init( bool manageZValue )
122122
setGraphicsEffect( mEffect );
123123

124124
// data defined strings
125+
mDataDefinedNames.insert( PageNumber, QString( "dataDefinedPageNumber" ) );
126+
mDataDefinedNames.insert( PositionX, QString( "dataDefinedPositionX" ) );
127+
mDataDefinedNames.insert( PositionY, QString( "dataDefinedPositionY" ) );
128+
mDataDefinedNames.insert( ItemWidth, QString( "dataDefinedWidth" ) );
129+
mDataDefinedNames.insert( ItemHeight, QString( "dataDefinedHeight" ) );
125130
mDataDefinedNames.insert( ItemRotation, QString( "dataDefinedRotation" ) );
126131
mDataDefinedNames.insert( Transparency, QString( "dataDefinedTransparency" ) );
127132
mDataDefinedNames.insert( BlendMode, QString( "dataDefinedBlendMode" ) );
@@ -351,7 +356,6 @@ bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument&
351356

352357
mLastValidViewScaleFactor = itemElem.attribute( "lastValidViewScaleFactor", "-1" ).toDouble();
353358

354-
setSceneRect( QRectF( x, y, width, height ) );
355359
setZValue( itemElem.attribute( "zValue" ).toDouble() );
356360

357361
//pen
@@ -409,6 +413,9 @@ bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument&
409413
mComposition->readDataDefinedPropertyMap( itemElem, &mDataDefinedNames, &mDataDefinedProperties );
410414
}
411415

416+
QRectF evaluatedRect = evalItemRect( QRectF( x, y, width, height ) );
417+
setSceneRect( evaluatedRect );
418+
412419
return true;
413420
}
414421

@@ -542,7 +549,7 @@ double QgsComposerItem::itemRotation( PropertyValueType valueType ) const
542549
void QgsComposerItem::move( double dx, double dy )
543550
{
544551
QRectF newSceneRect( pos().x() + dx, pos().y() + dy, rect().width(), rect().height() );
545-
setSceneRect( newSceneRect );
552+
setSceneRect( evalItemRect( newSceneRect ) );
546553
}
547554

548555
int QgsComposerItem::page() const
@@ -571,7 +578,11 @@ void QgsComposerItem::updatePagePos( double newPageWidth, double newPageHeight )
571578
Q_UNUSED( newPageWidth )
572579
QPointF curPagePos = pagePos();
573580
int curPage = page() - 1;
574-
setY( curPage * ( newPageHeight + composition()->spaceBetweenPages() ) + curPagePos.y() );
581+
582+
double y = curPage * ( newPageHeight + composition()->spaceBetweenPages() ) + curPagePos.y();
583+
QRectF newSceneRect( pos().x(), y, rect().width(), rect().height() );
584+
585+
setSceneRect( evalItemRect( newSceneRect ) );
575586
emit sizeChanged();
576587
}
577588

@@ -638,7 +649,10 @@ void QgsComposerItem::setItemPosition( double x, double y, double width, double
638649
height -= 2 * estimatedFrameBleed();
639650
}
640651

641-
setSceneRect( QRectF( upperLeftX, upperLeftY, width, height ) );
652+
//consider data defined item size and position before finalising rect
653+
QRectF newRect = evalItemRect( QRectF( upperLeftX, upperLeftY, width, height ) );
654+
655+
setSceneRect( newRect );
642656
}
643657

644658
void QgsComposerItem::setSceneRect( const QRectF& rectangle )
@@ -662,13 +676,109 @@ void QgsComposerItem::setSceneRect( const QRectF& rectangle )
662676
yTranslation -= newHeight;
663677
}
664678

665-
QRectF newRect( 0, 0, newWidth, newHeight );
666-
QGraphicsRectItem::setRect( newRect );
667-
setPos( xTranslation, yTranslation );
679+
QGraphicsRectItem::setRect( QRectF( 0, 0, newWidth, newHeight ) );
680+
setPos( QPointF( xTranslation, yTranslation ) );
668681

669682
emit sizeChanged();
670683
}
671684

685+
QRectF QgsComposerItem::evalItemRect( const QRectF &newRect )
686+
{
687+
QRectF result = newRect;
688+
689+
//data defined position or size set? if so, update rect with data defined values
690+
QVariant exprVal;
691+
//evaulate width and height first, since they may affect position if non-top-left reference point set
692+
if ( dataDefinedEvaluate( QgsComposerItem::ItemWidth, exprVal ) )
693+
{
694+
bool ok;
695+
double width = exprVal.toDouble( &ok );
696+
QgsDebugMsg( QString( "exprVal Width:%1" ).arg( width ) );
697+
if ( ok )
698+
{
699+
result.setWidth( width );
700+
}
701+
}
702+
if ( dataDefinedEvaluate( QgsComposerItem::ItemHeight, exprVal ) )
703+
{
704+
bool ok;
705+
double height = exprVal.toDouble( &ok );
706+
QgsDebugMsg( QString( "exprVal Height:%1" ).arg( height ) );
707+
if ( ok )
708+
{
709+
result.setHeight( height );
710+
}
711+
}
712+
713+
double x = result.left();
714+
//initially adjust for position mode to get top-left coordinate
715+
if ( mLastUsedPositionMode == UpperMiddle || mLastUsedPositionMode == Middle || mLastUsedPositionMode == LowerMiddle )
716+
{
717+
x += newRect.width() / 2.0;
718+
}
719+
else if ( mLastUsedPositionMode == UpperRight || mLastUsedPositionMode == MiddleRight || mLastUsedPositionMode == LowerRight )
720+
{
721+
x += newRect.width();
722+
}
723+
if ( dataDefinedEvaluate( QgsComposerItem::PositionX, exprVal ) )
724+
{
725+
bool ok;
726+
double positionX = exprVal.toDouble( &ok );
727+
QgsDebugMsg( QString( "exprVal Position X:%1" ).arg( positionX ) );
728+
if ( ok )
729+
{
730+
x = positionX;
731+
}
732+
}
733+
734+
double y = result.top();
735+
//adjust y-coordinate if placement is not done to an upper point
736+
if ( mLastUsedPositionMode == MiddleLeft || mLastUsedPositionMode == Middle || mLastUsedPositionMode == MiddleRight )
737+
{
738+
y += newRect.height() / 2.0;
739+
}
740+
else if ( mLastUsedPositionMode == LowerLeft || mLastUsedPositionMode == LowerMiddle || mLastUsedPositionMode == LowerRight )
741+
{
742+
y += newRect.height();
743+
}
744+
745+
if ( dataDefinedEvaluate( QgsComposerItem::PositionY, exprVal ) )
746+
{
747+
bool ok;
748+
double positionY = exprVal.toDouble( &ok );
749+
QgsDebugMsg( QString( "exprVal Position Y:%1" ).arg( positionY ) );
750+
if ( ok )
751+
{
752+
y = positionY;
753+
}
754+
}
755+
756+
//adjust x-coordinate if placement is not done to a left point
757+
if ( mLastUsedPositionMode == UpperMiddle || mLastUsedPositionMode == Middle || mLastUsedPositionMode == LowerMiddle )
758+
{
759+
x -= result.width() / 2.0;
760+
}
761+
else if ( mLastUsedPositionMode == UpperRight || mLastUsedPositionMode == MiddleRight || mLastUsedPositionMode == LowerRight )
762+
{
763+
x -= result.width();
764+
}
765+
766+
//adjust y-coordinate if placement is not done to an upper point
767+
if ( mLastUsedPositionMode == MiddleLeft || mLastUsedPositionMode == Middle || mLastUsedPositionMode == MiddleRight )
768+
{
769+
y -= result.height() / 2.0;
770+
}
771+
else if ( mLastUsedPositionMode == LowerLeft || mLastUsedPositionMode == LowerMiddle || mLastUsedPositionMode == LowerRight )
772+
{
773+
y -= result.height();
774+
}
775+
776+
result.moveLeft( x );
777+
result.moveTop( y );
778+
779+
return result;
780+
}
781+
672782
void QgsComposerItem::drawBackground( QPainter* p )
673783
{
674784
if ( mBackground && p )
@@ -1326,6 +1436,13 @@ void QgsComposerItem::repaint()
13261436
void QgsComposerItem::refreshDataDefinedProperty( QgsComposerItem::DataDefinedProperty property )
13271437
{
13281438
//update data defined properties and redraw item to match
1439+
if ( property == QgsComposerItem::PositionX || property == QgsComposerItem::PositionY ||
1440+
property == QgsComposerItem::ItemWidth || property == QgsComposerItem::ItemHeight ||
1441+
property == QgsComposerItem::AllProperties )
1442+
{
1443+
QRectF evaluatedRect = evalItemRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
1444+
setSceneRect( evaluatedRect );
1445+
}
13291446
if ( property == QgsComposerItem::ItemRotation || property == QgsComposerItem::AllProperties )
13301447
{
13311448
refreshRotation( false, true );

src/core/composer/qgscomposeritem.h

+3
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
697697
/**Map of current data defined properties*/
698698
QMap< QgsComposerItem::DataDefinedProperty, QgsDataDefined* > mDataDefinedProperties;
699699

700+
/**Update an item rect to consider data defined position and size of item*/
701+
QRectF evalItemRect( const QRectF &newRect );
702+
700703
/**Refresh item's rotation, considering data defined rotation setting
701704
*@param updateItem set to false to prevent the item being automatically updated
702705
*@param rotateAroundCenter set to true to rotate the item around its center rather

src/ui/qgscomposeritemwidgetbase.ui

+45-17
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>376</width>
10-
<height>695</height>
10+
<height>697</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -170,22 +170,9 @@
170170
</item>
171171
<item row="0" column="0" rowspan="2">
172172
<layout class="QGridLayout" name="gridLayout_3">
173-
<item row="4" column="1">
174-
<widget class="QLineEdit" name="mHeightLineEdit"/>
175-
</item>
176-
<item row="1" column="1">
177-
<widget class="QLineEdit" name="mXLineEdit"/>
178-
</item>
179173
<item row="3" column="1">
180174
<widget class="QLineEdit" name="mWidthLineEdit"/>
181175
</item>
182-
<item row="2" column="0">
183-
<widget class="QLabel" name="mYLabel">
184-
<property name="text">
185-
<string>Y</string>
186-
</property>
187-
</widget>
188-
</item>
189176
<item row="3" column="0">
190177
<widget class="QLabel" name="mWidthLabel">
191178
<property name="text">
@@ -207,8 +194,18 @@
207194
</property>
208195
</widget>
209196
</item>
210-
<item row="2" column="1">
211-
<widget class="QLineEdit" name="mYLineEdit"/>
197+
<item row="1" column="1">
198+
<widget class="QLineEdit" name="mXLineEdit"/>
199+
</item>
200+
<item row="4" column="1">
201+
<widget class="QLineEdit" name="mHeightLineEdit"/>
202+
</item>
203+
<item row="2" column="0">
204+
<widget class="QLabel" name="mYLabel">
205+
<property name="text">
206+
<string>Y</string>
207+
</property>
208+
</widget>
212209
</item>
213210
<item row="0" column="0">
214211
<widget class="QLabel" name="mPageLabel">
@@ -217,7 +214,38 @@
217214
</property>
218215
</widget>
219216
</item>
220-
<item row="0" column="1">
217+
<item row="2" column="1">
218+
<widget class="QLineEdit" name="mYLineEdit"/>
219+
</item>
220+
<item row="1" column="2">
221+
<widget class="QgsDataDefinedButton" name="mXPositionDDBtn">
222+
<property name="text">
223+
<string>...</string>
224+
</property>
225+
</widget>
226+
</item>
227+
<item row="2" column="2">
228+
<widget class="QgsDataDefinedButton" name="mYPositionDDBtn">
229+
<property name="text">
230+
<string>...</string>
231+
</property>
232+
</widget>
233+
</item>
234+
<item row="3" column="2">
235+
<widget class="QgsDataDefinedButton" name="mWidthDDBtn">
236+
<property name="text">
237+
<string>...</string>
238+
</property>
239+
</widget>
240+
</item>
241+
<item row="4" column="2">
242+
<widget class="QgsDataDefinedButton" name="mHeightDDBtn">
243+
<property name="text">
244+
<string>...</string>
245+
</property>
246+
</widget>
247+
</item>
248+
<item row="0" column="1" colspan="2">
221249
<widget class="QSpinBox" name="mPageSpinBox">
222250
<property name="minimum">
223251
<number>1</number>

0 commit comments

Comments
 (0)