QGIS API Documentation  3.2.0-Bonn (bc43194)
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"
31 
32 
34  : QgsPanelWidget( parent )
35  , mSizeProperty( ddSize )
36  , mMapCanvas( canvas )
37 {
38  setupUi( this );
39  setPanelTitle( tr( "Data-defined Size Legend" ) );
40 
41  QgsMarkerSymbol *symbol = nullptr;
42 
43  if ( !ddsLegend )
44  {
45  radDisabled->setChecked( true );
46  }
47  else
48  {
50  radSeparated->setChecked( true );
51  else
52  radCollapsed->setChecked( true );
53 
55  cboAlignSymbols->setCurrentIndex( 0 );
56  else
57  cboAlignSymbols->setCurrentIndex( 1 );
58 
59  symbol = ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr; // may be null (undefined)
60  }
61 
62  if ( overrideSymbol )
63  {
64  symbol = overrideSymbol; // takes ownership
65  mOverrideSymbol = true;
66  }
67 
68  if ( !symbol )
69  {
71  }
72  mSourceSymbol.reset( symbol );
73 
74  btnChangeSymbol->setEnabled( !mOverrideSymbol );
75 
76  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
77  btnChangeSymbol->setIcon( icon );
78 
79  editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
80 
81  mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
82  mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
83  mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
84  if ( ddsLegend )
85  {
86  groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
87  Q_FOREACH ( const QgsDataDefinedSizeLegend::SizeClass &sc, ddsLegend->classes() )
88  {
89  QStandardItem *item = new QStandardItem( QString::number( sc.size ) );
90  item->setData( sc.size );
91  QStandardItem *itemLabel = new QStandardItem( sc.label );
92  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
93  }
94  mSizeClassesModel->sort( 0 );
95  }
96 
97  connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
98  connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
99 
100  viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
101  viewSizeClasses->setModel( mSizeClassesModel );
102  connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
103 
104  // prepare layer and model to preview legend
105  mPreviewLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326" ), QStringLiteral( "Preview" ), QStringLiteral( "memory" ) );
106  mPreviewTree = new QgsLayerTree;
107  mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
108  mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
109  if ( canvas )
110  mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
111  viewLayerTree->setModel( mPreviewModel );
112 
113  connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ] { emit widgetChanged(); } );
114  connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
115  connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
116  connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
117  connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
118  connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
119  connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
120  connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
121  updatePreview();
122 }
123 
125 {
126  delete mPreviewModel;
127  delete mPreviewTree;
128  delete mPreviewLayer;
129 }
130 
132 {
133  if ( radDisabled->isChecked() )
134  return nullptr;
135 
138  ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
139  if ( !mOverrideSymbol )
140  {
141  ddsLegend->setSymbol( mSourceSymbol->clone() );
142  }
143 
144  ddsLegend->setTitle( editTitle->text() );
145 
146  if ( groupManualSizeClasses->isChecked() )
147  {
148  QList<QgsDataDefinedSizeLegend::SizeClass> classes;
149  for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
150  {
151  double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
152  QString label = mSizeClassesModel->item( i, 1 )->text();
153  classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
154  }
155  ddsLegend->setClasses( classes );
156  }
157  return ddsLegend;
158 }
159 
160 void QgsDataDefinedSizeLegendWidget::updatePreview()
161 {
162  QgsMarkerSymbol *symbol = mSourceSymbol->clone();
163  symbol->setDataDefinedSize( mSizeProperty );
166  mPreviewLayer->setRenderer( r );
167  mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
168  viewLayerTree->expandAll();
169 }
170 
171 void QgsDataDefinedSizeLegendWidget::changeSymbol()
172 {
173  std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
174  QgsSymbolWidgetContext context;
175  if ( mMapCanvas )
176  context.setMapCanvas( mMapCanvas );
177 
182  if ( mMapCanvas )
184  context.setExpressionContext( &ec );
185 
186  QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
187  std::unique_ptr<QgsVectorLayer> layer( new QgsVectorLayer( "Point?crs=" + crsAuthId, QStringLiteral( "tmp" ), QStringLiteral( "memory" ) ) );
188 
189  QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
190  d.setContext( context );
191 
192  if ( d.exec() != QDialog::Accepted )
193  return;
194 
195  mSourceSymbol = std::move( newSymbol );
196  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize() );
197  btnChangeSymbol->setIcon( icon );
198 
199  emit widgetChanged();
200 }
201 
202 void QgsDataDefinedSizeLegendWidget::addSizeClass()
203 {
204  bool ok;
205  double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ),
206  0, -2147483647, 2147483647, 6, &ok );
207  if ( !ok )
208  return;
209 
210  QStandardItem *item = new QStandardItem( QString::number( v ) );
211  item->setData( v );
212  QStandardItem *itemLabel = new QStandardItem( QString::number( v ) );
213  mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
214  mSizeClassesModel->sort( 0 );
215  emit widgetChanged();
216 }
217 
218 void QgsDataDefinedSizeLegendWidget::removeSizeClass()
219 {
220  QModelIndex idx = viewSizeClasses->currentIndex();
221  if ( !idx.isValid() )
222  return;
223 
224  mSizeClassesModel->removeRow( idx.row() );
225  emit widgetChanged();
226 }
227 
228 void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
229 {
230  for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
231  {
232  QStandardItem *item = mSizeClassesModel->item( row, 0 );
233  item->setData( item->text().toDouble() );
234  }
235 
236  mSizeClassesModel->sort( 0 );
237  emit widgetChanged();
238 }
Symbols are aligned to the center.
Each class (size value) has a separate legend node.
QString label
Label to be shown with the particular symbol size.
void setRenderer(QgsFeatureRenderer *r)
Set renderer which will be invoked to represent this layer.
Definition of one class for the legend.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
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:501
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:74
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.
static QIcon symbolPreviewIcon(QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
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...
double size
Marker size in units used by the symbol (usually millimeters). May be further scaled before rendering...
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.
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 null). 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:391
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:1087
void setDataDefinedSize(const QgsProperty &property)
Set data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1338
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
Gets a deep copy of this symbol.
Definition: qgssymbol.cpp:1531
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...