Skip to content
Permalink
Browse files
[FEATURE][composer] Data defined item size and position. Funded by Ca…
…nton of Neuchâtel, Switzerland
  • Loading branch information
nyalldawson committed Jul 5, 2014
1 parent 7604324 commit da976848782ef0cc53f06db9431db62e58fa598a
Showing with 227 additions and 25 deletions.
  1. +55 −1 src/app/composer/qgscomposeritemwidget.cpp
  2. +124 −7 src/core/composer/qgscomposeritem.cpp
  3. +3 −0 src/core/composer/qgscomposeritem.h
  4. +45 −17 src/ui/qgscomposeritemwidgetbase.ui
@@ -150,6 +150,23 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
}

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

connect( mYPositionDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYLineEdit, SLOT( setDisabled( bool ) ) );
connect( mYPositionDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mPageSpinBox, SLOT( setDisabled( bool ) ) );

connect( mWidthDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mWidthDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mWidthDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mWidthLineEdit, SLOT( setDisabled( bool ) ) );

connect( mHeightDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mHeightDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mHeightDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mHeightLineEdit, SLOT( setDisabled( bool ) ) );

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 ) ) );
@@ -518,11 +535,23 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
QgsVectorLayer* vl = atlasCoverageLayer();

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

//initialise buttons to use atlas coverage layer
mXPositionDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::PositionX ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mYPositionDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::PositionY ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mWidthDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemWidth ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mHeightDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemHeight ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mItemRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::ItemRotation ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
mTransparencyDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::Transparency ),
@@ -531,20 +560,45 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );

//initial state of controls - disable related controls when dd buttons are active
mXLineEdit->setEnabled( !mXPositionDDBtn->isActive() );
mYLineEdit->setEnabled( !mYPositionDDBtn->isActive() );
mPageSpinBox->setEnabled( !mYPositionDDBtn->isActive() );
mWidthLineEdit->setEnabled( !mWidthDDBtn->isActive() );
mHeightLineEdit->setEnabled( !mHeightDDBtn->isActive() );
mItemRotationSpinBox->setEnabled( !mItemRotationDDBtn->isActive() );
mTransparencySlider->setEnabled( !mTransparencyDDBtn->isActive() );
mTransparencySpnBx->setEnabled( !mTransparencyDDBtn->isActive() );
mBlendModeCombo->setEnabled( !mBlendModeDDBtn->isActive() );

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

QgsComposerItem::DataDefinedProperty QgsComposerItemWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
{
if ( widget == mItemRotationDDBtn )
if ( widget == mXPositionDDBtn )
{
return QgsComposerItem::PositionX;
}
else if ( widget == mYPositionDDBtn )
{
return QgsComposerItem::PositionY;
}
else if ( widget == mWidthDDBtn )
{
return QgsComposerItem::ItemWidth;
}
else if ( widget == mHeightDDBtn )
{
return QgsComposerItem::ItemHeight;
}
else if ( widget == mItemRotationDDBtn )
{
return QgsComposerItem::ItemRotation;
}
@@ -122,6 +122,11 @@ void QgsComposerItem::init( bool manageZValue )
setGraphicsEffect( mEffect );

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

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

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

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

QRectF evaluatedRect = evalItemRect( QRectF( x, y, width, height ) );
setSceneRect( evaluatedRect );

return true;
}

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

int QgsComposerItem::page() const
@@ -571,7 +578,11 @@ void QgsComposerItem::updatePagePos( double newPageWidth, double newPageHeight )
Q_UNUSED( newPageWidth )
QPointF curPagePos = pagePos();
int curPage = page() - 1;
setY( curPage * ( newPageHeight + composition()->spaceBetweenPages() ) + curPagePos.y() );

double y = curPage * ( newPageHeight + composition()->spaceBetweenPages() ) + curPagePos.y();
QRectF newSceneRect( pos().x(), y, rect().width(), rect().height() );

setSceneRect( evalItemRect( newSceneRect ) );
emit sizeChanged();
}

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

setSceneRect( QRectF( upperLeftX, upperLeftY, width, height ) );
//consider data defined item size and position before finalising rect
QRectF newRect = evalItemRect( QRectF( upperLeftX, upperLeftY, width, height ) );

setSceneRect( newRect );
}

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

QRectF newRect( 0, 0, newWidth, newHeight );
QGraphicsRectItem::setRect( newRect );
setPos( xTranslation, yTranslation );
QGraphicsRectItem::setRect( QRectF( 0, 0, newWidth, newHeight ) );
setPos( QPointF( xTranslation, yTranslation ) );

emit sizeChanged();
}

