QGIS API Documentation  3.25.0-Master (dec16ba68b)
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 #include "qgsproject.h"
36 #include "qgsprojectutils.h"
37 
38 #include "qgsorderbydialog.h"
39 #include "qgsapplication.h"
40 #include "qgslogger.h"
41 #include "qgsvectorlayer.h"
42 
43 #include <QKeyEvent>
44 #include <QMessageBox>
45 
46 static bool _initRenderer( const QString &name, QgsRendererWidgetFunc f, const QString &iconName = QString() )
47 {
50  if ( !am )
51  return false;
52  QgsRendererMetadata *m = dynamic_cast<QgsRendererMetadata *>( am );
53  if ( !m )
54  return false;
55 
56  m->setWidgetFunction( f );
57 
58  if ( !iconName.isEmpty() )
59  {
60  m->setIcon( QgsApplication::getThemeIcon( iconName ) );
61  }
62 
63  QgsDebugMsgLevel( "Set for " + name, 2 );
64  return true;
65 }
66 
67 static void _initRendererWidgetFunctions()
68 {
69  static bool sInitialized = false;
70  if ( sInitialized )
71  return;
72 
73  _initRenderer( QStringLiteral( "singleSymbol" ), QgsSingleSymbolRendererWidget::create, QStringLiteral( "rendererSingleSymbol.svg" ) );
74  _initRenderer( QStringLiteral( "categorizedSymbol" ), QgsCategorizedSymbolRendererWidget::create, QStringLiteral( "rendererCategorizedSymbol.svg" ) );
75  _initRenderer( QStringLiteral( "graduatedSymbol" ), QgsGraduatedSymbolRendererWidget::create, QStringLiteral( "rendererGraduatedSymbol.svg" ) );
76  _initRenderer( QStringLiteral( "RuleRenderer" ), QgsRuleBasedRendererWidget::create, QStringLiteral( "rendererRuleBasedSymbol.svg" ) );
77  _initRenderer( QStringLiteral( "pointDisplacement" ), QgsPointDisplacementRendererWidget::create, QStringLiteral( "rendererPointDisplacementSymbol.svg" ) );
78  _initRenderer( QStringLiteral( "pointCluster" ), QgsPointClusterRendererWidget::create, QStringLiteral( "rendererPointClusterSymbol.svg" ) );
79  _initRenderer( QStringLiteral( "invertedPolygonRenderer" ), QgsInvertedPolygonRendererWidget::create, QStringLiteral( "rendererInvertedSymbol.svg" ) );
80  _initRenderer( QStringLiteral( "mergedFeatureRenderer" ), QgsMergedFeatureRendererWidget::create, QStringLiteral( "rendererMergedFeatures.svg" ) );
81  _initRenderer( QStringLiteral( "heatmapRenderer" ), QgsHeatmapRendererWidget::create, QStringLiteral( "rendererHeatmapSymbol.svg" ) );
82  _initRenderer( QStringLiteral( "25dRenderer" ), Qgs25DRendererWidget::create, QStringLiteral( "renderer25dSymbol.svg" ) );
83  _initRenderer( QStringLiteral( "nullSymbol" ), QgsNullSymbolRendererWidget::create, QStringLiteral( "rendererNullSymbol.svg" ) );
84  _initRenderer( QStringLiteral( "embeddedSymbol" ), QgsEmbeddedSymbolRendererWidget::create );
85  sInitialized = true;
86 }
87 
88 QgsRendererPropertiesDialog::QgsRendererPropertiesDialog( QgsVectorLayer *layer, QgsStyle *style, bool embedded, QWidget *parent )
89  : QDialog( parent )
90  , mLayer( layer )
91  , mStyle( style )
92 
93 {
94  setupUi( this );
96  mLayerRenderingGroupBox->setSettingGroup( QStringLiteral( "layerRenderingGroupBox" ) );
97 
98  // can be embedded in vector layer properties
99  if ( embedded )
100  {
101  buttonBox->hide();
102  layout()->setContentsMargins( 0, 0, 0, 0 );
103  }
104 
105  // initialize registry's widget functions
106  _initRendererWidgetFunctions();
107 
109  const QStringList renderers = reg->renderersList( mLayer );
110  const auto constRenderers = renderers;
111  for ( const QString &name : constRenderers )
112  {
114  cboRenderers->addItem( m->icon(), m->visibleName(), name );
115  }
116 
117  cboRenderers->setCurrentIndex( -1 ); // set no current renderer
118 
119  connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererPropertiesDialog::onOK );
120 
121  // connect layer opacity slider and spin box
122  connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRendererPropertiesDialog::rendererChanged );
123  connect( checkboxEnableOrderBy, &QAbstractButton::toggled, btnOrderBy, &QWidget::setEnabled );
124  connect( btnOrderBy, &QAbstractButton::clicked, this, &QgsRendererPropertiesDialog::showOrderByDialog );
125 
126  syncToLayer();
127 
128  QList<QWidget *> widgets;
129  widgets << mOpacityWidget
130  << cboRenderers
131  << checkboxEnableOrderBy
132  << mBlendModeComboBox
133  << mFeatureBlendComboBox
134  << mEffectWidget;
135 
136  connectValueChanged( widgets, SIGNAL( widgetChanged() ) );
137  connect( mEffectWidget, &QgsPanelWidget::showPanel, this, &QgsRendererPropertiesDialog::openPanel );
138 }
139 
140 void QgsRendererPropertiesDialog::connectValueChanged( const QList<QWidget *> &widgets, const char *slot )
141 {
142  for ( QWidget *widget : widgets )
143  {
144  if ( QgsPropertyOverrideButton *w = qobject_cast<QgsPropertyOverrideButton *>( widget ) )
145  {
146  connect( w, SIGNAL( changed ), this, slot );
147  }
148  else if ( QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( widget ) )
149  {
150  connect( w, SIGNAL( fieldChanged( QString ) ), this, slot );
151  }
152  else if ( QgsOpacityWidget *w = qobject_cast<QgsOpacityWidget *>( widget ) )
153  {
154  connect( w, SIGNAL( opacityChanged( double ) ), this, slot );
155  }
156  else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
157  {
158  connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot );
159  }
160  else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
161  {
162  connect( w, SIGNAL( valueChanged( int ) ), this, slot );
163  }
164  else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
165  {
166  connect( w, SIGNAL( valueChanged( double ) ), this, slot );
167  }
168  else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
169  {
170  connect( w, SIGNAL( colorChanged( QColor ) ), this, slot );
171  }
172  else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
173  {
174  connect( w, SIGNAL( toggled( bool ) ), this, slot );
175  }
176  else if ( QLineEdit *w = qobject_cast<QLineEdit *>( widget ) )
177  {
178  connect( w, SIGNAL( textEdited( QString ) ), this, slot );
179  connect( w, SIGNAL( textChanged( QString ) ), this, slot );
180  }
181  else if ( QgsEffectStackCompactWidget *w = qobject_cast<QgsEffectStackCompactWidget *>( widget ) )
182  {
183  connect( w, SIGNAL( changed() ), this, slot );
184  }
185  }
186 }
187 
189 {
190  delete mPaintEffect;
191 }
192 
194 {
195  mMapCanvas = canvas;
196  if ( mActiveWidget )
197  {
198  QgsSymbolWidgetContext context;
199  context.setMapCanvas( mMapCanvas );
200  mActiveWidget->setContext( context );
201  }
202 }
203 
205 {
206  mMapCanvas = context.mapCanvas();
207  mMessageBar = context.messageBar();
208  if ( mActiveWidget )
209  {
210  mActiveWidget->setContext( context );
211  }
212 }
213 
215 {
216  mDockMode = dockMode;
217  mEffectWidget->setDockMode( dockMode );
218  if ( mActiveWidget )
219  mActiveWidget->setDockMode( mDockMode );
220 }
221 
222 
224 {
225  if ( cboRenderers->currentIndex() == -1 )
226  {
227  QgsDebugMsg( QStringLiteral( "No current item -- this should never happen!" ) );
228  return;
229  }
230 
231  const QString rendererName = cboRenderers->currentData().toString();
232 
233  //Retrieve the previous renderer: from the old active widget if possible, otherwise from the layer
234  QgsFeatureRenderer *oldRenderer = nullptr;
236  {
237  oldRenderer = mActiveWidget->renderer()->clone();
238  }
239  else
240  {
241  oldRenderer = mLayer->renderer()->clone();
242  }
243 
244  // get rid of old active widget (if any)
245  if ( mActiveWidget )
246  {
247  stackedWidget->removeWidget( mActiveWidget );
248 
249  delete mActiveWidget;
250  mActiveWidget = nullptr;
251  }
252 
253  QgsRendererWidget *w = nullptr;
255  if ( m )
256  w = m->createRendererWidget( mLayer, mStyle, oldRenderer );
257  delete oldRenderer;
258 
259  if ( w )
260  {
261  // instantiate the widget and set as active
262  mActiveWidget = w;
263  stackedWidget->addWidget( mActiveWidget );
264  stackedWidget->setCurrentWidget( mActiveWidget );
265  if ( mActiveWidget->renderer() )
266  {
267  if ( mMapCanvas || mMessageBar )
268  {
269  QgsSymbolWidgetContext context;
270  context.setMapCanvas( mMapCanvas );
271  context.setMessageBar( mMessageBar );
272  mActiveWidget->setContext( context );
273  }
274  changeOrderBy( mActiveWidget->renderer()->orderBy(), mActiveWidget->renderer()->orderByEnabled() );
276  }
279  w->setDockMode( mDockMode );
280  }
281  else
282  {
283  // set default "no edit widget available" page
284  stackedWidget->setCurrentWidget( pageNoWidget );
285  }
286 }
287 
289 {
290  if ( !mActiveWidget || !mLayer )
291  {
292  return;
293  }
294 
296 
298  if ( renderer )
299  {
300  renderer->setPaintEffect( mPaintEffect->clone() );
301  // set the order by
302  renderer->setOrderBy( mOrderBy );
303  renderer->setOrderByEnabled( checkboxEnableOrderBy->isChecked() );
304 
305  mLayer->setRenderer( renderer->clone() );
306  }
307 
308  // set the blend modes for the layer
309  mLayer->setBlendMode( mBlendModeComboBox->blendMode() );
310  mLayer->setFeatureBlendMode( mFeatureBlendComboBox->blendMode() );
311 
312  // set opacity for the layer
313  mLayer->setOpacity( mOpacityWidget->opacity() );
314 }
315 
317 {
318  apply();
319  accept();
320 }
321 
323 {
324  if ( mDockMode )
325  {
326  emit showPanel( panel );
327  }
328  else
329  {
330  // Show the dialog version if no one is connected
331  QDialog *dlg = new QDialog();
332  const QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( panel->panelTitle() );
333  QgsSettings settings;
334  dlg->restoreGeometry( settings.value( key ).toByteArray() );
335  dlg->setWindowTitle( panel->panelTitle() );
336  dlg->setLayout( new QVBoxLayout() );
337  dlg->layout()->addWidget( panel );
338  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok );
339  connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
340  dlg->layout()->addWidget( buttonBox );
341  dlg->exec();
342  settings.setValue( key, dlg->saveGeometry() );
343  panel->acceptPanel();
344  }
345 }
346 
347 void QgsRendererPropertiesDialog::syncToLayer()
348 {
349  mBlendModeComboBox->setShowClippingModes( QgsProjectUtils::layerIsContainedInGroupLayer( QgsProject::instance(), mLayer ) );
350  mFeatureBlendComboBox->setShowClippingModes( mBlendModeComboBox->showClippingModes() );
351 
352  // Blend mode
353  mBlendModeComboBox->setBlendMode( mLayer->blendMode() );
354 
355  // Feature blend mode
356  mFeatureBlendComboBox->setBlendMode( mLayer->featureBlendMode() );
357 
358  // Layer opacity
359  mOpacityWidget->setOpacity( mLayer->opacity() );
360 
361  //paint effect widget
362  if ( mLayer->renderer() )
363  {
364  if ( mLayer->renderer()->paintEffect() )
365  {
367  mEffectWidget->setPaintEffect( mPaintEffect );
368  }
369 
371  }
372 
373  // setup slot rendererChanged()
374  //setup order by
375  if ( mLayer->renderer() &&
377  {
378  checkboxEnableOrderBy->setChecked( true );
379  }
380  else
381  {
382  btnOrderBy->setEnabled( false );
383  checkboxEnableOrderBy->setChecked( false );
384  }
385 
386  if ( mLayer->renderer() )
387  {
388  // set current renderer from layer
389  const QString rendererName = mLayer->renderer()->type();
390 
391  const int rendererIdx = cboRenderers->findData( rendererName );
392  cboRenderers->setCurrentIndex( rendererIdx );
393 
394  // no renderer found... this mustn't happen
395  Q_ASSERT( rendererIdx != -1 && "there must be a renderer!" );
396  }
397 
398 }
399 
400 void QgsRendererPropertiesDialog::showOrderByDialog()
401 {
402  QgsOrderByDialog dlg( mLayer, this );
403 
404  dlg.setOrderBy( mOrderBy );
405  if ( dlg.exec() )
406  {
407  mOrderBy = dlg.orderBy();
408  emit widgetChanged();
409  }
410 }
411 
412 void QgsRendererPropertiesDialog::changeOrderBy( const QgsFeatureRequest::OrderBy &orderBy, bool orderByEnabled )
413 {
414  mOrderBy = orderBy;
415  checkboxEnableOrderBy->setChecked( orderByEnabled );
416 }
417 
418 void QgsRendererPropertiesDialog::updateUIState( bool hidden )
419 {
420  mLayerRenderingGroupBox->setHidden( hidden );
421  cboRenderers->setHidden( hidden );
422 }
423 
424 
426 {
427  // Ignore the ESC key to avoid close the dialog without the properties window
428  if ( !isWindow() && e->key() == Qt::Key_Escape )
429  {
430  e->ignore();
431  }
432  else
433  {
434  QDialog::keyPressEvent( e );
435  }
436 }
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:180
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:90
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)
static bool layerIsContainedInGroupLayer(QgsProject *project, QgsMapLayer *layer)
Returns true if the specified layer is a child layer from any QgsGroupLayer in the given project.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:474
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)
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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 *)