[FEATURE][composer] Data defined map scale, rotation and extents. Funded by Canton of Neuchâtel, Switzerland

This commit is contained in:
Nyall Dawson 2014-07-05 22:00:30 +10:00
parent 0fd99d2820
commit 6d7c5f885d
6 changed files with 587 additions and 163 deletions

View File

@ -127,7 +127,7 @@ class QgsComposerMap : QgsComposerItem
double scale() const;
/**Sets new scale and changes only mExtent*/
void setNewScale( double scaleDenominator );
void setNewScale( double scaleDenominator, bool forceUpdate = true );
/**Sets new Extent and changes width, height (and implicitely also scale)*/
void setNewExtent( const QgsRectangle& extent );
@ -341,8 +341,14 @@ class QgsComposerMap : QgsComposerItem
way the map is drawn within the item
@note this function was added in version 2.1*/
void setMapRotation( double r );
/**Returns the rotation used for drawing the map within the composer item*/
double mapRotation() const;
/**Returns the rotation used for drawing the map within the composer item
* @returns rotation for map
* @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).
*/
double mapRotation( PropertyValueType valueType = EvaluatedValue ) const;
void updateItem();
@ -506,4 +512,6 @@ class QgsComposerMap : QgsComposerItem
void renderModeUpdateCachedImage();
void overviewExtentChanged();
virtual void refreshDataDefinedProperty( DataDefinedProperty property = AllProperties );
};

View File

