QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
19#include "qgsdoublevalidator.h"
21#include "qgslayertree.h"
22#include "qgslayertreemodel.h"
23#include "qgslinesymbol.h"
24#include "qgsmapcanvas.h"
25#include "qgsmarkersymbol.h"
27#include "qgsstyle.h"
28#include "qgssymbol.h"
30#include "qgsvectorlayer.h"
31
32#include <QInputDialog>
33#include <QStyledItemDelegate>
34
35#include "moc_qgsdatadefinedsizelegendwidget.cpp"
36
38 : QgsPanelWidget( parent )
39 , mSizeProperty( ddSize )
40 , mMapCanvas( canvas )
41{
42 setupUi( this );
43 setPanelTitle( tr( "Data-defined Size Legend" ) );
44
45 mLineSymbolButton->setSymbolType( Qgis::SymbolType::Line );
46
47 std::unique_ptr< QgsMarkerSymbol > symbol;
48
49 if ( !ddsLegend )
50 {
51 radDisabled->setChecked( true );
52 }
53 else
54 {
56 radSeparated->setChecked( true );
57 else
58 radCollapsed->setChecked( true );
59
61 cboAlignSymbols->setCurrentIndex( 0 );
62 else
63 cboAlignSymbols->setCurrentIndex( 1 );
64
65 if ( ddsLegend->lineSymbol() )
66 mLineSymbolButton->setSymbol( ddsLegend->lineSymbol()->clone() );
67
68 symbol.reset( ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr ); // may be null (undefined)
69 }
70 groupBoxOptions->setEnabled( radSeparated->isChecked() );
71
72 if ( overrideSymbol )
73 {
74 symbol.reset( overrideSymbol ); // takes ownership
75 mOverrideSymbol = true;
76 }
77
78 if ( !symbol )
79 {
80 symbol = QgsMarkerSymbol::createSimple( QVariantMap() );
81 }
82 mSourceSymbol = std::move( symbol );
83
84 btnChangeSymbol->setEnabled( !mOverrideSymbol );
85
86 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize(), 0, nullptr, QgsScreenProperties( screen() ) );
87 btnChangeSymbol->setIcon( icon );
88
89 editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
90
91 mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
92 mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
93 mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
94 if ( ddsLegend )
95 {
96 groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
97 const auto constClasses = ddsLegend->classes();
98 for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
99 {
100 QStandardItem *item = new QStandardItem( QLocale().toString( sc.size ) );
101 item->setData( sc.size );
102 QStandardItem *itemLabel = new QStandardItem( sc.label );
103 mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
104 }
105 mSizeClassesModel->sort( 0 );
106 }
107
108 connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
109 connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
110
111 viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
112 viewSizeClasses->setModel( mSizeClassesModel );
113 connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
114
115 // prepare layer and model to preview legend
117 mPreviewLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326" ), QStringLiteral( "Preview" ), QStringLiteral( "memory" ), options );
118 mPreviewTree = new QgsLayerTree;
119 mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
120 mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
121 if ( canvas )
122 mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
123 viewLayerTree->setModel( mPreviewModel );
124
125 connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this] { emit widgetChanged(); } );
126 connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
127 connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
128 connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
129 connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
130 connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
131 connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
132 connect( mLineSymbolButton, &QgsSymbolButton::changed, this, &QgsPanelWidget::widgetChanged );
133 connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
134 connect( radCollapsed, &QRadioButton::toggled, this, [this]( bool toggled ) { groupBoxOptions->setEnabled( toggled ); } );
135 updatePreview();
136}
137
139{
140 delete mPreviewModel;
141 delete mPreviewTree;
142 delete mPreviewLayer;
143}
144
146{
147 if ( radDisabled->isChecked() )
148 return nullptr;
149
152 ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
153 if ( !mOverrideSymbol )
154 {
155 ddsLegend->setSymbol( mSourceSymbol->clone() );
156 }
157
158 ddsLegend->setTitle( editTitle->text() );
159
160 if ( groupManualSizeClasses->isChecked() )
161 {
162 QList<QgsDataDefinedSizeLegend::SizeClass> classes;
163 for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
164 {
165 const double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
166 const QString label = mSizeClassesModel->item( i, 1 )->text();
167 classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
168 }
169 ddsLegend->setClasses( classes );
170 }
171
172 ddsLegend->setLineSymbol( mLineSymbolButton->clonedSymbol<QgsLineSymbol>() );
173 return ddsLegend;
174}
175
176void QgsDataDefinedSizeLegendWidget::updatePreview()
177{
178 QgsMarkerSymbol *symbol = mSourceSymbol->clone();
179 symbol->setDataDefinedSize( mSizeProperty );
182 mPreviewLayer->setRenderer( r );
183 mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
184 viewLayerTree->expandAll();
185}
186
187void QgsDataDefinedSizeLegendWidget::changeSymbol()
188{
189 std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
190 QgsSymbolWidgetContext context;
191 if ( mMapCanvas )
192 context.setMapCanvas( mMapCanvas );
193
194 QgsExpressionContext ec;
195 if ( mMapCanvas )
196 {
197 ec = mMapCanvas->createExpressionContext();
198 }
199 else
200 {
204 << QgsExpressionContextUtils::mapSettingsScope( QgsMapSettings() );
205 }
206 context.setExpressionContext( &ec );
207
208 const QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
209 const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
210 const std::unique_ptr<QgsVectorLayer> layer = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=%1" ).arg( crsAuthId ), QStringLiteral( "tmp" ), QStringLiteral( "memory" ), options );
211
212 QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
213 d.setContext( context );
214
215 if ( d.exec() != QDialog::Accepted )
216 return;
217
218 mSourceSymbol = std::move( newSymbol );
219 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize(), 0, nullptr, QgsScreenProperties( screen() ) );
220 btnChangeSymbol->setIcon( icon );
221
222 emit widgetChanged();
223}
224
225void QgsDataDefinedSizeLegendWidget::addSizeClass()
226{
227 bool ok;
228 const double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ), 0, -2147483647, 2147483647, 6, &ok );
229 if ( !ok )
230 return;
231
232 QStandardItem *item = new QStandardItem( QLocale().toString( v ) );
233 item->setData( v );
234 QStandardItem *itemLabel = new QStandardItem( QLocale().toString( v ) );
235 mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
236 mSizeClassesModel->sort( 0 );
237 emit widgetChanged();
238}
239
240void QgsDataDefinedSizeLegendWidget::removeSizeClass()
241{
242 const QModelIndex idx = viewSizeClasses->currentIndex();
243 if ( !idx.isValid() )
244 return;
245
246 mSizeClassesModel->removeRow( idx.row() );
247 emit widgetChanged();
248}
249
250void QgsDataDefinedSizeLegendWidget::onSizeClassesChanged()
251{
252 for ( int row = 0; row < mSizeClassesModel->rowCount(); ++row )
253 {
254 QStandardItem *item = mSizeClassesModel->item( row, 0 );
255 item->setData( QgsDoubleValidator::toDouble( item->text() ) );
256 }
257
258 mSizeClassesModel->sort( 0 );
259 emit widgetChanged();
260}
@ Line
Line symbol.
Definition qgis.h:612
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.
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
A model representing the layer tree, including layers and groups of layers.
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.
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.
A marker symbol type, for rendering Point and MultiPoint geometries.
static std::unique_ptr< 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.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an 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:116
A store for object properties.
Stores properties relating to a screen.
A feature renderer which renders all features with the same symbol.
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:147
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.
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 dataset.
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.