Skip to content
Permalink
Browse files

[FEATURE] Annotations can be styled using fill symbol styles (fix #10552

)

This changes the rendering of annotation frames to use QGIS' symbology
engine, which means that all the existing fill styles can now be
used to style annotation frames.

Also paint effects & data defined symbol parameters. Whee!
  • Loading branch information
nyalldawson committed Jan 30, 2017
1 parent a94ca70 commit fac7887a9c64ee4cce632ce8f8ed49cba8a3f8bf
@@ -31,14 +31,8 @@ class QgsAnnotation : QObject
void setContentsMargin( const QgsMargins& margins );
QgsMargins contentsMargin() const;

void setFrameBorderWidth( double width );
double frameBorderWidth() const;

void setFrameColor( const QColor& color );
QColor frameColor() const;

void setFrameBackgroundColor( const QColor& color );
QColor frameBackgroundColor() const;
void setFillSymbol( QgsFillSymbol* symbol /Transfer/ );
QgsFillSymbol* fillSymbol() const;

void render( QgsRenderContext& context ) const;

@@ -46,19 +46,6 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem* item, QWid
{
mMapPositionFixedCheckBox->setCheckState( Qt::Unchecked );
}
mFrameWidthSpinBox->setValue( annotation->frameBorderWidth() );
mFrameColorButton->setColor( annotation->frameColor() );
mFrameColorButton->setColorDialogTitle( tr( "Select frame color" ) );
mFrameColorButton->setAllowAlpha( true );
mFrameColorButton->setContext( QStringLiteral( "symbology" ) );
mFrameColorButton->setNoColorString( tr( "Transparent frame" ) );
mFrameColorButton->setShowNoColor( true );
mBackgroundColorButton->setColor( annotation->frameBackgroundColor() );
mBackgroundColorButton->setColorDialogTitle( tr( "Select background color" ) );
mBackgroundColorButton->setAllowAlpha( true );
mBackgroundColorButton->setContext( QStringLiteral( "symbology" ) );
mBackgroundColorButton->setNoColorString( tr( "Transparent" ) );
mBackgroundColorButton->setShowNoColor( true );

whileBlocking( mSpinTopMargin )->setValue( annotation->contentsMargin().top() );
whileBlocking( mSpinLeftMargin )->setValue( annotation->contentsMargin().left() );
@@ -67,14 +54,18 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem* item, QWid

mLayerComboBox->setLayer( annotation->mapLayer() );

connect( mBackgroundColorButton, &QgsColorButton::colorChanged, this, &QgsAnnotationWidget::backgroundColorChanged );

const QgsMarkerSymbol* symbol = annotation->markerSymbol();
if ( symbol )
{
mMarkerSymbol.reset( symbol->clone() );
updateCenterIcon();
}
const QgsFillSymbol* fill = annotation->fillSymbol();
if ( fill )
{
mFillSymbol.reset( fill->clone() );
updateFillIcon();
}

blockAllSignals( false );
}
@@ -92,9 +83,7 @@ void QgsAnnotationWidget::apply()
if ( annotation )
{
annotation->setHasFixedMapPosition( mMapPositionFixedCheckBox->checkState() == Qt::Checked );
annotation->setFrameBorderWidth( mFrameWidthSpinBox->value() );
annotation->setFrameColor( mFrameColorButton->color() );
annotation->setFrameBackgroundColor( mBackgroundColorButton->color() );
annotation->setFillSymbol( mFillSymbol->clone() );
annotation->setMarkerSymbol( mMarkerSymbol->clone() );
annotation->setMapLayer( mLayerComboBox->currentLayer() );
annotation->setContentsMargin( QgsMargins( mSpinLeftMargin->value(),
@@ -110,8 +99,6 @@ void QgsAnnotationWidget::blockAllSignals( bool block )
{
mMapPositionFixedCheckBox->blockSignals( block );
mMapMarkerButton->blockSignals( block );
mFrameWidthSpinBox->blockSignals( block );
mFrameColorButton->blockSignals( block );
mLayerComboBox->blockSignals( block );
}

@@ -134,6 +121,26 @@ void QgsAnnotationWidget::on_mMapMarkerButton_clicked()
}
}

void QgsAnnotationWidget::on_mFrameStyleButton_clicked()
{
if ( !mFillSymbol )
{
return;
}
QgsFillSymbol* fillSymbol = mFillSymbol->clone();
QgsSymbolSelectorDialog dlg( fillSymbol, QgsStyle::defaultStyle(), nullptr, this );
if ( dlg.exec() == QDialog::Rejected )
{
delete fillSymbol;
}
else
{
mFillSymbol.reset( fillSymbol );
updateFillIcon();
backgroundColorChanged( fillSymbol->color() );
}
}

void QgsAnnotationWidget::updateCenterIcon()
{
if ( !mMarkerSymbol )
@@ -144,3 +151,13 @@ void QgsAnnotationWidget::updateCenterIcon()
mMapMarkerButton->setIcon( icon );
}

void QgsAnnotationWidget::updateFillIcon()
{
if ( !mFillSymbol )
{
return;
}
QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mFillSymbol.data(), mFrameStyleButton->iconSize() );
mFrameStyleButton->setIcon( icon );
}

