Skip to content

Commit

Permalink
[FEATURE] Annotations can be styled using fill symbol styles (fix #10552
Browse files Browse the repository at this point in the history
)

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 fac7887
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 235 deletions.
10 changes: 2 additions & 8 deletions python/core/annotations/qgsannotation.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
57 changes: 37 additions & 20 deletions src/app/qgsannotationwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() );
Expand All @@ -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 );
}
Expand All @@ -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(),
Expand All @@ -110,8 +99,6 @@ void QgsAnnotationWidget::blockAllSignals( bool block )
{
mMapPositionFixedCheckBox->blockSignals( block );
mMapMarkerButton->blockSignals( block );
mFrameWidthSpinBox->blockSignals( block );
mFrameColorButton->blockSignals( block );
mLayerComboBox->blockSignals( block );
}

Expand All @@ -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 )
Expand All @@ -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 );
}

4 changes: 4 additions & 0 deletions src/app/qgsannotationwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*/
Expand All @@ -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
2 changes: 1 addition & 1 deletion src/app/qgstextannotationdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
97 changes: 63 additions & 34 deletions src/core/annotations/qgsannotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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 );
Expand All @@ -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
Expand Down Expand Up @@ -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 )
{
Expand All @@ -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 );
}

Expand All @@ -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() );
Expand All @@ -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();
}
Expand Down
47 changes: 9 additions & 38 deletions src/core/annotations/qgsannotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit fac7887

Please sign in to comment.