QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include "qgsfontbutton.h"
21#include "qgsmapcanvas.h"
22#include "qgsmaplayerlegend.h"
23#include "qgsrenderer.h"
25#include "qgssymbollayerutils.h"
26#include "qgstextformatwidget.h"
27#include "qgsvectorlayer.h"
28
29#include <QBoxLayout>
30#include <QStandardItemModel>
31#include <QTreeView>
32#include <QTreeWidget>
33
34#include "moc_qgsvectorlayerlegendwidget.cpp"
35
37 : QWidget( parent )
38{
39 mIncludeByDefaultInLayoutLegendsCheck = new QCheckBox( tr( "Include automatically in print layout legend items" ) );
40
41 mLegendTreeView = new QTreeView;
42 mLegendTreeView->setRootIsDecorated( false );
43
44 mTextOnSymbolFormatButton = new QgsFontButton( nullptr, tr( "Legend Text Format" ) );
45 mTextOnSymbolFormatButton->setText( tr( "Text Format" ) );
46 mTextOnSymbolFormatButton->setMode( QgsFontButton::ModeTextRenderer );
47
48 mTextOnSymbolFromExpressionButton = new QPushButton( tr( "Set Labels from Expression…" ) );
49 connect( mTextOnSymbolFromExpressionButton, &QPushButton::clicked, this, &QgsVectorLayerLegendWidget::labelsFromExpression );
50
51 mTextOnSymbolGroupBox = new QgsCollapsibleGroupBox;
52
53 QHBoxLayout *buttonsLayout = new QHBoxLayout;
54 buttonsLayout->addWidget( mTextOnSymbolFormatButton );
55 buttonsLayout->addWidget( mTextOnSymbolFromExpressionButton );
56 buttonsLayout->addStretch();
57
58 QVBoxLayout *groupLayout = new QVBoxLayout;
59 groupLayout->addWidget( mLegendTreeView );
60 groupLayout->addLayout( buttonsLayout );
61
62 mTextOnSymbolGroupBox->setTitle( tr( "Text on Symbols" ) );
63 mTextOnSymbolGroupBox->setCheckable( true );
64 mTextOnSymbolGroupBox->setLayout( groupLayout );
65 mTextOnSymbolGroupBox->setCollapsed( false );
66
67 mLabelLegendGroupBox = new QgsCollapsibleGroupBox;
68 mLabelLegendGroupBox->setCheckable( true );
69 mLabelLegendGroupBox->setTitle( tr( "Show Label Legend" ) );
70
71 mLabelLegendTreeWidget = new QTreeWidget;
72 connect( mLabelLegendTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsVectorLayerLegendWidget::labelLegendTreeWidgetItemDoubleClicked );
73 QVBoxLayout *labelLegendLayout = new QVBoxLayout;
74 labelLegendLayout->addWidget( mLabelLegendTreeWidget );
75 mLabelLegendGroupBox->setLayout( labelLegendLayout );
76
77 mPlaceholderImageLabel = new QLabel( tr( "Legend placeholder image" ) );
78 mImageSourceLineEdit = new QgsImageSourceLineEdit();
79 mImageSourceLineEdit->setLastPathSettingsKey( QStringLiteral( "lastLegendPlaceholderDir" ) );
80 if ( mLayer )
81 {
82 mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
83 }
84
85 QGroupBox *generalGroupBox = new QGroupBox( tr( "General Settings" ) );
86
87 QGridLayout *generalLayout = new QGridLayout;
88 generalLayout->addWidget( mIncludeByDefaultInLayoutLegendsCheck, 0, 0, 1, 2 );
89 generalLayout->addWidget( mPlaceholderImageLabel, 1, 0 );
90 generalLayout->addWidget( mImageSourceLineEdit, 1, 1 );
91 generalLayout->setColumnStretch( 0, 1 );
92 generalLayout->setColumnStretch( 1, 2 );
93 generalGroupBox->setLayout( generalLayout );
94
95 QVBoxLayout *layout = new QVBoxLayout;
96 layout->setContentsMargins( 0, 0, 0, 0 );
97 layout->addWidget( generalGroupBox );
98 layout->addWidget( mLabelLegendGroupBox );
99 layout->addWidget( mTextOnSymbolGroupBox );
100
101 setLayout( layout );
102}
103
104void QgsVectorLayerLegendWidget::labelLegendTreeWidgetItemDoubleClicked( QTreeWidgetItem *item, int column )
105{
106 const Qt::ItemFlags flags = item->flags();
107 if ( column == 1 )
108 {
109 item->setFlags( flags | Qt::ItemIsEditable );
110 }
111 else
112 {
113 item->setFlags( flags & ( ~Qt::ItemIsEditable ) );
114 }
115}
116
118{
119 mCanvas = canvas;
120 mTextOnSymbolFormatButton->setMapCanvas( mCanvas );
121}
122
124{
125 mLayer = layer;
126
127 QgsDefaultVectorLayerLegend *legend = layer ? qobject_cast<QgsDefaultVectorLayerLegend *>( layer->legend() ) : nullptr;
128 if ( !legend )
129 return;
130
131 mIncludeByDefaultInLayoutLegendsCheck->setChecked( !legend->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) );
132 mLabelLegendGroupBox->setChecked( legend->showLabelLegend() );
133 populateLabelLegendTreeWidget();
134 mTextOnSymbolGroupBox->setChecked( legend->textOnSymbolEnabled() );
135 mTextOnSymbolFormatButton->setTextFormat( legend->textOnSymbolTextFormat() );
136 populateLegendTreeView( legend->textOnSymbolContent() );
137 mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
138}
139
140void QgsVectorLayerLegendWidget::populateLabelLegendTreeWidget()
141{
142 mLabelLegendTreeWidget->clear();
143 mLabelLegendTreeWidget->setColumnCount( 2 );
144 QTreeWidgetItem *headerItem = new QTreeWidgetItem( QStringList() << tr( "Description" ) << tr( "Legend Text" ) );
145 mLabelLegendTreeWidget->setHeaderItem( headerItem );
146
147 const QgsAbstractVectorLayerLabeling *labeling = mLayer->labeling();
148 if ( labeling )
149 {
150 const QStringList pList = labeling->subProviders();
151 for ( int i = 0; i < pList.size(); ++i )
152 {
153 const QgsPalLayerSettings s = labeling->settings( pList.at( i ) );
154 QString description;
155 const QgsRuleBasedLabeling *ruleBasedLabeling = dynamic_cast<const QgsRuleBasedLabeling *>( labeling );
156 if ( ruleBasedLabeling && ruleBasedLabeling->rootRule() )
157 {
158 const QgsRuleBasedLabeling::Rule *rule = ruleBasedLabeling->rootRule()->findRuleByKey( pList.at( i ) );
159 if ( rule )
160 {
161 description = rule->description();
162 }
163 }
164
165 QTreeWidgetItem *labelItem = new QTreeWidgetItem( QStringList() << description << s.legendString() );
166 labelItem->setData( 0, Qt::UserRole, pList.at( i ) );
167 mLabelLegendTreeWidget->addTopLevelItem( labelItem );
168 }
169 }
170}
171
172
173void QgsVectorLayerLegendWidget::populateLegendTreeView( const QHash<QString, QString> &content )
174{
175 QStandardItemModel *model = new QStandardItemModel( this );
176 model->setColumnCount( 2 );
177 model->setHorizontalHeaderLabels( QStringList() << tr( "Symbol" ) << tr( "Text" ) );
178
179 const QgsLegendSymbolList lst = mLayer->renderer() ? mLayer->renderer()->legendSymbolItems() : QgsLegendSymbolList();
180 for ( const QgsLegendSymbolItem &symbolItem : lst )
181 {
182 if ( !symbolItem.symbol() )
183 continue;
184
185 QgsRenderContext context;
186 const QSize iconSize( 16, 16 );
187 const QIcon icon = QgsSymbolLayerUtils::symbolPreviewPixmap( symbolItem.symbol(), iconSize, 0, &context );
188
189 QStandardItem *item1 = new QStandardItem( icon, symbolItem.label() );
190 item1->setEditable( false );
191 QStandardItem *item2 = new QStandardItem;
192 if ( symbolItem.ruleKey().isEmpty() )
193 {
194 item1->setEnabled( false );
195 item2->setEnabled( false );
196 }
197 else
198 {
199 item1->setData( symbolItem.ruleKey() );
200 if ( content.contains( symbolItem.ruleKey() ) )
201 item2->setText( content.value( symbolItem.ruleKey() ) );
202 }
203 model->appendRow( QList<QStandardItem *>() << item1 << item2 );
204 }
205 mLegendTreeView->setModel( model );
206 mLegendTreeView->resizeColumnToContents( 0 );
207}
208
209
211{
213 legend->setTextOnSymbolEnabled( mTextOnSymbolGroupBox->isChecked() );
214 legend->setTextOnSymbolTextFormat( mTextOnSymbolFormatButton->textFormat() );
215
216 QHash<QString, QString> content;
217 if ( QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mLegendTreeView->model() ) )
218 {
219 for ( int i = 0; i < model->rowCount(); ++i )
220 {
221 const QString ruleKey = model->item( i, 0 )->data().toString();
222 const QString label = model->item( i, 1 )->text();
223 if ( !label.isEmpty() )
224 content[ruleKey] = label;
225 }
226 }
227 legend->setTextOnSymbolContent( content );
228
229 const bool showLabelLegend = mLabelLegendGroupBox->isChecked();
230 legend->setShowLabelLegend( showLabelLegend );
231 if ( showLabelLegend )
232 {
233 applyLabelLegend();
234 }
235
236 legend->setFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault, !mIncludeByDefaultInLayoutLegendsCheck->isChecked() );
237
238 mLayer->setLegendPlaceholderImage( mImageSourceLineEdit->source() );
239
240 mLayer->setLegend( legend );
241}
242
243void QgsVectorLayerLegendWidget::labelsFromExpression()
244{
245 QHash<QString, QString> content;
247
248 QgsExpressionBuilderDialog dlgExpression( mLayer );
249 dlgExpression.setExpressionContext( context.expressionContext() );
250 if ( !dlgExpression.exec() )
251 return;
252
253 QgsExpression expr( dlgExpression.expressionText() );
254 expr.prepare( &context.expressionContext() );
255
256 std::unique_ptr<QgsFeatureRenderer> r( mLayer->renderer()->clone() );
257
258 QgsFeature f;
259 QgsFeatureRequest request;
260 request.setSubsetOfAttributes( r->usedAttributes( context ), mLayer->fields() );
261 QgsFeatureIterator fi = mLayer->getFeatures();
262
263 r->startRender( context, mLayer->fields() );
264 while ( fi.nextFeature( f ) )
265 {
266 context.expressionContext().setFeature( f );
267 const QSet<QString> keys = r->legendKeysForFeature( f, context );
268 for ( const QString &key : keys )
269 {
270 if ( content.contains( key ) )
271 continue;
272
273 const QString label = expr.evaluate( &context.expressionContext() ).toString();
274 if ( !label.isEmpty() )
275 content[key] = label;
276 }
277 }
278 r->stopRender( context );
279
280 populateLegendTreeView( content );
281}
282
283void QgsVectorLayerLegendWidget::applyLabelLegend()
284{
285 const QgsAbstractVectorLayerLabeling *layerLabeling = mLayer->labeling();
286 if ( !layerLabeling )
287 {
288 return;
289 }
290
291 QgsAbstractVectorLayerLabeling *labeling = layerLabeling->clone();
292 const QStringList ids = labeling->subProviders();
293 const int nIterations = std::min<int>( ids.size(), mLabelLegendTreeWidget->topLevelItemCount() );
294
295 for ( int i = 0; i < nIterations; ++i )
296 {
297 QTreeWidgetItem *item = mLabelLegendTreeWidget->topLevelItem( i );
298 if ( item )
299 {
300 const QString legendText = item->text( 1 );
301
302 QgsPalLayerSettings *s = new QgsPalLayerSettings( labeling->settings( ids.at( i ) ) );
303 s->setLegendString( legendText );
304 labeling->setSettings( s, ids.at( i ) );
305 }
306 }
307
308 mLayer->setLabeling( labeling );
309}
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4555
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.
Handles 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)
Fetch next feature and stores in f, returns true on success.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
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:58
A button for customizing QgsTextFormat settings.
@ ModeTextRenderer
Configure font settings for use with QgsTextRenderer.
A line edit widget with toolbutton for setting a raster image path.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void setFlag(Qgis::MapLayerLegendFlag flag, bool on=true)
Enables or disables a particular flag (other flags are not affected).
Qgis::MapLayerLegendFlags flags() const
Returns flags associated with the legend.
QgsMapLayerLegend * legend() const
Can be nullptr.
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, const QgsScreenProperties &screen=QgsScreenProperties())
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 dataset.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QList< QgsLegendSymbolItem > QgsLegendSymbolList