@ -103,9 +103,39 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ): QgsCo
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
this, SLOT( atlasLayerChanged( QgsVectorLayer* ) ) );
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( compositionAtlasToggled( bool ) ) );
// repopulate data defined buttons if atlas layer changes
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
this, SLOT( populateDataDefinedButtons() ) );
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( populateDataDefinedButtons() ) );
}
}
//connections for data defined buttons
connect( mScaleDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mScaleLineEdit, SLOT( setDisabled( bool ) ) );
connect( mMapRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mMapRotationSpinBox, SLOT( setDisabled( bool ) ) );
connect( mXMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMinLineEdit, SLOT( setDisabled( bool ) ) );
connect( mYMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMinLineEdit, SLOT( setDisabled( bool ) ) );
connect( mXMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMaxLineEdit, SLOT( setDisabled( bool ) ) );
connect( mYMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMaxLineEdit, SLOT( setDisabled( bool ) ) );
updateOverviewSymbolMarker();
updateLineSymbolMarker();
@ -117,6 +147,79 @@ QgsComposerMapWidget::~QgsComposerMapWidget()
{
}
void QgsComposerMapWidget::populateDataDefinedButtons()
{
QgsVectorLayer* vl = atlasCoverageLayer();
//block signals from data defined buttons
mScaleDDBtn->blockSignals( true );
mMapRotationDDBtn->blockSignals( true );
mXMinDDBtn->blockSignals( true );
mYMinDDBtn->blockSignals( true );
mXMaxDDBtn->blockSignals( true );
mYMaxDDBtn->blockSignals( true );
//initialise buttons to use atlas coverage layer
mScaleDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapScale ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mMapRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapRotation ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mXMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMin ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mYMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMin ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mXMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMax ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mYMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMax ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
//initial state of controls - disable related controls when dd buttons are active
mScaleLineEdit->setEnabled( !mScaleDDBtn->isActive() );
mMapRotationSpinBox->setEnabled( !mMapRotationDDBtn->isActive() );
mXMinLineEdit->setEnabled( !mXMinDDBtn->isActive() );
mYMinLineEdit->setEnabled( !mYMinDDBtn->isActive() );
mXMaxLineEdit->setEnabled( !mXMaxDDBtn->isActive() );
mYMaxLineEdit->setEnabled( !mYMaxDDBtn->isActive() );
//unblock signals from data defined buttons
mScaleDDBtn->blockSignals( false );
mMapRotationDDBtn->blockSignals( false );
mXMinDDBtn->blockSignals( false );
mYMinDDBtn->blockSignals( false );
mXMaxDDBtn->blockSignals( false );
mYMaxDDBtn->blockSignals( false );
}
QgsComposerItem::DataDefinedProperty QgsComposerMapWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
{
if ( widget == mScaleDDBtn )
{
return QgsComposerItem::MapScale;
}
else if ( widget == mMapRotationDDBtn )
{
return QgsComposerItem::MapRotation;
}
else if ( widget == mXMinDDBtn )
{
return QgsComposerItem::MapXMin;
}
else if ( widget == mYMinDDBtn )
{
return QgsComposerItem::MapYMin;
}
else if ( widget == mXMaxDDBtn )
{
return QgsComposerItem::MapXMax;
}
else if ( widget == mYMaxDDBtn )
{
return QgsComposerItem::MapYMax;
}
return QgsComposerItem::NoProperty;
}
void QgsComposerMapWidget::compositionAtlasToggled( bool atlasEnabled )
{
if ( atlasEnabled )
@ -328,43 +431,45 @@ void QgsComposerMapWidget::on_mMapRotationSpinBox_valueChanged( double value )
void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked()
{
if ( mComposerMap )
if ( !mComposerMap )
{
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();
//Make sure the width/height ratio is the same as in current composer map extent.
//This is to keep the map item frame and the page layout fixed
QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() );
double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
}
else
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
}
//fill text into line edits
mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) );
mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) );
mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );
mComposerMap->beginCommand( tr( "Map extent changed" ) );
mComposerMap->setNewExtent( newExtent );
mComposerMap->endCommand();
return;
}
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();
//Make sure the width/height ratio is the same as in current composer map extent.
//This is to keep the map item frame and the page layout fixed
QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() );
double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
}
else
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
}
//fill text into line edits
mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) );
mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) );
mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );
mComposerMap->beginCommand( tr( "Map extent changed" ) );
mComposerMap->setNewExtent( newExtent );
mComposerMap->endCommand();
}
void QgsComposerMapWidget::on_mViewExtentInCanvasButton_clicked()
@ -473,7 +578,7 @@ void QgsComposerMapWidget::updateGuiElements()
mYMinLineEdit->setText( QString::number( composerMapExtent.yMinimum(), 'f', 3 ) );
mYMaxLineEdit->setText( QString::number( composerMapExtent.yMaximum(), 'f', 3 ) );
mMapRotationSpinBox->setValue( mComposerMap->mapRotation() );
mMapRotationSpinBox->setValue( mComposerMap->mapRotation( QgsComposerItem::OriginalValue ) );
//keep layer list check box
if ( mComposerMap->keepLayerSet() )
@ -606,8 +711,9 @@ void QgsComposerMapWidget::updateGuiElements()
mAtlasPredefinedScaleRadio->setEnabled( false );
}
blockAllSignals( false );
populateDataDefinedButtons();
blockAllSignals( false );
}
void QgsComposerMapWidget::toggleAtlasScalingOptionsByLayerType()

View File

@ -108,6 +108,12 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
/**Sets the current composer map values to the GUI elements*/
virtual void updateGuiElements();
QgsComposerItem::DataDefinedProperty ddPropertyForWidget( QgsDataDefinedButton *widget );
protected slots:
/**Initializes data defined buttons to current atlas coverage layer*/
void populateDataDefinedButtons();
private slots:
/**Sets the GUI elements to the values of mPicture*/

View File

