Skip to content

Commit

Permalink
Provide hooks for symbol layers to notify them before and after
Browse files Browse the repository at this point in the history
rendering operations on a specific feature are performed

This allows symbol layer subclasses to know when a set of rendering
operations all relate to a single feature, e.g. when a number
of polygons are rendered which all belong to the same multi-polygon
geometry attached to a single feature.

Without these hooks, symbol layer subclasses have no stable method
of knowing that these parts all relate to a single feature, or
have no means of knowing when the final part of that feature has
been rendered.
  • Loading branch information
nyalldawson committed Oct 28, 2019
1 parent b5e92d4 commit 491a4a7
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 0 deletions.
74 changes: 74 additions & 0 deletions python/core/auto_generated/symbology/qgssymbollayer.sip.in
Expand Up @@ -216,7 +216,81 @@ Should match with the string used to register this symbol layer in the registry.
%End

virtual void startRender( QgsSymbolRenderContext &context ) = 0;
%Docstring
Called before a set of rendering operations commences on the supplied render ``context``.

This is always followed by a call to stopRender() after all rendering operations
have been completed.

Subclasses can use this method to prepare for a set of rendering operations, e.g. by
pre-evaluating paths or images to render, and performing other one-time optimisations.

.. seealso:: :py:func:`startFeatureRender`

.. seealso:: :py:func:`stopRender`
%End

virtual void stopRender( QgsSymbolRenderContext &context ) = 0;
%Docstring
Called after a set of rendering operations has finished on the supplied render ``context``.

This is always preceded by a call to startRender() before all rendering operations
are commenced.

Subclasses can use this method to cleanup after a set of rendering operations.

.. seealso:: :py:func:`startRender`

.. seealso:: :py:func:`stopFeatureRender`
%End

virtual void startFeatureRender( const QgsFeature &feature, QgsRenderContext &context );
%Docstring
Called before the layer will be rendered for a particular ``feature``.

This is always followed by a call to stopFeatureRender() after the feature
has been completely rendered (i.e. all parts have been rendered).

The default implementation does nothing.

.. note::

In some circumstances, startFeatureRender() and stopFeatureRender() may not be called
before a symbol layer is rendered. E.g., when a symbol layer is being rendered in isolation
and not as a result of rendering a feature (for instance, when rendering a legend patch or other
non-feature based shape).

.. seealso:: :py:func:`stopFeatureRender`

.. seealso:: :py:func:`startRender`


.. versionadded:: 3.12
%End

virtual void stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context );
%Docstring
Called after the layer has been rendered for a particular ``feature``.

This is always preceeded by a call to startFeatureRender() just before the feature
will be rendered.

The default implementation does nothing.

.. note::

In some circumstances, startFeatureRender() and stopFeatureRender() may not be called
before a symbol layer is rendered. E.g., when a symbol layer is being rendered in isolation
and not as a result of rendering a feature (for instance, when rendering a legend patch or other
non-feature based shape).

.. seealso:: :py:func:`startFeatureRender`

.. seealso:: :py:func:`stopRender`


.. versionadded:: 3.12
%End

virtual QgsSymbolLayer *clone() const = 0 /Factory/;
%Docstring
Expand Down
52 changes: 52 additions & 0 deletions src/core/symbology/qgssymbol.cpp
Expand Up @@ -856,6 +856,8 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont

QgsGeometry renderedBoundsGeom;

startFeatureRender( feature, context, layer );

switch ( QgsWkbTypes::flatType( segmentizedGeometry.constGet()->wkbType() ) )
{
case QgsWkbTypes::Point:
Expand Down Expand Up @@ -1115,6 +1117,8 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont
.arg( geom.wkbType(), 0, 16 ) );
}

stopFeatureRender( feature, context, layer );

if ( context.hasRenderedFeatureHandlers() )
{
QgsRenderedFeatureHandlerInterface::RenderedFeatureContext featureContext( context );
Expand Down Expand Up @@ -1172,6 +1176,54 @@ void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, int c
QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), static_cast< QgsSymbolLayerUtils::VertexMarkerType >( currentVertexMarkerType ), markerSize );
}

void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
{
if ( layer != -1 )
{
QgsSymbolLayer *symbolLayer = mLayers.value( layer );
if ( symbolLayer && symbolLayer->enabled() )
{
symbolLayer->startFeatureRender( feature, context );
}
return;
}
else
{
const QList< QgsSymbolLayer * > layers = mLayers;
for ( QgsSymbolLayer *symbolLayer : layers )
{
if ( !symbolLayer->enabled() )
continue;

symbolLayer->startFeatureRender( feature, context );
}
}
}

