[Selective masking]Fixes #34352 : use QUUid to identify symbol layer

Don't use pointers anymore because there are not stable when it comes
to clone
This commit is contained in:
Julien Cabieces 2022-10-19 11:46:52 +02:00
parent 07090ba36c
commit b3af80e3e4
24 changed files with 419 additions and 211 deletions

View File

@ -109,7 +109,39 @@ This is currently used to implement selective masking.
.. versionadded:: 3.12
%End
QSet<const QgsSymbolLayer *> disabledSymbolLayers() const;
void setDisabledSymbolLayers( const QSet<const QgsSymbolLayer *> &symbolLayers );
%Docstring
When rendering a map layer in a second pass (for selective masking),
some symbol layers may be disabled.
Sets the list of disabled symbol layers.
.. seealso:: :py:func:`disabledSymbolLayers`
.. seealso:: :py:func:`isSymbolLayerEnabled`
.. versionadded:: 3.12
.. deprecated:: QGIS 3.28
and replaced with setDisabledSymbolLayersV2
%End
void setDisabledSymbolLayersV2( const QSet<QString> &symbolLayers );
%Docstring
When rendering a map layer in a second pass (for selective masking),
some symbol layers may be disabled.
Sets the list of disabled symbol layer ids.
.. seealso:: :py:func:`disabledSymbolLayersV2`
.. seealso:: :py:func:`isSymbolLayerEnabledV2`
.. versionadded:: 3.28
%End
QSet<const QgsSymbolLayer *> disabledSymbolLayers() const;
%Docstring
When rendering a map layer in a second pass (for selective masking),
some symbol layers may be disabled.
@ -121,6 +153,26 @@ Returns the list of disabled symbol layers.
.. seealso:: :py:func:`isSymbolLayerEnabled`
.. versionadded:: 3.12
.. deprecated:: QGIS 3.28
and replaced with disabledSymbolLayersV2
%End
const QSet<QString> &disabledSymbolLayersV2() const;
%Docstring
When rendering a map layer in a second pass (for selective masking),
some symbol layers may be disabled.
Returns the list of disabled symbol layer ids.
.. seealso:: :py:func:`setDisabledSymbolLayers`
.. seealso:: :py:func:`isSymbolLayerEnabled`
.. versionadded:: 3.12
.. deprecated:: QGIS 3.28
and replaced with disabledSymbolLayersV2
%End
bool isSymbolLayerEnabled( const QgsSymbolLayer *layer ) const;
@ -515,20 +567,6 @@ of any rendering operations.
Multiple mask painters can be defined and the second parameter gives a unique identifier to each one.
.. seealso:: :py:func:`maskPainter`
%End
void setDisabledSymbolLayers( const QSet<const QgsSymbolLayer *> &symbolLayers );
%Docstring
When rendering a map layer in a second pass (for selective masking),
some symbol layers may be disabled.
Sets the list of disabled symbol layers.
.. seealso:: :py:func:`disabledSymbolLayers`
.. seealso:: :py:func:`isSymbolLayerEnabled`
.. versionadded:: 3.12
%End
void setForceVectorOutput( bool force );
@ -935,6 +973,19 @@ rendering using QBrush objects.
.. versionadded:: 3.16
%End
void addSymbolLayerClipPath( const QString &symbolLayerId, QPainterPath path );
%Docstring
Add a clip ``path`` to be applied to the ``symbolLayer`` before rendering
.. versionadded:: 3.26
%End
QList<QPainterPath> symbolLayerClipPaths( const QString &symbolLayerId ) const;
%Docstring
Returns clip paths to be applied to the ``symbolLayer`` before rendering
.. versionadded:: 3.26
%End
QgsDoubleRange zRange() const;
%Docstring

View File

@ -646,6 +646,22 @@ Prepares all mask internal objects according to what is defined in ``context``
This should be called prior to calling :py:func:`~QgsSymbolLayer.startRender` method.
.. versionadded:: 3.26
%End
void setId( const QString &id );
%Docstring
Set symbol layer identifier
This id has to be unique in the whole project
.. versionadded:: 3.28
%End
const QString &id() const;
%Docstring
Returns symbol layer identifier
This id is unique in the whole project
.. versionadded:: 3.28
%End
protected:
@ -664,7 +680,6 @@ Constructor for QgsSymbolLayer.
void restoreOldDataDefinedProperties( const QVariantMap &stringMap );
%Docstring
Restores older data defined properties from string map.

View File

@ -106,19 +106,31 @@ Type used to refer to a specific symbol layer in a symbol of a layer.
Default constructor
%End
QgsSymbolLayerReference( const QString &layerId, const QgsSymbolLayerId &symbolLayer );
QgsSymbolLayerReference( const QString &layerId, const QgsSymbolLayerId &symbolLayer );
%Docstring
Constructor
%End
QgsSymbolLayerReference( const QString &layerId, const QString &symbolLayerId );
QString layerId() const;
%Docstring
The referenced vector layer / feature renderer
%End
QgsSymbolLayerId symbolLayerId() const;
QgsSymbolLayerId symbolLayerId() const;
%Docstring
The symbol layer's id
.. deprecated:: QGIS 3.28
use symbolLayerIdV2 instead
%End
QString symbolLayerIdV2() const;
%Docstring
The symbol layer's id
.. versionadded:: 3.28
%End
bool operator==( const QgsSymbolLayerReference &other ) const;
@ -131,13 +143,13 @@ The symbol layer's id
{
pathString.append( QString::number( path ) );
}
QString str = QStringLiteral( "<QgsSymbolLayerReference: %1 - %2 (%3)>" ).arg( sipCpp->layerId(), sipCpp->symbolLayerId().symbolKey(), pathString.join( ',' ) );
QString str = QStringLiteral( "<QgsSymbolLayerReference: %1 - %2>" ).arg( sipCpp->layerId(), sipCpp->symbolLayerIdV2() );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End
};
uint qHash( const QgsSymbolLayerId &id );
uint qHash( const QgsSymbolLayerId &id );
uint qHash( const QgsSymbolLayerReference &r );

View File

