QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgslayoutchartwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutchartwidget.cpp
3 --------------------------
4 begin : August 2025
5 copyright : (C) 2025 by Mathieu
6 email : mathieu at opengis dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsapplication.h"
21#include "qgsgui.h"
22#include "qgslayout.h"
24#include "qgslayoutitemchart.h"
25#include "qgsplotregistry.h"
26#include "qgsplotwidget.h"
27
28#include "moc_qgslayoutchartwidget.cpp"
29
31 : QgsLayoutItemBaseWidget( nullptr, chartItem )
32 , mChartItem( chartItem )
33{
34 setupUi( this );
35
36 //add widget for general composer item properties
37 mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, chartItem );
38 mainLayout->addWidget( mItemPropertiesWidget );
39
40 QMap<QString, QString> plotTypes = QgsApplication::instance()->plotRegistry()->plotTypes();
41 for ( auto plotTypesIterator = plotTypes.keyValueBegin(); plotTypesIterator != plotTypes.keyValueEnd(); ++plotTypesIterator )
42 {
43 mChartTypeComboBox->addItem( plotTypesIterator->second, plotTypesIterator->first );
44 }
45
46 mLayerComboBox->setFilters( Qgis::LayerFilter::VectorLayer );
47
48 connect( mChartTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutChartWidget::mChartTypeComboBox_currentIndexChanged );
49 connect( mChartPropertiesButton, &QPushButton::clicked, this, &QgsLayoutChartWidget::mChartPropertiesButton_clicked );
50
51 connect( mLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutChartWidget::changeLayer );
52 connect( mLayerComboBox, &QgsMapLayerComboBox::layerChanged, mSortExpressionWidget, &QgsFieldExpressionWidget::setLayer );
53 connect( mSortCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutChartWidget::mSortCheckBox_stateChanged );
54 connect( mSortExpressionWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString &, bool )>( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsLayoutChartWidget::changeSortExpression );
55 connect( mSortDirectionButton, &QToolButton::clicked, this, &QgsLayoutChartWidget::mSortDirectionButton_clicked );
56
57 connect( mSeriesListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutChartWidget::mSeriesListWidget_currentItemChanged );
58 connect( mSeriesListWidget, &QListWidget::itemChanged, this, &QgsLayoutChartWidget::mSeriesListWidget_itemChanged );
59 connect( mAddSeriesPushButton, &QPushButton::clicked, this, &QgsLayoutChartWidget::mAddSeriesPushButton_clicked );
60 connect( mRemoveSeriesPushButton, &QPushButton::clicked, this, &QgsLayoutChartWidget::mRemoveSeriesPushButton_clicked );
61 connect( mSeriesPropertiesButton, &QPushButton::clicked, this, &QgsLayoutChartWidget::mSeriesPropertiesButton_clicked );
62
63 setGuiElementValues();
64
65 connect( mChartItem, &QgsLayoutObject::changed, this, &QgsLayoutChartWidget::setGuiElementValues );
66}
67
69{
70 if ( mItemPropertiesWidget )
71 mItemPropertiesWidget->setMasterLayout( masterLayout );
72}
73
75{
77 return false;
78
79 if ( mChartItem )
80 {
81 disconnect( mChartItem, &QgsLayoutObject::changed, this, &QgsLayoutChartWidget::setGuiElementValues );
82 }
83
84 mChartItem = qobject_cast<QgsLayoutItemChart *>( item );
85 mItemPropertiesWidget->setItem( mChartItem );
86
87 if ( mChartItem )
88 {
89 connect( mChartItem, &QgsLayoutObject::changed, this, &QgsLayoutChartWidget::setGuiElementValues );
90 }
91
92 setGuiElementValues();
93
94 return true;
95}
96
97void QgsLayoutChartWidget::setGuiElementValues()
98{
99 if ( mChartItem )
100 {
101 whileBlocking( mChartTypeComboBox )->setCurrentIndex( mChartTypeComboBox->findData( mChartItem->plot()->type() ) );
102 whileBlocking( mLayerComboBox )->setLayer( mChartItem->sourceLayer() );
103
104 whileBlocking( mSortCheckBox )->setCheckState( mChartItem->sortFeatures() ? Qt::Checked : Qt::Unchecked );
105
106 whileBlocking( mSortDirectionButton )->setEnabled( mChartItem->sortFeatures() );
107 whileBlocking( mSortDirectionButton )->setArrowType( mChartItem->sortAscending() ? Qt::UpArrow : Qt::DownArrow );
108
109 whileBlocking( mSortExpressionWidget )->setEnabled( mChartItem->sortFeatures() );
110 whileBlocking( mSortExpressionWidget )->setLayer( mChartItem->sourceLayer() );
111 whileBlocking( mSortExpressionWidget )->setField( mChartItem->sortExpression() );
112
113 mSeriesListWidget->clear();
114 const QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
115 for ( const QgsLayoutItemChart::SeriesDetails &series : seriesList )
116 {
117 addSeriesListItem( series.name() );
118 }
119 }
120 else
121 {
122 mSeriesListWidget->clear();
123 }
124}
125
126void QgsLayoutChartWidget::mChartTypeComboBox_currentIndexChanged( int )
127{
128 if ( !mChartItem )
129 {
130 return;
131 }
132
133 const QString plotType = mChartTypeComboBox->currentData().toString();
134 if ( mChartItem->plot()->type() == plotType )
135 {
136 return;
137 }
138
139 QgsPlot *newPlot = QgsApplication::instance()->plotRegistry()->createPlot( plotType );
140 Qgs2DXyPlot *newPlot2DXy = dynamic_cast<Qgs2DXyPlot *>( newPlot );
141 if ( newPlot2DXy )
142 {
143 Qgs2DXyPlot *oldPlot2DXy = dynamic_cast<Qgs2DXyPlot *>( mChartItem->plot() );
144 if ( oldPlot2DXy )
145 {
146 // Transfer a few basic details for a nicer UX
147 newPlot2DXy->setXMinimum( oldPlot2DXy->xMinimum() );
148 newPlot2DXy->setXMaximum( oldPlot2DXy->xMaximum() );
149 newPlot2DXy->setYMinimum( oldPlot2DXy->yMinimum() );
150 newPlot2DXy->setYMaximum( oldPlot2DXy->yMaximum() );
151
152 newPlot2DXy->xAxis().setType( oldPlot2DXy->xAxis().type() );
153 newPlot2DXy->xAxis().setGridIntervalMajor( oldPlot2DXy->xAxis().gridIntervalMajor() );
154 newPlot2DXy->xAxis().setGridIntervalMinor( oldPlot2DXy->xAxis().gridIntervalMinor() );
155 newPlot2DXy->xAxis().setLabelInterval( oldPlot2DXy->xAxis().labelInterval() );
156
157 newPlot2DXy->yAxis().setGridIntervalMajor( oldPlot2DXy->yAxis().gridIntervalMajor() );
158 newPlot2DXy->yAxis().setGridIntervalMinor( oldPlot2DXy->yAxis().gridIntervalMinor() );
159 newPlot2DXy->yAxis().setLabelInterval( oldPlot2DXy->yAxis().labelInterval() );
160 }
161 }
162
163 mChartItem->beginCommand( tr( "Change Chart Type" ) );
164 mChartItem->setPlot( newPlot );
165 mChartItem->update();
166 mChartItem->endCommand();
167}
168
169void QgsLayoutChartWidget::mChartPropertiesButton_clicked()
170{
171 if ( !mChartItem )
172 {
173 return;
174 }
175
177
178 const QString plotType = mChartTypeComboBox->currentData().toString();
179 QgsPlotAbstractMetadata *abstractMetadata = QgsApplication::instance()->plotRegistry()->plotMetadata( plotType );
180 if ( !abstractMetadata )
181 {
182 return;
183 }
184
185 QgsPlotWidget *widget = abstractMetadata->createPlotWidget( this );
186 if ( !widget )
187 {
188 return;
189 }
190
191 widget->registerExpressionContextGenerator( mChartItem );
192 widget->setPlot( mChartItem->plot() );
193
194 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget]() {
195 if ( !mChartItem )
196 {
197 return;
198 }
199
200 mChartItem->beginCommand( tr( "Modify Chart" ) );
201 mChartItem->setPlot( widget->createPlot() );
202 mChartItem->endCommand();
203 mChartItem->update();
204 } );
205
206 openPanel( widget );
207}
208
209void QgsLayoutChartWidget::changeLayer( QgsMapLayer *layer )
210{
211 if ( !mChartItem )
212 {
213 return;
214 }
215
216 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
217 if ( !vl )
218 {
219 return;
220 }
221
222 mChartItem->beginCommand( tr( "Change Chart Source Layer" ) );
223 mChartItem->setSourceLayer( vl );
224 mChartItem->update();
225 mChartItem->endCommand();
226}
227
228void QgsLayoutChartWidget::changeSortExpression( const QString &expression, bool )
229{
230 if ( !mChartItem )
231 {
232 return;
233 }
234
235 mChartItem->beginCommand( tr( "Change Chart Source Sort Expression" ) );
236 mChartItem->setSortExpression( expression );
237 mChartItem->update();
238 mChartItem->endCommand();
239}
240
241void QgsLayoutChartWidget::mSortCheckBox_stateChanged( int state )
242{
243 if ( !mChartItem )
244 return;
245
246 if ( state == Qt::Checked )
247 {
248 mSortDirectionButton->setEnabled( true );
249 mSortExpressionWidget->setEnabled( true );
250 }
251 else
252 {
253 mSortDirectionButton->setEnabled( false );
254 mSortExpressionWidget->setEnabled( false );
255 }
256
257 mChartItem->beginCommand( tr( "Toggle Atlas Sorting" ) );
258 mChartItem->setSortFeatures( state == Qt::Checked );
259 mChartItem->update();
260 mChartItem->endCommand();
261}
262
263void QgsLayoutChartWidget::mSortDirectionButton_clicked()
264{
265 if ( !mChartItem )
266 {
267 return;
268 }
269
270 Qt::ArrowType at = mSortDirectionButton->arrowType();
271 at = ( at == Qt::UpArrow ) ? Qt::DownArrow : Qt::UpArrow;
272 mSortDirectionButton->setArrowType( at );
273
274 mChartItem->beginCommand( tr( "Change Chart Source Sort Direction" ) );
275 mChartItem->setSortAscending( at == Qt::UpArrow );
276 mChartItem->update();
277 mChartItem->endCommand();
278}
279
280QListWidgetItem *QgsLayoutChartWidget::addSeriesListItem( const QString &name )
281{
282 QListWidgetItem *item = new QListWidgetItem( name, nullptr );
283 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
284 mSeriesListWidget->addItem( item );
285 return item;
286}
287
288void QgsLayoutChartWidget::mSeriesListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem * )
289{
290 mSeriesPropertiesButton->setEnabled( static_cast<bool>( current ) );
291}
292
293void QgsLayoutChartWidget::mSeriesListWidget_itemChanged( QListWidgetItem *item )
294{
295 if ( !mChartItem )
296 {
297 return;
298 }
299
300 QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
301 const int idx = mSeriesListWidget->row( item );
302 if ( idx >= seriesList.size() )
303 {
304 return;
305 }
306
307 mChartItem->beginCommand( tr( "Rename Chart Series" ) );
308 seriesList[idx].setName( item->text() );
309 mChartItem->setSeriesList( seriesList );
310 mChartItem->endCommand();
311}
312
313void QgsLayoutChartWidget::mAddSeriesPushButton_clicked()
314{
315 if ( !mChartItem )
316 {
317 return;
318 }
319
320 QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
321 const QString itemName = tr( "Series %1" ).arg( seriesList.size() + 1 );
322 addSeriesListItem( itemName );
323
324 mChartItem->beginCommand( tr( "Add Chart Series" ) );
325 seriesList << QgsLayoutItemChart::SeriesDetails( itemName );
326 mChartItem->setSeriesList( seriesList );
327 mChartItem->endCommand();
328 mChartItem->update();
329
330 mSeriesListWidget->setCurrentRow( mSeriesListWidget->count() - 1 );
331 mSeriesListWidget_currentItemChanged( mSeriesListWidget->currentItem(), nullptr );
332}
333
334void QgsLayoutChartWidget::mRemoveSeriesPushButton_clicked()
335{
336 QListWidgetItem *item = mSeriesListWidget->currentItem();
337 if ( !item || !mChartItem )
338 {
339 return;
340 }
341
342 QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
343 const int idx = mSeriesListWidget->row( item );
344 if ( idx >= seriesList.size() )
345 {
346 return;
347 }
348
349 QListWidgetItem *deletedItem = mSeriesListWidget->takeItem( mSeriesListWidget->row( item ) );
350 delete deletedItem;
351
352 mChartItem->beginCommand( tr( "Remove Chart Series" ) );
353 seriesList.removeAt( idx );
354 mChartItem->setSeriesList( seriesList );
355 mChartItem->endCommand();
356 mChartItem->update();
357}
358
359void QgsLayoutChartWidget::mSeriesPropertiesButton_clicked()
360{
361 QListWidgetItem *item = mSeriesListWidget->currentItem();
362 if ( !item || !mChartItem )
363 {
364 return;
365 }
366
367 QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
368 const int idx = mSeriesListWidget->row( item );
369 if ( idx >= seriesList.size() )
370 {
371 return;
372 }
373
374 QgsLayoutChartSeriesDetailsWidget *widget = new QgsLayoutChartSeriesDetailsWidget( mChartItem->sourceLayer(), idx, seriesList[idx], this );
375 widget->setPanelTitle( tr( "Series Details" ) );
376 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget]() {
377 if ( !mChartItem )
378 {
379 return;
380 }
381
382 QList<QgsLayoutItemChart::SeriesDetails> seriesList = mChartItem->seriesList();
383 const int idx = widget->index();
384 if ( idx >= seriesList.size() )
385 {
386 return;
387 }
388
389 mChartItem->beginCommand( tr( "Modify Chart Series" ) );
390 seriesList[idx].setXExpression( widget->xExpression() );
391 seriesList[idx].setYExpression( widget->yExpression() );
392 seriesList[idx].setFilterExpression( widget->filterExpression() );
393 mChartItem->setSeriesList( seriesList );
394 mChartItem->endCommand();
395 mChartItem->update();
396 } );
397
398 openPanel( widget );
399}
void setXMaximum(double maximum)
Sets the maximum value of the x axis.
Definition qgsplot.h:735
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:742
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
Definition qgsplot.h:721
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:700
QgsPlotAxis & yAxis()
Returns a reference to the plot's y axis.
Definition qgsplot.h:770
void setXMinimum(double minimum)
Sets the minimum value of the x axis.
Definition qgsplot.h:707
QgsPlotAxis & xAxis()
Returns a reference to the plot's x axis.
Definition qgsplot.h:756
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:714
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
Definition qgsplot.h:749
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:728
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsPlotRegistry * plotRegistry()
Returns the application's plot registry, used for plot types.
A widget for selection of layer fields or expression creation.
void setLayer(QgsMapLayer *layer)
Sets the layer used to display the fields and expression.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
static void initPlotWidgets()
Initializes plot widgets.
Definition qgsgui.cpp:458
QString yExpression() const
Returns the Y-axis expression.
QString filterExpression() const
Returns the filter expression.
QString xExpression() const
Returns the X-axis expression.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout) override
Sets the master layout associated with the item.
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
QgsLayoutChartWidget(QgsLayoutItemChart *chartItem)
constructor
QgsLayoutItemBaseWidget(QWidget *parent SIP_TRANSFERTHIS, QgsLayoutObject *layoutObject)
Constructor for QgsLayoutItemBaseWidget, linked with the specified layoutObject.
Chart series details covering all supported series types.
A layout item subclass that renders chart plots.
A widget for controlling the common properties of layout items (e.g.
Base class for graphical items within a QgsLayout.
int type() const override
Returns a unique graphics item type identifier.
void changed()
Emitted when the object's properties change.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
Base class for all map layer types.
Definition qgsmaplayer.h:80
Interface for master layout type objects, such as print layouts and reports.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
void widgetChanged()
Emitted when the widget state changes.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual QgsPlotWidget * createPlotWidget(QWidget *parent=nullptr)=0
Creates a widget for configuring plot of this type.
double gridIntervalMinor() const
Returns the interval of minor grid lines for the axis.
Definition qgsplot.h:389
double gridIntervalMajor() const
Returns the interval of major grid lines for the axis.
Definition qgsplot.h:403
void setType(Qgis::PlotAxisType type)
Sets the axis type.
Definition qgsplot.cpp:108
void setGridIntervalMajor(double interval)
Sets the interval of major grid lines for the axis.
Definition qgsplot.h:410
void setGridIntervalMinor(double interval)
Sets the interval of minor grid lines for the axis.
Definition qgsplot.h:396
void setLabelInterval(double interval)
Sets the interval of labels for the axis.
Definition qgsplot.h:424
Qgis::PlotAxisType type() const
Returns the axis type.
Definition qgsplot.cpp:103
double labelInterval() const
Returns the interval of labels for the axis.
Definition qgsplot.h:417
QgsPlot * createPlot(const QString &type) const
Creates a new instance of a plot given the type.
QMap< QString, QString > plotTypes() const
Returns a map of available plot types to translated name.
QgsPlotAbstractMetadata * plotMetadata(const QString &type) const
Returns the metadata for the specified plot type.
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
virtual QgsPlot * createPlot()=0
Creates a plot defined by the current settings in the widget.
virtual void setPlot(QgsPlot *plot)=0
Sets the widget to match the settings of the plot.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6511