[FEATURE][diagrams] Paint effect support for diagram renderer

Allows for diagrams to use paint effects, including drop shadows,
outer glows, etc...

Sponsored by SLYR
This commit is contained in:
Nyall Dawson 2019-11-24 17:45:45 +10:00
parent 204bd47b19
commit e8ec0042f4
8 changed files with 335 additions and 191 deletions

View File

@ -376,6 +376,7 @@ QgsDiagramLayerSettings stores settings which control how ALL diagrams within a
%Docstring
Constructor for QgsDiagramSettings
%End
~QgsDiagramSettings();
QgsDiagramSettings( const QgsDiagramSettings &other );
%Docstring
@ -582,6 +583,26 @@ Sets whether the diagram axis should be shown.
.. seealso:: :py:func:`setAxisLineSymbol`
.. versionadded:: 3.12
%End
QgsPaintEffect *paintEffect() const;
%Docstring
Returns the paint effect to use while rendering diagrams.
.. seealso:: :py:func:`setPaintEffect`
.. versionadded:: 3.12
%End
void setPaintEffect( QgsPaintEffect *effect /Transfer/ );
%Docstring
Sets the paint ``effect`` to use while rendering diagrams.
Ownership of ``effect`` is transferred to the settings.
.. seealso:: :py:func:`paintEffect`
.. versionadded:: 3.12
%End

View File

@ -44,6 +44,7 @@
#include "qgsauxiliarystorage.h"
#include "qgsexpressioncontextutils.h"
#include "qgspropertytransformer.h"
#include "qgspainteffectregistry.h"
#include <QList>
#include <QMessageBox>
@ -238,6 +239,8 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
newItem->setFlags( newItem->flags() & ~Qt::ItemIsDropEnabled );
}
mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
const QgsDiagramRenderer *dr = layer->diagramRenderer();
if ( !dr ) //no diagram renderer yet, insert reasonable default
{
@ -284,6 +287,7 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
mBackgroundColorButton->setColor( QColor( 255, 255, 255, 255 ) );
//force a refresh of widget status to match diagram type
mDiagramTypeComboBox_currentIndexChanged( mDiagramTypeComboBox->currentIndex() );
}
else // already a diagram renderer present
{
@ -329,6 +333,9 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
mLabelPlacementComboBox->setCurrentIndex( 1 );
}
if ( settingList.at( 0 ).paintEffect() )
mPaintEffect.reset( settingList.at( 0 ).paintEffect()->clone() );
mAngleOffsetComboBox->setCurrentIndex( mAngleOffsetComboBox->findData( settingList.at( 0 ).rotationOffset ) );
mAngleDirectionComboBox->setCurrentIndex( mAngleDirectionComboBox->findData( settingList.at( 0 ).direction() ) );
@ -483,6 +490,7 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
}
}
}
mPaintEffectWidget->setPaintEffect( mPaintEffect.get() );
connect( mAddAttributeExpression, &QPushButton::clicked, this, &QgsDiagramProperties::showAddAttributeExpressionDialog );
registerDataDefinedButton( mBackgroundColorDDBtn, QgsDiagramLayerSettings::BackgroundColor );
@ -841,6 +849,11 @@ void QgsDiagramProperties::apply()
ds.setSpacingUnit( mBarSpacingUnitComboBox->unit() );
ds.setSpacingMapUnitScale( mBarSpacingUnitComboBox->getMapUnitScale() );
if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
ds.setPaintEffect( mPaintEffect->clone() );
else
ds.setPaintEffect( nullptr );
QgsDiagramRenderer *renderer = nullptr;
if ( mFixedSizeRadio->isChecked() )
{

View File

@ -68,6 +68,8 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr
//! Polygon placement button group
QButtonGroup *mPlacePolygonBtnGrp = nullptr;
std::unique_ptr< QgsPaintEffect> mPaintEffect;
enum Columns
{
ColumnAttributeExpression = 0,

View File

@ -24,6 +24,9 @@
#include "qgslayertreemodellegendnode.h"
#include "qgsfontutils.h"
#include "qgssymbollayerutils.h"
#include "qgspainteffectregistry.h"
#include "qgspainteffect.h"
#include "qgsapplication.h"
#include <QDomElement>
#include <QPainter>
@ -330,6 +333,12 @@ void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteCon
categoryLabels.append( *catIt );
}
}
QDomElement effectElem = elem.firstChildElement( QStringLiteral( "effect" ) );
if ( !effectElem.isNull() )
setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
else
setPaintEffect( QgsApplication::paintEffectRegistry()->defaultStack() );
}
void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
@ -398,10 +407,6 @@ void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc,
case Up:
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
break;
default:
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
break;
}
categoryElem.setAttribute( QStringLiteral( "barWidth" ), QString::number( barWidth ) );
@ -425,6 +430,9 @@ void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc,
axisSymbolElem.appendChild( symbolElem );
categoryElem.appendChild( axisSymbolElem );
if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
mPaintEffect->saveProperties( doc, categoryElem );
rendererElem.appendChild( categoryElem );
}
@ -474,6 +482,13 @@ void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderCont
s.rotationOffset = properties.valueAsDouble( QgsDiagramLayerSettings::StartAngle, c.expressionContext(), s.rotationOffset );
}
QgsPaintEffect *effect = s.paintEffect();
std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
{
effectPainter = qgis::make_unique< QgsEffectPainter >( c, effect );
}
mDiagram->renderDiagram( feature, c, s, pos );
}
@ -807,11 +822,24 @@ void QgsDiagramSettings::setShowAxis( bool showAxis )
mShowAxis = showAxis;
}
QgsPaintEffect *QgsDiagramSettings::paintEffect() const
{
return mPaintEffect.get();
}
void QgsDiagramSettings::setPaintEffect( QgsPaintEffect *effect )
{
if ( effect != mPaintEffect.get() )
mPaintEffect.reset( effect );
}
QgsDiagramSettings::QgsDiagramSettings()
: mAxisLineSymbol( qgis::make_unique< QgsLineSymbol >() )
{
}
QgsDiagramSettings::~QgsDiagramSettings() = default;
QgsDiagramSettings::QgsDiagramSettings( const QgsDiagramSettings &other )
: enabled( other.enabled )
, font( other.font )
@ -842,6 +870,7 @@ QgsDiagramSettings::QgsDiagramSettings( const QgsDiagramSettings &other )
, mDirection( other.mDirection )
, mShowAxis( other.mShowAxis )
, mAxisLineSymbol( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr )
, mPaintEffect( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr )
{
}
@ -877,6 +906,7 @@ QgsDiagramSettings &QgsDiagramSettings::operator=( const QgsDiagramSettings &oth
mDirection = other.mDirection;
mAxisLineSymbol.reset( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr );
mShowAxis = other.mShowAxis;
mPaintEffect.reset( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr );
return *this;
}