@@ -23,6 +23,7 @@

class QgsMapCanvasAnnotationItem;
class QgsMarkerSymbol;
class QgsFillSymbol;

/** A configuration widget to configure the annotation item properties. Usually embedded by QgsAnnotation
subclass configuration dialogs*/
@@ -42,13 +43,16 @@ class APP_EXPORT QgsAnnotationWidget: public QWidget, private Ui::QgsAnnotationW

private slots:
void on_mMapMarkerButton_clicked();
void on_mFrameStyleButton_clicked();

private:
QgsMapCanvasAnnotationItem* mItem = nullptr;
QScopedPointer< QgsMarkerSymbol > mMarkerSymbol;
QScopedPointer< QgsFillSymbol > mFillSymbol;

void blockAllSignals( bool block );
void updateCenterIcon();
void updateFillIcon();
};

#endif // QGSANNOTATIONWIDGET_H
@@ -60,7 +60,7 @@ QgsTextAnnotationDialog::QgsTextAnnotationDialog( QgsMapCanvasAnnotationItem* it

void QgsTextAnnotationDialog::showEvent( QShowEvent* )
{
backgroundColorChanged( mItem && mItem->annotation() ? mItem->annotation()->frameBackgroundColor() : Qt::white );
backgroundColorChanged( mItem && mItem->annotation() && mItem->annotation()->fillSymbol() ? mItem->annotation()->fillSymbol()->color() : Qt::white );
}

void QgsTextAnnotationDialog::on_mButtonBox_clicked( QAbstractButton *button )
@@ -26,7 +26,14 @@ QgsAnnotation::QgsAnnotation( QObject* parent )
: QObject( parent )
, mMarkerSymbol( new QgsMarkerSymbol() )
{

QgsStringMap props;
props.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
props.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
props.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
props.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
props.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
props.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
mFillSymbol.reset( QgsFillSymbol::createSimple( props ) );
}

void QgsAnnotation::setVisible( bool visible )
@@ -89,21 +96,9 @@ void QgsAnnotation::setContentsMargin( const QgsMargins& margins )
emit appearanceChanged();
}

void QgsAnnotation::setFrameBorderWidth( double width )
void QgsAnnotation::setFillSymbol( QgsFillSymbol* symbol )
{
mFrameBorderWidth = width;
emit appearanceChanged();
}

void QgsAnnotation::setFrameColor( const QColor& c )
{
mFrameColor = c;
emit appearanceChanged();
}

void QgsAnnotation::setFrameBackgroundColor( const QColor& c )
{
mFrameBackgroundColor = c;
mFillSymbol.reset( symbol );
emit appearanceChanged();
}

