QGIS API Documentation 3.99.0-Master (d270888f95f)
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 <QString>
34#include <QStyledItemDelegate>
35
36#include "moc_qgsdatadefinedsizelegendwidget.cpp"
37
38using namespace Qt::StringLiterals;
39
41 : QgsPanelWidget( parent )
42 , mSizeProperty( ddSize )
43 , mMapCanvas( canvas )
44{
45 setupUi( this );
46 setPanelTitle( tr( "Data-defined Size Legend" ) );
47
48 mLineSymbolButton->setSymbolType( Qgis::SymbolType::Line );
49
50 std::unique_ptr< QgsMarkerSymbol > symbol;
51
52 if ( !ddsLegend )
53 {
54 radDisabled->setChecked( true );
55 }
56 else
57 {
59 radSeparated->setChecked( true );
60 else
61 radCollapsed->setChecked( true );
62
64 cboAlignSymbols->setCurrentIndex( 0 );
65 else
66 cboAlignSymbols->setCurrentIndex( 1 );
67
68 if ( ddsLegend->lineSymbol() )
69 mLineSymbolButton->setSymbol( ddsLegend->lineSymbol()->clone() );
70
71 symbol.reset( ddsLegend->symbol() ? ddsLegend->symbol()->clone() : nullptr ); // may be null (undefined)
72 }
73 groupBoxOptions->setEnabled( radSeparated->isChecked() );
74
75 if ( overrideSymbol )
76 {
77 symbol.reset( overrideSymbol ); // takes ownership
78 mOverrideSymbol = true;
79 }
80
81 if ( !symbol )
82 {
83 symbol = QgsMarkerSymbol::createSimple( QVariantMap() );
84 }
85 mSourceSymbol = std::move( symbol );
86
87 btnChangeSymbol->setEnabled( !mOverrideSymbol );
88
89 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize(), 0, nullptr, QgsScreenProperties( screen() ) );
90 btnChangeSymbol->setIcon( icon );
91
92 editTitle->setText( ddsLegend ? ddsLegend->title() : QString() );
93
94 mSizeClassesModel = new QStandardItemModel( viewSizeClasses );
95 mSizeClassesModel->setHorizontalHeaderLabels( QStringList() << tr( "Value" ) << tr( "Label" ) );
96 mSizeClassesModel->setSortRole( Qt::UserRole + 1 );
97 if ( ddsLegend )
98 {
99 groupManualSizeClasses->setChecked( !ddsLegend->classes().isEmpty() );
100 const auto constClasses = ddsLegend->classes();
101 for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
102 {
103 QStandardItem *item = new QStandardItem( QLocale().toString( sc.size ) );
104 item->setData( sc.size );
105 QStandardItem *itemLabel = new QStandardItem( sc.label );
106 mSizeClassesModel->appendRow( QList<QStandardItem *>() << item << itemLabel );
107 }
108 mSizeClassesModel->sort( 0 );
109 }
110
111 connect( btnAddClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::addSizeClass );
112 connect( btnRemoveClass, &QToolButton::clicked, this, &QgsDataDefinedSizeLegendWidget::removeSizeClass );
113
114 viewSizeClasses->setItemDelegateForColumn( 0, new SizeClassDelegate( viewSizeClasses ) );
115 viewSizeClasses->setModel( mSizeClassesModel );
116 connect( mSizeClassesModel, &QStandardItemModel::dataChanged, this, &QgsDataDefinedSizeLegendWidget::onSizeClassesChanged );
117
118 // prepare layer and model to preview legend
120 mPreviewLayer = new QgsVectorLayer( u"Point?crs=EPSG:4326"_s, u"Preview"_s, u"memory"_s, options );
121 mPreviewTree = new QgsLayerTree;
122 mPreviewLayerNode = mPreviewTree->addLayer( mPreviewLayer ); // node owned by the tree
123 mPreviewModel = new QgsLayerTreeModel( mPreviewTree );
124 if ( canvas )
125 mPreviewModel->setLegendMapViewData( canvas->mapUnitsPerPixel(), canvas->mapSettings().outputDpi(), canvas->scale() );
126 viewLayerTree->setModel( mPreviewModel );
127
128 connect( cboAlignSymbols, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this] { emit widgetChanged(); } );
129 connect( radDisabled, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
130 connect( radSeparated, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
131 connect( radCollapsed, &QRadioButton::clicked, this, &QgsPanelWidget::widgetChanged );
132 connect( groupManualSizeClasses, &QGroupBox::clicked, this, &QgsPanelWidget::widgetChanged );
133 connect( btnChangeSymbol, &QPushButton::clicked, this, &QgsDataDefinedSizeLegendWidget::changeSymbol );
134 connect( editTitle, &QLineEdit::textChanged, this, &QgsPanelWidget::widgetChanged );
135 connect( mLineSymbolButton, &QgsSymbolButton::changed, this, &QgsPanelWidget::widgetChanged );
136 connect( this, &QgsPanelWidget::widgetChanged, this, &QgsDataDefinedSizeLegendWidget::updatePreview );
137 connect( radCollapsed, &QRadioButton::toggled, this, [this]( bool toggled ) { groupBoxOptions->setEnabled( toggled ); } );
138 updatePreview();
139}
140
142{
143 delete mPreviewModel;
144 delete mPreviewTree;
145 delete mPreviewLayer;
146}
147
149{
150 if ( radDisabled->isChecked() )
151 return nullptr;
152
155 ddsLegend->setVerticalAlignment( cboAlignSymbols->currentIndex() == 0 ? QgsDataDefinedSizeLegend::AlignBottom : QgsDataDefinedSizeLegend::AlignCenter );
156 if ( !mOverrideSymbol )
157 {
158 ddsLegend->setSymbol( mSourceSymbol->clone() );
159 }
160
161 ddsLegend->setTitle( editTitle->text() );
162
163 if ( groupManualSizeClasses->isChecked() )
164 {
165 QList<QgsDataDefinedSizeLegend::SizeClass> classes;
166 for ( int i = 0; i < mSizeClassesModel->rowCount(); ++i )
167 {
168 const double value = mSizeClassesModel->item( i, 0 )->data().toDouble();
169 const QString label = mSizeClassesModel->item( i, 1 )->text();
170 classes << QgsDataDefinedSizeLegend::SizeClass( value, label );
171 }
172 ddsLegend->setClasses( classes );
173 }
174
175 ddsLegend->setLineSymbol( mLineSymbolButton->clonedSymbol<QgsLineSymbol>() );
176 return ddsLegend;
177}
178
179void QgsDataDefinedSizeLegendWidget::updatePreview()
180{
181 QgsMarkerSymbol *symbol = mSourceSymbol->clone();
182 symbol->setDataDefinedSize( mSizeProperty );
185 mPreviewLayer->setRenderer( r );
186 mPreviewModel->refreshLayerLegend( mPreviewLayerNode );
187 viewLayerTree->expandAll();
188}
189
190void QgsDataDefinedSizeLegendWidget::changeSymbol()
191{
192 std::unique_ptr<QgsMarkerSymbol> newSymbol( mSourceSymbol->clone() );
193 QgsSymbolWidgetContext context;
194 if ( mMapCanvas )
195 context.setMapCanvas( mMapCanvas );
196
197 QgsExpressionContext ec;
198 if ( mMapCanvas )
199 {
200 ec = mMapCanvas->createExpressionContext();
201 }
202 else
203 {
207 << QgsExpressionContextUtils::mapSettingsScope( QgsMapSettings() );
208 }
209 context.setExpressionContext( &ec );
210
211 const QString crsAuthId = mMapCanvas ? mMapCanvas->mapSettings().destinationCrs().authid() : QString();
212 const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
213 const std::unique_ptr<QgsVectorLayer> layer = std::make_unique<QgsVectorLayer>( u"Point?crs=%1"_s.arg( crsAuthId ), u"tmp"_s, u"memory"_s, options );
214
215 QgsSymbolSelectorDialog d( newSymbol.get(), QgsStyle::defaultStyle(), layer.get(), this );
216 d.setContext( context );
217
218 if ( d.exec() != QDialog::Accepted )
219 return;
220
221 mSourceSymbol = std::move( newSymbol );
222 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSourceSymbol.get(), btnChangeSymbol->iconSize(), 0, nullptr, QgsScreenProperties( screen() ) );
223 btnChangeSymbol->setIcon( icon );
224
225 emit widgetChanged();
226}
227
228void QgsDataDefinedSizeLegendWidget::addSizeClass()
229{
230 bool ok;
231 const double v = QInputDialog::getDouble( this, tr( "Add Size Class" ), tr( "Enter value for a new class" ), 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.
Definition qgis.h:631
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:119
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:150
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.