void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
{
if ( layer != -1 )
{
QgsSymbolLayer *symbolLayer = mLayers.value( layer );
if ( symbolLayer && symbolLayer->enabled() )
{
symbolLayer->stopFeatureRender( feature, context );
}
return;
}
else
{
const QList< QgsSymbolLayer * > layers = mLayers;
for ( QgsSymbolLayer *symbolLayer : layers )
{
if ( !symbolLayer->enabled() )
continue;

symbolLayer->stopFeatureRender( feature, context );
}
}
}

////////////////////


Expand Down
26 changes: 26 additions & 0 deletions src/core/symbology/qgssymbol.h
Expand Up @@ -628,6 +628,32 @@ class CORE_EXPORT QgsSymbol
//! Initialized in startRender, destroyed in stopRender
std::unique_ptr< QgsSymbolRenderContext > mSymbolRenderContext;

/**
* Called before symbol layers will be rendered for a particular \a feature.
*
* This is always followed by a call to stopFeatureRender() after the feature
* has been completely rendered (i.e. all parts have been rendered).
*
* Internally, this notifies all symbol layers which will be used via a call to
* QgsSymbolLayer::startFeatureRender().
*
* \since QGIS 3.12
*/
void startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer = -1 );

/**
* Called after symbol layers have been rendered for a particular \a feature.
*
* This is always preceeded by a call to startFeatureRender() just before the feature
* will be rendered.
*
* Internally, this notifies all symbol layers which were used via a call to
* QgsSymbolLayer::stopFeatureRender().
*
* \since QGIS 3.12
*/
void stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer = -1 );

Q_DISABLE_COPY( QgsSymbol )

};
Expand Down
10 changes: 10 additions & 0 deletions src/core/symbology/qgssymbollayer.cpp
Expand Up @@ -106,6 +106,16 @@ void QgsSymbolLayer::setDataDefinedProperty( QgsSymbolLayer::Property key, const
dataDefinedProperties().setProperty( key, property );
}

void QgsSymbolLayer::startFeatureRender( const QgsFeature &, QgsRenderContext & )
{

}

void QgsSymbolLayer::stopFeatureRender( const QgsFeature &, QgsRenderContext & )
{

}

bool QgsSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
{
Q_UNUSED( e )
Expand Down
64 changes: 64 additions & 0 deletions src/core/symbology/qgssymbollayer.h
Expand Up @@ -248,9 +248,73 @@ class CORE_EXPORT QgsSymbolLayer
*/
virtual QString layerType() const = 0;

/**
* Called before a set of rendering operations commences on the supplied render \a context.
*
* This is always followed by a call to stopRender() after all rendering operations
* have been completed.
*
* Subclasses can use this method to prepare for a set of rendering operations, e.g. by
* pre-evaluating paths or images to render, and performing other one-time optimisations.
*
* \see startFeatureRender()
* \see stopRender()
*/
virtual void startRender( QgsSymbolRenderContext &context ) = 0;

/**
* Called after a set of rendering operations has finished on the supplied render \a context.
*
* This is always preceded by a call to startRender() before all rendering operations
* are commenced.
*
* Subclasses can use this method to cleanup after a set of rendering operations.
*
* \see startRender()
* \see stopFeatureRender()
*/
virtual void stopRender( QgsSymbolRenderContext &context ) = 0;

/**
* Called before the layer will be rendered for a particular \a feature.
*
* This is always followed by a call to stopFeatureRender() after the feature
* has been completely rendered (i.e. all parts have been rendered).
*
* The default implementation does nothing.
*
* \note In some circumstances, startFeatureRender() and stopFeatureRender() may not be called
* before a symbol layer is rendered. E.g., when a symbol layer is being rendered in isolation
* and not as a result of rendering a feature (for instance, when rendering a legend patch or other
* non-feature based shape).
*
* \see stopFeatureRender()
* \see startRender()
*
* \since QGIS 3.12
*/
virtual void startFeatureRender( const QgsFeature &feature, QgsRenderContext &context );

/**
* Called after the layer has been rendered for a particular \a feature.
*
* This is always preceeded by a call to startFeatureRender() just before the feature
* will be rendered.
*
* The default implementation does nothing.
*
* \note In some circumstances, startFeatureRender() and stopFeatureRender() may not be called
* before a symbol layer is rendered. E.g., when a symbol layer is being rendered in isolation
* and not as a result of rendering a feature (for instance, when rendering a legend patch or other
* non-feature based shape).
*
* \see startFeatureRender()
* \see stopRender()
*
* \since QGIS 3.12
*/
virtual void stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context );

/**
* Shall be reimplemented by subclasses to create a deep copy of the instance.
*/
Expand Down

0 comments on commit 491a4a7

Please sign in to comment.