@ -42,17 +42,46 @@
#include <cmath>
QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
: QgsComposerItem( x, y, width, height, composition ), mMapRotation( 0 ), mKeepLayerSet( false )
, mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mOverviewCentered( false )
, mUpdatesEnabled( true ), mGridEnabled( false ), mGridStyle( Solid )
, mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) )
, mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver )
, mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame )
, mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 )
, mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal )
, mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 )
, mGridFramePenThickness( 0.5 ), mGridFramePenColor( QColor( 0, 0, 0 ) ), mGridFrameFillColor1( Qt::white ), mGridFrameFillColor2( Qt::black )
, mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true ), mAtlasDriven( false ), mAtlasScalingMode( Auto ), mAtlasMargin( 0.10 )
: QgsComposerItem( x, y, width, height, composition )
, mMapRotation( 0 )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mOverviewFrameMapId( -1 )
, mOverviewBlendMode( QPainter::CompositionMode_SourceOver )
, mOverviewInverted( false )
, mOverviewCentered( false )
, mUpdatesEnabled( true )
, mGridEnabled( false )
, mGridStyle( Solid )
, mGridIntervalX( 0.0 )
, mGridIntervalY( 0.0 )
, mGridOffsetX( 0.0 )
, mGridOffsetY( 0.0 )
, mGridAnnotationFontColor( QColor( 0, 0, 0 ) )
, mGridAnnotationPrecision( 3 )
, mShowGridAnnotation( false )
, mGridBlendMode( QPainter::CompositionMode_SourceOver )
, mLeftGridAnnotationPosition( OutsideMapFrame )
, mRightGridAnnotationPosition( OutsideMapFrame )
, mTopGridAnnotationPosition( OutsideMapFrame )
, mBottomGridAnnotationPosition( OutsideMapFrame )
, mAnnotationFrameDistance( 1.0 )
, mLeftGridAnnotationDirection( Horizontal )
, mRightGridAnnotationDirection( Horizontal )
, mTopGridAnnotationDirection( Horizontal )
, mBottomGridAnnotationDirection( Horizontal )
, mGridFrameStyle( NoGridFrame )
, mGridFrameWidth( 2.0 )
, mGridFramePenThickness( 0.5 )
, mGridFramePenColor( QColor( 0, 0, 0 ) )
, mGridFrameFillColor1( Qt::white )
, mGridFrameFillColor2( Qt::black )
, mCrossLength( 3 )
, mMapCanvas( 0 )
, mDrawCanvasItems( true )
, mAtlasDriven( false )
, mAtlasScalingMode( Auto )
, mAtlasMargin( 0.10 )
{
mComposition = composition;
mOverviewFrameMapSymbol = 0;
@ -88,29 +117,54 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
int bgBlueInt = QgsProject::instance()->readNumEntry( "Gui", "/CanvasColorBluePart", 255 );
setBackgroundColor( QColor( bgRedInt, bgGreenInt, bgBlueInt ) );
connectUpdateSlot();
//calculate mExtent based on width/height ratio and map canvas extent
mExtent = mComposition->mapSettings().visibleExtent();
setSceneRect( QRectF( x, y, width, height ) );
setToolTip( tr( "Map %1" ).arg( mId ) );
initGridAnnotationFormatFromProject();
init();
}
QgsComposerMap::QgsComposerMap( QgsComposition *composition )
: QgsComposerItem( 0, 0, 10, 10, composition ), mMapRotation( 0 ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 )
, mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mOverviewCentered( false )
, mUpdatesEnabled( true ), mGridEnabled( false ), mGridStyle( Solid )
, mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) )
, mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver )
, mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame )
, mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 )
, mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal )
, mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ), mGridFramePenThickness( 0.5 )
, mGridFramePenColor( QColor( 0, 0, 0 ) ), mGridFrameFillColor1( Qt::white ), mGridFrameFillColor2( Qt::black )
, mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true ), mAtlasDriven( false ), mAtlasScalingMode( Auto ), mAtlasMargin( 0.10 )
: QgsComposerItem( 0, 0, 10, 10, composition )
, mMapRotation( 0 )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mOverviewFrameMapId( -1 )
, mOverviewBlendMode( QPainter::CompositionMode_SourceOver )
, mOverviewInverted( false )
, mOverviewCentered( false )
, mUpdatesEnabled( true )
, mGridEnabled( false )
, mGridStyle( Solid )
, mGridIntervalX( 0.0 )
, mGridIntervalY( 0.0 )
, mGridOffsetX( 0.0 )
, mGridOffsetY( 0.0 )
, mGridAnnotationFontColor( QColor( 0, 0, 0 ) )
, mGridAnnotationPrecision( 3 )
, mShowGridAnnotation( false )
, mGridBlendMode( QPainter::CompositionMode_SourceOver )
, mLeftGridAnnotationPosition( OutsideMapFrame )
, mRightGridAnnotationPosition( OutsideMapFrame )
, mTopGridAnnotationPosition( OutsideMapFrame )
, mBottomGridAnnotationPosition( OutsideMapFrame )
, mAnnotationFrameDistance( 1.0 )
, mLeftGridAnnotationDirection( Horizontal )
, mRightGridAnnotationDirection( Horizontal )
, mTopGridAnnotationDirection( Horizontal )
, mBottomGridAnnotationDirection( Horizontal )
, mGridFrameStyle( NoGridFrame )
, mGridFrameWidth( 2.0 )
, mGridFramePenThickness( 0.5 )
, mGridFramePenColor( QColor( 0, 0, 0 ) )
, mGridFrameFillColor1( Qt::white )
, mGridFrameFillColor2( Qt::black )
, mCrossLength( 3 )
, mMapCanvas( 0 )
, mDrawCanvasItems( true )
, mAtlasDriven( false )
, mAtlasScalingMode( Auto )
, mAtlasMargin( 0.10 )
{
mOverviewFrameMapSymbol = 0;
mGridLineSymbol = 0;
@ -120,16 +174,29 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
mXOffset = 0.0;
mYOffset = 0.0;
connectUpdateSlot();
mComposition = composition;
mId = mComposition->composerMapItems().size();
mPreviewMode = QgsComposerMap::Rectangle;
mCurrentRectangle = rect();
init();
}
void QgsComposerMap::init()
{
connectUpdateSlot();
setToolTip( tr( "Map %1" ).arg( mId ) );
initGridAnnotationFormatFromProject();
// data defined strings
mDataDefinedNames.insert( QgsComposerItem::MapRotation, QString( "dataDefinedMapRotation" ) );
mDataDefinedNames.insert( QgsComposerItem::MapScale, QString( "dataDefinedMapScale" ) );
mDataDefinedNames.insert( QgsComposerItem::MapXMin, QString( "dataDefinedMapXMin" ) );
mDataDefinedNames.insert( QgsComposerItem::MapYMin, QString( "dataDefinedMapYMin" ) );
mDataDefinedNames.insert( QgsComposerItem::MapXMax, QString( "dataDefinedMapXMax" ) );
mDataDefinedNames.insert( QgsComposerItem::MapYMax, QString( "dataDefinedMapYMax" ) );
}
void QgsComposerMap::adjustExtentToItemShape( double itemWidth, double itemHeight, QgsRectangle& extent ) const
@ -347,7 +414,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
painter->translate( mXOffset, mYOffset );
painter->translate( xTopLeftShift, yTopLeftShift );
painter->rotate( mMapRotation );
painter->rotate( mEvaluatedMapRotation );
painter->translate( xShiftMM, -yShiftMM );
painter->scale( scale, scale );
painter->drawImage( 0, 0, mCacheImage );
@ -398,7 +465,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
painter->save();
painter->translate( mXOffset, mYOffset );
painter->translate( xTopLeftShift, yTopLeftShift );
painter->rotate( mMapRotation );
painter->rotate( mEvaluatedMapRotation );
painter->translate( xShiftMM, -yShiftMM );
double dotsPerMM = thePaintDevice->logicalDpiX() / 25.4;
@ -502,7 +569,6 @@ bool QgsComposerMap::shouldDrawPart( PartType part ) const
return true; // for Layer
}
void QgsComposerMap::updateCachedImage( void )
{
syncLayerSet(); //layer list may have changed
@ -588,6 +654,10 @@ void QgsComposerMap::moveContent( double dx, double dy )
currentMapExtent()->setXMaximum( currentMapExtent()->xMaximum() + dx );
currentMapExtent()->setYMinimum( currentMapExtent()->yMinimum() + dy );
currentMapExtent()->setYMaximum( currentMapExtent()->yMaximum() + dy );
//in case data defined extents are set, these override the calculated values
refreshMapExtents();
cache();
update();
emit itemChanged();
@ -669,6 +739,9 @@ void QgsComposerMap::zoomContent( int delta, double x, double y )
mExtent.scale( scaleRatio );
}
//recalculate data defined scale and extents, since that may override zoom
refreshMapExtents();
cache();
update();
emit itemChanged();
@ -686,6 +759,9 @@ void QgsComposerMap::setSceneRect( const QRectF& rectangle )
//QGraphicsRectItem::update();
double newHeight = mExtent.width() * h / w ;
mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
//recalculate data defined scale and extents
refreshMapExtents();
mCacheUpdated = false;
updateBoundingRect();
@ -696,16 +772,19 @@ void QgsComposerMap::setSceneRect( const QRectF& rectangle )
void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
{
if ( mExtent == extent )
if ( *currentMapExtent() == extent )
{
return;
}
mExtent = extent;
*currentMapExtent() = extent;
//recalculate data defined scale and extents, since that may override extent
refreshMapExtents();
//adjust height
QRectF currentRect = rect();
double newHeight = currentRect.width() * extent.height() / extent.width();
double newHeight = currentRect.width() * currentMapExtent()->height() / currentMapExtent()->width();
setSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
updateItem();
@ -713,37 +792,35 @@ void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
void QgsComposerMap::setNewAtlasFeatureExtent( const QgsRectangle& extent )
{
if ( mAtlasFeatureExtent == extent )
if ( mAtlasFeatureExtent != extent )
{
emit preparedForAtlas();
return;
//don't adjust size of item, instead adjust size of bounds to fit
QgsRectangle newExtent = extent;
//Make sure the width/height ratio is the same as the map item size
double currentWidthHeightRatio = rect().width() / rect().height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( extent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( extent.yMaximum() + deltaHeight / 2 );
}
else if ( currentWidthHeightRatio >= newWidthHeightRatio )
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( extent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( extent.xMaximum() + deltaWidth / 2 );
}
mAtlasFeatureExtent = newExtent;
}
//don't adjust size of item, instead adjust size of bounds to fit
QgsRectangle newExtent = extent;
//Make sure the width/height ratio is the same as the map item size
double currentWidthHeightRatio = rect().width() / rect().height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( extent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( extent.yMaximum() + deltaHeight / 2 );
}
else if ( currentWidthHeightRatio >= newWidthHeightRatio )
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( extent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( extent.xMaximum() + deltaWidth / 2 );
}
mAtlasFeatureExtent = newExtent;
mCacheUpdated = false;
emit preparedForAtlas();
updateItem();
@ -792,7 +869,7 @@ const QgsRectangle* QgsComposerMap::currentMapExtent() const
}
}
void QgsComposerMap::setNewScale( double scaleDenominator )
void QgsComposerMap::setNewScale( double scaleDenominator, bool forceUpdate )
{
double currentScaleDenominator = scale();
@ -817,9 +894,12 @@ void QgsComposerMap::setNewScale( double scaleDenominator )
}
mCacheUpdated = false;
cache();
update();
emit itemChanged();
if ( forceUpdate )
{
cache();
update();
emit itemChanged();
}
emit extentChanged();
}
@ -844,11 +924,131 @@ void QgsComposerMap::setRotation( double r )
void QgsComposerMap::setMapRotation( double r )
{
mMapRotation = r;
mEvaluatedMapRotation = mMapRotation;
emit mapRotationChanged( r );
emit itemChanged();
update();
}
double QgsComposerMap::mapRotation( PropertyValueType valueType ) const
{
return valueType == EvaluatedValue ? mEvaluatedMapRotation : mMapRotation;
}
void QgsComposerMap::refreshMapExtents()
{
//data defined map extents set?
QVariant exprVal;
QgsRectangle newExtent = *currentMapExtent();
if ( dataDefinedEvaluate( QgsComposerItem::MapXMin, exprVal ) )
{
bool ok;
double minXD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map XMin:%1" ).arg( minXD ) );
if ( ok )
{
newExtent.setXMinimum( minXD );
}
}
if ( dataDefinedEvaluate( QgsComposerItem::MapYMin, exprVal ) )
{
bool ok;
double minYD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map YMin:%1" ).arg( minYD ) );
if ( ok )
{
newExtent.setYMinimum( minYD );
}
}
if ( dataDefinedEvaluate( QgsComposerItem::MapXMax, exprVal ) )
{
bool ok;
double maxXD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map XMax:%1" ).arg( maxXD ) );
if ( ok )
{
newExtent.setXMaximum( maxXD );
}
}
if ( dataDefinedEvaluate( QgsComposerItem::MapYMax, exprVal ) )
{
bool ok;
double maxYD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map YMax:%1" ).arg( maxYD ) );
if ( ok )
{
newExtent.setYMaximum( maxYD );
}
}
if ( newExtent != *currentMapExtent() )
{
//calculate new extents to fit data defined extents
//Make sure the width/height ratio is the same as in current map extent.
//This is to keep the map item frame and the page layout fixed
double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
}
else
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
}
*currentMapExtent() = newExtent;
}
//now refresh scale, as this potentially overrides extents
//data defined map scale set?
if ( dataDefinedEvaluate( QgsComposerItem::MapScale, exprVal ) )
{
bool ok;
double scaleD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map Scale:%1" ).arg( scaleD ) );
if ( ok )
{
setNewScale( scaleD, false );
}
}
//lastly, map rotation overrides all
double mapRotation = mMapRotation;
//data defined map rotation set?
if ( dataDefinedEvaluate( QgsComposerItem::MapRotation, exprVal ) )
{
bool ok;
double rotationD = exprVal.toDouble( &ok );
QgsDebugMsg( QString( "exprVal Map Rotation:%1" ).arg( rotationD ) );
if ( ok )
{
mapRotation = rotationD;
}
}
if ( mEvaluatedMapRotation != mapRotation )
{
mEvaluatedMapRotation = mapRotation;
emit mapRotationChanged( mapRotation );
}
}
void QgsComposerMap::updateItem()
{
if ( !mUpdatesEnabled )
@ -1879,7 +2079,7 @@ int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
if ( qgsDoubleNear( mMapRotation, 0.0 ) )
if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
{
//no rotation. Do it 'the easy way'
@ -1947,7 +2147,7 @@ int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
if ( qgsDoubleNear( mMapRotation, 0.0 ) )
if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
{
//no rotation. Do it 'the easy way'
double xCanvasCoord;
@ -2136,7 +2336,7 @@ double QgsComposerMap::maxExtension() const
void QgsComposerMap::mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const
{
poly.clear();
if ( mMapRotation == 0 )
if ( mEvaluatedMapRotation == 0 )
{
poly << QPointF( extent.xMinimum(), extent.yMaximum() );
poly << QPointF( extent.xMaximum(), extent.yMaximum() );
@ -2152,25 +2352,25 @@ void QgsComposerMap::mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) c
//top left point
dx = rotationPoint.x() - extent.xMinimum();
dy = rotationPoint.y() - extent.yMaximum();
rotate( mMapRotation, dx, dy );
rotate( mEvaluatedMapRotation, dx, dy );
poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
//top right point
dx = rotationPoint.x() - extent.xMaximum();
dy = rotationPoint.y() - extent.yMaximum();
rotate( mMapRotation, dx, dy );
rotate( mEvaluatedMapRotation, dx, dy );
poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
//bottom right point
dx = rotationPoint.x() - extent.xMaximum();
dy = rotationPoint.y() - extent.yMinimum();
rotate( mMapRotation, dx, dy );
rotate( mEvaluatedMapRotation, dx, dy );
poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
//bottom left point
dx = rotationPoint.x() - extent.xMinimum();
dy = rotationPoint.y() - extent.yMinimum();
rotate( mMapRotation, dx, dy );
rotate( mEvaluatedMapRotation, dx, dy );
poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
}
@ -2181,10 +2381,10 @@ QPolygonF QgsComposerMap::visibleExtentPolygon() const
return poly;
}
void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
void QgsComposerMap::requestedExtent( QgsRectangle& extent )
{
QgsRectangle newExtent = *currentMapExtent();
if ( mMapRotation == 0 )
if ( mEvaluatedMapRotation == 0 )
{
extent = newExtent;
}
@ -2258,6 +2458,24 @@ void QgsComposerMap::overviewExtentChanged()
update();
}
void QgsComposerMap::refreshDataDefinedProperty( QgsComposerItem::DataDefinedProperty property )
{
//updates data defined properties and redraws item to match
if ( property == QgsComposerItem::MapRotation || property == QgsComposerItem::MapScale ||
property == QgsComposerItem::MapXMin || property == QgsComposerItem::MapYMin ||
property == QgsComposerItem::MapXMax || property == QgsComposerItem::MapYMax ||
property == QgsComposerItem::AllProperties )
{
refreshMapExtents();
emit itemChanged();
emit extentChanged();
}
//force redraw
cache();
QgsComposerItem::refreshDataDefinedProperty( property );
}
void QgsComposerMap::setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol )
{
@ -2295,7 +2513,7 @@ void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
double dxScaled = xShift * mmToMapUnits;
double dyScaled = - yShift * mmToMapUnits;
rotate( mMapRotation, dxScaled, dyScaled );
rotate( mEvaluatedMapRotation, dxScaled, dyScaled );
xShift = dxScaled;
yShift = dyScaled;
@ -2313,7 +2531,7 @@ QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
double dx = mapCoords.x() - rotationPoint.x();
double dy = mapCoords.y() - rotationPoint.y();
rotate( -mMapRotation, dx, dy );
rotate( -mEvaluatedMapRotation, dx, dy );
QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
QgsRectangle unrotatedExtent = transformedExtent();
@ -2711,19 +2929,31 @@ void QgsComposerMap::assignFreeId()
bool QgsComposerMap::imageSizeConsideringRotation( double& width, double& height ) const
{
//kept for api compatibility with QGIS 2.0 - use mMapRotation
return QgsComposerItem::imageSizeConsideringRotation( width, height, mMapRotation );
return QgsComposerItem::imageSizeConsideringRotation( width, height, mEvaluatedMapRotation );
}
bool QgsComposerMap::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
{
//kept for api compatibility with QGIS 2.0 - use mMapRotation
return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mMapRotation );
return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mEvaluatedMapRotation );
}
void QgsComposerMap::sizeChangedByRotation( double& width, double& height )
{
//kept for api compatibility with QGIS 2.0 - use mMapRotation
return QgsComposerItem::sizeChangedByRotation( width, height, mMapRotation );
return QgsComposerItem::sizeChangedByRotation( width, height, mEvaluatedMapRotation );
}
void QgsComposerMap::setAtlasDriven( bool enabled )
{
mAtlasDriven = enabled;
if ( !enabled )
{
//if not enabling the atlas, we still need to refresh the map extents
//so that data defined extents and scale are recalculated
refreshMapExtents();
}
}
bool QgsComposerMap::atlasFixedScale() const