@ -203,7 +203,7 @@ Write settings into a DOM element.
.. seealso:: :py:func:`readXml`
%End
QList<QgsSymbolLayerReference> maskedSymbolLayers() const;
const QList<QgsSymbolLayerReference> &maskedSymbolLayers() const;
%Docstring
Returns a list of references to symbol layers that are masked by this buffer.

View File

@ -653,7 +653,7 @@ std::vector< LayerRenderJob > QgsMapRendererJob::prepareSecondPassJobs( std::vec
// We collect for each layer, the set of symbol layers that will be "masked"
// and the list of source layers that have a mask
QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;
QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
const bool forceVector = mapSettings().testFlag( Qgis::MapSettingsFlag::ForceVectorOutput )
&& !mapSettings().testFlag( Qgis::MapSettingsFlag::ForceRasterMasks );
@ -732,10 +732,8 @@ std::vector< LayerRenderJob > QgsMapRendererJob::prepareSecondPassJobs( std::vec
if ( !layerJobMapping.contains( sourceLayerId ) )
continue;
for ( auto slIt = mit.value().symbolLayerIds.begin(); slIt != mit.value().symbolLayerIds.end(); slIt++ )
{
slRefs.insert( QgsSymbolLayerReference( mit.key(), *slIt ) );
}
for ( QString symbolLayerId : mit.value().symbolLayerIds )
slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
hasEffects |= mit.value().hasEffects;
}
@ -865,7 +863,7 @@ std::vector< LayerRenderJob > QgsMapRendererJob::prepareSecondPassJobs( std::vec
continue;
QList<MaskSource> &sourceList = it->second;
const QSet<QgsSymbolLayerId> &symbolList = it->first;
const QSet<QString> &symbolList = it->first;
secondPassJobs.emplace_back( LayerRenderJob() );
LayerRenderJob &job2 = secondPassJobs.back();
@ -919,7 +917,7 @@ std::vector< LayerRenderJob > QgsMapRendererJob::prepareSecondPassJobs( std::vec
// Render only the non masked symbol layer and we will compose 2nd pass with mask and first pass rendering in composeSecondPass
// If vector output is enabled, disabled symbol layers would be actually rendered and masked with clipping path set in QgsMapRendererJob::initSecondPassJobs
job2.context()->setDisabledSymbolLayers( QgsSymbolLayerUtils::toSymbolLayerPointers( mapRenderer->featureRenderer(), symbolList ) );
job2.context()->setDisabledSymbolLayersV2( symbolList );
}
return secondPassJobs;
@ -942,13 +940,13 @@ void QgsMapRendererJob::initSecondPassJobs( std::vector< LayerRenderJob > &secon
{
QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
QPainterPath path = static_cast<QgsMaskPaintDevice *>( maskPainter->device() )->maskPainterPath();
for ( const QgsSymbolLayer *symbolLayer : job.context()->disabledSymbolLayers() )
for ( const QString &symbolLayerId : job.context()->disabledSymbolLayersV2() )
{
job.context()->addSymbolLayerClipPath( symbolLayer, path );
job.context()->addSymbolLayerClipPath( symbolLayerId, path );
}
}
job.context()->setDisabledSymbolLayers( QSet<const QgsSymbolLayer *>() );
job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
}
}

View File

@ -23,6 +23,7 @@
#include "qgslogger.h"
#include "qgselevationmap.h"
#include "qgsunittypes.h"
#include "qgssymbollayer.h"
#define POINTS_TO_MM 2.83464567
#define INCH_TO_MM 25.4
@ -708,12 +709,39 @@ void QgsRenderContext::setElevationMap( QgsElevationMap *map )
mElevationMap = map;
}
void QgsRenderContext::addSymbolLayerClipPath( const QgsSymbolLayer *symbolLayer, QPainterPath path )
void QgsRenderContext::addSymbolLayerClipPath( const QString &symbolLayerId, QPainterPath path )
{
mSymbolLayerClipPaths[ symbolLayer ].append( path );
mSymbolLayerClipPaths[ symbolLayerId ].append( path );
}
QList<QPainterPath> QgsRenderContext::symbolLayerClipPaths( const QgsSymbolLayer *symbolLayer ) const
QList<QPainterPath> QgsRenderContext::symbolLayerClipPaths( const QString &symbolLayerId ) const
{
return mSymbolLayerClipPaths[ symbolLayer ];
return mSymbolLayerClipPaths[ symbolLayerId ];
}
void QgsRenderContext::setDisabledSymbolLayers( const QSet<const QgsSymbolLayer *> &symbolLayers )
{
mDisabledSymbolLayers.clear();
for ( const QgsSymbolLayer *symbolLayer : symbolLayers )
mDisabledSymbolLayers << symbolLayer->id();
}
void QgsRenderContext::setDisabledSymbolLayersV2( const QSet<QString> &symbolLayers )
{
mDisabledSymbolLayers = symbolLayers;
}
QSet<const QgsSymbolLayer *> QgsRenderContext::disabledSymbolLayers() const
{
return QSet<const QgsSymbolLayer *>();
}
const QSet<QString> &QgsRenderContext::disabledSymbolLayersV2() const
{
return mDisabledSymbolLayers;
}
bool QgsRenderContext::isSymbolLayerEnabled( const QgsSymbolLayer *layer ) const
{
return !mDisabledSymbolLayers.contains( layer->id() );
}

View File

