QGIS API Documentation  3.12.1-București (121cc00ff0)
qgsrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrendererwidget.cpp
3  ---------------------
4  begin : November 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  ***************************************************************************/
15 #include "qgsrendererwidget.h"
16 
18 #include "qgssymbol.h"
19 #include "qgsvectorlayer.h"
20 #include "qgscolordialog.h"
21 #include "qgssymbollevelsdialog.h"
22 #include "qgssymbollayer.h"
24 #include "qgsmapcanvas.h"
25 #include "qgspanelwidget.h"
26 #include "qgsproject.h"
28 #include "qgssymbollayerutils.h"
29 
30 #include <QMessageBox>
31 #include <QInputDialog>
32 #include <QMenu>
33 #include <QClipboard>
34 
36  : mLayer( layer )
37  , mStyle( style )
38 {
39  contextMenu = new QMenu( tr( "Renderer Options" ), this );
40 
41  mCopyAction = new QAction( tr( "Copy" ), this );
42  connect( mCopyAction, &QAction::triggered, this, &QgsRendererWidget::copy );
43  mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
44  mPasteAction = new QAction( tr( "Paste" ), this );
45  mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
46  connect( mPasteAction, &QAction::triggered, this, &QgsRendererWidget::paste );
47 
48  mCopySymbolAction = new QAction( tr( "Copy Symbol" ), this );
49  contextMenu->addAction( mCopySymbolAction );
50  connect( mCopySymbolAction, &QAction::triggered, this, &QgsRendererWidget::copySymbol );
51  mPasteSymbolAction = new QAction( tr( "Paste Symbol" ), this );
52  contextMenu->addAction( mPasteSymbolAction );
53  connect( mPasteSymbolAction, &QAction::triggered, this, &QgsRendererWidget::pasteSymbolToSelection );
54 
55  contextMenu->addSeparator();
56  contextMenu->addAction( tr( "Change Color…" ), this, SLOT( changeSymbolColor() ) );
57  contextMenu->addAction( tr( "Change Opacity…" ), this, SLOT( changeSymbolOpacity() ) );
58  contextMenu->addAction( tr( "Change Output Unit…" ), this, SLOT( changeSymbolUnit() ) );
59 
61  {
62  contextMenu->addAction( tr( "Change Width…" ), this, SLOT( changeSymbolWidth() ) );
63  }
65  {
66  contextMenu->addAction( tr( "Change Size…" ), this, SLOT( changeSymbolSize() ) );
67  contextMenu->addAction( tr( "Change Angle…" ), this, SLOT( changeSymbolAngle() ) );
68  }
69 
70  connect( contextMenu, &QMenu::aboutToShow, this, [ = ]
71  {
72  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
73  mPasteSymbolAction->setEnabled( static_cast< bool >( tempSymbol ) );
74  } );
75 }
76 
78 {
79  contextMenu->exec( QCursor::pos() );
80 }
81 
83 {
84  const QList<QgsSymbol *> symbolList = selectedSymbols();
85  if ( symbolList.isEmpty() )
86  {
87  return;
88  }
89 
90  QgsSymbol *firstSymbol = nullptr;
91  for ( QgsSymbol *symbol : symbolList )
92  {
93  if ( symbol )
94  {
95  firstSymbol = symbol;
96  break;
97  }
98  }
99  if ( !firstSymbol )
100  return;
101 
102  QColor currentColor = firstSymbol->color();
103 
104  QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast< QWidget * >( parent() ) );
105  if ( panel && panel->dockMode() )
106  {
108  colorWidget->setPanelTitle( tr( "Change Symbol Color" ) );
109  colorWidget->setAllowOpacity( true );
110  connect( colorWidget, &QgsCompoundColorWidget::currentColorChanged, this, [ = ]( const QColor & color )
111  {
112  for ( QgsSymbol *symbol : symbolList )
113  {
114  if ( symbol )
115  symbol->setColor( color );
116  }
118  } );
119  panel->openPanel( colorWidget );
120  }
121  else
122  {
123  // modal dialog version... yuck
124  QColor color = QgsColorDialog::getColor( firstSymbol->color(), this, QStringLiteral( "Change Symbol Color" ), true );
125  if ( color.isValid() )
126  {
127  for ( QgsSymbol *symbol : symbolList )
128  {
129  if ( symbol )
130  symbol->setColor( color );
131  }
133  }
134  }
135 }
136 
138 {
139  QList<QgsSymbol *> symbolList = selectedSymbols();
140  if ( symbolList.isEmpty() )
141  {
142  return;
143  }
144 
145  QgsSymbol *firstSymbol = nullptr;
146  const auto constSymbolList = symbolList;
147  for ( QgsSymbol *symbol : constSymbolList )
148  {
149  if ( symbol )
150  {
151  firstSymbol = symbol;
152  break;
153  }
154  }
155  if ( !firstSymbol )
156  return;
157 
158  bool ok;
159  double oldOpacity = firstSymbol->opacity() * 100; // convert to %
160  double opacity = QInputDialog::getDouble( this, tr( "Opacity" ), tr( "Change symbol opacity [%]" ), oldOpacity, 0.0, 100.0, 1, &ok );
161  if ( ok )
162  {
163  const auto constSymbolList = symbolList;
164  for ( QgsSymbol *symbol : constSymbolList )
165  {
166  if ( symbol )
167  symbol->setOpacity( opacity / 100.0 );
168  }
170  }
171 }
172 
174 {
175  QList<QgsSymbol *> symbolList = selectedSymbols();
176  if ( symbolList.isEmpty() )
177  {
178  return;
179  }
180 
181  QgsSymbol *firstSymbol = nullptr;
182  const auto constSymbolList = symbolList;
183  for ( QgsSymbol *symbol : constSymbolList )
184  {
185  if ( symbol )
186  {
187  firstSymbol = symbol;
188  break;
189  }
190  }
191  if ( !firstSymbol )
192  return;
193 
194  bool ok;
195  int currentUnit = ( firstSymbol->outputUnit() == QgsUnitTypes::RenderMillimeters ) ? 0 : 1;
196  QString item = QInputDialog::getItem( this, tr( "Symbol unit" ), tr( "Select symbol unit" ), QStringList() << tr( "Millimeter" ) << tr( "Map unit" ), currentUnit, false, &ok );
197  if ( ok )
198  {
199  QgsUnitTypes::RenderUnit unit = ( item.compare( tr( "Millimeter" ) ) == 0 ) ? QgsUnitTypes::RenderMillimeters : QgsUnitTypes::RenderMapUnits;
200 
201  const auto constSymbolList = symbolList;
202  for ( QgsSymbol *symbol : constSymbolList )
203  {
204  if ( symbol )
205  symbol->setOutputUnit( unit );
206  }
208  }
209 }
210 
212 {
213  QList<QgsSymbol *> symbolList = selectedSymbols();
214  if ( symbolList.isEmpty() )
215  {
216  return;
217  }
218 
219  QgsDataDefinedWidthDialog dlg( symbolList, mLayer );
220 
221  dlg.setContext( mContext );
222 
223  if ( QDialog::Accepted == dlg.exec() )
224  {
225  if ( !dlg.mDDBtn->isActive() )
226  {
227  const auto constSymbolList = symbolList;
228  for ( QgsSymbol *symbol : constSymbolList )
229  {
230  if ( !symbol )
231  continue;
232 
233  if ( symbol->type() == QgsSymbol::Line )
234  static_cast<QgsLineSymbol *>( symbol )->setWidth( dlg.mSpinBox->value() );
235  }
236  }
238  }
239 }
240 
242 {
243  QList<QgsSymbol *> symbolList = selectedSymbols();
244  if ( symbolList.isEmpty() )
245  {
246  return;
247  }
248 
249  QgsDataDefinedSizeDialog dlg( symbolList, mLayer );
250  dlg.setContext( mContext );
251 
252  if ( QDialog::Accepted == dlg.exec() )
253  {
254  if ( !dlg.mDDBtn->isActive() )
255  {
256  const auto constSymbolList = symbolList;
257  for ( QgsSymbol *symbol : constSymbolList )
258  {
259  if ( !symbol )
260  continue;
261 
262  if ( symbol->type() == QgsSymbol::Marker )
263  static_cast<QgsMarkerSymbol *>( symbol )->setSize( dlg.mSpinBox->value() );
264  }
265  }
267  }
268 }
269 
271 {
272  QList<QgsSymbol *> symbolList = selectedSymbols();
273  if ( symbolList.isEmpty() )
274  {
275  return;
276  }
277 
278  QgsDataDefinedRotationDialog dlg( symbolList, mLayer );
279  dlg.setContext( mContext );
280 
281  if ( QDialog::Accepted == dlg.exec() )
282  {
283  if ( !dlg.mDDBtn->isActive() )
284  {
285  const auto constSymbolList = symbolList;
286  for ( QgsSymbol *symbol : constSymbolList )
287  {
288  if ( !symbol )
289  continue;
290 
291  if ( symbol->type() == QgsSymbol::Marker )
292  static_cast<QgsMarkerSymbol *>( symbol )->setAngle( dlg.mSpinBox->value() );
293  }
294  }
296  }
297 }
298 
300 {
301 
302 }
303 
304 void QgsRendererWidget::copySymbol()
305 {
306  QList<QgsSymbol *> symbolList = selectedSymbols();
307  if ( symbolList.isEmpty() )
308  {
309  return;
310  }
311 
312  QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( symbolList.at( 0 ) ) );
313 }
314 
316 {
318  if ( panel && panel->dockMode() )
319  {
320  QgsSymbolLevelsWidget *widget = new QgsSymbolLevelsWidget( r, r->usingSymbolLevels(), panel );
321  widget->setPanelTitle( tr( "Symbol Levels" ) );
322  connect( widget, &QgsPanelWidget::widgetChanged, widget, &QgsSymbolLevelsWidget::apply );
323  connect( widget, &QgsPanelWidget::widgetChanged, this, [ = ]() { emit widgetChanged(); emit symbolLevelsChanged(); } );
324  panel->openPanel( widget );
325  return;
326  }
327 
328  QgsSymbolLevelsDialog dlg( r, r->usingSymbolLevels(), panel );
329  if ( dlg.exec() )
330  {
331  emit widgetChanged();
332  emit symbolLevelsChanged();
333  }
334 }
335 
337 {
338  mContext = context;
339 }
340 
342 {
343  return mContext;
344 }
345 
347 {
348  apply();
349 }
350 
352 {
353  if ( dockMode )
354  {
355  // when in dock mode, these shortcuts conflict with the main window shortcuts and cannot be used
356  if ( mCopyAction )
357  mCopyAction->setShortcut( QKeySequence() );
358  if ( mPasteAction )
359  mPasteAction->setShortcut( QKeySequence() );
360  }
361  QgsPanelWidget::setDockMode( dockMode );
362 }
363 
364 QgsDataDefinedSizeLegendWidget *QgsRendererWidget::createDataDefinedSizeLegendWidget( const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend )
365 {
366  QgsProperty ddSize = symbol->dataDefinedSize();
367  if ( !ddSize || !ddSize.isActive() )
368  {
369  QMessageBox::warning( this, tr( "Data-defined Size Legend" ), tr( "Data-defined size is not enabled!" ) );
370  return nullptr;
371  }
372 
373  QgsDataDefinedSizeLegendWidget *panel = new QgsDataDefinedSizeLegendWidget( ddsLegend, ddSize, symbol->clone(), mContext.mapCanvas() );
375  return panel;
376 }
377 
378 
379 //
380 // QgsDataDefinedValueDialog
381 //
382 
383 QgsDataDefinedValueDialog::QgsDataDefinedValueDialog( const QList<QgsSymbol *> &symbolList, QgsVectorLayer *layer, const QString &label )
384  : mSymbolList( symbolList )
385  , mLayer( layer )
386 {
387  setupUi( this );
388  setWindowFlags( Qt::WindowStaysOnTopHint );
389  mLabel->setText( label );
391 }
392 
394 {
395  mContext = context;
396 }
397 
399 {
400  return mContext;
401 }
402 
403 QgsExpressionContext QgsDataDefinedValueDialog::createExpressionContext() const
404 {
405  QgsExpressionContext expContext;
409  if ( mContext.mapCanvas() )
410  {
413  }
414  else
415  {
417  }
418 
419  if ( vectorLayer() )
421 
422  // additional scopes
423  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
424  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
425  {
426  expContext.appendScope( new QgsExpressionContextScope( scope ) );
427  }
428 
429  return expContext;
430 }
431 
432 void QgsDataDefinedValueDialog::init( int propertyKey )
433 {
434  QgsProperty dd( symbolDataDefined() );
435 
436  mDDBtn->init( propertyKey, dd, QgsSymbolLayer::propertyDefinitions(), mLayer );
437  mDDBtn->registerExpressionContextGenerator( this );
438 
439  QgsSymbol *initialSymbol = nullptr;
440  const auto constMSymbolList = mSymbolList;
441  for ( QgsSymbol *symbol : constMSymbolList )
442  {
443  if ( symbol )
444  {
445  initialSymbol = symbol;
446  }
447  }
448  mSpinBox->setValue( initialSymbol ? value( initialSymbol ) : 0 );
449  mSpinBox->setEnabled( !mDDBtn->isActive() );
450 }
451 
452 QgsProperty QgsDataDefinedValueDialog::symbolDataDefined() const
453 {
454  if ( mSymbolList.isEmpty() || !mSymbolList.back() )
455  return QgsProperty();
456 
457  // check that all symbols share the same size expression
458  QgsProperty dd = symbolDataDefined( mSymbolList.back() );
459  const auto constMSymbolList = mSymbolList;
460  for ( QgsSymbol *it : constMSymbolList )
461  {
462  QgsProperty symbolDD( symbolDataDefined( it ) );
463  if ( !it || !dd || !symbolDD || symbolDD != dd )
464  return QgsProperty();
465  }
466  return dd;
467 }
468 
470 {
471  QgsProperty dd( mDDBtn->toProperty() );
472  mSpinBox->setEnabled( !dd.isActive() );
473 
474  QgsProperty symbolDD( symbolDataDefined() );
475 
476  if ( // shall we remove datadefined expressions for layers ?
477  ( symbolDD && symbolDD.isActive() && !dd.isActive() )
478  // shall we set the "en masse" expression for properties ?
479  || dd.isActive() )
480  {
481  const auto constMSymbolList = mSymbolList;
482  for ( QgsSymbol *it : constMSymbolList )
483  setDataDefined( it, dd );
484  }
485 }
486 
488 {
489  const QgsMarkerSymbol *marker = static_cast<const QgsMarkerSymbol *>( symbol );
490  return marker->dataDefinedSize();
491 }
492 
494 {
495  static_cast<QgsMarkerSymbol *>( symbol )->setDataDefinedSize( dd );
496  static_cast<QgsMarkerSymbol *>( symbol )->setScaleMethod( QgsSymbol::ScaleDiameter );
497 }
498 
499 
501 {
502  const QgsMarkerSymbol *marker = static_cast<const QgsMarkerSymbol *>( symbol );
503  return marker->dataDefinedAngle();
504 }
505 
507 {
508  static_cast<QgsMarkerSymbol *>( symbol )->setDataDefinedAngle( dd );
509 }
510 
511 
513 {
514  const QgsLineSymbol *line = static_cast<const QgsLineSymbol *>( symbol );
515  return line->dataDefinedWidth();
516 }
517 
519 {
520  static_cast<QgsLineSymbol *>( symbol )->setDataDefinedWidth( dd );
521 }
522 
523 void QgsRendererWidget::apply()
524 {
525 
526 }
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
static QgsSymbol * symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
void changeSymbolOpacity()
Change opacity of selected symbols.
void changeSymbolWidth()
Change line widths of selected symbols.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
bool dockMode()
Returns the dock mode state.
Calculate scale by the diameter.
Definition: qgssymbol.h:98
double value(const QgsSymbol *symbol) const override
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:62
virtual void refreshSymbolView()
void changeSymbolAngle()
Change marker angles of selected symbols.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
void showSymbolLevelsDialog(QgsFeatureRenderer *r)
show a dialog with renderer&#39;s symbol level settings
double value(const QgsSymbol *symbol) const override
QgsVectorLayer * mLayer
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Base class for any widget that can be shown as a inline panel.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Line symbol.
Definition: qgssymbol.h:87
QgsProperty symbolDataDefined(const QgsSymbol *symbol) const override
void symbolLevelsChanged()
Emitted when the symbol levels settings have been changed.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:895
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1095
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs...
The QgsMapSettings class contains configuration for rendering of the map.
QgsUnitTypes::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:234
void applyChanges()
This method should be called whenever the renderer is actually set on the layer.
QgsProperty dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1396
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QAction * mCopySymbolAction
Copy symbol action.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
void setDataDefined(QgsSymbol *symbol, const QgsProperty &dd) override
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown...
A custom QGIS widget for selecting a color, including options for selecting colors via hue wheel...
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs...
static QMimeData * symbolToMimeData(const QgsSymbol *symbol)
Creates new mime data from a symbol.
A widget which allows the user to modify the rendering order of symbol layers.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void changeSymbolSize()
Change marker sizes of selected symbols.
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:491
QgsDataDefinedSizeLegendWidget * createDataDefinedSizeLegendWidget(const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend)
Creates widget to setup data-defined size legend.
Single scope for storing variables and functions for use within a QgsExpressionContext.
A store for object properties.
Definition: qgsproperty.h:229
virtual void paste()
virtual void copy()
void widgetChanged()
Emitted when the widget state changes.
QgsProperty symbolDataDefined(const QgsSymbol *symbol) const override
virtual void pasteSymbolToSelection()
Pastes the clipboard symbol over selected items.
A dialog which allows the user to modify the rendering order of symbol layers.
double value(const QgsSymbol *symbol) const override
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1601
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void changed()
Emitted when property definition changes.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
Marker symbol.
Definition: qgssymbol.h:86
void apply()
Apply button.
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:283
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:583
virtual QList< QgsSymbol * > selectedSymbols()
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
void currentColorChanged(const QColor &color)
Emitted when the dialog&#39;s color changes.
void init(int propertyKey)
Should be called in the constructor of child classes.
void setDataDefined(QgsSymbol *symbol, const QgsProperty &dd) override
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
void contextMenuViewCategories(QPoint p)
QgsProperty symbolDataDefined(const QgsSymbol *symbol) const override
QgsDataDefinedValueDialog(const QList< QgsSymbol *> &symbolList, QgsVectorLayer *layer, const QString &label)
Constructor.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:442
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol layer property definitions.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsRendererWidget(QgsVectorLayer *layer, QgsStyle *style)
void changeSymbolColor()
Change color of selected symbols.
Represents a vector layer which manages a vector based data sets.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1887
void changeSymbolUnit()
Change units mm/map units of selected symbols.
Use a narrower, vertically stacked layout.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1766
bool isActive() const
Returns whether the property is currently active.
void setDataDefined(QgsSymbol *symbol, const QgsProperty &dd) override
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer...
QAction * mPasteSymbolAction
Paste symbol action.