View File

@ -161,7 +161,7 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
double scale() const;
/**Sets new scale and changes only mExtent*/
void setNewScale( double scaleDenominator );
void setNewScale( double scaleDenominator, bool forceUpdate = true );
/**Sets new Extent and changes width, height (and implicitely also scale)*/
void setNewExtent( const QgsRectangle& extent );
@ -376,8 +376,14 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
way the map is drawn within the item
@note this function was added in version 2.1*/
void setMapRotation( double r );
/**Returns the rotation used for drawing the map within the composer item*/
double mapRotation() const { return mMapRotation;};
/**Returns the rotation used for drawing the map within the composer item
* @returns rotation for map
* @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).
*/
double mapRotation( PropertyValueType valueType = EvaluatedValue ) const;
void updateItem();
@ -455,7 +461,7 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
* @see atlasDriven
* @see setAtlasScalingMode
*/
void setAtlasDriven( bool enabled ) { mAtlasDriven = enabled; }
void setAtlasDriven( bool enabled );
/**Returns true if the map uses a fixed scale when in atlas mode
* @deprecated since 2.4 Use atlasScalingMode() instead
@ -542,6 +548,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
void overviewExtentChanged();
virtual void refreshDataDefinedProperty( DataDefinedProperty property = AllProperties );
private:
enum AnnotationCoordinate
@ -585,6 +593,9 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Map rotation*/
double mMapRotation;
/**Temporary evaluated map rotation. Data defined rotation may mean this value
* differs from mMapRotation*/
double mEvaluatedMapRotation;
/**Flag if layers to be displayed should be read from qgis canvas (true) or from stored list in mLayerSet (false)*/
bool mKeepLayerSet;
@ -684,6 +695,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Margin size for atlas driven extents (percentage of feature size) - when in auto scaling mode*/
double mAtlasMargin;
void init();
/**Returns a list of the layers to render for this map item*/
QStringList layersToRender() const;
@ -719,7 +732,7 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
void mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const;
/**Calculates the extent to request and the yShift of the top-left point in case of rotation.*/
void requestedExtent( QgsRectangle& extent ) const;
void requestedExtent( QgsRectangle& extent );
/**Scales a composer map shift (in MM) and rotates it by mRotation
@param xShift in: shift in x direction (in item units), out: xShift in map units
@param yShift in: shift in y direction (in item units), out: yShift in map units*/
@ -753,6 +766,11 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Test if a part of the copmosermap needs to be drawn, considering mCurrentExportLayer*/
bool shouldDrawPart( PartType part ) const;
/**Refresh the map's extents, considering data defined extent, scale and rotation
* @note this method was added in version 2.5
*/
void refreshMapExtents();
};
#endif

