QGIS API Documentation  3.25.0-Master (10b47c2603)
qgsdatadefinedsizelegendwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdatadefinedsizelegendwidget.cpp
3  --------------------------------------
4  Date : June 2017
5  Copyright : (C) 2017 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 
17 
18 #include <QInputDialog>
19 #include <QStyledItemDelegate>
20 
22 #include "qgslayertree.h"
23 #include "qgslayertreemodel.h"
24 #include "qgsmapcanvas.h"
26 #include "qgsstyle.h"
27 #include "qgssymbol.h"
28 #include "qgssymbollayer.h"
30 #include "qgsvectorlayer.h"
32 #include "qgsdoublevalidator.h"
33 #include "qgsmarkersymbol.h"
34 #include "qgslinesymbol.h"
35 
37  : QgsPanelWidget( parent )
38  , mSizeProperty( ddSize )
39  , mMapCanvas( canvas )
40 {
41  setupUi( this );
42  setPanelTitle( tr( "Data-defined Size Legend" ) );
43 
44  mLineSymbolButton->setSymbolType( Qgis::SymbolType::Line );
45 
46  QgsMarkerSymbol *symbol = nullptr;
47 
48  if ( !ddsLegend )
49  {
50  radDisabled->setChecked( true );
51  }
52  else
53  {
55  radSeparated->setChecked( true );
56  else
57  radCollapsed->setChecked( true );
58 
60  cboAlignSymbols->setCurrentIndex( 0 );
61  else
62  cboAlignSymbols->setCurrentIndex( 1 );
63 
64  if ( ddsLegend->lineSymbol() )
65  mLineSymbolButton->setSymbol( ddsLegend->lineSymbol()->clone() );
66 
67  symbol = ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr; // may be null (undefined)
68  }
69  groupBoxOptions->setEnabled( radSeparated->isChecked() );
70 
71  if ( overrideSymbol )
72  {
73  symbol = overrideSymbol; // takes ownership
74  mOverrideSymbol = true;
75  }
76 
77  if ( !symbol )
78  {
79  symbol = QgsMarkerSymbol::createSimple( QVariantMap() );
80  }
81  mSourceSymbol.reset( symbol );
82 
83  btnChangeSymbol->setEnabled( !mOverrideSymbol );
84 
85  const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
86  btnChangeSymbol->setIcon( icon );
87 
88  editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
89 
90  mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
91  mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
92  mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
93  if ( ddsLegend )
94  {
95  groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
96  const auto constClasses = ddsLegend->classes();
97  for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
98  {
99  QStandardItem *item = new QStandardItem( QLocale().toString( sc.size ) );
100  item->setData( sc.size );
101  QStandardItem *itemLabel = new QStandardItem( sc.label );
102  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
103  }
104  mSizeClassesModel->sort( 0 );
105  }
106 
107  connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
108  connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
109 
110  viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
111  viewSizeClasses->setModel( mSizeClassesModel );
112  connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
113 
114  // prepare layer and model to preview legend
116  mPreviewLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326" ), QStringLiteral( "Preview" ), QStringLiteral( "memory" ), options );
117  mPreviewTree = new QgsLayerTree;
118  mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
119  mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
120  if ( canvas )
121  mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
122  viewLayerTree->setModel( mPreviewModel );
123 
124  connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ] { emit widgetChanged(); } );
125  connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
126  connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
127  connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
128  connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
129  connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
130  connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
131  connect( mLineSymbolButton, &QgsSymbolButton::changed, this, &QgsPanelWidget::widgetChanged );
132  connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
133  connect( radCollapsed, &QRadioButton::toggled, this, [ = ]( bool toggled ) {groupBoxOptions->setEnabled( toggled );} );
134  updatePreview();
135 }
136 
138 {
139  delete mPreviewModel;
140  delete mPreviewTree;
141  delete mPreviewLayer;
142 }
143 
145 {
146  if ( radDisabled->isChecked() )
147  return nullptr;
148 
151  ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
152  if ( !mOverrideSymbol )
153  {
154  ddsLegend->setSymbol( mSourceSymbol->clone() );
155  }
156 
157  ddsLegend->setTitle( editTitle->text() );
158 
159  if ( groupManualSizeClasses->isChecked() )
160  {
161  QList<QgsDataDefinedSizeLegend::SizeClass> classes;
162  for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
163  {
164  const double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
165  const QString label = mSizeClassesModel->item( i, 1 )->text();
166  classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
167  }
168  ddsLegend->setClasses( classes );
169  }
170 
171  ddsLegend->setLineSymbol( mLineSymbolButton->clonedSymbol< QgsLineSymbol >() );
172  return ddsLegend;
173 }
174 
175 void QgsDataDefinedSizeLegendWidget::updatePreview()
176 {
177  QgsMarkerSymbol *symbol = mSourceSymbol->clone();
178  symbol->setDataDefinedSize( mSizeProperty );
181  mPreviewLayer->setRenderer( r );
182  mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
183  viewLayerTree->expandAll();
184 }
185 
186 void QgsDataDefinedSizeLegendWidget::changeSymbol()
187 {
188  std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
189  QgsSymbolWidgetContext context;
190  if ( mMapCanvas )
191  context.setMapCanvas( mMapCanvas );
192 
197  if ( mMapCanvas )
199  context.setExpressionContext( &ec );
200 
201  const QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
203  const std::unique_ptr<QgsVectorLayer> layer = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=%1" ).arg( crsAuthId ),
204  QStringLiteral( "tmp" ),
205  QStringLiteral( "memory" ),
206  options ) ;
207 
208  QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
209  d.setContext( context );
210 
211  if ( d.exec() != QDialog::Accepted )
212  return;
213 
214  mSourceSymbol = std::move( newSymbol );
215  const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
216  btnChangeSymbol->setIcon( icon );
217 
218  emit widgetChanged();
219 }
220 
221 void QgsDataDefinedSizeLegendWidget::addSizeClass()
222 {
223  bool ok;
224  const double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ),
225  0, -2147483647, 2147483647, 6, &ok );
226  if ( !ok )
227  return;
228 
229  QStandardItem *item = new QStandardItem( QLocale().toString( v ) );
230  item->setData( v );
231  QStandardItem *itemLabel = new QStandardItem( QLocale().toString( v ) );
232  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
233  mSizeClassesModel->sort( 0 );
234  emit widgetChanged();
235 }
236 
237 void QgsDataDefinedSizeLegendWidget::removeSizeClass()
238 {
239  const QModelIndex idx = viewSizeClasses->currentIndex();
240  if ( !idx.isValid() )
241  return;
242 
243  mSizeClassesModel->removeRow( idx.row() );
244  emit widgetChanged();
245 }
246 
247 void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
248 {
249  for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
250  {
251  QStandardItem *item = mSizeClassesModel->item( row, 0 );
252  item->setData( QgsDoubleValidator::toDouble( item->text() ) );
253  }
254 
255  mSizeClassesModel->sort( 0 );
256  emit widgetChanged();
257 }
@ Line
Line symbol.
QgsDataDefinedSizeLegendWidget(const QgsDataDefinedSizeLegend *ddsLegend, const QgsProperty &ddSize, QgsMarkerSymbol *overrideSymbol, QgsMapCanvas *canvas=nullptr, QWidget *parent=nullptr)
Creates the dialog and initializes the content to what is passed in the legend configuration (may be ...
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
void setTitle(const QString &title)
Sets title label for data-defined size legend.
QList< QgsDataDefinedSizeLegend::SizeClass > classes() const
Returns list of classes: each class is a pair of symbol size (in units used by the symbol) and label.
void setSymbol(QgsMarkerSymbol *symbol SIP_TRANSFER)
Sets marker symbol that will be used to draw markers in legend.
void setVerticalAlignment(VerticalAlignment vAlign)
Sets vertical alignment of symbols - only valid for collapsed legend.
void setLineSymbol(QgsLineSymbol *symbol SIP_TRANSFER)
Sets the line symbol that will be used to draw callout lines in legend.
LegendType legendType() const
Returns how the legend should be rendered.
QgsMarkerSymbol * symbol() const
Returns marker symbol that will be used to draw markers in legend.
@ AlignCenter
Symbols are aligned to the center.
@ AlignBottom
Symbols are aligned to the bottom.
void setClasses(const QList< QgsDataDefinedSizeLegend::SizeClass > &classes)
Sets list of classes: each class is a pair of symbol size (in units used by the symbol) and label.
void setLegendType(LegendType type)
Sets how the legend should be rendered.
QString title() const
Returns title label for data-defined size legend.
@ LegendSeparated
Each class (size value) has a separate legend node.
@ LegendCollapsed
All classes are rendered within one legend node.
QgsLineSymbol * lineSymbol() const
Returns the line symbol that will be used to draw callout lines in legend.
VerticalAlignment verticalAlignment() const
Returns vertical alignment of symbols - only valid for collapsed legend.
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
void setLegendMapViewData(double mapUnitsPerPixel, int dpi, double scale)
Give the layer tree model hints about the currently associated map view so that legend nodes that use...
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
double scale() const
Returns the last reported scale of the canvas.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
A marker symbol type, for rendering Point and MultiPoint geometries.
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
void setDataDefinedSize(const QgsProperty &property) const
Set data defined size for whole symbol (including all symbol layers).
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Base class for any widget that can be shown as a inline panel.
void widgetChanged()
Emitted when the widget state changes.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:479
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
A store for object properties.
Definition: qgsproperty.h:231
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
void changed()
Emitted when the symbol's settings are changed.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr)
Returns an icon preview for a color ramp.
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 setExpressionContext(QgsExpressionContext *context)
Sets the optional expression context used for the widget.
Represents a vector layer which manages a vector based data sets.
void setRenderer(QgsFeatureRenderer *r)
Sets the feature renderer which will be invoked to represent this layer in 2D map views.
Definition of one class for the legend.
Setting options for loading vector layers.