Skip to content
Permalink
Browse files
Support paint effects for group layers
  • Loading branch information
nyalldawson committed Nov 23, 2021
1 parent 2b8f137 commit 853689e5cb9c070cd8c3c25a3e80f24fb3adc059
Showing with 118 additions and 2 deletions.
  1. +16 −0 python/core/auto_generated/qgsgrouplayer.sip.in
  2. +33 −0 src/core/qgsgrouplayer.cpp
  3. +17 −0 src/core/qgsgrouplayer.h
  4. +2 −0 src/core/qgsgrouplayerrenderer.cpp
  5. +50 −2 tests/src/python/test_qgsgrouplayer.py
@@ -92,6 +92,22 @@ the parent :py:class:`QgsProject` wherever appropriate.
Returns the child layers contained by the group.

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

QgsPaintEffect *paintEffect() const;
%Docstring
Returns the current paint effect for the group layer.

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

void setPaintEffect( QgsPaintEffect *effect /Transfer/ );
%Docstring
Sets the current paint ``effect`` for the renderer.

Ownership is transferred to the renderer.

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

};
@@ -23,7 +23,10 @@
#include "qgsmaplayerref.h"
#include "qgsvectorlayer.h"
#include "qgscoordinatetransform.h"
#include "qgspainteffect.h"
#include "qgsmessagelog.h"
#include "qgspainteffectregistry.h"
#include "qgsapplication.h"

QgsGroupLayer::QgsGroupLayer( const QString &name, const LayerOptions &options )
: QgsMapLayer( QgsMapLayerType::GroupLayer, name )
@@ -49,6 +52,7 @@ QgsGroupLayer *QgsGroupLayer::clone() const
std::unique_ptr< QgsGroupLayer > layer = std::make_unique< QgsGroupLayer >( name(), options );
QgsMapLayer::clone( layer.get() );
layer->setChildLayers( _qgis_listRefToRaw( mChildren ) );
layer->setPaintEffect( mPaintEffect ? mPaintEffect->clone() : nullptr );
return layer.release();
}

@@ -179,6 +183,13 @@ bool QgsGroupLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &
const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
layerOpacityElem.appendChild( layerOpacityText );
node.appendChild( layerOpacityElem );

if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
{
QDomElement paintEffectElement = doc.createElement( QStringLiteral( "paintEffect" ) );
mPaintEffect->saveProperties( doc, paintEffectElement );
node.appendChild( paintEffectElement );
}
}

if ( categories.testFlag( Symbology ) )
@@ -203,6 +214,18 @@ bool QgsGroupLayer::readSymbology( const QDomNode &node, QString &, QgsReadWrite
const QDomElement e = layerOpacityNode.toElement();
setOpacity( e.text().toDouble() );
}

//restore layer effect
const QDomElement effectElem = node.namedItem( QStringLiteral( "paintEffect" ) ).toElement();
if ( !effectElem.isNull() )
{
const QDomElement effectPropertiesElem = effectElem.firstChildElement( QStringLiteral( "effect" ) ).toElement();
mPaintEffect.reset( QgsApplication::paintEffectRegistry()->createEffect( effectPropertiesElem ) );
}
else
{
mPaintEffect.reset();
}
}

if ( categories.testFlag( Symbology ) )
@@ -303,6 +326,16 @@ QList< QgsMapLayer * > QgsGroupLayer::childLayers()
return _qgis_listRefToRaw( mChildren );
}

QgsPaintEffect *QgsGroupLayer::paintEffect() const
{
return mPaintEffect.get();
}

void QgsGroupLayer::setPaintEffect( QgsPaintEffect *effect )
{
mPaintEffect.reset( effect );
}

//
// QgsGroupLayerDataProvider
//
@@ -25,6 +25,7 @@
#include "qgsmaplayerref.h"

class QgsGroupLayerDataProvider;
class QgsPaintEffect;

/**
* \ingroup core
@@ -110,12 +111,28 @@ class CORE_EXPORT QgsGroupLayer : public QgsMapLayer
*/
QList< QgsMapLayer * > childLayers();

/**
* Returns the current paint effect for the group layer.
* \see setPaintEffect()
*/
QgsPaintEffect *paintEffect() const;

