QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 );
168  r->setDataDefinedSizeLegend( dataDefinedSizeLegend() );
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 )
186  ec << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() );
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 }
QgsSymbolSelectorDialog
Definition: qgssymbolselectordialog.h:263
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:369
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:66
QgsProperty
A store for object properties.
Definition: qgsproperty.h:231
QgsDataDefinedSizeLegend::AlignBottom
@ AlignBottom
Symbols are aligned to the bottom.
Definition: qgsdatadefinedsizelegend.h:67
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:33
qgsmapcanvas.h
QgsDataDefinedSizeLegend::title
QString title() const
Returns title label for data-defined size legend.
Definition: qgsdatadefinedsizelegend.h:119
qgssinglesymbolrenderer.h
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:390
QgsMarkerSymbol::createSimple
static QgsMarkerSymbol * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1418
QgsSymbolWidgetContext
Definition: qgssymbolwidgetcontext.h:35
QgsMapCanvas
Definition: qgsmapcanvas.h:83
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:99
QgsDataDefinedSizeLegend
Definition: qgsdatadefinedsizelegend.h:41
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:356
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:458
QgsMapCanvas::scale
double scale() const
Returns the last reported scale of the canvas.
Definition: qgsmapcanvas.cpp:322
QgsMarkerSymbol::setDataDefinedSize
void setDataDefinedSize(const QgsProperty &property)
Set data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1694
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:384
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:34
QgsLayerTreeModel
Definition: qgslayertreemodel.h:53
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:112
QgsMarkerSymbol::clone
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1896
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:66
QgsStyle::defaultStyle
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:111
QgsDataDefinedSizeLegend::SizeClass
Definition of one class for the legend.
Definition: qgsdatadefinedsizelegend.h:71
QgsDataDefinedSizeLegend::LegendCollapsed
@ LegendCollapsed
All classes are rendered within one legend node.
Definition: qgsdatadefinedsizelegend.h:60
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:762
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:221
QgsLayerTree
Definition: qgslayertree.h:32
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsMarkerSymbol
Definition: qgssymbol.h:917
QgsLayerTreeGroup::addLayer
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
Definition: qgslayertreegroup.cpp:82
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsDataDefinedSizeLegend::LegendSeparated
@ LegendSeparated
Each class (size value) has a separate legend node.
Definition: qgsdatadefinedsizelegend.h:59
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:122
qgssymbolselectordialog.h
qgsstyle.h
qgsvectorlayer.h
QgsDataDefinedSizeLegend::setTitle
void setTitle(const QString &title)
Sets title label for data-defined size legend.
Definition: qgsdatadefinedsizelegend.h:117
QgsVectorLayer::LayerOptions
Setting options for loading vector layers.
Definition: qgsvectorlayer.h:423
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:714
QgsVectorLayer
Definition: qgsvectorlayer.h:385
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:134
QgsDataDefinedSizeLegend::symbol
QgsMarkerSymbol * symbol() const
Returns marker symbol that will be used to draw markers in legend.
Definition: qgsdatadefinedsizelegend.cpp:71
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:687
QgsExpressionContextUtils::atlasScope
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
Definition: qgsexpressioncontextutils.cpp:570
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:114
qgslayertreemodel.h
QgsDataDefinedSizeLegend::setLegendType
void setLegendType(LegendType type)
Sets how the legend should be rendered.
Definition: qgsdatadefinedsizelegend.h:80
qgsdatadefinedsizelegend.h
QgsSymbolWidgetContext::setExpressionContext
void setExpressionContext(QgsExpressionContext *context)
Sets the optional expression context used for the widget.
Definition: qgssymbolwidgetcontext.cpp:69
QgsMapSettings::outputDpi
double outputDpi() const
Returns DPI used for conversion between real world units (e.g.
Definition: qgsmapsettings.cpp:262
QgsMapCanvas::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
Definition: qgsmapcanvas.cpp:2124
qgssymbol.h
QgsDataDefinedSizeLegend::verticalAlignment
VerticalAlignment verticalAlignment() const
Returns vertical alignment of symbols - only valid for collapsed legend.
Definition: qgsdatadefinedsizelegend.h:124
QgsDataDefinedSizeLegend::legendType
LegendType legendType() const
Returns how the legend should be rendered.
Definition: qgsdatadefinedsizelegend.h:82
QgsDataDefinedSizeLegendWidget::~QgsDataDefinedSizeLegendWidget
~QgsDataDefinedSizeLegendWidget() override
Definition: qgsdatadefinedsizelegendwidget.cpp:127