Navigation Menu

Skip to content

Commit

Permalink
supports dynamic SVGs in layouts (#43119)
Browse files Browse the repository at this point in the history
* add QgsSvgOrImageSourceLineEdit to dynamically switch between SVG and Image line edit

* use QgsXmlUtils to save custom properties

* adapt QgsSvgSelectorWidget to support other images:

* source line edit can be switched to image
* SVG specific components can be hidden

* [FEATURE] supports dynamic SVGs in layouts

also fix a UX bug where you couldn't switch between raster and SVG radios if the data defined property was active (unreported in tracker)

* add tests for dynamic SVGs in layouts

* use public method instead of protected member

* better wording and dox

* fix dox

* fix build

* refresh picture after setting params

* fix expressions

* add control image

* address review + fix file filter for any image

* avoid adding unclear API
  • Loading branch information
3nids committed May 11, 2021
2 parents d5b1fdb + c8c534d commit 5084127
Show file tree
Hide file tree
Showing 17 changed files with 424 additions and 485 deletions.
14 changes: 14 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitempicture.sip.in
Expand Up @@ -283,6 +283,20 @@ the result of data defined path overrides.
.. seealso:: :py:func:`picturePath`

.. versionadded:: 3.6
%End

QMap<QString, QgsProperty> svgDynamicParameters() const;
%Docstring
Returns the SVG dynamic parameters

.. versionadded:: 3.20
%End

void setSvgDynamicParameters( const QMap<QString, QgsProperty> &parameters );
%Docstring
Sets the SVG dynamic parameters

.. versionadded:: 3.20
%End

public slots:
Expand Down
51 changes: 46 additions & 5 deletions python/gui/auto_generated/qgsfilecontentsourcelineedit.sip.in
Expand Up @@ -95,7 +95,51 @@ Emitted whenever the file source is changed in the widget.

};

class QgsSvgSourceLineEdit : QgsAbstractFileContentSourceLineEdit