@ -144,6 +144,31 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
QPainter *maskPainter( int id = 0 ) { return mMaskPainter.value( id, nullptr ); }
// TODO QGIS 4 : remove the V2 from method name
/**
* When rendering a map layer in a second pass (for selective masking),
* some symbol layers may be disabled.
*
* Sets the list of disabled symbol layers.
* \see disabledSymbolLayers()
* \see isSymbolLayerEnabled()
* \since QGIS 3.12
* \deprecated since QGIS 3.28 and replaced with setDisabledSymbolLayersV2
*/
Q_DECL_DEPRECATED void setDisabledSymbolLayers( const QSet<const QgsSymbolLayer *> &symbolLayers );
/**
* When rendering a map layer in a second pass (for selective masking),
* some symbol layers may be disabled.
*
* Sets the list of disabled symbol layer ids.
* \see disabledSymbolLayersV2()
* \see isSymbolLayerEnabledV2()
* \since QGIS 3.28
*/
void setDisabledSymbolLayersV2( const QSet<QString> &symbolLayers );
/**
* When rendering a map layer in a second pass (for selective masking),
* some symbol layers may be disabled.
@ -152,8 +177,21 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
* \see setDisabledSymbolLayers()
* \see isSymbolLayerEnabled()
* \since QGIS 3.12
* \deprecated since QGIS 3.28 and replaced with disabledSymbolLayersV2
*/
QSet<const QgsSymbolLayer *> disabledSymbolLayers() const { return mDisabledSymbolLayers; }
Q_DECL_DEPRECATED QSet<const QgsSymbolLayer *> disabledSymbolLayers() const;
/**
* When rendering a map layer in a second pass (for selective masking),
* some symbol layers may be disabled.
*
* Returns the list of disabled symbol layer ids.
* \see setDisabledSymbolLayers()
* \see isSymbolLayerEnabled()
* \since QGIS 3.12
* \deprecated since QGIS 3.28 and replaced with disabledSymbolLayersV2
*/
const QSet<QString> &disabledSymbolLayersV2() const;
/**
* When rendering a map layer in a second pass (for selective masking),
@ -164,7 +202,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
* \see disabledSymbolLayers()
* \since QGIS 3.12
*/
bool isSymbolLayerEnabled( const QgsSymbolLayer *layer ) const { return ! mDisabledSymbolLayers.contains( layer ); }
bool isSymbolLayerEnabled( const QgsSymbolLayer *layer ) const;
/**
* Returns the current coordinate transform for the context.
@ -521,17 +559,6 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
void setMaskPainter( QPainter *p, int id = 0 ) { mMaskPainter[id] = p; }
/**
* When rendering a map layer in a second pass (for selective masking),
* some symbol layers may be disabled.
*
* Sets the list of disabled symbol layers.
* \see disabledSymbolLayers()
* \see isSymbolLayerEnabled()
* \since QGIS 3.12
*/
void setDisabledSymbolLayers( const QSet<const QgsSymbolLayer *> &symbolLayers ) { mDisabledSymbolLayers = symbolLayers; }
/**
* Sets whether rendering operations should use vector operations instead
* of any faster raster shortcuts.
@ -891,21 +918,17 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
void setTextureOrigin( const QPointF &origin );
#ifndef SIP_RUN
/**
* Add a clip \a path to be applied to the \a symbolLayer before rendering
* \since QGIS 3.26
* \since QGIS 3.26, arguments changed and public API since 3.28
*/
void addSymbolLayerClipPath( const QgsSymbolLayer *symbolLayer, QPainterPath path );
void addSymbolLayerClipPath( const QString &symbolLayerId, QPainterPath path );
/**
* Returns clip paths to be applied to the \a symbolLayer before rendering
* \since QGIS 3.26
* \since QGIS 3.26, arguments changed and public API since 3.28
*/
QList<QPainterPath> symbolLayerClipPaths( const QgsSymbolLayer *symbolLayer ) const;
#endif
QList<QPainterPath> symbolLayerClipPaths( const QString &symbolLayerId ) const;
/**
* Returns the range of z-values which should be rendered.
@ -1170,7 +1193,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
bool mHasRenderedFeatureHandlers = false;
QVariantMap mCustomRenderingFlags;
QSet<const QgsSymbolLayer *> mDisabledSymbolLayers;
QSet<QString> mDisabledSymbolLayers;
QList< QgsMapClippingRegion > mClippingRegions;
QgsGeometry mFeatureClipGeometry;
@ -1189,7 +1212,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
long long mCurrentFrame = -1;
//! clip paths to be applied to the symbol layer before rendering
QMap< const QgsSymbolLayer *, QList<QPainterPath> > mSymbolLayerClipPaths;
QMap< QString, QList<QPainterPath> > mSymbolLayerClipPaths;
#ifdef QGISDEBUG
bool mHasTransformContext = false;

View File

@ -1153,6 +1153,7 @@ QgsSymbolLayerList QgsSymbol::cloneLayers() const
layer->setLocked( ( *it )->isLocked() );
layer->setRenderingPass( ( *it )->renderingPass() );
layer->setEnabled( ( *it )->enabled() );
layer->setId( ( *it )->id() );
lst.append( layer );
}
return lst;

View File

@ -32,6 +32,7 @@
#include <QPainter>
#include <QPointF>
#include <QPolygonF>
#include <QUuid>
QgsPropertiesDefinition QgsSymbolLayer::sPropertyDefinitions;
@ -230,6 +231,7 @@ void QgsSymbolLayer::setPaintEffect( QgsPaintEffect *effect )
QgsSymbolLayer::QgsSymbolLayer( Qgis::SymbolType type, bool locked )
: mType( type )
, mLocked( locked )
, mId( QUuid::createUuid().toString() )
{
}
@ -911,7 +913,7 @@ void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context )
mClipPath.clear();
const QgsRenderContext &renderContext = context.renderContext();
const QList<QPainterPath> clipPaths = renderContext.symbolLayerClipPaths( this );
const QList<QPainterPath> clipPaths = renderContext.symbolLayerClipPaths( id() );
if ( !clipPaths.isEmpty() )
{
QPainterPath mergedPaths;
@ -929,3 +931,13 @@ void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context )
}
}
}
void QgsSymbolLayer::setId( const QString &id )
{
mId = id;
}
const QString &QgsSymbolLayer::id() const
{
return mId;
}

View File

@ -630,6 +630,20 @@ class CORE_EXPORT QgsSymbolLayer
*/
virtual void prepareMasks( const QgsSymbolRenderContext &context );
/**
* Set symbol layer identifier
* This id has to be unique in the whole project
* \since QGIS 3.28
*/
void setId( const QString &id );
/**
* Returns symbol layer identifier
* This id is unique in the whole project
* \since QGIS 3.28
*/
const QString &id() const;
protected:
/**
@ -647,7 +661,7 @@ class CORE_EXPORT QgsSymbolLayer
bool mLocked = false;
QColor mColor;
int mRenderingPass = 0;
QString mId;
QgsPropertyCollection mDataDefinedProperties;
std::unique_ptr< QgsPaintEffect > mPaintEffect;

View File

@ -20,19 +20,11 @@
QString symbolLayerReferenceListToString( const QgsSymbolLayerReferenceList &lst )
{
QStringList slst;
slst.reserve( lst.size() );
slst.reserve( lst.size() * 2 );
for ( const QgsSymbolLayerReference &ref : lst )
{
QStringList indexPathStr;
const QVector<int> indexPath = ref.symbolLayerId().symbolLayerIndexPath();
indexPathStr.reserve( indexPath.size() );
for ( const int index : indexPath )
{
indexPathStr.append( QString::number( index ) );
}
// this is BAD BAD BAD -- it assumes that the component parts eg the symbolKey has no commas!
// a more unique string should have been used as a concatenator here, but it's too late to fix that without breaking projects...
slst.append( QStringLiteral( "%1,%2,%3" ).arg( ref.layerId(), ref.symbolLayerId().symbolKey(), indexPathStr.join( ',' ) ) );
slst << ref.layerId();
slst << ref.symbolLayerIdV2();
}
return slst.join( ';' );
}
@ -43,36 +35,57 @@ QgsSymbolLayerReferenceList stringToSymbolLayerReferenceList( const QString &str
if ( str.isEmpty() )
return lst;
// when saving we used ; as a concatenator... but that was silly, cos maybe the symbol keys contain this string!
// try to handle this gracefully via regex...
const thread_local QRegularExpression partsRx( QStringLiteral( "((?:.*?),(?:.*?),(?:(?:\\d+,)+)?(?:\\d+);)" ) );
QRegularExpressionMatchIterator partsIt = partsRx.globalMatch( str + ';' );
while ( partsIt.hasNext() )
if ( str.contains( "," ) )
{
const QRegularExpressionMatch partMatch = partsIt.next();
const QString tuple = partMatch.captured( 1 );
// TODO QGIS 4 : remove this if branch, keep only else part
Q_NOWARN_DEPRECATED_PUSH
// We should have "layer_id,symbol_key,symbol_layer_index0,symbol_layer_index1,..."
// EXCEPT that the symbol_key CAN have commas, so this whole logic is extremely broken.
// Let's see if a messy regex can save the day!
const thread_local QRegularExpression rx( QStringLiteral( "(.*?),(.*?),((?:\\d+,)+)?(\\d+)" ) );
// old masked symbol layer format (before 3.28), we use unique id now!
// we load it the old fashion way and we will update the new one later when
// the whole project is loaded
const QRegularExpressionMatch match = rx.match( tuple );
if ( !match.hasMatch() )
continue;
// when saving we used ; as a concatenator... but that was silly, cos maybe the symbol keys contain this string!
// try to handle this gracefully via regex...
const thread_local QRegularExpression partsRx( QStringLiteral( "((?:.*?),(?:.*?),(?:(?:\\d+,)+)?(?:\\d+);)" ) );
QRegularExpressionMatchIterator partsIt = partsRx.globalMatch( str + ';' );
const QString layerId = match.captured( 1 );
const QString symbolKey = match.captured( 2 );
const QStringList indices = QString( match.captured( 3 ) + match.captured( 4 ) ).split( ',' );
QVector<int> indexPath;
indexPath.reserve( indices.size() );
for ( const QString &index : indices )
while ( partsIt.hasNext() )
{
indexPath.append( index.toInt() );
const QRegularExpressionMatch partMatch = partsIt.next();
const QString tuple = partMatch.captured( 1 );
// We should have "layer_id,symbol_key,symbol_layer_index0,symbol_layer_index1,..."
// EXCEPT that the symbol_key CAN have commas, so this whole logic is extremely broken.
// Let's see if a messy regex can save the day!
const thread_local QRegularExpression rx( QStringLiteral( "(.*?),(.*?),((?:\\d+,)+)?(\\d+)" ) );
const QRegularExpressionMatch match = rx.match( tuple );
if ( !match.hasMatch() )
continue;
const QString layerId = match.captured( 1 );
const QString symbolKey = match.captured( 2 );
const QStringList indices = QString( match.captured( 3 ) + match.captured( 4 ) ).split( ',' );
QVector<int> indexPath;
indexPath.reserve( indices.size() );
for ( const QString &index : indices )
{
indexPath.append( index.toInt() );
}
lst.append( QgsSymbolLayerReference( layerId, QgsSymbolLayerId( symbolKey, indexPath ) ) );
Q_NOWARN_DEPRECATED_POP
}
lst.append( QgsSymbolLayerReference( layerId, QgsSymbolLayerId( symbolKey, indexPath ) ) );
}
else
{
const QStringList elems = str.split( ";" );
for ( int i = 0; i < elems.size(); )
{
lst << QgsSymbolLayerReference( elems[i], elems[i + 1] );
i += 2;
}
}
return lst;
}

View File

@ -137,8 +137,12 @@ class CORE_EXPORT QgsSymbolLayerReference
QgsSymbolLayerReference() = default;
//! Constructor
QgsSymbolLayerReference( const QString &layerId, const QgsSymbolLayerId &symbolLayer )
: mLayerId( layerId ), mSymbolLayerId( symbolLayer )
Q_DECL_DEPRECATED QgsSymbolLayerReference( const QString &layerId, const QgsSymbolLayerId &symbolLayer )
: mLayerId( layerId ), mDeprecatedSymbolLayerId( symbolLayer )
{}
QgsSymbolLayerReference( const QString &layerId, const QString &symbolLayerId )
: mLayerId( layerId ), mSymbolLayerId( symbolLayerId )
{}
/**
@ -148,14 +152,22 @@ class CORE_EXPORT QgsSymbolLayerReference
/**
* The symbol layer's id
* \deprecated since QGIS 3.28, use symbolLayerIdV2 instead
*/
QgsSymbolLayerId symbolLayerId() const { return mSymbolLayerId; }
Q_DECL_DEPRECATED QgsSymbolLayerId symbolLayerId() const { return mDeprecatedSymbolLayerId; }
/**
* The symbol layer's id
* \since QGIS 3.28
*/
QString symbolLayerIdV2() const { return mSymbolLayerId; }
//! Comparison operator
bool operator==( const QgsSymbolLayerReference &other ) const
{
return mLayerId == other.mLayerId &&
mSymbolLayerId == other.mSymbolLayerId;
return mLayerId == other.mLayerId
&& mSymbolLayerId == other.mSymbolLayerId
&& mDeprecatedSymbolLayerId == other.mDeprecatedSymbolLayerId;
}
#ifdef SIP_RUN
@ -167,24 +179,26 @@ class CORE_EXPORT QgsSymbolLayerReference
{
pathString.append( QString::number( path ) );
}
QString str = QStringLiteral( "<QgsSymbolLayerReference: %1 - %2 (%3)>" ).arg( sipCpp->layerId(), sipCpp->symbolLayerId().symbolKey(), pathString.join( ',' ) );
QString str = QStringLiteral( "<QgsSymbolLayerReference: %1 - %2>" ).arg( sipCpp->layerId(), sipCpp->symbolLayerIdV2() );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
% End
#endif
private:
QString mLayerId;
QgsSymbolLayerId mSymbolLayerId;
// TODO QGIS 4 : remove mDeprecatedSymbolLayerId
QgsSymbolLayerId mDeprecatedSymbolLayerId;
QString mSymbolLayerId;
};
inline uint qHash( const QgsSymbolLayerId &id )
Q_DECL_DEPRECATED inline uint qHash( const QgsSymbolLayerId &id )
{
return qHash( id.symbolKey() ) ^ qHash( id.symbolLayerIndexPath() );
}
inline uint qHash( const QgsSymbolLayerReference &r )
{
return qHash( r.layerId() ) ^ qHash( r.symbolLayerId() );
return qHash( r.layerId() ) ^ qHash( r.symbolLayerIdV2() );
}
typedef QList<QgsSymbolLayerReference> QgsSymbolLayerReferenceList;

