QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsmaskingwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmaskingwidget.cpp
3 ---------------------
4 begin : September 2019
5 copyright : (C) 2019 by Hugo Mercier
6 email : hugo dot mercier at oslandia dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include "qgsmaskingwidget.h"
20
22#include "qgsmasksymbollayer.h"
23#include "qgsmessagebaritem.h"
24#include "qgsproject.h"
26#include "qgssymbol.h"
29#include "qgsvectorlayer.h"
31#include "qgsvectorlayerutils.h"
33
34#include <QCheckBox>
35#include <QSet>
36
37#include "moc_qgsmaskingwidget.cpp"
38
39QgsMaskingWidget::QgsMaskingWidget( QWidget *parent )
40 : QgsPanelWidget( parent )
41{
42 setupUi( this );
43
44 connect( mMaskTargetsWidget, &QgsSymbolLayerSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
45 connect( mMaskSourcesWidget, &QgsMaskSourceSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
46}
47
48void QgsMaskingWidget::onSelectionChanged()
49{
50 // display message if configuration is not consistent
51 const bool printMessage = mMaskTargetsWidget->selection().empty() != mMaskSourcesWidget->selection().empty();
52
53 if ( mMessageBarItem && !printMessage )
54 {
55 mMessageBar->popWidget( mMessageBarItem );
56 delete mMessageBarItem;
57 }
58 else if ( !mMessageBarItem && printMessage )
59 {
60 mMessageBarItem = new QgsMessageBarItem( tr( "Select both masking and masked symbol layers or your configuration will be lost" ), Qgis::MessageLevel::Warning, 0, this );
61 mMessageBar->pushItem( mMessageBarItem );
62 }
63
64 emit widgetChanged();
65}
66
76QList<QPair<QString, QList<QgsSymbolLayerReference>>> symbolLayerMasks( const QgsVectorLayer *layer )
77{
78 if ( !layer->renderer() )
79 return {};
80
81 QList<QPair<QString, QList<QgsSymbolLayerReference>>> mMasks;
82 SymbolLayerVisitor collector( [&]( const QgsSymbolLayer *sl, const QString &lid ) {
83 if ( !sl->masks().isEmpty() )
84 mMasks.push_back( qMakePair( lid, sl->masks() ) );
85 } );
86 layer->renderer()->accept( &collector );
87 return mMasks;
88}
89
90void QgsMaskingWidget::setLayer( QgsVectorLayer *layer )
91{
92 mLayer = layer;
93 populate();
94}
95
96void QgsMaskingWidget::populate()
97{
98 const QSignalBlocker blockerSourceWidget( mMaskSourcesWidget );
99 const QSignalBlocker blockerTargetWidget( mMaskTargetsWidget );
100
101 mMaskSourcesWidget->update();
102 mMaskTargetsWidget->setLayer( mLayer );
103
104 // collect masks and filter on those which have the current layer as destination
105 QSet<QString> maskedSymbolLayers;
106 QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources;
107 QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
108
110 for ( auto layerIt = layers.begin(); layerIt != layers.end(); layerIt++ )
111 {
112 const QString layerId = layerIt.key();
113 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
114 if ( !vl )
115 continue;
116
117 // collect symbol layer masks
118 const QList<QPair<QString, QList<QgsSymbolLayerReference>>> slMasks = symbolLayerMasks( vl );
119 for ( const QPair<QString, QList<QgsSymbolLayerReference>> &p : slMasks )
120 {
121 const QString &sourceSymbolLayerId = p.first;
122 for ( const QgsSymbolLayerReference &ref : p.second )
123 {
124 if ( ref.layerId() == mLayer->id() )
125 {
126 // add to the set of destinations
127 maskedSymbolLayers.insert( ref.symbolLayerIdV2() );
128 // add to the list of mask sources
129 source.layerId = layerId;
130 source.isLabeling = false;
131 source.symbolLayerId = sourceSymbolLayerId;
132 maskSources.append( source );
133 }
134 }
135 }
136
137 // collect label masks
138 QHash<QString, QgsMaskedLayers> labelMasks = QgsVectorLayerUtils::labelMasks( vl );
139 for ( auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
140 {
141 const QString &ruleKey = it.key();
142 for ( auto it2 = it.value().begin(); it2 != it.value().end(); it2++ )
143 {
144 if ( it2.key() == mLayer->id() )
145 {
146 // merge with masked symbol layers
147 maskedSymbolLayers.unite( it2.value().symbolLayerIds );
148 // add the mask source
149 source.layerId = layerId;
150 source.isLabeling = true;
151 source.symbolLayerId = ruleKey;
152 maskSources.append( source );
153 }
154 }
155 }
156 }
157
158 mMaskSourcesWidget->setSelection( maskSources );
159 mMaskTargetsWidget->setSelection( maskedSymbolLayers );
160}
161
162void QgsMaskingWidget::apply()
163{
164 QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources = mMaskSourcesWidget->selection();
165 QSet<QString> maskedSymbolLayers = mMaskTargetsWidget->selection();
166
167 QSet<QString> layersToRefresh;
168
169 QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
170 for ( auto layerIt = layers.begin(); layerIt != layers.end(); layerIt++ )
171 {
172 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
173 if ( !vl )
174 continue;
175
176 //
177 // First reset symbol layer masks
178 SymbolLayerVisitor maskSetter( [&]( const QgsSymbolLayer *sl, const QString &slId ) {
179 if ( sl->layerType() == "MaskMarker" )
180 {
181 QgsMaskMarkerSymbolLayer *maskSl = const_cast<QgsMaskMarkerSymbolLayer *>( static_cast<const QgsMaskMarkerSymbolLayer *>( sl ) );
182
183 const QgsSymbolLayerReferenceList masks = maskSl->masks();
185 for ( const QgsSymbolLayerReference &ref : masks )
186 {
187 // copy the original masks, only those with another destination layer
188 if ( ref.layerId() != mLayer->id() )
189 newMasks.append( ref );
190 }
191 for ( const QgsMaskSourceSelectionWidget::MaskSource &source : maskSources )
192 {
193 if ( !source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId == slId )
194 {
195 // ... then add the new masked symbol layers, if any
196 for ( const QString &maskedId : maskedSymbolLayers )
197 {
198 newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
199 }
200 // invalidate the cache of the source layer
201 layersToRefresh.insert( source.layerId );
202 }
203 }
204 maskSl->setMasks( newMasks );
205 }
206 } );
207 if ( vl->renderer() )
208 vl->renderer()->accept( &maskSetter );
209
210 //
211 // Now reset label masks
212 if ( !vl->labeling() )
213 continue;
214 for ( const QString &labelProvider : vl->labeling()->subProviders() )
215 {
216 // clear symbol layers
217 QgsPalLayerSettings settings = vl->labeling()->settings( labelProvider );
218 QgsTextFormat format = settings.format();
219 if ( !format.mask().enabled() )
220 continue;
221 const QgsSymbolLayerReferenceList masks = format.mask().maskedSymbolLayers();
223 for ( const QgsSymbolLayerReference &ref : masks )
224 {
225 // copy the original masks, only those with another destination layer
226 if ( ref.layerId() != mLayer->id() )
227 newMasks.append( ref );
228 }
229 for ( const QgsMaskSourceSelectionWidget::MaskSource &source : maskSources )
230 {
231 // ... then add the new masked symbol layers, if any
232
233 if ( source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId == labelProvider )
234 {
235 for ( const QString &maskedId : maskedSymbolLayers )
236 {
237 newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
238 }
239 // invalidate the cache of the source layer
240 layersToRefresh.insert( source.layerId );
241 }
242 }
243 format.mask().setMaskedSymbolLayers( newMasks );
244 settings.setFormat( format );
245 vl->labeling()->setSettings( new QgsPalLayerSettings( settings ), labelProvider );
246 }
247 }
248
250 // trigger refresh of the current layer
251 mLayer->triggerRepaint();
252 // trigger refresh of dependent layers (i.e. mask source layers)
253 for ( const QString &layerId : layersToRefresh )
254 {
255 QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId );
256 layer->triggerRepaint();
257 }
258}
259
260SymbolLayerVisitor::SymbolLayerVisitor( SymbolLayerVisitor::SymbolLayerCallback callback )
261 : mCallback( std::move( callback ) )
262{}
263
264bool SymbolLayerVisitor::visitEnter( const QgsStyleEntityVisitorInterface::Node &node )
265{
267 return false;
268
269 return true;
270}
271
272void SymbolLayerVisitor::visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier )
273{
274 for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
275 {
276 const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
277
278 mCallback( sl, sl->id() );
279
280 // recurse over sub symbols
281 const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
282 if ( subSymbol )
283 visitSymbol( subSymbol, leafIdentifier );
284 }
285}
286
287bool SymbolLayerVisitor::visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf )
288{
289 if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
290 {
291 auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
292 if ( symbolEntity->symbol() )
293 visitSymbol( symbolEntity->symbol(), leaf.identifier );
294 }
295 return true;
296}
297
@ Warning
Warning message.
Definition qgis.h:158
virtual QStringList subProviders() const
Gets list of sub-providers within the layer's labeling.
virtual void setSettings(QgsPalLayerSettings *settings, const QString &providerId=QString())=0
Set pal settings for a specific provider (takes ownership).
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
Base class for all map layer types.
Definition qgsmaplayer.h:80
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
Special symbol layer that uses its sub symbol as a selective mask.
void setMasks(const QList< QgsSymbolLayerReference > &maskedLayers)
Sets the symbol layers that will be masked by the sub symbol's shape.
QList< QgsSymbolLayerReference > masks() const override
Returns a list of references to symbol layers that are masked by the sub symbol's shape.
void changed()
Emitted when an item was changed.
Represents an item shown within a QgsMessageBar widget.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
Base class for any widget that can be shown as an inline panel.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void setDirty(bool b=true)
Flag the project as dirty (modified).
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
virtual QgsStyle::StyleEntity type() const =0
Returns the type of style entity.
@ SymbolRule
Rule based symbology or label child rule.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1397
@ SymbolEntity
Symbols.
Definition qgsstyle.h:205
Type used to refer to a specific symbol layer in a symbol of a layer.
QString symbolLayerIdV2() const
The symbol layer's id.
QString layerId() const
The referenced vector layer / feature renderer.
void changed()
Signal emitted when something the configuration is changed.
Abstract base class for symbol layers.
virtual QString layerType() const =0
Returns a string that represents this layer type.
QString id() const
Returns symbol layer identifier This id is unique in the whole project.
virtual QList< QgsSymbolLayerReference > masks() const
Returns masks defined by this symbol layer.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:353
Container for all settings relating to text rendering.
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
void setMaskedSymbolLayers(const QList< QgsSymbolLayerReference > &maskedLayers)
Sets the symbol layers that will be masked by this buffer.
QList< QgsSymbolLayerReference > maskedSymbolLayers() const
Returns a list of references to symbol layers that are masked by this buffer.
bool enabled() const
Returns whether the mask is enabled.
static QHash< QString, QgsMaskedLayers > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
Represents a vector layer which manages a vector based dataset.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QList< QgsSymbolLayerReference > QgsSymbolLayerReferenceList
bool isLabeling
Whether it is a labeling mask or not.
Contains information relating to a node (i.e.
QgsStyleEntityVisitorInterface::NodeType type
Node type.
Contains information relating to the style entity currently being visited.
const QgsStyleEntityInterface * entity
Reference to style entity being visited.
QString identifier
A string identifying the style entity.