/**
* Sets the current paint \a effect for the renderer.
*
* Ownership is transferred to the renderer.
*
* \see paintEffect()
*/
void setPaintEffect( QgsPaintEffect *effect SIP_TRANSFER );

private:

QgsGroupLayerDataProvider *mDataProvider = nullptr;
QgsCoordinateTransformContext mTransformContext;

QList< QgsMapLayerRef > mChildren;
std::unique_ptr< QgsPaintEffect > mPaintEffect;

};

@@ -53,6 +53,8 @@ QgsGroupLayerRenderer::QgsGroupLayerRenderer( QgsGroupLayer *layer, QgsRenderCon
mRendererOpacity.emplace_back( childLayer->type() != QgsMapLayerType::RasterLayer ? childLayer->opacity() : 1.0 );
mTransforms.emplace_back( layerToDestTransform );
}

mPaintEffect.reset( layer->paintEffect() ? layer->paintEffect()->clone() : nullptr );
}

QgsGroupLayerRenderer::~QgsGroupLayerRenderer() = default;
@@ -22,7 +22,8 @@
QDir
)
from qgis.PyQt.QtGui import (
QPainter
QPainter,
QColor
)
from tempfile import TemporaryDirectory

@@ -35,7 +36,10 @@
QgsGeometry,
QgsPointXY,
QgsMapSettings,
QgsMultiRenderChecker
QgsMultiRenderChecker,
QgsDropShadowEffect,
QgsEffectStack,
QgsDrawSourceEffect
)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
@@ -157,6 +161,9 @@ def test_save_restore(self):
group_layer2 = QgsGroupLayer('group 2', options)
group_layer2.setChildLayers([layer3, layer1])

drop_shadow = QgsDropShadowEffect()
group_layer2.setPaintEffect(drop_shadow)

p.addMapLayer(group_layer1, False)
p.addMapLayer(group_layer2, False)

@@ -178,6 +185,8 @@ def test_save_restore(self):
self.assertEqual(restored_group_1.childLayers(), [restored_layer1, restored_layer2, restored_layer3])
self.assertEqual(restored_group_2.childLayers(), [restored_layer3, restored_layer1])

self.assertIsInstance(restored_group_2.paintEffect(), QgsDropShadowEffect)

def test_render_group_opacity(self):
"""
Test rendering layers as a group with opacity
@@ -239,6 +248,45 @@ def test_render_group_blend_mode(self):
TestQgsGroupLayer.report += renderchecker.report()
self.assertTrue(result)

def test_render_paint_effect(self):
"""
Test rendering layers as a group with paint effect
"""
vl1 = QgsVectorLayer(TEST_DATA_DIR + '/lines.shp')
self.assertTrue(vl1.isValid())
vl2 = QgsVectorLayer(TEST_DATA_DIR + '/points.shp')
self.assertTrue(vl2.isValid())

options = QgsGroupLayer.LayerOptions(QgsCoordinateTransformContext())
group_layer = QgsGroupLayer('group', options)
group_layer.setChildLayers([vl2, vl1])

drop_shadow = QgsDropShadowEffect()
drop_shadow.setBlurLevel(0)
drop_shadow.setOpacity(1)
drop_shadow.setColor(QColor(255, 0, 255))
drop_shadow.setOffsetDistance(3)

effect_stack = QgsEffectStack()
effect_stack.appendEffect(drop_shadow)
effect_stack.appendEffect(QgsDrawSourceEffect())
group_layer.setPaintEffect(effect_stack)

mapsettings = QgsMapSettings()
mapsettings.setOutputSize(QSize(600, 400))
mapsettings.setOutputDpi(96)
mapsettings.setDestinationCrs(group_layer.crs())
mapsettings.setExtent(group_layer.extent())
mapsettings.setLayers([group_layer])

renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('group_layer')
renderchecker.setControlName('expected_group_paint_effect')
result = renderchecker.runTest('expected_group_paint_effect')
TestQgsGroupLayer.report += renderchecker.report()
self.assertTrue(result)


if __name__ == '__main__':
unittest.main()

0 comments on commit 853689e

Please sign in to comment.