@@ -263,17 +258,13 @@ QPointF QgsAnnotation::pointOnLineWithDistance( QPointF startPoint, QPointF dire

void QgsAnnotation::drawFrame( QgsRenderContext& context ) const
{
QPen framePen( mFrameColor );
framePen.setWidthF( context.convertToPainterUnits( mFrameBorderWidth, QgsUnitTypes::RenderPixels ) );

QPainter* p = context.painter();
if ( !mFillSymbol )
return;

p->setPen( framePen );
QBrush frameBrush( mFrameBackgroundColor );
p->setBrush( frameBrush );
p->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );

QPolygonF poly;
QList<QPolygonF> rings; //empty list
for ( int i = 0; i < 4; ++i )
{
QLineF currentSegment = segment( i );
@@ -286,7 +277,10 @@ void QgsAnnotation::drawFrame( QgsRenderContext& context ) const
}
poly << currentSegment.p2();
}
p->drawPolygon( poly );

mFillSymbol->startRender( context );
mFillSymbol->renderPolygon( poly, &rings, nullptr, context );
mFillSymbol->stopRender( context );
}

void QgsAnnotation::drawMarkerSymbol( QgsRenderContext& context ) const
@@ -322,12 +316,7 @@ void QgsAnnotation::_writeXml( QDomElement& itemElem, QDomDocument& doc ) const
annotationElem.setAttribute( QStringLiteral( "frameHeight" ), qgsDoubleToString( mFrameSize.height() ) );
annotationElem.setAttribute( QStringLiteral( "canvasPosX" ), qgsDoubleToString( mRelativePosition.x() ) );
annotationElem.setAttribute( QStringLiteral( "canvasPosY" ), qgsDoubleToString( mRelativePosition.y() ) );
annotationElem.setAttribute( QStringLiteral( "frameBorderWidth" ), qgsDoubleToString( mFrameBorderWidth ) );
annotationElem.setAttribute( QStringLiteral( "contentsMargin" ), mContentsMargins.toString() );
annotationElem.setAttribute( QStringLiteral( "frameColor" ), mFrameColor.name() );
annotationElem.setAttribute( QStringLiteral( "frameColorAlpha" ), mFrameColor.alpha() );
annotationElem.setAttribute( QStringLiteral( "frameBackgroundColor" ), mFrameBackgroundColor.name() );
annotationElem.setAttribute( QStringLiteral( "frameBackgroundColorAlpha" ), mFrameBackgroundColor.alpha() );
annotationElem.setAttribute( QStringLiteral( "visible" ), isVisible() );
if ( mMapLayer )
{
@@ -341,6 +330,16 @@ void QgsAnnotation::_writeXml( QDomElement& itemElem, QDomDocument& doc ) const
annotationElem.appendChild( symbolElem );
}
}
if ( mFillSymbol )
{
QDomElement fillElem = doc.createElement( QStringLiteral( "fillSymbol" ) );
QDomElement symbolElem = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "fill symbol" ), mFillSymbol.data(), doc );
if ( !symbolElem.isNull() )
{
fillElem.appendChild( symbolElem );
annotationElem.appendChild( fillElem );
}
}
itemElem.appendChild( annotationElem );
}

@@ -365,12 +364,7 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
mMapPositionCrs = QgsCoordinateReferenceSystem();
}

mFrameBorderWidth = annotationElem.attribute( QStringLiteral( "frameBorderWidth" ), QStringLiteral( "0.5" ) ).toDouble();
mContentsMargins = QgsMargins::fromString( annotationElem.attribute( QStringLiteral( "contentsMargin" ) ) );
mFrameColor.setNamedColor( annotationElem.attribute( QStringLiteral( "frameColor" ), QStringLiteral( "#000000" ) ) );
mFrameColor.setAlpha( annotationElem.attribute( QStringLiteral( "frameColorAlpha" ), QStringLiteral( "255" ) ).toInt() );
mFrameBackgroundColor.setNamedColor( annotationElem.attribute( QStringLiteral( "frameBackgroundColor" ) ) );
mFrameBackgroundColor.setAlpha( annotationElem.attribute( QStringLiteral( "frameBackgroundColorAlpha" ), QStringLiteral( "255" ) ).toInt() );
mFrameSize.setWidth( annotationElem.attribute( QStringLiteral( "frameWidth" ), QStringLiteral( "50" ) ).toDouble() );
mFrameSize.setHeight( annotationElem.attribute( QStringLiteral( "frameHeight" ), QStringLiteral( "50" ) ).toDouble() );
mOffsetFromReferencePoint.setX( annotationElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble() );
@@ -393,6 +387,41 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
}
}

