QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 #include <QSet>
17 #include <QCheckBox>
18 
19 #include "qgsmaskingwidget.h"
23 #include "qgsvectorlayer.h"
24 #include "qgssymbol.h"
25 #include "qgsstyleentityvisitor.h"
26 #include "symbology/qgsrenderer.h"
27 #include "qgsproject.h"
28 #include "qgsvectorlayerutils.h"
29 #include "qgsmasksymbollayer.h"
30 #include "qgsvectorlayerlabeling.h"
31 #include "qgsmessagebaritem.h"
32 
34  QgsPanelWidget( parent )
35 {
36  setupUi( this );
37 }
38 
39 void QgsMaskingWidget::onSelectionChanged()
40 {
41  // display message if configuration is not consistent
42  bool printMessage = mMaskTargetsWidget->selection().empty() != mMaskSourcesWidget->selection().empty();
43 
44  if ( mMessageBarItem && !printMessage )
45  {
46  mMessageBar->popWidget( mMessageBarItem );
47  delete mMessageBarItem;
48  }
49  else if ( !mMessageBarItem && printMessage )
50  {
51  mMessageBarItem = new QgsMessageBarItem( tr( "Select both sources and symbol layers or your configuration will be lost" ), Qgis::Warning, 0, this );
52  mMessageBar->pushItem( mMessageBarItem );
53  }
54 
55  emit widgetChanged();
56 }
57 
58 void QgsMaskingWidget::showEvent( QShowEvent *event )
59 {
60  Q_UNUSED( event );
61 
62  // populate is quite long, so we delay it when the widget is first shown
63  if ( mMustPopulate )
64  {
65  disconnect( mMaskTargetsWidget, &QgsSymbolLayerSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
66  disconnect( mMaskSourcesWidget, &QgsMaskSourceSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
67 
68  mMustPopulate = false;
69  populate();
70 
71  connect( mMaskTargetsWidget, &QgsSymbolLayerSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
72  connect( mMaskSourcesWidget, &QgsMaskSourceSelectionWidget::changed, this, &QgsMaskingWidget::onSelectionChanged );
73 
74  onSelectionChanged();
75  }
76 }
77 
87 {
88  public:
89  typedef std::function<void( const QgsSymbolLayer *, const QgsSymbolLayerId & )> SymbolLayerCallback;
90 
93  mCallback( callback )
94  {}
95 
96  bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
97  {
99  return false;
100 
101  mSymbolKey = node.identifier;
102  return true;
103  }
104 
106  void visitSymbol( const QgsSymbol *symbol, const QString &leafIdentifier, QVector<int> rootPath )
107  {
108  for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
109  {
110  QVector<int> indexPath = rootPath;
111  indexPath.push_back( idx );
112 
113  const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
114 
115  mCallback( sl, QgsSymbolLayerId( mSymbolKey + leafIdentifier, indexPath ) );
116 
117  // recurse over sub symbols
118  const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
119  if ( subSymbol )
120  visitSymbol( subSymbol, leafIdentifier, indexPath );
121  }
122  }
123 
125  {
126  if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
127  {
128  auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
129  if ( symbolEntity->symbol() )
130  visitSymbol( symbolEntity->symbol(), leaf.identifier, {} );
131  }
132  return true;
133  }
134 
135  private:
136  QString mSymbolKey;
137  QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> mMasks;
138  SymbolLayerCallback mCallback;
139 };
140 
150 QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> symbolLayerMasks( const QgsVectorLayer *layer )
151 {
152  if ( ! layer->renderer() )
153  return {};
154 
155  QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> mMasks;
156  SymbolLayerVisitor collector( [&]( const QgsSymbolLayer * sl, const QgsSymbolLayerId & lid )
157  {
158  if ( ! sl->masks().isEmpty() )
159  mMasks.push_back( qMakePair( lid, sl->masks() ) );
160  } );
161  layer->renderer()->accept( &collector );
162  return mMasks;
163 }
164 
166 {
167  if ( mLayer != layer )
168  {
169  mLayer = layer;
170  mMustPopulate = true;
171  }
172 }
173 
174 void QgsMaskingWidget::populate()
175 {
176  mMaskSourcesWidget->update();
177  mMaskTargetsWidget->setLayer( mLayer );
178 
179  // collect masks and filter on those which have the current layer as destination
180  QSet<QgsSymbolLayerId> maskedSymbolLayers;
181  QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources;
182  QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
183 
185  for ( auto layerIt = layers.begin(); layerIt != layers.end(); layerIt++ )
186  {
187  QString layerId = layerIt.key();
188  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
189  if ( ! vl )
190  continue;
191 
192  // collect symbol layer masks
193  QList<QPair<QgsSymbolLayerId, QList<QgsSymbolLayerReference>>> slMasks = symbolLayerMasks( vl );
194  for ( auto p : slMasks )
195  {
196  const QgsSymbolLayerId &sourceSymbolLayerId = p.first;
197  for ( const QgsSymbolLayerReference &ref : p.second )
198  {
199  if ( ref.layerId() == mLayer->id() )
200  {
201  // add to the set of destinations
202  maskedSymbolLayers.insert( ref.symbolLayerId() );
203  // add to the list of mask sources
204  source.layerId = layerId;
205  source.isLabeling = false;
206  source.symbolLayerId = sourceSymbolLayerId;
207  maskSources.append( source );
208  }
209  }
210  }
211 
212  // collect label masks
213  QHash<QString, QHash<QString, QSet<QgsSymbolLayerId>>> labelMasks = QgsVectorLayerUtils::labelMasks( vl );
214  for ( auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
215  {
216  const QString &ruleKey = it.key();
217  for ( auto it2 = it.value().begin(); it2 != it.value().end(); it2++ )
218  {
219  if ( it2.key() == mLayer->id() )
220  {
221  // merge with masked symbol layers
222  maskedSymbolLayers.unite( it2.value() );
223  // add the mask source
224  source.layerId = layerId;
225  source.isLabeling = true;
226  source.symbolLayerId = QgsSymbolLayerId( ruleKey, {} );
227  maskSources.append( source );
228  }
229  }
230  }
231  }
232 
233  mMaskSourcesWidget->setSelection( maskSources );
234  mMaskTargetsWidget->setSelection( maskedSymbolLayers );
235 }
236 
238 {
239  QList<QgsMaskSourceSelectionWidget::MaskSource> maskSources = mMaskSourcesWidget->selection();
240  QSet<QgsSymbolLayerId> maskedSymbolLayers = mMaskTargetsWidget->selection();
241 
242  QSet<QString> layersToRefresh;
243 
244  QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
245  for ( auto layerIt = layers.begin(); layerIt != layers.end(); layerIt++ )
246  {
247  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
248  if ( ! vl )
249  continue;
250 
251  //
252  // First reset symbol layer masks
253  SymbolLayerVisitor maskSetter( [&]( const QgsSymbolLayer * sl, const QgsSymbolLayerId & slId )
254  {
255  if ( sl->layerType() == "MaskMarker" )
256  {
257  QgsMaskMarkerSymbolLayer *maskSl = const_cast<QgsMaskMarkerSymbolLayer *>( static_cast<const QgsMaskMarkerSymbolLayer *>( sl ) );
258 
259  QgsSymbolLayerReferenceList masks = maskSl->masks();
260  QgsSymbolLayerReferenceList newMasks;
261  for ( const QgsSymbolLayerReference &ref : masks )
262  {
263  // copy the original masks, only those with another destination layer
264  if ( ref.layerId() != mLayer->id() )
265  newMasks.append( ref );
266  }
267  for ( const QgsMaskSourceSelectionWidget::MaskSource &source : maskSources )
268  {
269  if ( ! source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId == slId )
270  {
271  // ... then add the new masked symbol layers, if any
272  for ( const QgsSymbolLayerId &maskedId : maskedSymbolLayers )
273  {
274  newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
275  }
276  // invalidate the cache of the source layer
277  layersToRefresh.insert( source.layerId );
278  }
279  }
280  maskSl->setMasks( newMasks );
281  }
282  } );
283  if ( vl->renderer() )
284  vl->renderer()->accept( &maskSetter );
285 
286  //
287  // Now reset label masks
288  if ( ! vl->labeling() )
289  continue;
290  for ( QString labelProvider : vl->labeling()->subProviders() )
291  {
292  // clear symbol layers
293  QgsPalLayerSettings settings = vl->labeling()->settings( labelProvider );
294  QgsTextFormat format = settings.format();
295  if ( ! format.mask().enabled() )
296  continue;
299  for ( const QgsSymbolLayerReference &ref : masks )
300  {
301  // copy the original masks, only those with another destination layer
302  if ( ref.layerId() != mLayer->id() )
303  newMasks.append( ref );
304  }
305  for ( const QgsMaskSourceSelectionWidget::MaskSource &source : maskSources )
306  {
307  // ... then add the new masked symbol layers, if any
308 
309  if ( source.isLabeling && source.layerId == layerIt.key() && source.symbolLayerId.symbolKey() == labelProvider )
310  {
311  for ( const QgsSymbolLayerId &maskedId : maskedSymbolLayers )
312  {
313  newMasks.append( QgsSymbolLayerReference( mLayer->id(), maskedId ) );
314  }
315  // invalidate the cache of the source layer
316  layersToRefresh.insert( source.layerId );
317  }
318  }
319  format.mask().setMaskedSymbolLayers( newMasks );
320  settings.setFormat( format );
321  vl->labeling()->setSettings( new QgsPalLayerSettings( settings ), labelProvider );
322  }
323  }
324 
326  // trigger refresh of the current layer
327  mLayer->triggerRepaint();
328  // trigger refresh of dependent layers (i.e. mask source layers)
329  for ( QString layerId : layersToRefresh )
330  {
331  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId );
332  layer->triggerRepaint();
333  }
334 }
335 
337 {
338  return !mMustPopulate;
339 }
QgsSymbolLayerSelectionWidget::changed
void changed()
Signal emitted when something the configuration is changed.
QgsTextMaskSettings::enabled
bool enabled() const
Returns whether the mask is enabled.
Definition: qgstextmasksettings.cpp:64
SymbolLayerVisitor::visitEnter
bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node) override
Called when the visitor starts visiting a node.
Definition: qgsmaskingwidget.cpp:96
qgsmessagebaritem.h
QgsMaskSourceSelectionWidget::MaskSource::layerId
QString layerId
The source layer id.
Definition: qgsmasksourceselectionwidget.h:45
QgsStyleSymbolEntity
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1201
QgsPalLayerSettings
Definition: qgspallabeling.h:207
QgsProject::mapLayers
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Definition: qgsproject.cpp:3436
QgsFeatureRenderer::accept
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
Definition: qgsrenderer.cpp:465
QgsMaskingWidget::widgetChanged
void widgetChanged()
Emitted when a change is performed.
Qgis::Warning
@ Warning
Definition: qgis.h:91
QgsSymbolLayerReference
Type used to refer to a specific symbol layer in a symbol of a layer.
Definition: qgssymbollayerreference.h:118
SymbolLayerVisitor
Generic visitor that collects symbol layers of a vector layer's renderer and call a callback function...
Definition: qgsmaskingwidget.cpp:87
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsStyleEntityVisitorInterface
An interface for classes which can visit style entity (e.g.
Definition: qgsstyleentityvisitor.h:34
QgsSymbol
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:64
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3208
QgsSymbol::symbolLayer
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:385
SymbolLayerVisitor::visitSymbol
void visitSymbol(const QgsSymbol *symbol, const QString &leafIdentifier, QVector< int > rootPath)
Process a symbol.
Definition: qgsmaskingwidget.cpp:106
QgsStyleEntityVisitorInterface::StyleLeaf
Contains information relating to the style entity currently being visited.
Definition: qgsstyleentityvisitor.h:61
QgsAbstractVectorLayerLabeling::setSettings
virtual void setSettings(QgsPalLayerSettings *settings, const QString &providerId=QString())=0
Set pal settings for a specific provider (takes ownership).
QgsVectorLayerUtils::labelMasks
static QHash< QString, QHash< QString, QSet< QgsSymbolLayerId > > > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
Definition: qgsvectorlayerutils.cpp:885
QgsStyle::SymbolEntity
@ SymbolEntity
Symbols.
Definition: qgsstyle.h:180
qgssymbollayerreference.h
QgsMaskingWidget::apply
void apply()
Applies the changes.
Definition: qgsmaskingwidget.cpp:237
qgsmasksourceselectionwidget.h
QgsMapLayer::triggerRepaint
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
Definition: qgsmaplayer.cpp:1826
QgsTextFormat
Container for all settings relating to text rendering.
Definition: qgstextformat.h:40
QgsSymbolLayer
Definition: qgssymbollayer.h:53
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:30
qgsmaskingwidget.h
QgsMessageBarItem
Represents an item shown within a QgsMessageBar widget.
Definition: qgsmessagebaritem.h:39
QgsAbstractVectorLayerLabeling::subProviders
virtual QStringList subProviders() const
Gets list of sub-providers within the layer's labeling.
Definition: qgsvectorlayerlabeling.h:76
QgsMaskSourceSelectionWidget::MaskSource::isLabeling
bool isLabeling
Whether it is a labeling mask or not.
Definition: qgsmasksourceselectionwidget.h:48
QgsVectorLayer::labeling
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
Definition: qgsvectorlayer.h:1648
QgsPalLayerSettings::format
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
Definition: qgspallabeling.h:984
QgsMaskSourceSelectionWidget::MaskSource
Definition: qgsmasksourceselectionwidget.h:43
QgsPalLayerSettings::setFormat
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Definition: qgspallabeling.h:992
qgsmasksymbollayer.h
qgsvectorlayerutils.h
QgsStyleEntityInterface::type
virtual QgsStyle::StyleEntity type() const =0
Returns the type of style entity.
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsProject::setDirty
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:519
QgsMaskSourceSelectionWidget::MaskSource::symbolLayerId
QgsSymbolLayerId symbolLayerId
The symbol layer id.
Definition: qgsmasksourceselectionwidget.h:51
QgsStyleEntityVisitorInterface::NodeType::SymbolRule
@ SymbolRule
Rule based symbology or label child rule.
qgssymbollayerselectionwidget.h
QgsStyleEntityVisitorInterface::Node
Contains information relating to a node (i.e.
Definition: qgsstyleentityvisitor.h:111
qgsrenderer.h
QgsSymbolLayerReference::symbolLayerId
QgsSymbolLayerId symbolLayerId() const
The symbol layer's id.
Definition: qgssymbollayerreference.h:136
QgsTextMaskSettings::maskedSymbolLayers
QgsSymbolLayerReferenceList maskedSymbolLayers() const
Returns a list of references to symbol layers that are masked by this buffer.
Definition: qgstextmasksettings.cpp:231
QgsMaskingWidget::showEvent
void showEvent(QShowEvent *) override
Definition: qgsmaskingwidget.cpp:58
QgsStyleEntityVisitorInterface::StyleLeaf::entity
const QgsStyleEntityInterface * entity
Reference to style entity being visited.
Definition: qgsstyleentityvisitor.h:103
qgsvectorlayer.h
QgsTextFormat::mask
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
Definition: qgstextformat.cpp:139
QgsSymbolLayer::masks
virtual QgsSymbolLayerReferenceList masks() const
Returns masks defined by this symbol layer.
Definition: qgssymbollayer.cpp:804
QgsTextMaskSettings::setMaskedSymbolLayers
void setMaskedSymbolLayers(QgsSymbolLayerReferenceList maskedLayers)
Sets the symbol layers that will be masked by this buffer.
Definition: qgstextmasksettings.cpp:236
QgsStyleEntityVisitorInterface::StyleLeaf::identifier
QString identifier
A string identifying the style entity.
Definition: qgsstyleentityvisitor.h:85
QgsSymbolLayerReferenceList
QList< QgsSymbolLayerReference > QgsSymbolLayerReferenceList
Definition: qgssymbollayerreference.h:160
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsMaskingWidget::QgsMaskingWidget
QgsMaskingWidget(QWidget *parent=nullptr)
constructor
Definition: qgsmaskingwidget.cpp:33
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsMaskSourceSelectionWidget::changed
void changed()
Emitted when an item was changed.
SymbolLayerVisitor::SymbolLayerCallback
std::function< void(const QgsSymbolLayer *, const QgsSymbolLayerId &)> SymbolLayerCallback
Definition: qgsmaskingwidget.cpp:89
SymbolLayerVisitor::SymbolLayerVisitor
SymbolLayerVisitor(SymbolLayerCallback callback)
constructor
Definition: qgsmaskingwidget.cpp:92
QgsSymbolLayerId
We may need stable references to symbol layers, when pointers to symbol layers is not usable (when a ...
Definition: qgssymbollayerreference.h:54
QgsSymbolLayer::layerType
virtual QString layerType() const =0
Returns a string that represents this layer type.
QgsSymbolLayerId::symbolKey
QString symbolKey() const
Returns the key associated to the symbol.
Definition: qgssymbollayerreference.h:81
qgsvectorlayerlabeling.h
SymbolLayerVisitor::visit
bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &leaf) override
Called when the visitor will visit a style entity.
Definition: qgsmaskingwidget.cpp:124
QgsSymbolLayerReference::layerId
QString layerId() const
The referenced vector layer / feature renderer.
Definition: qgssymbollayerreference.h:131
QgsStyleEntityVisitorInterface::Node::identifier
QString identifier
A string identifying the node.
Definition: qgsstyleentityvisitor.h:133
qgssymbol.h
qgsproject.h
QgsAbstractVectorLayerLabeling::settings
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
QgsStyleEntityVisitorInterface::Node::type
QgsStyleEntityVisitorInterface::NodeType type
Node type.
Definition: qgsstyleentityvisitor.h:125
symbolLayerMasks
QList< QPair< QgsSymbolLayerId, QList< QgsSymbolLayerReference > > > symbolLayerMasks(const QgsVectorLayer *layer)
Symbol layer masks collector.
Definition: qgsmaskingwidget.cpp:150
QgsMaskingWidget::setLayer
void setLayer(QgsVectorLayer *layer)
Sets the layer to configure.
Definition: qgsmaskingwidget.cpp:165
QgsMaskingWidget::hasBeenPopulated
bool hasBeenPopulated()
Widget has been populated or not.
Definition: qgsmaskingwidget.cpp:336
qgsstyleentityvisitor.h
QgsSymbol::symbolLayerCount
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:183
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:892