QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsrendererpropertiesdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrendererpropertiesdialog.cpp
3  ---------------------
4  begin : December 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail 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  ***************************************************************************/
16 
17 #include "qgsrenderer.h"
18 #include "qgsrendererregistry.h"
19 
20 #include "qgsrendererwidget.h"
30 #include "qgs25drendererwidget.h"
33 #include "qgspanelwidget.h"
34 #include "qgspainteffect.h"
35 
36 #include "qgsorderbydialog.h"
37 #include "qgsapplication.h"
38 #include "qgslogger.h"
39 #include "qgsvectorlayer.h"
40 
41 #include <QKeyEvent>
42 #include <QMessageBox>
43 
44 static bool _initRenderer( const QString &name, QgsRendererWidgetFunc f, const QString &iconName = QString() )
45 {
48  if ( !am )
49  return false;
50  QgsRendererMetadata *m = dynamic_cast<QgsRendererMetadata *>( am );
51  if ( !m )
52  return false;
53 
54  m->setWidgetFunction( f );
55 
56  if ( !iconName.isEmpty() )
57  {
58  m->setIcon( QgsApplication::getThemeIcon( iconName ) );
59  }
60 
61  QgsDebugMsgLevel( "Set for " + name, 2 );
62  return true;
63 }
64 
65 static void _initRendererWidgetFunctions()
66 {
67  static bool sInitialized = false;
68  if ( sInitialized )
69  return;
70 
71  _initRenderer( QStringLiteral( "singleSymbol" ), QgsSingleSymbolRendererWidget::create, QStringLiteral( "rendererSingleSymbol.svg" ) );
72  _initRenderer( QStringLiteral( "categorizedSymbol" ), QgsCategorizedSymbolRendererWidget::create, QStringLiteral( "rendererCategorizedSymbol.svg" ) );
73  _initRenderer( QStringLiteral( "graduatedSymbol" ), QgsGraduatedSymbolRendererWidget::create, QStringLiteral( "rendererGraduatedSymbol.svg" ) );
74  _initRenderer( QStringLiteral( "RuleRenderer" ), QgsRuleBasedRendererWidget::create, QStringLiteral( "rendererRuleBasedSymbol.svg" ) );
75  _initRenderer( QStringLiteral( "pointDisplacement" ), QgsPointDisplacementRendererWidget::create, QStringLiteral( "rendererPointDisplacementSymbol.svg" ) );
76  _initRenderer( QStringLiteral( "pointCluster" ), QgsPointClusterRendererWidget::create, QStringLiteral( "rendererPointClusterSymbol.svg" ) );
77  _initRenderer( QStringLiteral( "invertedPolygonRenderer" ), QgsInvertedPolygonRendererWidget::create, QStringLiteral( "rendererInvertedSymbol.svg" ) );
78  _initRenderer( QStringLiteral( "mergedFeatureRenderer" ), QgsMergedFeatureRendererWidget::create, QStringLiteral( "rendererMergedFeatures.svg" ) );
79  _initRenderer( QStringLiteral( "heatmapRenderer" ), QgsHeatmapRendererWidget::create, QStringLiteral( "rendererHeatmapSymbol.svg" ) );
80  _initRenderer( QStringLiteral( "25dRenderer" ), Qgs25DRendererWidget::create, QStringLiteral( "renderer25dSymbol.svg" ) );
81  _initRenderer( QStringLiteral( "nullSymbol" ), QgsNullSymbolRendererWidget::create, QStringLiteral( "rendererNullSymbol.svg" ) );
82  _initRenderer( QStringLiteral( "embeddedSymbol" ), QgsEmbeddedSymbolRendererWidget::create );
83  sInitialized = true;
84 }
85 
86 QgsRendererPropertiesDialog::QgsRendererPropertiesDialog( QgsVectorLayer *layer, QgsStyle *style, bool embedded, QWidget *parent )
87  : QDialog( parent )
88  , mLayer( layer )
89  , mStyle( style )
90 
91 {
92  setupUi( this );
94  mLayerRenderingGroupBox->setSettingGroup( QStringLiteral( "layerRenderingGroupBox" ) );
95 
96  // can be embedded in vector layer properties
97  if ( embedded )
98  {
99  buttonBox->hide();
100  layout()->setContentsMargins( 0, 0, 0, 0 );
101  }
102 
103  // initialize registry's widget functions
104  _initRendererWidgetFunctions();
105 
107  const QStringList renderers = reg->renderersList( mLayer );
108  const auto constRenderers = renderers;
109  for ( const QString &name : constRenderers )
110  {
112  cboRenderers->addItem( m->icon(), m->visibleName(), name );
113  }
114 
115  cboRenderers->setCurrentIndex( -1 ); // set no current renderer
116 
117  connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererPropertiesDialog::onOK );
118 
119  // connect layer opacity slider and spin box
120  connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRendererPropertiesDialog::rendererChanged );
121  connect( checkboxEnableOrderBy, &QAbstractButton::toggled, btnOrderBy, &QWidget::setEnabled );
122  connect( btnOrderBy, &QAbstractButton::clicked, this, &QgsRendererPropertiesDialog::showOrderByDialog );
123 
124  syncToLayer();
125 
126  QList<QWidget *> widgets;
127  widgets << mOpacityWidget
128  << cboRenderers
129  << checkboxEnableOrderBy
130  << mBlendModeComboBox
131  << mFeatureBlendComboBox
132  << mEffectWidget;
133 
134  connectValueChanged( widgets, SIGNAL( widgetChanged() ) );
135  connect( mEffectWidget, &QgsPanelWidget::showPanel, this, &QgsRendererPropertiesDialog::openPanel );
136 }
137 
138 void QgsRendererPropertiesDialog::connectValueChanged( const QList<QWidget *> &widgets, const char *slot )
139 {
140  for ( QWidget *widget : widgets )
141  {
142  if ( QgsPropertyOverrideButton *w = qobject_cast<QgsPropertyOverrideButton *>( widget ) )
143  {
144  connect( w, SIGNAL( changed ), this, slot );
145  }
146  else if ( QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( widget ) )
147  {
148  connect( w, SIGNAL( fieldChanged( QString ) ), this, slot );
149  }
150  else if ( QgsOpacityWidget *w = qobject_cast<QgsOpacityWidget *>( widget ) )
151  {
152  connect( w, SIGNAL( opacityChanged( double ) ), this, slot );
153  }
154  else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
155  {
156  connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot );
157  }
158  else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
159  {
160  connect( w, SIGNAL( valueChanged( int ) ), this, slot );
161  }
162  else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
163  {
164  connect( w, SIGNAL( valueChanged( double ) ), this, slot );
165  }
166  else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
167  {
168  connect( w, SIGNAL( colorChanged( QColor ) ), this, slot );
169  }
170  else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
171  {
172  connect( w, SIGNAL( toggled( bool ) ), this, slot );
173  }
174  else if ( QLineEdit *w = qobject_cast<QLineEdit *>( widget ) )
175  {
176  connect( w, SIGNAL( textEdited( QString ) ), this, slot );
177  connect( w, SIGNAL( textChanged( QString ) ), this, slot );
178  }
179  else if ( QgsEffectStackCompactWidget *w = qobject_cast<QgsEffectStackCompactWidget *>( widget ) )
180  {
181  connect( w, SIGNAL( changed() ), this, slot );
182  }
183  }
184 }
185 
187 {
188  delete mPaintEffect;
189 }
190 
192 {
193  mMapCanvas = canvas;
194  if ( mActiveWidget )
195  {
196  QgsSymbolWidgetContext context;
197  context.setMapCanvas( mMapCanvas );
198  mActiveWidget->setContext( context );
199  }
200 }
201 
203 {
204  mMapCanvas = context.mapCanvas();
205  mMessageBar = context.messageBar();
206  if ( mActiveWidget )
207  {
208  mActiveWidget->setContext( context );
209  }
210 }
211 
213 {
214  mDockMode = dockMode;
215  mEffectWidget->setDockMode( dockMode );
216  if ( mActiveWidget )
217  mActiveWidget->setDockMode( mDockMode );
218 }
219 
220 
222 {
223  if ( cboRenderers->currentIndex() == -1 )
224  {
225  QgsDebugMsg( QStringLiteral( "No current item -- this should never happen!" ) );
226  return;
227  }
228 
229  const QString rendererName = cboRenderers->currentData().toString();
230 
231  //Retrieve the previous renderer: from the old active widget if possible, otherwise from the layer
232  QgsFeatureRenderer *oldRenderer = nullptr;
234  {
235  oldRenderer = mActiveWidget->renderer()->clone();
236  }
237  else
238  {
239  oldRenderer = mLayer->renderer()->clone();
240  }
241 
242  // get rid of old active widget (if any)
243  if ( mActiveWidget )
244  {
245  stackedWidget->removeWidget( mActiveWidget );
246 
247  delete mActiveWidget;
248  mActiveWidget = nullptr;
249  }
250 
251  QgsRendererWidget *w = nullptr;
253  if ( m )
254  w = m->createRendererWidget( mLayer, mStyle, oldRenderer );
255  delete oldRenderer;
256 
257  if ( w )
258  {
259  // instantiate the widget and set as active
260  mActiveWidget = w;
261  stackedWidget->addWidget( mActiveWidget );
262  stackedWidget->setCurrentWidget( mActiveWidget );
263  if ( mActiveWidget->renderer() )
264  {
265  if ( mMapCanvas || mMessageBar )
266  {
267  QgsSymbolWidgetContext context;
268  context.setMapCanvas( mMapCanvas );
269  context.setMessageBar( mMessageBar );
270  mActiveWidget->setContext( context );
271  }
272  changeOrderBy( mActiveWidget->renderer()->orderBy(), mActiveWidget->renderer()->orderByEnabled() );
274  }
277  w->setDockMode( mDockMode );
278  }
279  else
280  {
281  // set default "no edit widget available" page
282  stackedWidget->setCurrentWidget( pageNoWidget );
283  }
284 }
285 
287 {
288  if ( !mActiveWidget || !mLayer )
289  {
290  return;
291  }
292 
294 
296  if ( renderer )
297  {
298  renderer->setPaintEffect( mPaintEffect->clone() );
299  // set the order by
300  renderer->setOrderBy( mOrderBy );
301  renderer->setOrderByEnabled( checkboxEnableOrderBy->isChecked() );
302 
303  mLayer->setRenderer( renderer->clone() );
304  }
305 
306  // set the blend modes for the layer
307  mLayer->setBlendMode( mBlendModeComboBox->blendMode() );
308  mLayer->setFeatureBlendMode( mFeatureBlendComboBox->blendMode() );
309 
310  // set opacity for the layer
311  mLayer->setOpacity( mOpacityWidget->opacity() );
312 }
313 
315 {
316  apply();
317  accept();
318 }
319 
321 {
322  if ( mDockMode )
323  {
324  emit showPanel( panel );
325  }
326  else
327  {
328  // Show the dialog version if no one is connected
329  QDialog *dlg = new QDialog();
330  const QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( panel->panelTitle() );
331  QgsSettings settings;
332  dlg->restoreGeometry( settings.value( key ).toByteArray() );
333  dlg->setWindowTitle( panel->panelTitle() );
334  dlg->setLayout( new QVBoxLayout() );
335  dlg->layout()->addWidget( panel );
336  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok );
337  connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
338  dlg->layout()->addWidget( buttonBox );
339  dlg->exec();
340  settings.setValue( key, dlg->saveGeometry() );
341  panel->acceptPanel();
342  }
343 }
344 
345 void QgsRendererPropertiesDialog::syncToLayer()
346 {
347  // Blend mode
348  mBlendModeComboBox->setBlendMode( mLayer->blendMode() );
349 
350  // Feature blend mode
351  mFeatureBlendComboBox->setBlendMode( mLayer->featureBlendMode() );
352 
353  // Layer opacity
354  mOpacityWidget->setOpacity( mLayer->opacity() );
355 
356  //paint effect widget
357  if ( mLayer->renderer() )
358  {
359  if ( mLayer->renderer()->paintEffect() )
360  {
362  mEffectWidget->setPaintEffect( mPaintEffect );
363  }
364 
366  }
367 
368  // setup slot rendererChanged()
369  //setup order by
370  if ( mLayer->renderer() &&
372  {
373  checkboxEnableOrderBy->setChecked( true );
374  }
375  else
376  {
377  btnOrderBy->setEnabled( false );
378  checkboxEnableOrderBy->setChecked( false );
379  }
380 
381  if ( mLayer->renderer() )
382  {
383  // set current renderer from layer
384  const QString rendererName = mLayer->renderer()->type();
385 
386  const int rendererIdx = cboRenderers->findData( rendererName );
387  cboRenderers->setCurrentIndex( rendererIdx );
388 
389  // no renderer found... this mustn't happen
390  Q_ASSERT( rendererIdx != -1 && "there must be a renderer!" );
391  }
392 
393 }
394 
395 void QgsRendererPropertiesDialog::showOrderByDialog()
396 {
397  QgsOrderByDialog dlg( mLayer, this );
398 
399  dlg.setOrderBy( mOrderBy );
400  if ( dlg.exec() )
401  {
402  mOrderBy = dlg.orderBy();
403  emit widgetChanged();
404  }
405 }
406 
407 void QgsRendererPropertiesDialog::changeOrderBy( const QgsFeatureRequest::OrderBy &orderBy, bool orderByEnabled )
408 {
409  mOrderBy = orderBy;
410  checkboxEnableOrderBy->setChecked( orderByEnabled );
411 }
412 
413 void QgsRendererPropertiesDialog::updateUIState( bool hidden )
414 {
415  mLayerRenderingGroupBox->setHidden( hidden );
416  cboRenderers->setHidden( hidden );
417 }
418 
419 
421 {
422  // Ignore the ESC key to avoid close the dialog without the properties window
423  if ( !isWindow() && e->key() == Qt::Key_Escape )
424  {
425  e->ignore();
426  }
427  else
428  {
429  QDialog::keyPressEvent( e );
430  }
431 }
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Static creation method.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsRendererRegistry * rendererRegistry()
Returns the application's renderer registry, used for managing vector layer renderers.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
A cross platform button subclass for selecting colors.
A small widget consisting of a checkbox for enabling/disabling an effect stack and a button for openi...
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Static creation method.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the renderer.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the renderer.
QString type() const
Definition: qgsrenderer.h:142
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer.
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
Represents a list of OrderByClauses, with the most important first and the least important last.
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:168
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Static creation method.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Static creation method.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:88
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
double opacity
Definition: qgsmaplayer.h:82
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Static creation method.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Creates a new QgsNullSymbolRendererWidget object.
A widget for setting an opacity value.
This is a dialog to build and manage a list of order by clauses.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
Base class for any widget that can be shown as a inline panel.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
QString panelTitle()
The title of the panel.
void widgetChanged()
Emitted when the widget state changes.
void acceptPanel()
Accept the panel.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Returns a new QgsPointClusterRendererWidget.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
A button for controlling property overrides which may apply to a widget.
Stores metadata about one renderer class.
QIcon icon() const
Returns an icon representing the renderer.
void setIcon(const QIcon &icon)
Sets an icon representing the renderer.
virtual QgsRendererWidget * createRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *oldRenderer)
Returns new instance of settings widget for the renderer.
QString visibleName() const
Returns a friendly display name of the renderer.
Convenience metadata class that uses static functions to create renderer and its widget.
void setWidgetFunction(QgsRendererWidgetFunc f)
void connectValueChanged(const QList< QWidget * > &widgets, const char *slot)
Connect the given slot to the value changed event for the set of widgets Each widget is checked for t...
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the dialog is shown, e.g., the associated map canvas and expression context...
QgsFeatureRequest::OrderBy mOrderBy
void apply()
Apply the changes from the dialog to the layer.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the dialog.
void layerVariablesChanged()
Emitted when expression context variables on the associated vector layers have been changed.
void widgetChanged()
Emitted when something on the widget has changed.
void onOK()
Apply and accept the changes for the dialog.
void keyPressEvent(QKeyEvent *event) override
void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsRendererPropertiesDialog(QgsVectorLayer *layer, QgsStyle *style, bool embedded=false, QWidget *parent=nullptr)
Constructor for QgsRendererPropertiesDialog.
void rendererChanged()
called when user changes renderer type
Registry of renderers.
QStringList renderersList(QgsRendererAbstractMetadata::LayerTypes layerTypes=QgsRendererAbstractMetadata::All) const
Returns a list of available renderers.
QgsRendererAbstractMetadata * rendererMetadata(const QString &rendererName)
Returns the metadata for a specified renderer.
Base class for renderer settings widgets.
void layerVariablesChanged()
Emitted when expression context variables on the associated vector layers have been changed.
virtual QgsFeatureRenderer * renderer()=0
Returns pointer to the renderer (no transfer of ownership)
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
void applyChanges()
This method should be called whenever the renderer is actually set on the layer.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Represents a vector layer which manages a vector based data sets.
QPainter::CompositionMode featureBlendMode() const
Returns the current blending mode for features.
void setFeatureBlendMode(QPainter::CompositionMode blendMode)
Sets the blending mode used for rendering each feature.
void setRenderer(QgsFeatureRenderer *r)
Sets the feature renderer which will be invoked to represent this layer in 2D map views.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsRendererWidget *(* QgsRendererWidgetFunc)(QgsVectorLayer *, QgsStyle *, QgsFeatureRenderer *)