diff --git a/python/PyQt6/core/auto_additions/qgssymbollayerutils.py b/python/PyQt6/core/auto_additions/qgssymbollayerutils.py index de49d5e3840c..58e412ae6838 100644 --- a/python/PyQt6/core/auto_additions/qgssymbollayerutils.py +++ b/python/PyQt6/core/auto_additions/qgssymbollayerutils.py @@ -161,6 +161,7 @@ QgsSymbolLayerUtils.tileSize = staticmethod(QgsSymbolLayerUtils.tileSize) QgsSymbolLayerUtils.clearSymbolLayerIds = staticmethod(QgsSymbolLayerUtils.clearSymbolLayerIds) QgsSymbolLayerUtils.resetSymbolLayerIds = staticmethod(QgsSymbolLayerUtils.resetSymbolLayerIds) + QgsSymbolLayerUtils.clearSymbolLayerMasks = staticmethod(QgsSymbolLayerUtils.clearSymbolLayerMasks) QgsSymbolLayerUtils.collectSymbolLayerClipGeometries = staticmethod(QgsSymbolLayerUtils.collectSymbolLayerClipGeometries) QgsSymbolLayerUtils.__group__ = ['symbology'] except (NameError, AttributeError): diff --git a/python/PyQt6/core/auto_generated/symbology/qgsmasksymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgsmasksymbollayer.sip.in index 03583d69ad2c..ebf042501778 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgsmasksymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgsmasksymbollayer.sip.in @@ -87,6 +87,9 @@ Returns a list of references to symbol layers that are masked by the sub symbol' .. seealso:: :py:func:`setMasks` %End + virtual void clearMasks(); + + void setMasks( const QList &maskedLayers ); %Docstring Sets the symbol layers that will be masked by the sub symbol's shape. @@ -100,8 +103,6 @@ Sets the symbol layers that will be masked by the sub symbol's shape. QgsMaskMarkerSymbolLayer( const QgsMaskMarkerSymbolLayer & ); }; - - /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in index 1fc8cb3e9da4..7e4a82517e98 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in @@ -639,6 +639,15 @@ Returns masks defined by this symbol layer. This is a list of symbol layers of other layers that should be occluded. .. versionadded:: 3.12 +%End + + virtual void clearMasks(); +%Docstring +Remove masks defined by this symbol layer. + +.. seealso:: :py:func:`masks` + +.. versionadded:: 3.42 %End virtual void prepareMasks( const QgsSymbolRenderContext &context ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in b/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in index 4e60a056d2dc..5dfda106322a 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in @@ -1020,6 +1020,13 @@ Regenerate recursively unique id from all ``symbol`` symbol layers Regenerate recursively unique id from ``symbolLayer`` and its children .. versionadded:: 3.30 +%End + + static void clearSymbolLayerMasks( QgsSymbol *symbol ); +%Docstring +Remove recursively masks from all ``symbol`` symbol layers + +.. versionadded:: 3.42 %End static QVector< QgsGeometry > collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ); diff --git a/python/core/auto_additions/qgssymbollayerutils.py b/python/core/auto_additions/qgssymbollayerutils.py index de49d5e3840c..58e412ae6838 100644 --- a/python/core/auto_additions/qgssymbollayerutils.py +++ b/python/core/auto_additions/qgssymbollayerutils.py @@ -161,6 +161,7 @@ QgsSymbolLayerUtils.tileSize = staticmethod(QgsSymbolLayerUtils.tileSize) QgsSymbolLayerUtils.clearSymbolLayerIds = staticmethod(QgsSymbolLayerUtils.clearSymbolLayerIds) QgsSymbolLayerUtils.resetSymbolLayerIds = staticmethod(QgsSymbolLayerUtils.resetSymbolLayerIds) + QgsSymbolLayerUtils.clearSymbolLayerMasks = staticmethod(QgsSymbolLayerUtils.clearSymbolLayerMasks) QgsSymbolLayerUtils.collectSymbolLayerClipGeometries = staticmethod(QgsSymbolLayerUtils.collectSymbolLayerClipGeometries) QgsSymbolLayerUtils.__group__ = ['symbology'] except (NameError, AttributeError): diff --git a/python/core/auto_generated/symbology/qgsmasksymbollayer.sip.in b/python/core/auto_generated/symbology/qgsmasksymbollayer.sip.in index 03583d69ad2c..ebf042501778 100644 --- a/python/core/auto_generated/symbology/qgsmasksymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgsmasksymbollayer.sip.in @@ -87,6 +87,9 @@ Returns a list of references to symbol layers that are masked by the sub symbol' .. seealso:: :py:func:`setMasks` %End + virtual void clearMasks(); + + void setMasks( const QList &maskedLayers ); %Docstring Sets the symbol layers that will be masked by the sub symbol's shape. @@ -100,8 +103,6 @@ Sets the symbol layers that will be masked by the sub symbol's shape. QgsMaskMarkerSymbolLayer( const QgsMaskMarkerSymbolLayer & ); }; - - /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/core/auto_generated/symbology/qgssymbollayer.sip.in b/python/core/auto_generated/symbology/qgssymbollayer.sip.in index d4af3ce22a91..9009a1184efb 100644 --- a/python/core/auto_generated/symbology/qgssymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgssymbollayer.sip.in @@ -639,6 +639,15 @@ Returns masks defined by this symbol layer. This is a list of symbol layers of other layers that should be occluded. .. versionadded:: 3.12 +%End + + virtual void clearMasks(); +%Docstring +Remove masks defined by this symbol layer. + +.. seealso:: :py:func:`masks` + +.. versionadded:: 3.42 %End virtual void prepareMasks( const QgsSymbolRenderContext &context ); diff --git a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in index 4e60a056d2dc..5dfda106322a 100644 --- a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in +++ b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in @@ -1020,6 +1020,13 @@ Regenerate recursively unique id from all ``symbol`` symbol layers Regenerate recursively unique id from ``symbolLayer`` and its children .. versionadded:: 3.30 +%End + + static void clearSymbolLayerMasks( QgsSymbol *symbol ); +%Docstring +Remove recursively masks from all ``symbol`` symbol layers + +.. versionadded:: 3.42 %End static QVector< QgsGeometry > collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ); diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 44681556e85a..aa8495d5614f 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -55,10 +55,10 @@ #include "qgsmapcanvasutils.h" #include "qgsmaplayeraction.h" #include "qgsvectortilelayer.h" -#include "qgsvectortiledataprovider.h" #include "qgsproviderregistry.h" #include "qgsprovidermetadata.h" #include "qgsrasterlabeling.h" +#include "qgssymbollayerutils.h" QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas ) : mView( view ) @@ -1185,6 +1185,7 @@ void QgsAppLayerTreeViewMenuProvider::pasteVectorSymbol( const QString &layerId originalSymbol = embeddedRenderer->symbol(); } std::unique_ptr tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) ); + QgsSymbolLayerUtils::resetSymbolLayerIds( tempSymbol.get() ); if ( !tempSymbol ) return; @@ -1312,6 +1313,7 @@ void QgsAppLayerTreeViewMenuProvider::pasteSymbolLegendNodeSymbol( const QString QgsVectorLayer *vlayer = qobject_cast( node->layerNode()->layer() ); std::unique_ptr tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) ); + QgsSymbolLayerUtils::resetSymbolLayerIds( tempSymbol.get() ); if ( tempSymbol && tempSymbol->type() == originalSymbol->type() ) { node->setSymbol( tempSymbol.release() ); diff --git a/src/core/symbology/qgscategorizedsymbolrenderer.cpp b/src/core/symbology/qgscategorizedsymbolrenderer.cpp index daa303306ad2..65e66f66548d 100644 --- a/src/core/symbology/qgscategorizedsymbolrenderer.cpp +++ b/src/core/symbology/qgscategorizedsymbolrenderer.cpp @@ -1445,7 +1445,10 @@ QgsCategorizedSymbolRenderer *QgsCategorizedSymbolRenderer::convertFromRenderer( QgsSymbolList symbols = const_cast( renderer )->symbols( context ); if ( !symbols.isEmpty() ) { - r->setSourceSymbol( symbols.at( 0 )->clone() ); + QgsSymbol *newSymbol = symbols.at( 0 )->clone(); + QgsSymbolLayerUtils::resetSymbolLayerIds( newSymbol ); + QgsSymbolLayerUtils::clearSymbolLayerMasks( newSymbol ); + r->setSourceSymbol( newSymbol ); } } @@ -1539,6 +1542,7 @@ QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QListclone(); + QgsSymbolLayerUtils::resetSymbolLayerIds( newSymbol ); if ( !QgsVariantUtils::isNull( value ) ) { const int fieldIdx = fields.lookupField( attributeName ); @@ -1557,6 +1561,7 @@ QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QListclone(); + QgsSymbolLayerUtils::resetSymbolLayerIds( newSymbol ); cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) ); return cats; diff --git a/src/core/symbology/qgsmasksymbollayer.cpp b/src/core/symbology/qgsmasksymbollayer.cpp index 416a74d13974..8b2227824918 100644 --- a/src/core/symbology/qgsmasksymbollayer.cpp +++ b/src/core/symbology/qgsmasksymbollayer.cpp @@ -136,6 +136,11 @@ void QgsMaskMarkerSymbolLayer::setMasks( const QList &m mMaskedSymbolLayers = maskedLayers; } +void QgsMaskMarkerSymbolLayer::clearMasks() +{ + mMaskedSymbolLayers.clear(); +} + QRectF QgsMaskMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context ) { return mSymbol->bounds( point, context.renderContext() ); diff --git a/src/core/symbology/qgsmasksymbollayer.h b/src/core/symbology/qgsmasksymbollayer.h index c3868fe59e4b..a1ecbe1b5e7c 100644 --- a/src/core/symbology/qgsmasksymbollayer.h +++ b/src/core/symbology/qgsmasksymbollayer.h @@ -79,6 +79,8 @@ class CORE_EXPORT QgsMaskMarkerSymbolLayer : public QgsMarkerSymbolLayer */ QList masks() const override; + void clearMasks() override; + /** * Sets the symbol layers that will be masked by the sub symbol's shape. * \param maskedLayers list of references to symbol layers @@ -101,5 +103,3 @@ class CORE_EXPORT QgsMaskMarkerSymbolLayer : public QgsMarkerSymbolLayer }; #endif - - diff --git a/src/core/symbology/qgssymbollayer.cpp b/src/core/symbology/qgssymbollayer.cpp index e7317a6873e4..61f770dd92c4 100644 --- a/src/core/symbology/qgssymbollayer.cpp +++ b/src/core/symbology/qgssymbollayer.cpp @@ -953,6 +953,10 @@ QList QgsSymbolLayer::masks() const return {}; } +void QgsSymbolLayer::clearMasks() +{ +} + double QgsMarkerSymbolLayer::dxfSize( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const { double size = mSize; diff --git a/src/core/symbology/qgssymbollayer.h b/src/core/symbology/qgssymbollayer.h index 137570d7989c..0247743fc217 100644 --- a/src/core/symbology/qgssymbollayer.h +++ b/src/core/symbology/qgssymbollayer.h @@ -630,6 +630,13 @@ class CORE_EXPORT QgsSymbolLayer */ virtual QList masks() const; + /** + * Remove masks defined by this symbol layer. + * \see masks() + * \since QGIS 3.42 + */ + virtual void clearMasks(); + /** * Prepares all mask internal objects according to what is defined in \a context * This should be called prior to calling startRender() method. diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index c2b5ed72e94c..49693ec52a5a 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -5605,6 +5605,26 @@ void QgsSymbolLayerUtils::resetSymbolLayerIds( QgsSymbolLayer *symbolLayer ) changeSymbolLayerIds( symbolLayer, []() { return QUuid::createUuid().toString(); } ); } +void QgsSymbolLayerUtils::clearSymbolLayerMasks( QgsSymbol *symbol ) +{ + if ( !symbol ) + return; + + for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ ) + { + if ( QgsSymbolLayer *sl = symbol->symbolLayer( idx ) ) + { + sl->clearMasks(); + + // recurse over sub symbols + if ( QgsSymbol *subSymbol = sl->subSymbol() ) + { + clearSymbolLayerMasks( subSymbol ); + } + } + } +} + QVector QgsSymbolLayerUtils::collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ) { QVector clipGeometries = context.symbolLayerClipGeometries( symbolLayerId ); diff --git a/src/core/symbology/qgssymbollayerutils.h b/src/core/symbology/qgssymbollayerutils.h index 233f56ed7993..c1a3221209ba 100644 --- a/src/core/symbology/qgssymbollayerutils.h +++ b/src/core/symbology/qgssymbollayerutils.h @@ -949,6 +949,12 @@ class CORE_EXPORT QgsSymbolLayerUtils */ static void resetSymbolLayerIds( QgsSymbolLayer *symbolLayer ); + /** + * Remove recursively masks from all \a symbol symbol layers + * \since QGIS 3.42 + */ + static void clearSymbolLayerMasks( QgsSymbol *symbol ); + /** * Returns a list of the symbol layer clip geometries to be used for the symbol layer with the specified * ID. diff --git a/src/gui/qgsmaskingwidget.cpp b/src/gui/qgsmaskingwidget.cpp index 86e353d20401..aa42c2dc4715 100644 --- a/src/gui/qgsmaskingwidget.cpp +++ b/src/gui/qgsmaskingwidget.cpp @@ -264,17 +264,13 @@ bool SymbolLayerVisitor::visitEnter( const QgsStyleEntityVisitorInterface::Node if ( node.type != QgsStyleEntityVisitorInterface::NodeType::SymbolRule ) return false; - mSymbolKey = node.identifier; return true; } -void SymbolLayerVisitor::visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier, QVector rootPath ) +void SymbolLayerVisitor::visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier ) { for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ ) { - QVector indexPath = rootPath; - indexPath.push_back( idx ); - const QgsSymbolLayer *sl = symbol->symbolLayer( idx ); mCallback( sl, sl->id() ); @@ -282,7 +278,7 @@ void SymbolLayerVisitor::visitSymbol( const QgsSymbol *symbol, const QString &le // recurse over sub symbols const QgsSymbol *subSymbol = const_cast( sl )->subSymbol(); if ( subSymbol ) - visitSymbol( subSymbol, leafIdentifier, indexPath ); + visitSymbol( subSymbol, leafIdentifier ); } } @@ -292,7 +288,7 @@ bool SymbolLayerVisitor::visit( const QgsStyleEntityVisitorInterface::StyleLeaf { auto symbolEntity = static_cast( leaf.entity ); if ( symbolEntity->symbol() ) - visitSymbol( symbolEntity->symbol(), leaf.identifier, {} ); + visitSymbol( symbolEntity->symbol(), leaf.identifier ); } return true; } diff --git a/src/gui/qgsmaskingwidget.h b/src/gui/qgsmaskingwidget.h index 019ed9b167cf..245f311fbb7c 100644 --- a/src/gui/qgsmaskingwidget.h +++ b/src/gui/qgsmaskingwidget.h @@ -86,13 +86,11 @@ class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override; //! Process a symbol - void visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier, QVector rootPath ); + void visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier ); bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override; private: - QString mSymbolKey; - QList>> mMasks; SymbolLayerCallback mCallback; }; diff --git a/tests/src/python/test_qgscategorizedsymbolrenderer.py b/tests/src/python/test_qgscategorizedsymbolrenderer.py index 3cb84fd7b5a1..b66c2097439b 100644 --- a/tests/src/python/test_qgscategorizedsymbolrenderer.py +++ b/tests/src/python/test_qgscategorizedsymbolrenderer.py @@ -29,6 +29,7 @@ QgsLineSymbol, QgsMapSettings, QgsMarkerSymbol, + QgsMaskMarkerSymbolLayer, QgsProject, QgsProperty, QgsReadWriteContext, @@ -36,9 +37,11 @@ QgsRenderContext, QgsRendererCategory, QgsSimpleMarkerSymbolLayer, + QgsSingleSymbolRenderer, QgsStyle, QgsSymbol, QgsSymbolLayer, + QgsSymbolLayerReference, QgsVectorLayer, ) import unittest @@ -815,6 +818,36 @@ def testConvertFromEmbedded(self): self.assertEqual(cc.label(), "") self.assertEqual(cc.symbol().color().name(), "#ff00ff") + def testConvertNoMasks(self): + """ + Test converting an embedded symbol renderer to a categorized renderer + """ + points_layer = QgsVectorLayer("Point", "Polys", "memory") + f = QgsFeature() + f.setGeometry(QgsGeometry.fromWkt("Point(-100 30)")) + self.assertTrue(points_layer.dataProvider().addFeature(f)) + f.setGeometry(QgsGeometry.fromWkt("Point(-110 40)")) + self.assertTrue(points_layer.dataProvider().addFeature(f)) + f.setGeometry(QgsGeometry.fromWkt("Point(-90 50)")) + self.assertTrue(points_layer.dataProvider().addFeature(f)) + + p = QgsMarkerSymbol.createSimple({"color": "#fdbf6f", "size": "7"}) + points_layer.setRenderer(QgsSingleSymbolRenderer(p)) + + circle_symbol = QgsMarkerSymbol.createSimple({"size": "10"}) + mask_layer = QgsMaskMarkerSymbolLayer() + mask_layer.setSubSymbol(circle_symbol) + mask_layer.setMasks( + [QgsSymbolLayerReference("test_layer_id", "test_symbollayer_id")] + ) + points_layer.renderer().symbol().appendSymbolLayer(mask_layer) + + categorized = QgsCategorizedSymbolRenderer.convertFromRenderer( + points_layer.renderer(), points_layer + ) + + self.assertFalse(categorized.sourceSymbol().symbolLayers()[1].masks()) + def test_displayString(self): """Test the displayString method"""