View File

@ -44,6 +44,7 @@ class QgsReadWriteContext;
class QgsVectorLayer;
class QgsLayerTreeModelLegendNode;
class QgsLayerTreeLayer;
class QgsPaintEffect;
namespace pal { class Layer; } SIP_SKIP
@ -407,6 +408,7 @@ class CORE_EXPORT QgsDiagramSettings
//! Constructor for QgsDiagramSettings
QgsDiagramSettings();
~QgsDiagramSettings();
//! Copy constructor
QgsDiagramSettings( const QgsDiagramSettings &other );
@ -618,6 +620,26 @@ class CORE_EXPORT QgsDiagramSettings
*/
void setShowAxis( bool showAxis );
/**
* Returns the paint effect to use while rendering diagrams.
*
* \see setPaintEffect()
*
* \since QGIS 3.12
*/
QgsPaintEffect *paintEffect() const;
/**
* Sets the paint \a effect to use while rendering diagrams.
*
* Ownership of \a effect is transferred to the settings.
*
* \see paintEffect()
*
* \since QGIS 3.12
*/
void setPaintEffect( QgsPaintEffect *effect SIP_TRANSFER );
private:
double mSpacing = 0;
@ -627,6 +649,7 @@ class CORE_EXPORT QgsDiagramSettings
bool mShowAxis = false;
std::unique_ptr< QgsLineSymbol > mAxisLineSymbol;
std::unique_ptr< QgsPaintEffect > mPaintEffect;
};

View File

