QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  QgsMarkerSymbol *symbol = nullptr;
43 
44  if ( !ddsLegend )
45  {
46  radDisabled->setChecked( true );
47  }
48  else
49  {
51  radSeparated->setChecked( true );
52  else
53  radCollapsed->setChecked( true );
54 
56  cboAlignSymbols->setCurrentIndex( 0 );
57  else
58  cboAlignSymbols->setCurrentIndex( 1 );
59 
60  symbol = ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr; // may be null (undefined)
61  }
62 
63  if ( overrideSymbol )
64  {
65  symbol = overrideSymbol; // takes ownership
66  mOverrideSymbol = true;
67  }
68 
69  if ( !symbol )
70  {
72  }
73  mSourceSymbol.reset( symbol );
74 
75  btnChangeSymbol->setEnabled( !mOverrideSymbol );
76 
77  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
78  btnChangeSymbol->setIcon( icon );
79 
80  editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
81 
82  mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
83  mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
84  mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
85  if ( ddsLegend )
86  {
87  groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
88  const auto constClasses = ddsLegend->classes();
89  for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
90  {
91  QStandardItem *item = new QStandardItem( QString::number( sc.size ) );
92  item->setData( sc.size );
93  QStandardItem *itemLabel = new QStandardItem( sc.label );
94  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
95  }
96  mSizeClassesModel->sort( 0 );
97  }
98 
99  connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
100  connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
101 
102  viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
103  viewSizeClasses->setModel( mSizeClassesModel );
104  connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
105 
106  // prepare layer and model to preview legend
108  mPreviewLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326" ), QStringLiteral( "Preview" ), QStringLiteral( "memory" ), options );
109  mPreviewTree = new QgsLayerTree;
110  mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
111  mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
112  if ( canvas )
113  mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
114  viewLayerTree->setModel( mPreviewModel );
115 
116  connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ] { emit widgetChanged(); } );
117  connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
118  connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
119  connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
120  connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
121  connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
122  connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
123  connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
124  updatePreview();
125 }
126 
128 {
129  delete mPreviewModel;
130  delete mPreviewTree;
131  delete mPreviewLayer;
132 }
133 
135 {
136  if ( radDisabled->isChecked() )
137  return nullptr;
138 
141  ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
142  if ( !mOverrideSymbol )
143  {
144  ddsLegend->setSymbol( mSourceSymbol->clone() );
145  }
146 
147  ddsLegend->setTitle( editTitle->text() );
148 
149  if ( groupManualSizeClasses->isChecked() )
150  {
151  QList<QgsDataDefinedSizeLegend::SizeClass> classes;
152  for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
153  {
154  double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
155  QString label = mSizeClassesModel->item( i, 1 )->text();
156  classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
157  }
158  ddsLegend->setClasses( classes );
159  }
160  return ddsLegend;
161 }
162 
163 void QgsDataDefinedSizeLegendWidget::updatePreview()
164 {
165  QgsMarkerSymbol *symbol = mSourceSymbol->clone();
166  symbol->setDataDefinedSize( mSizeProperty );
169  mPreviewLayer->setRenderer( r );
170  mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
171  viewLayerTree->expandAll();
172 }
173 
174 void QgsDataDefinedSizeLegendWidget::changeSymbol()
175 {
176  std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
177  QgsSymbolWidgetContext context;
178  if ( mMapCanvas )
179  context.setMapCanvas( mMapCanvas );
180 
185  if ( mMapCanvas )
187  context.setExpressionContext( &ec );
188 
189  QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
191  std::unique_ptr<QgsVectorLayer> layer = qgis::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=%1" ).arg( crsAuthId ),
192  QStringLiteral( "tmp" ),
193  QStringLiteral( "memory" ),
194  options ) ;
195 
196  QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
197  d.setContext( context );
198 
199  if ( d.exec() != QDialog::Accepted )
200  return;
201 
202  mSourceSymbol = std::move( newSymbol );
203  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
204  btnChangeSymbol->setIcon( icon );
205 
206  emit widgetChanged();
207 }
208 
209 void QgsDataDefinedSizeLegendWidget::addSizeClass()
210 {
211  bool ok;
212  double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ),
213  0, -2147483647, 2147483647, 6, &ok );
214  if ( !ok )
215  return;
216 
217  QStandardItem *item = new QStandardItem( QString::number( v ) );
218  item->setData( v );
219  QStandardItem *itemLabel = new QStandardItem( QString::number( v ) );
220  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
221  mSizeClassesModel->sort( 0 );
222  emit widgetChanged();
223 }
224 
225 void QgsDataDefinedSizeLegendWidget::removeSizeClass()
226 {
227  QModelIndex idx = viewSizeClasses->currentIndex();
228  if ( !idx.isValid() )
229  return;
230 
231  mSizeClassesModel->removeRow( idx.row() );
232  emit widgetChanged();
233 }
234 
235 void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
236 {
237  for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
238  {
239  QStandardItem *item = mSizeClassesModel->item( row, 0 );
240  item->setData( item->text().toDouble() );
241  }
242 
243  mSizeClassesModel->sort( 0 );
244  emit widgetChanged();
245 }
Symbols are aligned to the center.
Each class (size value) has a separate legend node.
void setRenderer(QgsFeatureRenderer *r)
Sets renderer which will be invoked to represent this layer.
Definition of one class for the legend.
Setting options for loading vector layers.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
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 ...
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.
void setTitle(const QString &title)
Sets title label for data-defined size legend.
void setLegendType(LegendType type)
Sets how the legend should be rendered.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
QgsMarkerSymbol * symbol() const
Returns marker symbol that will be used to draw markers in legend.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:73
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:766
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:46
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
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...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A store for object properties.
Definition: qgsproperty.h:229
void widgetChanged()
Emitted when the widget state changes.
void setVerticalAlignment(VerticalAlignment vAlign)
Sets vertical alignment of symbols - only valid for collapsed legend.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
Symbols are aligned to the bottom.
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...
double scale() const
Returns the last reported scale of the canvas.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:96
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
double outputDpi() const
Returns DPI used for conversion between real world units (e.g.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
static QgsExpressionContextScope * atlasScope(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...
void setSymbol(QgsMarkerSymbol *symbol SIP_TRANSFER)
Sets marker symbol that will be used to draw markers in legend.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller...
QString title() const
Returns title label for data-defined size legend.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
LegendType legendType() const
Returns how the legend should be rendered.
VerticalAlignment verticalAlignment() const
Returns vertical alignment of symbols - only valid for collapsed legend.
static QgsMarkerSymbol * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1162
void setDataDefinedSize(const QgsProperty &property)
Set data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1439
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.
All classes are rendered within one legend node.
QString authid() const
Returns the authority identifier for the CRS.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1641
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...