class QgsPictureSourceLineEditBase : QgsAbstractFileContentSourceLineEdit
{
%Docstring(signature="appended")
A line edit widget with toolbutton for setting a raster image path.

.. seealso:: :py:class:`QgsSvgSourceLineEdit`

.. versionadded:: 3.20
%End

%TypeHeaderCode
#include "qgsfilecontentsourcelineedit.h"
%End
public:

enum Format
{
Svg,
Image,
};

QgsPictureSourceLineEditBase( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for :py:class:`QgsImageSourceLineEdit`, with the specified ``parent`` widget.
The default format is SVG.
%End

void setMode( Format format );
%Docstring
Defines the mode of the source line edit
%End

protected:

QgsPictureSourceLineEditBase( Format format, QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for :py:class:`QgsImageSourceLineEdit`, with the specified ``parent`` widget.
%End

};


class QgsSvgSourceLineEdit : QgsPictureSourceLineEditBase
{
%Docstring(signature="appended")
A line edit widget with toolbutton for setting an SVG image path.
Expand All @@ -116,11 +160,9 @@ Designed for use with :py:class:`QgsSvgCache`.
%Docstring
Constructor for QgsSvgSourceLineEdit, with the specified ``parent`` widget.
%End

};


class QgsImageSourceLineEdit : QgsAbstractFileContentSourceLineEdit
class QgsImageSourceLineEdit : QgsPictureSourceLineEditBase
{
%Docstring(signature="appended")
A line edit widget with toolbutton for setting a raster image path.
Expand All @@ -141,7 +183,6 @@ Designed for use with :py:class:`QgsImageCache`.
%Docstring
Constructor for QgsImageSourceLineEdit, with the specified ``parent`` widget.
%End

};

/************************************************************************
Expand Down
25 changes: 23 additions & 2 deletions python/gui/auto_generated/symbology/qgssvgselectorwidget.sip.in
Expand Up @@ -110,7 +110,7 @@ class QgsSvgSelectorWidget : QWidget
Constructor for QgsSvgSelectorWidget
%End

void initParametersModel( const QgsExpressionContextGenerator *generator, QgsVectorLayer *layer );
void initParametersModel( const QgsExpressionContextGenerator *generator, QgsVectorLayer *layer = 0 );
%Docstring
Initialize the parameters model so the context and the layer are referenced.

Expand All @@ -119,7 +119,7 @@ Initialize the parameters model so the context and the layer are referenced.

QString currentSvgPath() const;

QgsSvgSourceLineEdit *sourceLineEdit() const;
QgsPictureSourceLineEditBase *sourceLineEdit() const;
%Docstring
Returns the source line edit

Expand All @@ -138,6 +138,27 @@ Defines if the group box to fill parameters is visible
Returns if the group box to fill parameters is visible

.. versionadded:: 3.18
%End

void setBrowserVisible( bool visible );
%Docstring
Defines if the SVG browser should be visible

.. versionadded:: 3.20
%End

bool browserVisible() const;
%Docstring
Returns if the SVG browser should be visible

.. versionadded:: 3.20
%End

QgsPropertyOverrideButton *propertyOverrideToolButton() const;
%Docstring
Returns the property override tool button of the file line edit

.. versionadded:: 3.20
%End

public slots:
Expand Down
38 changes: 34 additions & 4 deletions src/core/layout/qgslayoutitempicture.cpp
Expand Up @@ -413,8 +413,10 @@ void QgsLayoutItemPicture::loadLocalPicture( const QString &path )
QColor fillColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgBackgroundColor, context, mSvgFillColor );
QColor strokeColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgStrokeColor, context, mSvgStrokeColor );
double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PictureSvgStrokeWidth, context, mSvgStrokeWidth );
QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( svgDynamicParameters(), context );

const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, rect().width(), fillColor, strokeColor, strokeWidth,
1.0 );
1.0, 0, false, evaluatedParameters );
mSVG.load( svgContent );
if ( mSVG.isValid() )
{
Expand Down Expand Up @@ -473,11 +475,12 @@ void QgsLayoutItemPicture::loadPictureUsingCache( const QString &path )
QColor fillColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgBackgroundColor, context, mSvgFillColor );
QColor strokeColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgStrokeColor, context, mSvgStrokeColor );
double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PictureSvgStrokeWidth, context, mSvgStrokeWidth );
// TODO parameters (handle this in the gui part)
QMap<QString, QString> parameters;

QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( svgDynamicParameters(), context );

bool isMissingImage = false;
const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, rect().width(), fillColor, strokeColor, strokeWidth,
1.0, 0, false, parameters, &isMissingImage );
1.0, 0, false, evaluatedParameters, &isMissingImage );
mSVG.load( svgContent );
if ( mSVG.isValid() && !isMissingImage )
{
Expand Down Expand Up @@ -620,6 +623,33 @@ QString QgsLayoutItemPicture::evaluatedPath() const
return mEvaluatedPath;
}

QMap<QString, QgsProperty> QgsLayoutItemPicture::svgDynamicParameters() const
{
const QVariantMap parameters = mCustomProperties.value( QStringLiteral( "svg-dynamic-parameters" ), QVariantMap() ).toMap();

QMap<QString, QgsProperty> parametersProperties;
QVariantMap::const_iterator it = parameters.constBegin();
for ( ; it != parameters.constEnd(); ++it )
{
QgsProperty property;
if ( property.loadVariant( it.value() ) )
parametersProperties.insert( it.key(), property );
}

return parametersProperties;
}

void QgsLayoutItemPicture::setSvgDynamicParameters( const QMap<QString, QgsProperty> &parameters )
{
QVariantMap variantParameters;
QMap<QString, QgsProperty>::const_iterator it = parameters.constBegin();
for ( ; it != parameters.constEnd(); ++it )
variantParameters.insert( it.key(), it.value().toVariant() );

mCustomProperties.setValue( QStringLiteral( "svg-dynamic-parameters" ), variantParameters );
refreshPicture();
}

void QgsLayoutItemPicture::shapeChanged()
{
if ( mMode == FormatSVG && !mLoadingSvg )
Expand Down
12 changes: 12 additions & 0 deletions src/core/layout/qgslayoutitempicture.h
Expand Up @@ -260,6 +260,18 @@ class CORE_EXPORT QgsLayoutItemPicture: public QgsLayoutItem
*/
QString evaluatedPath() const;

/**
* Returns the SVG dynamic parameters
* \since QGIS 3.20
*/
QMap<QString, QgsProperty> svgDynamicParameters() const;

/**
* Sets the SVG dynamic parameters
* \since QGIS 3.20
*/
void setSvgDynamicParameters( const QMap<QString, QgsProperty> &parameters );

public slots:

/**
Expand Down
1 change: 0 additions & 1 deletion src/core/qgsobjectcustomproperties.cpp
Expand Up @@ -138,6 +138,5 @@ void QgsObjectCustomProperties::writeXml( QDomNode &parentNode, QDomDocument &do

QDomElement propsElement = doc.createElement( QStringLiteral( "customproperties" ) );
propsElement.appendChild( QgsXmlUtils::writeVariant( mMap, doc ) );

parentNode.appendChild( propsElement );
}
1 change: 0 additions & 1 deletion src/gui/layout/qgslayoutitemwidget.h
Expand Up @@ -201,7 +201,6 @@ class GUI_EXPORT QgsLayoutItemBaseWidget: public QgsPanelWidget
QgsLayoutAtlas *layoutAtlas() const;

private:

QgsLayoutConfigObject *mConfigObject = nullptr;

QgsLayoutObject *mObject = nullptr;
Expand Down

0 comments on commit 5084127

Please sign in to comment.