@ -570,130 +570,7 @@
<property name="syncGroup" stdset="0">
<string notr="true">labelrenderinggroup</string>
</property>
<layout class="QGridLayout" name="gridLayout_7" rowstretch="0,0,0,0,0,0,0,0,0,0">
<item row="5" column="0">
<widget class="QLabel" name="mPenWidthLabel">
<property name="text">
<string>Line width</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mPenColorLabel">
<property name="text">
<string>Line color</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<widget class="QComboBox" name="mAngleOffsetComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QgsUnitSelectionWidget" name="mDiagramLineUnitComboBox" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QgsPropertyOverrideButton" name="mLineColorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QgsColorButton" name="mDiagramPenColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QgsPropertyOverrideButton" name="mBackgroundColorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="mAngleOffsetLabel">
<property name="text">
<string>Start angle</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="mAngleDirectionLabel">
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mBarWidthLabel">
<property name="text">
<string>Bar width</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QgsDoubleSpinBox" name="mPenWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>5</number>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QgsOpacityWidget" name="mOpacityWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<layout class="QGridLayout" name="gridLayout_7" rowstretch="0,0,0,0,0,0,0,0,0,0,0">
<item row="2" column="0">
<widget class="QLabel" name="mBarSpacingLabel">
<property name="text">
@ -701,41 +578,10 @@
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QgsDoubleSpinBox" name="mBarWidthSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsDoubleSpinBox" name="mBarSpacingSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
<item row="3" column="0">
<widget class="QLabel" name="mBackgroundColorLabel">
<property name="text">
<string>Background color</string>
</property>
</widget>
</item>
@ -764,6 +610,75 @@
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QgsPropertyOverrideButton" name="mStartAngleDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mPenColorLabel">
<property name="text">
<string>Line color</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QgsOpacityWidget" name="mOpacityWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="mAngleOffsetLabel">
<property name="text">
<string>Start angle</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<widget class="QComboBox" name="mAngleOffsetComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QgsColorButton" name="mDiagramPenColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="1" colspan="3">
<widget class="QComboBox" name="mAngleDirectionComboBox">
<property name="sizePolicy">
@ -774,37 +689,22 @@
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QgsPropertyOverrideButton" name="mLineWidthDDBtn">
<property name="text">
<string>…</string>
<item row="1" column="1" colspan="3">
<widget class="QgsDoubleSpinBox" name="mBarWidthSpinBox">
<property name="decimals">
<number>5</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mOpacityLabel">
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="text">
<string>Opacity</string>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mBackgroundColorLabel">
<property name="text">
<string>Background color</string>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QgsPropertyOverrideButton" name="mStartAngleDDBtn">
<property name="text">
<string>…</string>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
@ -821,6 +721,80 @@
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QgsPropertyOverrideButton" name="mLineColorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QgsPropertyOverrideButton" name="mLineWidthDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QgsUnitSelectionWidget" name="mDiagramLineUnitComboBox" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsDoubleSpinBox" name="mBarSpacingSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="mAngleDirectionLabel">
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mBarWidthLabel">
<property name="text">
<string>Bar width</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="mPenWidthLabel">
<property name="text">
<string>Line width</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mOpacityLabel">
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Opacity</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QgsUnitSelectionWidget" name="mBarSpacingUnitComboBox" native="true">
<property name="focusPolicy">
@ -828,6 +802,42 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QgsDoubleSpinBox" name="mPenWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>5</number>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QgsPropertyOverrideButton" name="mBackgroundColorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="4">
<widget class="QgsEffectStackCompactWidget" name="mPaintEffectWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -840,7 +850,7 @@
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="1">
<item row="0" column="2">
<widget class="QgsSymbolButton" name="mAxisLineStyleButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -2289,6 +2299,12 @@
<extends>QToolButton</extends>
<header>qgssymbolbutton.h</header>
</customwidget>
<customwidget>
<class>QgsEffectStackCompactWidget</class>
<extends>QWidget</extends>
<header>effects/qgseffectstackpropertieswidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mDiagramTypeComboBox</tabstop>

View File

@ -40,6 +40,7 @@
#include "qgsmultirenderchecker.h"
#include "qgspallabeling.h"
#include "qgsproject.h"
#include "qgsshadoweffect.h"
/**
* \ingroup UnitTests
@ -172,6 +173,44 @@ class TestQgsDiagram : public QObject
QVERIFY( imageCheck( "piediagram" ) );
}
void testPaintEffect()
{
QgsDiagramSettings ds;
QColor col1 = Qt::red;
QColor col2 = Qt::yellow;
col1.setAlphaF( 0.5 );
col2.setAlphaF( 0.5 );
ds.categoryColors = QList<QColor>() << col1 << col2;
ds.categoryAttributes = QList<QString>() << QStringLiteral( "\"Pilots\"" ) << QStringLiteral( "\"Cabin Crew\"" );
ds.minimumScale = -1;
ds.maximumScale = -1;
ds.minimumSize = 0;
ds.penColor = Qt::green;
ds.penWidth = .5;
ds.scaleByArea = true;
ds.sizeType = QgsUnitTypes::RenderMillimeters;
ds.size = QSizeF( 5, 5 );
ds.rotationOffset = 0;
ds.setPaintEffect( new QgsDropShadowEffect() );
QgsLinearlyInterpolatedDiagramRenderer *dr = new QgsLinearlyInterpolatedDiagramRenderer();
dr->setLowerValue( 0.0 );
dr->setLowerSize( QSizeF( 0.0, 0.0 ) );
dr->setUpperValue( 10 );
dr->setUpperSize( QSizeF( 40, 40 ) );
dr->setClassificationField( QStringLiteral( "Staff" ) );
dr->setDiagram( new QgsPieDiagram() );
dr->setDiagramSettings( ds );
mPointsLayer->setDiagramRenderer( dr );
QgsDiagramLayerSettings dls = QgsDiagramLayerSettings();
dls.setPlacement( QgsDiagramLayerSettings::OverPoint );
dls.setShowAllDiagrams( true );
mPointsLayer->setDiagramLayerSettings( dls );
QVERIFY( imageCheck( "diagram_effects" ) );
}
void testHistogram()
{
QgsDiagramSettings ds;

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB