QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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, [ = ]() { 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 
351 QgsDataDefinedSizeLegendWidget *QgsRendererWidget::createDataDefinedSizeLegendWidget( const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend )
352 {
353  QgsProperty ddSize = symbol->dataDefinedSize();
354  if ( !ddSize || !ddSize.isActive() )
355  {
356  QMessageBox::warning( this, tr( "Data-defined Size Legend" ), tr( "Data-defined size is not enabled!" ) );
357  return nullptr;
358  }
359 
360  QgsDataDefinedSizeLegendWidget *panel = new QgsDataDefinedSizeLegendWidget( ddsLegend, ddSize, symbol->clone(), mContext.mapCanvas() );
362  return panel;
363 }
364 
365 
366 //
367 // QgsDataDefinedValueDialog
368 //
369 
370 QgsDataDefinedValueDialog::QgsDataDefinedValueDialog( const QList<QgsSymbol *> &symbolList, QgsVectorLayer *layer, const QString &label )
371  : mSymbolList( symbolList )
372  , mLayer( layer )
373 {
374  setupUi( this );
375  setWindowFlags( Qt::WindowStaysOnTopHint );
376  mLabel->setText( label );
378 }
379 
381 {
382  mContext = context;
383 }
384 
386 {
387  return mContext;
388 }
389 
390 QgsExpressionContext QgsDataDefinedValueDialog::createExpressionContext() const
391 {
392  QgsExpressionContext expContext;
396  if ( mContext.mapCanvas() )
397  {
400  }
401  else
402  {
404  }
405 
406  if ( vectorLayer() )
408 
409  // additional scopes
410  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
411  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
412  {
413  expContext.appendScope( new QgsExpressionContextScope( scope ) );
414  }
415 
416  return expContext;
417 }
418 
419 void QgsDataDefinedValueDialog::init( int propertyKey )
420 {
421  QgsProperty dd( symbolDataDefined() );
422 
423  mDDBtn->init( propertyKey, dd, QgsSymbolLayer::propertyDefinitions(), mLayer );
424  mDDBtn->registerExpressionContextGenerator( this );
425 
426  QgsSymbol *initialSymbol = nullptr;
427  const auto constMSymbolList = mSymbolList;
428  for ( QgsSymbol *symbol : constMSymbolList )
429  {
430  if ( symbol )
431  {
432  initialSymbol = symbol;
433  }
434  }
435  mSpinBox->setValue( initialSymbol ? value( initialSymbol ) : 0 );
436  mSpinBox->setEnabled( !mDDBtn->isActive() );
437 }
438 
439 QgsProperty QgsDataDefinedValueDialog::symbolDataDefined() const
440 {
441  if ( mSymbolList.isEmpty() || !mSymbolList.back() )
442  return QgsProperty();
443 
444  // check that all symbols share the same size expression
445  QgsProperty dd = symbolDataDefined( mSymbolList.back() );
446  const auto constMSymbolList = mSymbolList;
447  for ( QgsSymbol *it : constMSymbolList )
448  {
449  QgsProperty symbolDD( symbolDataDefined( it ) );
450  if ( !it || !dd || !symbolDD || symbolDD != dd )
451  return QgsProperty();
452  }
453  return dd;
454 }
455 
457 {
458  QgsProperty dd( mDDBtn->toProperty() );
459  mSpinBox->setEnabled( !dd.isActive() );
460 
461  QgsProperty symbolDD( symbolDataDefined() );
462 
463  if ( // shall we remove datadefined expressions for layers ?
464  ( symbolDD && symbolDD.isActive() && !dd.isActive() )
465  // shall we set the "en masse" expression for properties ?
466  || dd.isActive() )
467  {
468  const auto constMSymbolList = mSymbolList;
469  for ( QgsSymbol *it : constMSymbolList )
470  setDataDefined( it, dd );
471  }
472 }
473 
475 {
476  const QgsMarkerSymbol *marker = static_cast<const QgsMarkerSymbol *>( symbol );
477  return marker->dataDefinedSize();
478 }
479 
481 {
482  static_cast<QgsMarkerSymbol *>( symbol )->setDataDefinedSize( dd );
483  static_cast<QgsMarkerSymbol *>( symbol )->setScaleMethod( QgsSymbol::ScaleDiameter );
484 }
485 
486 
488 {
489  const QgsMarkerSymbol *marker = static_cast<const QgsMarkerSymbol *>( symbol );
490  return marker->dataDefinedAngle();
491 }
492 
494 {
495  static_cast<QgsMarkerSymbol *>( symbol )->setDataDefinedAngle( dd );
496 }
497 
498 
500 {
501  const QgsLineSymbol *line = static_cast<const QgsLineSymbol *>( symbol );
502  return line->dataDefinedWidth();
503 }
504 
506 {
507  static_cast<QgsLineSymbol *>( symbol )->setDataDefinedWidth( dd );
508 }
509 
510 void QgsRendererWidget::apply()
511 {
512 
513 }
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:97
double value(const QgsSymbol *symbol) const override
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
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
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:86
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:860
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1060
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:1338
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...
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:484
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:1543
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:85
void apply()
Apply button.
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:272
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:573
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:442
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:433
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:1829
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:1708
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.