QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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#include "moc_qgsdatadefinedsizelegendwidget.cpp"
18
19#include <QInputDialog>
20#include <QStyledItemDelegate>
21
23#include "qgslayertree.h"
24#include "qgslayertreemodel.h"
25#include "qgsmapcanvas.h"
27#include "qgsstyle.h"
28#include "qgssymbol.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(), 0, nullptr, QgsScreenProperties( screen() ) );
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
175void QgsDataDefinedSizeLegendWidget::updatePreview()
176{
177 QgsMarkerSymbol *symbol = mSourceSymbol->clone();
178 symbol->setDataDefinedSize( mSizeProperty );
181 mPreviewLayer->setRenderer( r );
182 mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
183 viewLayerTree->expandAll();
184}
185
186void QgsDataDefinedSizeLegendWidget::changeSymbol()
187{
188 std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
190 if ( mMapCanvas )
191 context.setMapCanvas( mMapCanvas );
192
194 if ( mMapCanvas )
195 {
196 ec = mMapCanvas->createExpressionContext();
197 }
198 else
199 {
204 }
205 context.setExpressionContext( &ec );
206
207 const QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
209 const std::unique_ptr<QgsVectorLayer> layer = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=%1" ).arg( crsAuthId ),
210 QStringLiteral( "tmp" ),
211 QStringLiteral( "memory" ),
212 options ) ;
213
214 QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
215 d.setContext( context );
216
217 if ( d.exec() != QDialog::Accepted )
218 return;
219
220 mSourceSymbol = std::move( newSymbol );
221 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize(), 0, nullptr, QgsScreenProperties( screen() ) );
222 btnChangeSymbol->setIcon( icon );
223
224 emit widgetChanged();
225}
226
227void QgsDataDefinedSizeLegendWidget::addSizeClass()
228{
229 bool ok;
230 const double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ),
231 0, -2147483647, 2147483647, 6, &ok );
232 if ( !ok )
233 return;
234
235 QStandardItem *item = new QStandardItem( QLocale().toString( v ) );
236 item->setData( v );
237 QStandardItem *itemLabel = new QStandardItem( QLocale().toString( v ) );
238 mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
239 mSizeClassesModel->sort( 0 );
240 emit widgetChanged();
241}
242
243void QgsDataDefinedSizeLegendWidget::removeSizeClass()
244{
245 const QModelIndex idx = viewSizeClasses->currentIndex();
246 if ( !idx.isValid() )
247 return;
248
249 mSizeClassesModel->removeRow( idx.row() );
250 emit widgetChanged();
251}
252
253void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
254{
255 for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
256 {
257 QStandardItem *item = mSizeClassesModel->item( row, 0 );
258 item->setData( QgsDoubleValidator::toDouble( item->text() ) );
259 }
260
261 mSizeClassesModel->sort( 0 );
262 emit widgetChanged();
263}
@ Line
Line symbol.
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 double toDouble(const QString &input, bool *ok)
Converts input string to double value.
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.
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
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.
The QgsMapSettings class contains configuration for rendering of the map.
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.
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
void setDataDefinedSize(const QgsProperty &property) const
Set data defined size for whole symbol (including all symbol layers).
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
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.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
A store for object properties.
Stores properties relating to a screen.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:146
void changed()
Emitted when the symbol's settings are changed.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns an icon preview for a color ramp.
A dialog that can be used to select and build a symbol.
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.
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.