QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 );
180  r->setDataDefinedSizeLegend( dataDefinedSizeLegend() );
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 )
198  ec << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() );
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 }
QgsSymbolSelectorDialog
Definition: qgssymbolselectordialog.h:276
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
qgsexpressioncontextutils.h
QgsDataDefinedSizeLegend::setSymbol
void setSymbol(QgsMarkerSymbol *symbol SIP_TRANSFER)
Sets marker symbol that will be used to draw markers in legend.
Definition: qgsdatadefinedsizelegend.cpp:68
QgsSymbolButton::changed
void changed()
Emitted when the symbol's settings are changed.
QgsProperty
A store for object properties.
Definition: qgsproperty.h:230
Qgis::SymbolType::Line
@ Line
Line symbol.
QgsDataDefinedSizeLegend::AlignBottom
@ AlignBottom
Symbols are aligned to the bottom.
Definition: qgsdatadefinedsizelegend.h:68
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:40
qgsmapcanvas.h
QgsDataDefinedSizeLegend::title
QString title() const
Returns title label for data-defined size legend.
Definition: qgsdatadefinedsizelegend.h:123
qgssinglesymbolrenderer.h
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:437
QgsLineSymbol::clone
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgslinesymbol.cpp:303
QgsSymbolWidgetContext
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
Definition: qgssymbolwidgetcontext.h:35
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
QgsDataDefinedSizeLegend
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
Definition: qgsdatadefinedsizelegend.h:42
QgsExpressionContextUtils::mapSettingsScope
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
Definition: qgsexpressioncontextutils.cpp:427
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsMapCanvas::scale
double scale() const
Returns the last reported scale of the canvas.
Definition: qgsmapcanvas.cpp:367
QgsSingleSymbolRenderer
Definition: qgssinglesymbolrenderer.h:29
QgsSingleSymbolRenderer::setDataDefinedSizeLegend
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
Definition: qgssinglesymbolrenderer.cpp:388
qgsdatadefinedsizelegendwidget.h
QgsDataDefinedSizeLegendWidget::QgsDataDefinedSizeLegendWidget
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 ...
Definition: qgsdatadefinedsizelegendwidget.cpp:36
QgsLayerTreeModel
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Definition: qgslayertreemodel.h:55
QgsDataDefinedSizeLegend::setClasses
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.
Definition: qgsdatadefinedsizelegend.h:116
QgsMarkerSymbol::setDataDefinedSize
void setDataDefinedSize(const QgsProperty &property) const
Set data defined size for whole symbol (including all symbol layers).
Definition: qgsmarkersymbol.cpp:306
QgsMarkerSymbol::clone
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgsmarkersymbol.cpp:523
QgsSymbolWidgetContext::setMapCanvas
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
Definition: qgssymbolwidgetcontext.cpp:49
QgsDataDefinedSizeLegend::AlignCenter
@ AlignCenter
Symbols are aligned to the center.
Definition: qgsdatadefinedsizelegend.h:67
QgsStyle::defaultStyle
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
QgsDataDefinedSizeLegend::SizeClass
Definition of one class for the legend.
Definition: qgsdatadefinedsizelegend.h:72
QgsDataDefinedSizeLegend::LegendCollapsed
@ LegendCollapsed
All classes are rendered within one legend node.
Definition: qgsdatadefinedsizelegend.h:61
QgsSymbolLayerUtils::symbolPreviewIcon
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr)
Returns an icon preview for a color ramp.
Definition: qgssymbollayerutils.cpp:871
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:291
QgsLayerTree
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsMarkerSymbol
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgsmarkersymbol.h:30
QgsLayerTreeGroup::addLayer
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
Definition: qgslayertreegroup.cpp:94
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsLineSymbol
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:29
QgsDataDefinedSizeLegend::LegendSeparated
@ LegendSeparated
Each class (size value) has a separate legend node.
Definition: qgsdatadefinedsizelegend.h:60
qgssymbollayer.h
qgslayertree.h
QgsPanelWidget::setPanelTitle
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Definition: qgspanelwidget.h:44
QgsDataDefinedSizeLegend::setVerticalAlignment
void setVerticalAlignment(VerticalAlignment vAlign)
Sets vertical alignment of symbols - only valid for collapsed legend.
Definition: qgsdatadefinedsizelegend.h:126
QgsDataDefinedSizeLegend::lineSymbol
QgsLineSymbol * lineSymbol() const
Returns the line symbol that will be used to draw callout lines in legend.
Definition: qgsdatadefinedsizelegend.cpp:83
qgssymbolselectordialog.h
qgsstyle.h
qgsvectorlayer.h
QgsDataDefinedSizeLegend::setTitle
void setTitle(const QString &title)
Sets title label for data-defined size legend.
Definition: qgsdatadefinedsizelegend.h:121
QgsVectorLayer::LayerOptions
Setting options for loading vector layers.
Definition: qgsvectorlayer.h:408
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
qgsmarkersymbol.h
QgsDoubleValidator::toDouble
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
Definition: qgsdoublevalidator.cpp:134
QgsDataDefinedSizeLegendWidget::dataDefinedSizeLegend
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller.
Definition: qgsdatadefinedsizelegendwidget.cpp:144
QgsDataDefinedSizeLegend::symbol
QgsMarkerSymbol * symbol() const
Returns marker symbol that will be used to draw markers in legend.
Definition: qgsdatadefinedsizelegend.cpp:73
QgsLayerTreeModel::setLegendMapViewData
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...
Definition: qgslayertreemodel.cpp:655
QgsExpressionContextUtils::atlasScope
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
Definition: qgsexpressioncontextutils.cpp:660
QgsDataDefinedSizeLegend::classes
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.
Definition: qgsdatadefinedsizelegend.h:118
qgslayertreemodel.h
QgsDataDefinedSizeLegend::setLegendType
void setLegendType(LegendType type)
Sets how the legend should be rendered.
Definition: qgsdatadefinedsizelegend.h:84
qgsdatadefinedsizelegend.h
QgsSymbolWidgetContext::setExpressionContext
void setExpressionContext(QgsExpressionContext *context)
Sets the optional expression context used for the widget.
Definition: qgssymbolwidgetcontext.cpp:69
qgsdoublevalidator.h
QgsMapSettings::outputDpi
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
Definition: qgsmapsettings.cpp:267
QgsMapCanvas::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
Definition: qgsmapcanvas.cpp:2628
QgsDataDefinedSizeLegend::setLineSymbol
void setLineSymbol(QgsLineSymbol *symbol SIP_TRANSFER)
Sets the line symbol that will be used to draw callout lines in legend.
Definition: qgsdatadefinedsizelegend.cpp:78
QgsMarkerSymbol::createSimple
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgsmarkersymbol.cpp:22
qgssymbol.h
QgsDataDefinedSizeLegend::verticalAlignment
VerticalAlignment verticalAlignment() const
Returns vertical alignment of symbols - only valid for collapsed legend.
Definition: qgsdatadefinedsizelegend.h:128
QgsDataDefinedSizeLegend::legendType
LegendType legendType() const
Returns how the legend should be rendered.
Definition: qgsdatadefinedsizelegend.h:86
qgslinesymbol.h
QgsDataDefinedSizeLegendWidget::~QgsDataDefinedSizeLegendWidget
~QgsDataDefinedSizeLegendWidget() override
Definition: qgsdatadefinedsizelegendwidget.cpp:137