View File

@ -23,16 +23,7 @@
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
@ -63,9 +54,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-348</y>
<width>438</width>
<height>1415</height>
<y>0</y>
<width>439</width>
<height>1711</height>
</rect>
</property>
<property name="sizePolicy">
@ -87,12 +78,6 @@
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
@ -125,11 +110,22 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="mScaleLineEdit">
<property name="inputMask">
<string/>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="mScaleLineEdit">
<property name="inputMask">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mScaleDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mMapRotationLabel">
@ -139,14 +135,25 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="mMapRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QDoubleSpinBox" name="mMapRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mMapRotationDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="mDrawCanvasItemsCheckBox">
@ -200,7 +207,18 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="mXMinLineEdit"/>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mXMinLineEdit"/>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mXMinDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mYMinLabel">
@ -216,7 +234,18 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="mYMinLineEdit"/>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="mYMinLineEdit"/>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mYMinDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mXMaxLabel">
@ -232,7 +261,18 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="mXMaxLineEdit"/>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="mXMaxLineEdit"/>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mXMaxDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mYMaxLabel">
@ -248,7 +288,18 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="mYMaxLineEdit"/>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="mYMaxLineEdit"/>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mYMaxDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="mSetToMapCanvasExtentButton">
@ -875,7 +926,7 @@
<customwidget>
<class>QgsCollapsibleGroupBoxBasic</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<header location="global">qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
@ -883,6 +934,11 @@
<extends>QPushButton</extends>
<header>qgscolorbutton.h</header>
</customwidget>
<customwidget>
<class>QgsDataDefinedButton</class>
<extends>QToolButton</extends>
<header>qgsdatadefinedbutton.h</header>
</customwidget>
<customwidget>
<class>QgsBlendModeComboBox</class>
<extends>QComboBox</extends>