View File

@ -1327,6 +1327,7 @@ QgsSymbolLayer *QgsSymbolLayerUtils::loadSymbolLayer( QDomElement &element, cons
const bool locked = element.attribute( QStringLiteral( "locked" ) ).toInt();
const bool enabled = element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
const int pass = element.attribute( QStringLiteral( "pass" ) ).toInt();
const QString id = element.attribute( QStringLiteral( "id" ) );
// parse properties
QVariantMap props = parseProperties( element );
@ -1344,6 +1345,10 @@ QgsSymbolLayer *QgsSymbolLayerUtils::loadSymbolLayer( QDomElement &element, cons
layer->setRenderingPass( pass );
layer->setEnabled( enabled );
// old project format, empty is missing, keep the actual layer one
if ( !id.isEmpty() )
layer->setId( id );
//restore layer effect
const QDomElement effectElem = element.firstChildElement( QStringLiteral( "effect" ) );
if ( !effectElem.isNull() )
@ -1424,6 +1429,7 @@ QDomElement QgsSymbolLayerUtils::saveSymbol( const QString &name, const QgsSymbo
layerEl.setAttribute( QStringLiteral( "enabled" ), layer->enabled() );
layerEl.setAttribute( QStringLiteral( "locked" ), layer->isLocked() );
layerEl.setAttribute( QStringLiteral( "pass" ), layer->renderingPass() );
layerEl.setAttribute( QStringLiteral( "id" ), layer->id() );
QVariantMap props = layer->properties();

View File

@ -234,7 +234,7 @@ QDomElement QgsTextMaskSettings::writeXml( QDomDocument &doc ) const
return textMaskElem;
}
QList<QgsSymbolLayerReference> QgsTextMaskSettings::maskedSymbolLayers() const
const QList<QgsSymbolLayerReference> &QgsTextMaskSettings::maskedSymbolLayers() const
{
return d->maskedSymbolLayers;
}

View File

@ -198,7 +198,7 @@ class CORE_EXPORT QgsTextMaskSettings
* \returns a list of references to masked symbol layers
* \see setMaskedSymbolLayers
*/
QList<QgsSymbolLayerReference> maskedSymbolLayers() const;
const QList<QgsSymbolLayerReference> &maskedSymbolLayers() const;
/**
* Sets the symbol layers that will be masked by this buffer.

View File

@ -977,7 +977,7 @@ QHash<QString, QgsMaskedLayers> QgsVectorLayerUtils::labelMasks( const QgsVector
for ( const auto &r : maskSettings.maskedSymbolLayers() )
{
QgsMaskedLayer &maskedLayer = maskedLayers[currentRule][r.layerId()];
maskedLayer.symbolLayerIds.insert( r.symbolLayerId() );
maskedLayer.symbolLayerIds.insert( r.symbolLayerIdV2() );
maskedLayer.hasEffects = hasEffects;
}
}
@ -1032,7 +1032,7 @@ QgsMaskedLayers QgsVectorLayerUtils::symbolLayerMasks( const QgsVectorLayer *lay
{
QgsMaskedLayer &maskedLayer = maskedLayers[mask.layerId()];
maskedLayer.hasEffects |= slHasEffects;
maskedLayer.symbolLayerIds.insert( mask.symbolLayerId() );
maskedLayer.symbolLayerIds.insert( mask.symbolLayerIdV2() );
}
}

View File

@ -33,9 +33,10 @@ struct QgsMaskedLayer
bool hasEffects = false;
// masked symbol layers
QSet<QgsSymbolLayerId> symbolLayerIds;
QSet<QString> symbolLayerIds;
};
//! masked layers where key is the layer id
typedef QHash<QString, QgsMaskedLayer> QgsMaskedLayers;
#endif

View File

@ -90,13 +90,13 @@ void QgsMaskingWidget::showEvent( QShowEvent *event )
* - mask source symbol layer id
* - list of target mask symbol layer references
*/
QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> symbolLayerMasks( const QgsVectorLayer *layer )
QList<QPair<QString, QList<QgsSymbolLayerReference>>> symbolLayerMasks( const QgsVectorLayer *layer )
{
if ( ! layer->renderer() )
return {};
QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> mMasks;
SymbolLayerVisitor collector( [&]( const QgsSymbolLayer * sl, const QgsSymbolLayerId & lid )
QList<QPair<QString, QList<QgsSymbolLayerReference>>> mMasks;
SymbolLayerVisitor collector( [&]( const QgsSymbolLayer * sl, const QString & lid )
{
if ( ! sl->masks().isEmpty() )
mMasks.push_back( qMakePair( lid, sl->masks() ) );
@ -120,7 +120,7 @@ void QgsMaskingWidget::populate()
mMaskTargetsWidget->setLayer( mLayer );
// collect masks and filter on those which have the current layer as destination
QSet<QgsSymbolLayerId> maskedSymbolLayers;
QSet<QString> maskedSymbolLayers;
QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources;
QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
@ -133,16 +133,16 @@ void QgsMaskingWidget::populate()
continue;
// collect symbol layer masks
const QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> slMasks = symbolLayerMasks( vl );
for ( const QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>> &p : slMasks )
const QList<QPair<QString, QList<QgsSymbolLayerReference>>> slMasks = symbolLayerMasks( vl );
for ( const QPair<QString, QList<QgsSymbolLayerReference>> &p : slMasks )
{
const QgsSymbolLayerId &sourceSymbolLayerId = p.first;
const QString &sourceSymbolLayerId = p.first;
for ( const QgsSymbolLayerReference &ref : p.second )
{
if ( ref.layerId() == mLayer->id() )
{
// add to the set of destinations
maskedSymbolLayers.insert( ref.symbolLayerId() );
maskedSymbolLayers.insert( ref.symbolLayerIdV2() );
// add to the list of mask sources
source.layerId = layerId;
source.isLabeling = false;
@ -166,7 +166,7 @@ void QgsMaskingWidget::populate()
// add the mask source
source.layerId = layerId;
source.isLabeling = true;
source.symbolLayerId = QgsSymbolLayerId( ruleKey, {} );
source.symbolLayerId = ruleKey;
maskSources.append( source );
}
}
@ -180,7 +180,7 @@ void QgsMaskingWidget::populate()
void QgsMaskingWidget::apply()
{
QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources = mMaskSourcesWidget->selection();
QSet<QgsSymbolLayerId> maskedSymbolLayers = mMaskTargetsWidget->selection();
QSet<QString> maskedSymbolLayers = mMaskTargetsWidget->selection();
QSet<QString> layersToRefresh;
@ -193,7 +193,7 @@ void QgsMaskingWidget::apply()
//
// First reset symbol layer masks
SymbolLayerVisitor maskSetter( [&]( const QgsSymbolLayer * sl, const QgsSymbolLayerId & slId )
SymbolLayerVisitor maskSetter( [&]( const QgsSymbolLayer * sl, const QString & slId )
{
if ( sl->layerType() == "MaskMarker" )
{
@ -212,7 +212,7 @@ void QgsMaskingWidget::apply()
if ( ! source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId == slId )
{
// ... then add the new masked symbol layers, if any
for ( const QgsSymbolLayerId &maskedId : maskedSymbolLayers )
for ( const QString &maskedId : maskedSymbolLayers )
{
newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
}
@ -249,9 +249,9 @@ void QgsMaskingWidget::apply()
{
// ... then add the new masked symbol layers, if any
if ( source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId.symbolKey() == labelProvider )
if ( source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId == labelProvider )
{
for ( const QgsSymbolLayerId &maskedId : maskedSymbolLayers )
for ( const QString &maskedId : maskedSymbolLayers )
{
newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
}
@ -303,7 +303,7 @@ void SymbolLayerVisitor::visitSymbol( const QgsSymbol *symbol, const QString &le
const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
mCallback( sl, QgsSymbolLayerId( mSymbolKey + leafIdentifier, indexPath ) );
mCallback( sl, sl->id() );
// recurse over sub symbols
const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();

View File

@ -84,7 +84,7 @@ class GUI_EXPORT QgsMaskingWidget: public QgsPanelWidget, private Ui::QgsMasking
class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
{
public:
typedef std::function<void( const QgsSymbolLayer *, const QgsSymbolLayerId & )> SymbolLayerCallback;
typedef std::function<void( const QgsSymbolLayer *, const QString & )> SymbolLayerCallback;
//! constructor
SymbolLayerVisitor( SymbolLayerCallback callback );

View File

@ -34,20 +34,9 @@ static void expandAll( QTreeWidgetItem *item )
item->setExpanded( true );
}
void printSymbolLayerId( const QgsSymbolLayerId &lid )
{
std::cout << lid.symbolKey().toLocal8Bit().constData() << "/";
QVector<int> path = lid.symbolLayerIndexPath();
for ( int i = 0; i < path.size(); i++ )
{
std::cout << path[i] << "/";
}
}
void printSymbolLayerRef( const QgsSymbolLayerReference &ref )
{
std::cout << ref.layerId().toLocal8Bit().constData() << "/";
printSymbolLayerId( ref.symbolLayerId() );
std::cout << ref.layerId().toLocal8Bit().constData() << "/" << ref.symbolLayerIdV2().toLocal8Bit().constData();
}
QgsMaskSourceSelectionWidget::QgsMaskSourceSelectionWidget( QWidget *parent )
@ -113,7 +102,7 @@ void QgsMaskSourceSelectionWidget::update()
if ( ( sl->layerType() == "MaskMarker" ) ||
( subSymbol && visitSymbol( slItem.get(), identifier, subSymbol, indexPath ) ) )
{
const QgsSymbolLayerReference ref( mLayer->id(), QgsSymbolLayerId( mCurrentIdentifier + identifier, indexPath ) );
const QgsSymbolLayerReference ref( mLayer->id(), sl->id() );
mItems[ref] = slItem.get();
rootItem->addChild( slItem.release() );
ret = true;
@ -180,7 +169,7 @@ void QgsMaskSourceSelectionWidget::update()
slItem->setFlags( slItem->flags() | Qt::ItemIsUserCheckable );
slItem->setCheckState( 0, Qt::Unchecked );
mLayerItem->addChild( slItem );
mItems[QgsSymbolLayerReference( "__labels__" + mLayer->id(), { currentRule, 0 } )] = slItem;
mItems[QgsSymbolLayerReference( "__labels__" + mLayer->id(), currentRule )] = slItem;
}
}
return true;
@ -237,7 +226,7 @@ QList<QgsMaskSourceSelectionWidget::MaskSource> QgsMaskSourceSelectionWidget::se
QgsMaskSourceSelectionWidget::MaskSource source;
source.isLabeling = ref.layerId().startsWith( "__labels__" );
source.layerId = source.isLabeling ? ref.layerId().mid( 10 ) : ref.layerId();
source.symbolLayerId = ref.symbolLayerId();
source.symbolLayerId = ref.symbolLayerIdV2();
sel.append( source );
}
}
@ -263,4 +252,3 @@ void QgsMaskSourceSelectionWidget::setSelection( const QList<QgsMaskSourceSelect
}
}
}

View File

@ -48,7 +48,7 @@ class GUI_EXPORT QgsMaskSourceSelectionWidget : public QWidget
bool isLabeling = false;
//! The symbol layer id
QgsSymbolLayerId symbolLayerId;
QString symbolLayerId;
};
//! constructor

View File

@ -49,7 +49,7 @@ void QgsSymbolLayerSelectionWidget::setLayer( const QgsVectorLayer *layer )
class TreeFillVisitor : public QgsStyleEntityVisitorInterface
{
public:
TreeFillVisitor( QTreeWidgetItem *layerItem, const QgsVectorLayer *layer, QHash<QgsSymbolLayerId, QTreeWidgetItem *> &items ):
TreeFillVisitor( QTreeWidgetItem *layerItem, const QgsVectorLayer *layer, QHash<QString, QTreeWidgetItem *> &items ):
mLayerItem( layerItem ), mLayer( layer ), mItems( items )
{}
@ -96,7 +96,7 @@ void QgsSymbolLayerSelectionWidget::setLayer( const QgsVectorLayer *layer )
rootItem->addChild( slItem );
slItem->setExpanded( true );
mItems[QgsSymbolLayerId( mCurrentIdentifier + identifier, indexPath )] = slItem;
mItems[sl->id()] = slItem;
if ( subSymbol )
{
@ -133,7 +133,7 @@ void QgsSymbolLayerSelectionWidget::setLayer( const QgsVectorLayer *layer )
QString mCurrentIdentifier;
QTreeWidgetItem *mLayerItem;
const QgsVectorLayer *mLayer;
QHash<QgsSymbolLayerId, QTreeWidgetItem *> &mItems;
QHash<QString, QTreeWidgetItem *> &mItems;
};
// populate the tree
@ -146,9 +146,9 @@ void QgsSymbolLayerSelectionWidget::setLayer( const QgsVectorLayer *layer )
mLayer->renderer()->accept( &visitor );
}
QSet<QgsSymbolLayerId> QgsSymbolLayerSelectionWidget::selection() const
QSet<QString> QgsSymbolLayerSelectionWidget::selection() const
{
QSet<QgsSymbolLayerId> sel;
QSet<QString> sel;
for ( auto it = mItems.begin(); it != mItems.end(); it++ )
{
if ( it.value()->checkState( 0 ) == Qt::Checked )
@ -157,7 +157,7 @@ QSet<QgsSymbolLayerId> QgsSymbolLayerSelectionWidget::selection() const
return sel;
}
void QgsSymbolLayerSelectionWidget::setSelection( const QSet<QgsSymbolLayerId> &sel )
void QgsSymbolLayerSelectionWidget::setSelection( const QSet<QString> &sel )
{
// clear selection
for ( auto it = mItems.begin(); it != mItems.end(); it++ )
@ -167,7 +167,7 @@ void QgsSymbolLayerSelectionWidget::setSelection( const QSet<QgsSymbolLayerId> &
}
// apply selection passed in parameter
for ( const QgsSymbolLayerId &lid : sel )
for ( const QString &lid : sel )
{
const auto it = mItems.find( lid );
if ( it != mItems.end() )

View File

@ -45,10 +45,10 @@ class GUI_EXPORT QgsSymbolLayerSelectionWidget : public QWidget
void setLayer( const QgsVectorLayer *layer );
//! Returns current symbol layer selection
QSet<QgsSymbolLayerId> selection() const;
QSet<QString> selection() const;
//! Sets the symbol layer selection
void setSelection( const QSet<QgsSymbolLayerId> &sel );
void setSelection( const QSet<QString> &sel );
signals:
//! Signal emitted when something the configuration is changed
@ -61,7 +61,7 @@ class GUI_EXPORT QgsSymbolLayerSelectionWidget : public QWidget
const QgsVectorLayer *mLayer = nullptr;
// Mapping between symbol layer id and tree elements
QHash<QgsSymbolLayerId, QTreeWidgetItem *> mItems;
QHash<QString, QTreeWidgetItem *> mItems;
};
#endif

View File

@ -133,6 +133,28 @@ class TestSelectiveMasking(unittest.TestCase):
with open(report_file_path, 'a') as report_file:
report_file.write(self.report)
def get_symbollayer_ref(self, layer, ruleId, symbollayer_ids):
"""
Returns the symbol layer according to given path to
"""
renderer = layer.renderer()
symbol = None
if renderer.type() == "categorizedSymbol":
i = renderer.categoryIndexForValue(ruleId)
cat = renderer.categories()[i]
symbol = cat.symbol()
elif renderer.type() == "singleSymbol":
symbol = renderer.symbol()
symbollayer = symbol.symbolLayer(symbollayer_ids[0])
for i in range(1, len(symbollayer_ids)):
symbol = symbollayer.subSymbol()
symbollayer = symbol.symbolLayer(symbollayer_ids[i])
return QgsSymbolLayerReference(layer.id(), symbollayer.id())
def check_renderings(self, map_settings, control_name):
"""Test a rendering with different configurations:
- parallel rendering, no cache
@ -159,6 +181,10 @@ class TestSelectiveMasking(unittest.TestCase):
suffix = ("_parallel" if do_parallel else "_sequential") + ("_cache" if use_cache else "_nocache")
res = self.checker.compareImages(control_name + suffix)
self.report += self.checker.report()
f = open("/tmp/merdier.html", "w")
f.write(self.report)
f.close()
self.assertTrue(res)
print(f"=== Rendering took {float(t) / 1000.0}s")
@ -216,6 +242,11 @@ class TestSelectiveMasking(unittest.TestCase):
self.checker.setRenderedImage(image_result_filename)
res = self.checker.compareImages(control_name)
self.report += self.checker.report()
f = open("/tmp/merdier.html", "w")
f.write(self.report)
f.close()
self.assertTrue(res)
def test_save_restore_references(self):
@ -226,7 +257,7 @@ class TestSelectiveMasking(unittest.TestCase):
# simple ids
mask_layer = QgsMaskMarkerSymbolLayer()
mask_layer.setMasks([
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
])
@ -235,7 +266,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
self.assertEqual(mask_layer2.masks(), [
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
])
@ -243,7 +274,7 @@ class TestSelectiveMasking(unittest.TestCase):
# complex ids
mask_layer = QgsMaskMarkerSymbolLayer()
mask_layer.setMasks([
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other id, like, this", [4, 5])),
])
@ -252,7 +283,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
self.assertEqual(mask_layer2.masks(), [
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other id, like, this", [4, 5])),
])
@ -260,7 +291,7 @@ class TestSelectiveMasking(unittest.TestCase):
# complex ids, v2
mask_layer = QgsMaskMarkerSymbolLayer()
mask_layer.setMasks([
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("a string; with bits", 0)),
self.get_symbollayer_ref(self.lines_layer, "a string; with bits", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
])
@ -269,7 +300,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
self.assertEqual(mask_layer2.masks(), [
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("a string; with bits", 0)),
self.get_symbollayer_ref(self.lines_layer, "a string; with bits", [0]),
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
])
@ -284,10 +315,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -307,10 +338,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_with_labels, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -327,7 +358,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# polygons
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.polys_layer, "", [0]),
])
label_settings.setFormat(fmt)
self.lines_with_labels.labeling().setSettings(label_settings)
@ -348,7 +379,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_with_labels, "", [0]),
])
label_settings.setFormat(fmt)
@ -366,7 +397,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_with_labels, "", [0]),
])
label_settings.setFormat(fmt)
self.lines_with_labels.labeling().setSettings(label_settings)
@ -390,10 +421,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# mask only vertical segments of "roads"
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("", [1, 0])),
self.get_symbollayer_ref(self.lines_layer2, "", [1, 0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -416,10 +447,10 @@ class TestSelectiveMasking(unittest.TestCase):
# mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
# overwrite with data-defined properties
fmt.dataDefinedProperties().setProperty(QgsPalLayerSettings.MaskEnabled, QgsProperty.fromExpression('1'))
@ -455,10 +486,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
child.setSettings(label_settings)
@ -475,7 +506,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the polygons
QgsSymbolLayerReference(self.polys_layer2.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.polys_layer2, "", [0]),
])
label_settings.setFormat(fmt)
child.setSettings(label_settings)
@ -495,10 +526,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -520,7 +551,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -540,7 +571,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -558,7 +589,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))
self.get_symbollayer_ref(self.lines_layer, "", [0])
])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -579,7 +610,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the yellow part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1)),
self.get_symbollayer_ref(self.lines_layer, "", [1]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -597,7 +628,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))
self.get_symbollayer_ref(self.lines_layer, "", [0])
])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -618,7 +649,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -636,7 +667,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the yellow part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1))
self.get_symbollayer_ref(self.lines_layer, "", [1])
])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -679,7 +710,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the yellow part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1)),
self.get_symbollayer_ref(self.lines_layer, "", [1]),
])
# add an outer glow effect to the mask layer
blur = QgsOuterGlowEffect.create({"enabled": "1",
@ -707,10 +738,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
# add an outer glow effect to the mask
blur = QgsOuterGlowEffect.create({"enabled": "1",
@ -748,7 +779,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))])
self.get_symbollayer_ref(self.lines_layer, "", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -763,6 +794,7 @@ class TestSelectiveMasking(unittest.TestCase):
def test_layout_export(self):
"""Test mask effects in a layout export at 300 dpi"""
# modify labeling settings
label_settings = self.polys_layer.labeling().settings()
fmt = label_settings.format()
@ -775,10 +807,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -799,10 +831,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
# add an outer glow effect to the mask
blur = QgsOuterGlowEffect.create({"enabled": "1",
@ -833,7 +865,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -851,7 +883,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add an outer glow effect to the mask
@ -892,10 +924,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -918,7 +950,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -939,7 +971,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
# add this mask layer to the point layer
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -963,10 +995,10 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
# the black jets
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])
self.get_symbollayer_ref(self.points_layer, "B52", [0]),
self.get_symbollayer_ref(self.points_layer, "Jet", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -989,7 +1021,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))])
self.get_symbollayer_ref(self.lines_layer, "", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)
@ -1055,7 +1087,7 @@ class TestSelectiveMasking(unittest.TestCase):
mask_layer.setSubSymbol(circle_symbol)
mask_layer.setMasks([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
self.get_symbollayer_ref(self.lines_layer, "", [0]),
])
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
@ -1069,7 +1101,7 @@ class TestSelectiveMasking(unittest.TestCase):
# and mask other symbol layers underneath
fmt.mask().setMaskedSymbolLayers([
# the black part of roads
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))])
self.get_symbollayer_ref(self.lines_layer, "", [0])])
label_settings.setFormat(fmt)
self.polys_layer.labeling().setSettings(label_settings)