QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
33 
35  : QgsPanelWidget( parent )
36  , mSizeProperty( ddSize )
37  , mMapCanvas( canvas )
38 {
39  setupUi( this );
40  setPanelTitle( tr( "Data-defined Size Legend" ) );
41 
42  mLineSymbolButton->setSymbolType( QgsSymbol::Line );
43 
44  QgsMarkerSymbol *symbol = nullptr;
45 
46  if ( !ddsLegend )
47  {
48  radDisabled->setChecked( true );
49  }
50  else
51  {
53  radSeparated->setChecked( true );
54  else
55  radCollapsed->setChecked( true );
56 
58  cboAlignSymbols->setCurrentIndex( 0 );
59  else
60  cboAlignSymbols->setCurrentIndex( 1 );
61 
62  if ( ddsLegend->lineSymbol() )
63  mLineSymbolButton->setSymbol( ddsLegend->lineSymbol()->clone() );
64 
65  symbol = ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr; // may be null (undefined)
66  }
67 
68  if ( overrideSymbol )
69  {
70  symbol = overrideSymbol; // takes ownership
71  mOverrideSymbol = true;
72  }
73 
74  if ( !symbol )
75  {
76  symbol = QgsMarkerSymbol::createSimple( QVariantMap() );
77  }
78  mSourceSymbol.reset( symbol );
79 
80  btnChangeSymbol->setEnabled( !mOverrideSymbol );
81 
82  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
83  btnChangeSymbol->setIcon( icon );
84 
85  editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
86 
87  mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
88  mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
89  mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
90  if ( ddsLegend )
91  {
92  groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
93  const auto constClasses = ddsLegend->classes();
94  for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
95  {
96  QStandardItem *item = new QStandardItem( QString::number( sc.size ) );
97  item->setData( sc.size );
98  QStandardItem *itemLabel = new QStandardItem( sc.label );
99  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
100  }
101  mSizeClassesModel->sort( 0 );
102  }
103 
104  connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
105  connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
106 
107  viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
108  viewSizeClasses->setModel( mSizeClassesModel );
109  connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
110 
111  // prepare layer and model to preview legend
113  mPreviewLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326" ), QStringLiteral( "Preview" ), QStringLiteral( "memory" ), options );
114  mPreviewTree = new QgsLayerTree;
115  mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
116  mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
117  if ( canvas )
118  mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
119  viewLayerTree->setModel( mPreviewModel );
120 
121  connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ] { emit widgetChanged(); } );
122  connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
123  connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
124  connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
125  connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
126  connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
127  connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
128  connect( mLineSymbolButton, &QgsSymbolButton::changed, this, &QgsPanelWidget::widgetChanged );
129  connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
130  updatePreview();
131 }
132 
134 {
135  delete mPreviewModel;
136  delete mPreviewTree;
137  delete mPreviewLayer;
138 }
139 
141 {
142  if ( radDisabled->isChecked() )
143  return nullptr;
144 
147  ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
148  if ( !mOverrideSymbol )
149  {
150  ddsLegend->setSymbol( mSourceSymbol->clone() );
151  }
152 
153  ddsLegend->setTitle( editTitle->text() );
154 
155  if ( groupManualSizeClasses->isChecked() )
156  {
157  QList<QgsDataDefinedSizeLegend::SizeClass> classes;
158  for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
159  {
160  double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
161  QString label = mSizeClassesModel->item( i, 1 )->text();
162  classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
163  }
164  ddsLegend->setClasses( classes );
165  }
166 
167  ddsLegend->setLineSymbol( mLineSymbolButton->clonedSymbol< QgsLineSymbol >() );
168  return ddsLegend;
169 }
170 
171 void QgsDataDefinedSizeLegendWidget::updatePreview()
172 {
173  QgsMarkerSymbol *symbol = mSourceSymbol->clone();
174  symbol->setDataDefinedSize( mSizeProperty );
177  mPreviewLayer->setRenderer( r );
178  mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
179  viewLayerTree->expandAll();
180 }
181 
182 void QgsDataDefinedSizeLegendWidget::changeSymbol()
183 {
184  std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
185  QgsSymbolWidgetContext context;
186  if ( mMapCanvas )
187  context.setMapCanvas( mMapCanvas );
188 
193  if ( mMapCanvas )
195  context.setExpressionContext( &ec );
196 
197  QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
199  std::unique_ptr<QgsVectorLayer> layer = qgis::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=%1" ).arg( crsAuthId ),
200  QStringLiteral( "tmp" ),
201  QStringLiteral( "memory" ),
202  options ) ;
203 
204  QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
205  d.setContext( context );
206 
207  if ( d.exec() != QDialog::Accepted )
208  return;
209 
210  mSourceSymbol = std::move( newSymbol );
211  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
212  btnChangeSymbol->setIcon( icon );
213 
214  emit widgetChanged();
215 }
216 
217 void QgsDataDefinedSizeLegendWidget::addSizeClass()
218 {
219  bool ok;
220  double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ),
221  0, -2147483647, 2147483647, 6, &ok );
222  if ( !ok )
223  return;
224 
225  QStandardItem *item = new QStandardItem( QString::number( v ) );
226  item->setData( v );
227  QStandardItem *itemLabel = new QStandardItem( QString::number( v ) );
228  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
229  mSizeClassesModel->sort( 0 );
230  emit widgetChanged();
231 }
232 
233 void QgsDataDefinedSizeLegendWidget::removeSizeClass()
234 {
235  QModelIndex idx = viewSizeClasses->currentIndex();
236  if ( !idx.isValid() )
237  return;
238 
239  mSizeClassesModel->removeRow( idx.row() );
240  emit widgetChanged();
241 }
242 
243 void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
244 {
245  for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
246  {
247  QStandardItem *item = mSizeClassesModel->item( row, 0 );
248  item->setData( item->text().toDouble() );
249  }
250 
251  mSizeClassesModel->sort( 0 );
252  emit widgetChanged();
253 }
QString authid() const
Returns the authority identifier for the CRS.
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 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: qgssymbol.h:1204
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2295
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
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.
Definition: qgssymbol.h:1004
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1556
void setDataDefinedSize(const QgsProperty &property)
Set data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1832
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2036
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:501
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
A store for object properties.
Definition: qgsproperty.h:232
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:128
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.
@ Line
Line symbol.
Definition: qgssymbol.h:89
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.