mFillSymbol.reset( nullptr );
QDomElement fillElem = annotationElem.firstChildElement( QStringLiteral( "fillSymbol" ) );
if ( !fillElem.isNull() )
{
QDomElement symbolElem = fillElem.firstChildElement( QStringLiteral( "symbol" ) );
if ( !symbolElem.isNull() )
{
QgsFillSymbol* symbol = QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElem );
if ( symbol )
{
mFillSymbol.reset( symbol );
}
}
}
if ( !mFillSymbol )
{
QColor frameColor;
frameColor.setNamedColor( annotationElem.attribute( QStringLiteral( "frameColor" ), QStringLiteral( "#000000" ) ) );
frameColor.setAlpha( annotationElem.attribute( QStringLiteral( "frameColorAlpha" ), QStringLiteral( "255" ) ).toInt() );
QColor frameBackgroundColor;
frameBackgroundColor.setNamedColor( annotationElem.attribute( QStringLiteral( "frameBackgroundColor" ) ) );
frameBackgroundColor.setAlpha( annotationElem.attribute( QStringLiteral( "frameBackgroundColorAlpha" ), QStringLiteral( "255" ) ).toInt() );
double frameBorderWidth = annotationElem.attribute( QStringLiteral( "frameBorderWidth" ), QStringLiteral( "0.5" ) ).toDouble();
// need to roughly convert border width from pixels to mm - just assume 96 dpi
frameBorderWidth = frameBorderWidth * 25.4 / 96.0;
QgsStringMap props;
props.insert( QStringLiteral( "color" ), frameBackgroundColor.name() );
props.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
props.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
props.insert( QStringLiteral( "color_border" ), frameColor.name() );
props.insert( QStringLiteral( "width_border" ), QString::number( frameBorderWidth ) );
props.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
mFillSymbol.reset( QgsFillSymbol::createSimple( props ) );
}

updateBalloon();
emit mapLayerChanged();
}
@@ -172,40 +172,17 @@ class CORE_EXPORT QgsAnnotation : public QObject
QgsMargins contentsMargin() const { return mContentsMargins; }

/**
* Sets the annotation's frame's border width (in pixels).
* @see frameBorderWidth()
*/
void setFrameBorderWidth( double width );

/**
* Returns the annotation's frame's border width (in pixels).
* @see setFrameBorderWidth
*/
double frameBorderWidth() const { return mFrameBorderWidth; }

/**
* Sets the annotation's frame's border color.
* @see frameColor()
*/
void setFrameColor( const QColor& color );

/**
* Returns the annotation's frame's border color.
* @see setFrameColor()
*/
QColor frameColor() const { return mFrameColor; }

/**
* Sets the annotation's frame's background color.
* @see frameBackgroundColor()
* Sets the fill symbol used for rendering the annotation frame. Ownership
* of the symbol is transferred to the annotation.
* @see fillSymbol()
*/
void setFrameBackgroundColor( const QColor& color );
void setFillSymbol( QgsFillSymbol* symbol );

/**
* Returns the annotation's frame's background color.
* @see setFrameBackgroundColor()
* Returns the symbol that is used for rendering the annotation frame.
* @see setFillSymbol()
*/
QColor frameBackgroundColor() const { return mFrameBackgroundColor; }
QgsFillSymbol* fillSymbol() const { return mFillSymbol.data(); }

/**
* Renders the annotation to a target render context.
@@ -354,14 +331,8 @@ class CORE_EXPORT QgsAnnotation : public QObject

QgsMargins mContentsMargins;

//! Width of the frame
double mFrameBorderWidth = 1.0;

//! Frame / balloon color
QColor mFrameColor = QColor( 0, 0, 0 );

//! Frame background color
QColor mFrameBackgroundColor = QColor( 255, 255, 255 );
//! Fill symbol used for drawing annotation
QScopedPointer<QgsFillSymbol> mFillSymbol;

//! Segment number where the connection to the map point is attached. -1 if no balloon needed (e.g. if point is contained in frame)
int mBalloonSegment = -1;

0 comments on commit fac7887

Please sign in to comment.
You can’t perform that action at this time.