QRectF QgsComposerItem::evalItemRect( const QRectF &newRect )
{
QRectF result = newRect;

//data defined position or size set? if so, update rect with data defined values
QVariant exprVal;
//evaulate width and height first, since they may affect position if non-top-left reference point set
if ( dataDefinedEvaluate( QgsComposerItem::ItemWidth, exprVal ) )
{
bool ok;
double width = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Width:%1" ).arg( width ) );
if ( ok )
{
result.setWidth( width );
}
}
if ( dataDefinedEvaluate( QgsComposerItem::ItemHeight, exprVal ) )
{
bool ok;
double height = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Height:%1" ).arg( height ) );
if ( ok )
{
result.setHeight( height );
}
}

double x = result.left();
//initially adjust for position mode to get top-left coordinate
if ( mLastUsedPositionMode == UpperMiddle || mLastUsedPositionMode == Middle || mLastUsedPositionMode == LowerMiddle )
{
x += newRect.width() / 2.0;
}
else if ( mLastUsedPositionMode == UpperRight || mLastUsedPositionMode == MiddleRight || mLastUsedPositionMode == LowerRight )
{
x += newRect.width();
}
if ( dataDefinedEvaluate( QgsComposerItem::PositionX, exprVal ) )
{
bool ok;
double positionX = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Position X:%1" ).arg( positionX ) );
if ( ok )
{
x = positionX;
}
}

double y = result.top();
//adjust y-coordinate if placement is not done to an upper point
if ( mLastUsedPositionMode == MiddleLeft || mLastUsedPositionMode == Middle || mLastUsedPositionMode == MiddleRight )
{
y += newRect.height() / 2.0;
}
else if ( mLastUsedPositionMode == LowerLeft || mLastUsedPositionMode == LowerMiddle || mLastUsedPositionMode == LowerRight )
{
y += newRect.height();
}

if ( dataDefinedEvaluate( QgsComposerItem::PositionY, exprVal ) )
{
bool ok;
double positionY = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Position Y:%1" ).arg( positionY ) );
if ( ok )
{
y = positionY;
}
}

//adjust x-coordinate if placement is not done to a left point
if ( mLastUsedPositionMode == UpperMiddle || mLastUsedPositionMode == Middle || mLastUsedPositionMode == LowerMiddle )
{
x -= result.width() / 2.0;
}
else if ( mLastUsedPositionMode == UpperRight || mLastUsedPositionMode == MiddleRight || mLastUsedPositionMode == LowerRight )
{
x -= result.width();
}

//adjust y-coordinate if placement is not done to an upper point
if ( mLastUsedPositionMode == MiddleLeft || mLastUsedPositionMode == Middle || mLastUsedPositionMode == MiddleRight )
{
y -= result.height() / 2.0;
}
else if ( mLastUsedPositionMode == LowerLeft || mLastUsedPositionMode == LowerMiddle || mLastUsedPositionMode == LowerRight )
{
y -= result.height();
}

result.moveLeft( x );
result.moveTop( y );

return result;
}

void QgsComposerItem::drawBackground( QPainter* p )
{
if ( mBackground && p )
@@ -1326,6 +1436,13 @@ void QgsComposerItem::repaint()
void QgsComposerItem::refreshDataDefinedProperty( QgsComposerItem::DataDefinedProperty property )
{
//update data defined properties and redraw item to match
if ( property == QgsComposerItem::PositionX || property == QgsComposerItem::PositionY ||
property == QgsComposerItem::ItemWidth || property == QgsComposerItem::ItemHeight ||
property == QgsComposerItem::AllProperties )
{
QRectF evaluatedRect = evalItemRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
setSceneRect( evaluatedRect );
}
if ( property == QgsComposerItem::ItemRotation || property == QgsComposerItem::AllProperties )
{
refreshRotation( false, true );
@@ -697,6 +697,9 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
/**Map of current data defined properties*/
QMap< QgsComposerItem::DataDefinedProperty, QgsDataDefined* > mDataDefinedProperties;

/**Update an item rect to consider data defined position and size of item*/
QRectF evalItemRect( const QRectF &newRect );

/**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
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>376</width>
<height>695</height>
<height>697</height>
</rect>
</property>
<property name="windowTitle">
@@ -170,22 +170,9 @@
</item>
<item row="0" column="0" rowspan="2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="4" column="1">
<widget class="QLineEdit" name="mHeightLineEdit"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="mXLineEdit"/>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="mWidthLineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mYLabel">
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mWidthLabel">
<property name="text">
@@ -207,8 +194,18 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="mYLineEdit"/>
<item row="1" column="1">
<widget class="QLineEdit" name="mXLineEdit"/>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="mHeightLineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mYLabel">
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mPageLabel">
@@ -217,7 +214,38 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="2" column="1">
<widget class="QLineEdit" name="mYLineEdit"/>
</item>
<item row="1" column="2">
<widget class="QgsDataDefinedButton" name="mXPositionDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsDataDefinedButton" name="mYPositionDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QgsDataDefinedButton" name="mWidthDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QgsDataDefinedButton" name="mHeightDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QSpinBox" name="mPageSpinBox">
<property name="minimum">
<number>1</number>

0 comments on commit da97684

Please sign in to comment.