QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
28 #include "qgsrulebasedlabeling.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 
94 void 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 
132 void 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 
165 void 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 
233 void 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 
273 void 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 id = item->data( 0, Qt::UserRole ).toString();
291  const QString legendText = item->text( 1 );
292 
293  QgsPalLayerSettings *s = new QgsPalLayerSettings( labeling->settings( ids.at( i ) ) );
294  s->setLegendString( legendText );
295  labeling->setSettings( s, ids.at( i ) );
296  }
297  }
298 
299  mLayer->setLabeling( labeling );
300 }
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 QgsAbstractVectorLayerLabeling * clone() const =0
Returns a new copy of the object.
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.
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.
QHash< QString, QString > textOnSymbolContent() const
Returns 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.
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 QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
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:48
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the widget.
QgsTextFormat textFormat
Definition: qgsfontbutton.h:54
@ ModeTextRenderer
Configure font settings for use with QgsTextRenderer.
Definition: qgsfontbutton.h:61
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:89
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:1500
void setLegendPlaceholderImage(const QString &imgPath)
Set placeholder image for legend.
Definition: qgsmaplayer.h:1507
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