QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsvectorlayerlegendwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayerlegendwidget.cpp
3 ---------------------
4 Date : April 2018
5 Copyright : (C) 2018 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 <QBoxLayout>
19#include <QStandardItemModel>
20#include <QTreeView>
21#include <QTreeWidget>
22
25#include "qgsmapcanvas.h"
26#include "qgsmaplayerlegend.h"
27#include "qgsrenderer.h"
29#include "qgssymbollayerutils.h"
30#include "qgstextformatwidget.h"
31#include "qgsvectorlayer.h"
32#include "qgsfontbutton.h"
33
35 : QWidget( parent )
36{
37 mLegendTreeView = new QTreeView;
38 mLegendTreeView->setRootIsDecorated( false );
39
40 mTextOnSymbolFormatButton = new QgsFontButton( nullptr, tr( "Legend Text Format" ) );
41 mTextOnSymbolFormatButton->setText( tr( "Text Format" ) );
42 mTextOnSymbolFormatButton->setMode( QgsFontButton::ModeTextRenderer );
43
44 mTextOnSymbolFromExpressionButton = new QPushButton( tr( "Set Labels from Expression…" ) );
45 connect( mTextOnSymbolFromExpressionButton, &QPushButton::clicked, this, &QgsVectorLayerLegendWidget::labelsFromExpression );
46
47 mTextOnSymbolGroupBox = new QgsCollapsibleGroupBox;
48
49 QHBoxLayout *buttonsLayout = new QHBoxLayout;
50 buttonsLayout->addWidget( mTextOnSymbolFormatButton );
51 buttonsLayout->addWidget( mTextOnSymbolFromExpressionButton );
52 buttonsLayout->addStretch();
53
54 QVBoxLayout *groupLayout = new QVBoxLayout;
55 groupLayout->addWidget( mLegendTreeView );
56 groupLayout->addLayout( buttonsLayout );
57
58 mTextOnSymbolGroupBox->setTitle( tr( "Text on Symbols" ) );
59 mTextOnSymbolGroupBox->setCheckable( true );
60 mTextOnSymbolGroupBox->setLayout( groupLayout );
61 mTextOnSymbolGroupBox->setCollapsed( false );
62
63 mLabelLegendGroupBox = new QgsCollapsibleGroupBox;
64 mLabelLegendGroupBox->setCheckable( true );
65 mLabelLegendGroupBox->setTitle( tr( "Show Label Legend" ) );
66
67 mLabelLegendTreeWidget = new QTreeWidget;
68 connect( mLabelLegendTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsVectorLayerLegendWidget::labelLegendTreeWidgetItemDoubleClicked );
69 QVBoxLayout *labelLegendLayout = new QVBoxLayout;
70 labelLegendLayout->addWidget( mLabelLegendTreeWidget );
71 mLabelLegendGroupBox->setLayout( labelLegendLayout );
72
73 mPlaceholderImageLabel = new QLabel( tr( "Legend placeholder image" ) );
74 mImageSourceLineEdit = new QgsImageSourceLineEdit();
75 mImageSourceLineEdit->setLastPathSettingsKey( QStringLiteral( "lastLegendPlaceholderDir" ) );
76 if ( mLayer )
77 {
78 mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
79 }
80
81 QHBoxLayout *placeholderLayout = new QHBoxLayout;
82 placeholderLayout->addWidget( mPlaceholderImageLabel );
83 placeholderLayout->addWidget( mImageSourceLineEdit );
84
85 QVBoxLayout *layout = new QVBoxLayout;
86 layout->setContentsMargins( 0, 0, 0, 0 );
87 layout->addLayout( placeholderLayout );
88 layout->addWidget( mLabelLegendGroupBox );
89 layout->addWidget( mTextOnSymbolGroupBox );
90
91 setLayout( layout );
92}
93
94void QgsVectorLayerLegendWidget::labelLegendTreeWidgetItemDoubleClicked( QTreeWidgetItem *item, int column )
95{
96 const Qt::ItemFlags flags = item->flags();
97 if ( column == 1 )
98 {
99 item->setFlags( flags | Qt::ItemIsEditable );
100 }
101 else
102 {
103 item->setFlags( flags & ( ~Qt::ItemIsEditable ) );
104 }
105}
106
108{
109 mCanvas = canvas;
110 mTextOnSymbolFormatButton->setMapCanvas( mCanvas );
111}
112
114{
115 mLayer = layer;
116
117 QgsDefaultVectorLayerLegend *legend = qobject_cast<QgsDefaultVectorLayerLegend *>( layer->legend() );
118 if ( !legend )
119 return;
120
121 mLabelLegendGroupBox->setChecked( legend->showLabelLegend() );
122 populateLabelLegendTreeWidget();
123 mTextOnSymbolGroupBox->setChecked( legend->textOnSymbolEnabled() );
124 mTextOnSymbolFormatButton->setTextFormat( legend->textOnSymbolTextFormat() );
125 populateLegendTreeView( legend->textOnSymbolContent() );
126 if ( mLayer )
127 {
128 mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
129 }
130}
131
132void QgsVectorLayerLegendWidget::populateLabelLegendTreeWidget()
133{
134 mLabelLegendTreeWidget->clear();
135 mLabelLegendTreeWidget->setColumnCount( 2 );
136 QTreeWidgetItem *headerItem = new QTreeWidgetItem( QStringList() << tr( "Description" ) << tr( "Legend Text" ) );
137 mLabelLegendTreeWidget->setHeaderItem( headerItem );
138
139 const QgsAbstractVectorLayerLabeling *labeling = mLayer->labeling();
140 if ( labeling )
141 {
142 const QStringList pList = labeling->subProviders();
143 for ( int i = 0; i < pList.size(); ++i )
144 {
145 const QgsPalLayerSettings s = labeling->settings( pList.at( i ) );
146 QString description;
147 const QgsRuleBasedLabeling *ruleBasedLabeling = dynamic_cast<const QgsRuleBasedLabeling *>( labeling );
148 if ( ruleBasedLabeling && ruleBasedLabeling->rootRule() )
149 {
150 const QgsRuleBasedLabeling::Rule *rule = ruleBasedLabeling->rootRule()->findRuleByKey( pList.at( i ) );
151 if ( rule )
152 {
153 description = rule->description();
154 }
155 }
156
157 QTreeWidgetItem *labelItem = new QTreeWidgetItem( QStringList() << description << s.legendString() );
158 labelItem->setData( 0, Qt::UserRole, pList.at( i ) );
159 mLabelLegendTreeWidget->addTopLevelItem( labelItem );
160 }
161 }
162}
163
164
165void QgsVectorLayerLegendWidget::populateLegendTreeView( const QHash<QString, QString> &content )
166{
167 QStandardItemModel *model = new QStandardItemModel( this );
168 model->setColumnCount( 2 );
169 model->setHorizontalHeaderLabels( QStringList() << tr( "Symbol" ) << tr( "Text" ) );
170
171 const QgsLegendSymbolList lst = mLayer->renderer() ? mLayer->renderer()->legendSymbolItems() : QgsLegendSymbolList();
172 for ( const QgsLegendSymbolItem &symbolItem : lst )
173 {
174 if ( !symbolItem.symbol() )
175 continue;
176
177 QgsRenderContext context;
178 const QSize iconSize( 16, 16 );
179 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewPixmap( symbolItem.symbol(), iconSize, 0, &context );
180
181 QStandardItem *item1 = new QStandardItem( icon, symbolItem.label() );
182 item1->setEditable( false );
183 QStandardItem *item2 = new QStandardItem;
184 if ( symbolItem.ruleKey().isEmpty() )
185 {
186 item1->setEnabled( false );
187 item2->setEnabled( false );
188 }
189 else
190 {
191 item1->setData( symbolItem.ruleKey() );
192 if ( content.contains( symbolItem.ruleKey() ) )
193 item2->setText( content.value( symbolItem.ruleKey() ) );
194 }
195 model->appendRow( QList<QStandardItem *>() << item1 << item2 );
196 }
197 mLegendTreeView->setModel( model );
198 mLegendTreeView->resizeColumnToContents( 0 );
199}
200
201
203{
205 legend->setTextOnSymbolEnabled( mTextOnSymbolGroupBox->isChecked() );
206 legend->setTextOnSymbolTextFormat( mTextOnSymbolFormatButton->textFormat() );
207
208 QHash<QString, QString> content;
209 if ( QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mLegendTreeView->model() ) )
210 {
211 for ( int i = 0; i < model->rowCount(); ++i )
212 {
213 const QString ruleKey = model->item( i, 0 )->data().toString();
214 const QString label = model->item( i, 1 )->text();
215 if ( !label.isEmpty() )
216 content[ruleKey] = label;
217 }
218 }
219 legend->setTextOnSymbolContent( content );
220
221 const bool showLabelLegend = mLabelLegendGroupBox->isChecked();
222 legend->setShowLabelLegend( showLabelLegend );
223 if ( showLabelLegend )
224 {
225 applyLabelLegend();
226 }
227
228 mLayer->setLegendPlaceholderImage( mImageSourceLineEdit->source() );
229
230 mLayer->setLegend( legend );
231}
232
233void QgsVectorLayerLegendWidget::labelsFromExpression()
234{
235 QHash<QString, QString> content;
237
238 QgsExpressionBuilderDialog dlgExpression( mLayer );
239 dlgExpression.setExpressionContext( context.expressionContext() );
240 if ( !dlgExpression.exec() )
241 return;
242
243 QgsExpression expr( dlgExpression.expressionText() );
244 expr.prepare( &context.expressionContext() );
245
246 std::unique_ptr< QgsFeatureRenderer > r( mLayer->renderer()->clone() );
247
248 QgsFeature f;
249 QgsFeatureRequest request;
250 request.setSubsetOfAttributes( r->usedAttributes( context ), mLayer->fields() );
251 QgsFeatureIterator fi = mLayer->getFeatures();
252
253 r->startRender( context, mLayer->fields() );
254 while ( fi.nextFeature( f ) )
255 {
256 context.expressionContext().setFeature( f );
257 const QSet<QString> keys = r->legendKeysForFeature( f, context );
258 for ( const QString &key : keys )
259 {
260 if ( content.contains( key ) )
261 continue;
262
263 const QString label = expr.evaluate( &context.expressionContext() ).toString();
264 if ( !label.isEmpty() )
265 content[key] = label;
266 }
267 }
268 r->stopRender( context );
269
270 populateLegendTreeView( content );
271}
272
273void QgsVectorLayerLegendWidget::applyLabelLegend()
274{
275 const QgsAbstractVectorLayerLabeling *layerLabeling = mLayer->labeling();
276 if ( !layerLabeling )
277 {
278 return;
279 }
280
281 QgsAbstractVectorLayerLabeling *labeling = layerLabeling->clone();
282 const QStringList ids = labeling->subProviders();
283 const int nIterations = std::min< int >( ids.size(), mLabelLegendTreeWidget->topLevelItemCount() );
284
285 for ( int i = 0; i < nIterations; ++i )
286 {
287 QTreeWidgetItem *item = mLabelLegendTreeWidget->topLevelItem( i );
288 if ( item )
289 {
290 const QString legendText = item->text( 1 );
291
292 QgsPalLayerSettings *s = new QgsPalLayerSettings( labeling->settings( ids.at( i ) ) );
293 s->setLegendString( legendText );
294 labeling->setSettings( s, ids.at( i ) );
295 }
296 }
297
298 mLayer->setLabeling( labeling );
299}
void setLastPathSettingsKey(const QString &key)
Sets a specific settings key to use when storing the last used path for the file source.
void setSource(const QString &source)
Sets a new source to show in the widget.
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
virtual QStringList subProviders() const
Gets list of sub-providers within the layer's labeling.
virtual void setSettings(QgsPalLayerSettings *settings, const QString &providerId=QString())=0
Set pal settings for a specific provider (takes ownership).
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
virtual QgsAbstractVectorLayerLabeling * clone() const =0
Returns a new copy of the object.
void setCollapsed(bool collapse)
Collapse or uncollapse this groupbox.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
Default legend implementation for vector layers.
void setTextOnSymbolEnabled(bool enabled)
Sets whether the "text on symbol" functionality is enabled.
void setTextOnSymbolContent(const QHash< QString, QString > &content)
Sets per-symbol content of labels for "text on symbol" functionality.
void setShowLabelLegend(bool enabled)
Sets if a legend for the labeling should be shown.
bool textOnSymbolEnabled() const
Returns whether the "text on symbol" functionality is enabled.
QgsTextFormat textOnSymbolTextFormat() const
Returns text format of symbol labels for "text on symbol" functionality.
bool showLabelLegend() const
Returns whether the legend for the labeling is shown.
void setTextOnSymbolTextFormat(const QgsTextFormat &format)
Sets text format of symbol labels for "text on symbol" functionality.
QHash< QString, QString > textOnSymbolContent() const
Returns per-symbol content of labels for "text on symbol" functionality.
A generic dialog for building expression strings.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
A button for customizing QgsTextFormat settings.
Definition: qgsfontbutton.h:49
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the widget.
QgsTextFormat textFormat
Definition: qgsfontbutton.h:55
@ ModeTextRenderer
Configure font settings for use with QgsTextRenderer.
Definition: qgsfontbutton.h:62
void setTextFormat(const QgsTextFormat &format)
Sets the current text format to show in the widget.
void setMode(Mode mode)
Sets the current button mode.
A line edit widget with toolbutton for setting a raster image path.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapLayerLegend * legend() const
Can be nullptr.
QString legendPlaceholderImage() const
Returns path to the placeholder image or an empty string if a generated legend is shown.
Definition: qgsmaplayer.h:1517
void setLegendPlaceholderImage(const QString &imgPath)
Set placeholder image for legend.
Definition: qgsmaplayer.h:1524
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
Contains settings for how a map layer will be labeled.
QString legendString() const
legendString
void setLegendString(const QString &legendString)
setLegendString
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
A child rule for QgsRuleBasedLabeling.
const QgsRuleBasedLabeling::Rule * findRuleByKey(const QString &key) const
Try to find a rule given its unique key.
QString description() const
A human readable description for this rule.
Rule based labeling for a vector layer.
QgsRuleBasedLabeling::Rule * rootRule()
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
void setLayer(QgsVectorLayer *layer)
Initialize widget with a map layer.
QgsVectorLayerLegendWidget(QWidget *parent=nullptr)
void applyToLayer()
Store changes made in the widget to the layer.
void setMapCanvas(QgsMapCanvas *canvas)
Sets pointer to map canvas.
Represents a vector layer which manages a vector based data sets.
void setLabeling(QgsAbstractVectorLayerLabeling *labeling)
Sets labeling configuration.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QList< QgsLegendSymbolItem > QgsLegendSymbolList