1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29#include "mainwidget.h"
30#include "customslice.h"
31#include "pentool.h"
32#include "brushtool.h"
33#include <QtWidgets/QPushButton>
34#include <QtWidgets/QComboBox>
35#include <QtWidgets/QCheckBox>
36#include <QtWidgets/QLineEdit>
37#include <QtWidgets/QGroupBox>
38#include <QtWidgets/QDoubleSpinBox>
39#include <QtWidgets/QFormLayout>
40#include <QtWidgets/QFontDialog>
41#include <QtCharts/QChartView>
42#include <QtCharts/QPieSeries>
43
44QT_CHARTS_USE_NAMESPACE
45
46MainWidget::MainWidget(QWidget *parent)
47 : QWidget(parent),
48 m_slice(0)
49{
50 // create chart
51 QChart *chart = new QChart;
52 chart->setTitle("Piechart customization");
53 chart->setAnimationOptions(QChart::AllAnimations);
54
55 // create series
56 m_series = new QPieSeries();
57 *m_series << new CustomSlice("Slice 1", 10.0);
58 *m_series << new CustomSlice("Slice 2", 20.0);
59 *m_series << new CustomSlice("Slice 3", 30.0);
60 *m_series << new CustomSlice("Slice 4", 40.0);
61 *m_series << new CustomSlice("Slice 5", 50.0);
62 m_series->setLabelsVisible();
63 chart->addSeries(series: m_series);
64
65 connect(sender: m_series, signal: &QPieSeries::clicked, receiver: this, slot: &MainWidget::handleSliceClicked);
66
67 // chart settings
68 m_themeComboBox = new QComboBox();
69 m_themeComboBox->addItem(atext: "Light", auserData: QChart::ChartThemeLight);
70 m_themeComboBox->addItem(atext: "BlueCerulean", auserData: QChart::ChartThemeBlueCerulean);
71 m_themeComboBox->addItem(atext: "Dark", auserData: QChart::ChartThemeDark);
72 m_themeComboBox->addItem(atext: "BrownSand", auserData: QChart::ChartThemeBrownSand);
73 m_themeComboBox->addItem(atext: "BlueNcs", auserData: QChart::ChartThemeBlueNcs);
74 m_themeComboBox->addItem(atext: "High Contrast", auserData: QChart::ChartThemeHighContrast);
75 m_themeComboBox->addItem(atext: "Blue Icy", auserData: QChart::ChartThemeBlueIcy);
76 m_themeComboBox->addItem(atext: "Qt", auserData: QChart::ChartThemeQt);
77
78 m_aaCheckBox = new QCheckBox();
79 m_animationsCheckBox = new QCheckBox();
80 m_animationsCheckBox->setCheckState(Qt::Checked);
81
82 m_legendCheckBox = new QCheckBox();
83
84 QScrollArea *settingsScrollBar = new QScrollArea();
85 QWidget *settingsContentWidget = new QWidget();
86
87 QFormLayout *chartSettingsLayout = new QFormLayout(settingsContentWidget);
88 chartSettingsLayout->addRow(labelText: "Theme", field: m_themeComboBox);
89 chartSettingsLayout->addRow(labelText: "Antialiasing", field: m_aaCheckBox);
90 chartSettingsLayout->addRow(labelText: "Animations", field: m_animationsCheckBox);
91 chartSettingsLayout->addRow(labelText: "Legend", field: m_legendCheckBox);
92 QGroupBox *chartSettings = new QGroupBox("Chart");
93 chartSettings->setLayout(chartSettingsLayout);
94
95 connect(sender: m_themeComboBox, signal: static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
96 receiver: this, slot: &MainWidget::updateChartSettings);
97 connect(sender: m_aaCheckBox, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateChartSettings);
98 connect(sender: m_animationsCheckBox, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateChartSettings);
99 connect(sender: m_legendCheckBox, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateChartSettings);
100
101 // series settings
102 m_hPosition = new QDoubleSpinBox();
103 m_hPosition->setMinimum(0.0);
104 m_hPosition->setMaximum(1.0);
105 m_hPosition->setSingleStep(0.1);
106 m_hPosition->setValue(m_series->horizontalPosition());
107
108 m_vPosition = new QDoubleSpinBox();
109 m_vPosition->setMinimum(0.0);
110 m_vPosition->setMaximum(1.0);
111 m_vPosition->setSingleStep(0.1);
112 m_vPosition->setValue(m_series->verticalPosition());
113
114 m_sizeFactor = new QDoubleSpinBox();
115 m_sizeFactor->setMinimum(0.0);
116 m_sizeFactor->setMaximum(1.0);
117 m_sizeFactor->setSingleStep(0.1);
118 m_sizeFactor->setValue(m_series->pieSize());
119
120 m_startAngle = new QDoubleSpinBox();
121 m_startAngle->setMinimum(-720);
122 m_startAngle->setMaximum(720);
123 m_startAngle->setValue(m_series->pieStartAngle());
124 m_startAngle->setSingleStep(1);
125
126 m_endAngle = new QDoubleSpinBox();
127 m_endAngle->setMinimum(-720);
128 m_endAngle->setMaximum(720);
129 m_endAngle->setValue(m_series->pieEndAngle());
130 m_endAngle->setSingleStep(1);
131
132 m_holeSize = new QDoubleSpinBox();
133 m_holeSize->setMinimum(0.0);
134 m_holeSize->setMaximum(1.0);
135 m_holeSize->setSingleStep(0.1);
136 m_holeSize->setValue(m_series->holeSize());
137
138 QPushButton *appendSlice = new QPushButton("Append slice");
139 QPushButton *insertSlice = new QPushButton("Insert slice");
140 QPushButton *removeSlice = new QPushButton("Remove selected slice");
141
142 QFormLayout *seriesSettingsLayout = new QFormLayout(settingsContentWidget);
143 seriesSettingsLayout->addRow(labelText: "Horizontal position", field: m_hPosition);
144 seriesSettingsLayout->addRow(labelText: "Vertical position", field: m_vPosition);
145 seriesSettingsLayout->addRow(labelText: "Size factor", field: m_sizeFactor);
146 seriesSettingsLayout->addRow(labelText: "Start angle", field: m_startAngle);
147 seriesSettingsLayout->addRow(labelText: "End angle", field: m_endAngle);
148 seriesSettingsLayout->addRow(labelText: "Hole size", field: m_holeSize);
149 seriesSettingsLayout->addRow(widget: appendSlice);
150 seriesSettingsLayout->addRow(widget: insertSlice);
151 seriesSettingsLayout->addRow(widget: removeSlice);
152 QGroupBox *seriesSettings = new QGroupBox("Series");
153 seriesSettings->setLayout(seriesSettingsLayout);
154
155 connect(sender: m_vPosition,
156 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
157 receiver: this, slot: &MainWidget::updateSerieSettings);
158 connect(sender: m_hPosition,
159 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
160 receiver: this, slot: &MainWidget::updateSerieSettings);
161 connect(sender: m_sizeFactor,
162 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
163 receiver: this, slot: &MainWidget::updateSerieSettings);
164 connect(sender: m_startAngle,
165 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
166 receiver: this, slot: &MainWidget::updateSerieSettings);
167 connect(sender: m_endAngle,
168 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
169 receiver: this, slot: &MainWidget::updateSerieSettings);
170 connect(sender: m_holeSize,
171 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
172 receiver: this, slot: &MainWidget::updateSerieSettings);
173 connect(sender: appendSlice, signal: &QPushButton::clicked, receiver: this, slot: &MainWidget::appendSlice);
174 connect(sender: insertSlice, signal: &QPushButton::clicked, receiver: this, slot: &MainWidget::insertSlice);
175 connect(sender: removeSlice, signal: &QPushButton::clicked, receiver: this, slot: &MainWidget::removeSlice);
176
177 // slice settings
178 m_sliceName = new QLineEdit("<click a slice>");
179 m_sliceName->setSizePolicy(hor: QSizePolicy::Maximum, ver: QSizePolicy::Maximum);
180 m_sliceValue = new QDoubleSpinBox();
181 m_sliceValue->setMaximum(1000);
182 m_sliceLabelVisible = new QCheckBox();
183 m_sliceLabelArmFactor = new QDoubleSpinBox();
184 m_sliceLabelArmFactor->setSingleStep(0.01);
185 m_sliceExploded = new QCheckBox();
186 m_sliceExplodedFactor = new QDoubleSpinBox();
187 m_sliceExplodedFactor->setSingleStep(0.01);
188 m_pen = new QPushButton();
189 m_penTool = new PenTool("Slice pen", this);
190 m_brush = new QPushButton();
191 m_brushTool = new BrushTool("Slice brush", this);
192 m_font = new QPushButton();
193 m_labelBrush = new QPushButton();
194 m_labelBrushTool = new BrushTool("Label brush", this);
195 m_labelPosition = new QComboBox(this);
196 m_labelPosition->addItem(atext: "Outside", auserData: QPieSlice::LabelOutside);
197 m_labelPosition->addItem(atext: "Inside horizontal", auserData: QPieSlice::LabelInsideHorizontal);
198 m_labelPosition->addItem(atext: "Inside tangential", auserData: QPieSlice::LabelInsideTangential);
199 m_labelPosition->addItem(atext: "Inside normal", auserData: QPieSlice::LabelInsideNormal);
200
201 QFormLayout *sliceSettingsLayout = new QFormLayout(settingsContentWidget);
202 sliceSettingsLayout->addRow(labelText: "Label", field: m_sliceName);
203 sliceSettingsLayout->addRow(labelText: "Value", field: m_sliceValue);
204 sliceSettingsLayout->addRow(labelText: "Pen", field: m_pen);
205 sliceSettingsLayout->addRow(labelText: "Brush", field: m_brush);
206 sliceSettingsLayout->addRow(labelText: "Label visible", field: m_sliceLabelVisible);
207 sliceSettingsLayout->addRow(labelText: "Label font", field: m_font);
208 sliceSettingsLayout->addRow(labelText: "Label brush", field: m_labelBrush);
209 sliceSettingsLayout->addRow(labelText: "Label position", field: m_labelPosition);
210 sliceSettingsLayout->addRow(labelText: "Label arm length", field: m_sliceLabelArmFactor);
211 sliceSettingsLayout->addRow(labelText: "Exploded", field: m_sliceExploded);
212 sliceSettingsLayout->addRow(labelText: "Explode distance", field: m_sliceExplodedFactor);
213 QGroupBox *sliceSettings = new QGroupBox("Selected slice");
214 sliceSettings->setLayout(sliceSettingsLayout);
215
216 connect(sender: m_sliceName, signal: &QLineEdit::textChanged, receiver: this, slot: &MainWidget::updateSliceSettings);
217 connect(sender: m_sliceValue,
218 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
219 receiver: this, slot: &MainWidget::updateSliceSettings);
220 connect(sender: m_pen, signal: &QPushButton::clicked, receiver: m_penTool, slot: &PenTool::show);
221 connect(sender: m_penTool, signal: &PenTool::changed, receiver: this, slot: &MainWidget::updateSliceSettings);
222 connect(sender: m_brush, signal: &QPushButton::clicked, receiver: m_brushTool, slot: &BrushTool::show);
223 connect(sender: m_brushTool, signal: &BrushTool::changed, receiver: this, slot: &MainWidget::updateSliceSettings);
224 connect(sender: m_font, signal: &QPushButton::clicked, receiver: this, slot: &MainWidget::showFontDialog);
225 connect(sender: m_labelBrush, signal: &QPushButton::clicked, receiver: m_labelBrushTool, slot: &BrushTool::show);
226 connect(sender: m_labelBrushTool, signal: &BrushTool::changed, receiver: this, slot: &MainWidget::updateSliceSettings);
227 connect(sender: m_sliceLabelVisible, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateSliceSettings);
228 connect(sender: m_sliceLabelVisible, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateSliceSettings);
229 connect(sender: m_sliceLabelArmFactor,
230 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
231 receiver: this, slot: &MainWidget::updateSliceSettings);
232 connect(sender: m_sliceExploded, signal: &QCheckBox::toggled, receiver: this, slot: &MainWidget::updateSliceSettings);
233 connect(sender: m_sliceExplodedFactor,
234 signal: static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
235 receiver: this, slot: &MainWidget::updateSliceSettings);
236 connect(sender: m_labelPosition,
237 signal: static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
238 receiver: this, slot: &MainWidget::updateSliceSettings);
239
240 // create chart view
241 m_chartView = new QChartView(chart);
242
243 // create main layout
244 QVBoxLayout *settingsLayout = new QVBoxLayout();
245 settingsLayout->addWidget(chartSettings);
246 settingsLayout->addWidget(seriesSettings);
247 settingsLayout->addWidget(sliceSettings);
248
249 settingsContentWidget->setLayout(settingsLayout);
250 settingsScrollBar->setWidget(settingsContentWidget);
251 settingsScrollBar->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Preferred);
252
253 QGridLayout *baseLayout = new QGridLayout();
254 baseLayout->addWidget(settingsScrollBar, row: 0, column: 0);
255 baseLayout->addWidget(m_chartView, row: 0, column: 1);
256 setLayout(baseLayout);
257
258 updateSerieSettings();
259 updateChartSettings();
260}
261
262
263void MainWidget::updateChartSettings()
264{
265 QChart::ChartTheme theme = static_cast<QChart::ChartTheme>(m_themeComboBox->itemData(
266 index: m_themeComboBox->currentIndex()).toInt());
267 m_chartView->chart()->setTheme(theme);
268 m_chartView->setRenderHint(hint: QPainter::Antialiasing, enabled: m_aaCheckBox->isChecked());
269
270 if (m_animationsCheckBox->checkState() == Qt::Checked)
271 m_chartView->chart()->setAnimationOptions(QChart::AllAnimations);
272 else
273 m_chartView->chart()->setAnimationOptions(QChart::NoAnimation);
274
275 if (m_legendCheckBox->checkState() == Qt::Checked)
276 m_chartView->chart()->legend()->show();
277 else
278 m_chartView->chart()->legend()->hide();
279}
280
281void MainWidget::updateSerieSettings()
282{
283 m_series->setHorizontalPosition(m_hPosition->value());
284 m_series->setVerticalPosition(m_vPosition->value());
285 m_series->setPieSize(m_sizeFactor->value());
286 m_holeSize->setMaximum(m_sizeFactor->value());
287 m_series->setPieStartAngle(m_startAngle->value());
288 m_series->setPieEndAngle(m_endAngle->value());
289 m_series->setHoleSize(m_holeSize->value());
290}
291
292void MainWidget::updateSliceSettings()
293{
294 if (!m_slice)
295 return;
296
297 m_slice->setLabel(m_sliceName->text());
298
299 m_slice->setValue(m_sliceValue->value());
300
301 m_slice->setPen(m_penTool->pen());
302 m_slice->setBrush(m_brushTool->brush());
303
304 m_slice->setLabelBrush(m_labelBrushTool->brush());
305 m_slice->setLabelVisible(m_sliceLabelVisible->isChecked());
306 m_slice->setLabelArmLengthFactor(m_sliceLabelArmFactor->value());
307 // We assume that label position index is in sync with the enum
308 m_slice->setLabelPosition((QPieSlice::LabelPosition)m_labelPosition->currentIndex());
309
310 m_slice->setExploded(m_sliceExploded->isChecked());
311 m_slice->setExplodeDistanceFactor(m_sliceExplodedFactor->value());
312}
313
314void MainWidget::handleSliceClicked(QPieSlice *slice)
315{
316 m_slice = static_cast<CustomSlice *>(slice);
317
318 // name
319 m_sliceName->blockSignals(b: true);
320 m_sliceName->setText(slice->label());
321 m_sliceName->blockSignals(b: false);
322
323 // value
324 m_sliceValue->blockSignals(b: true);
325 m_sliceValue->setValue(slice->value());
326 m_sliceValue->blockSignals(b: false);
327
328 // pen
329 m_pen->setText(PenTool::name(pen: m_slice->pen()));
330 m_penTool->setPen(m_slice->pen());
331
332 // brush
333 m_brush->setText(m_slice->originalBrush().color().name());
334 m_brushTool->setBrush(m_slice->originalBrush());
335
336 // label
337 m_labelBrush->setText(BrushTool::name(brush: m_slice->labelBrush()));
338 m_labelBrushTool->setBrush(m_slice->labelBrush());
339 m_font->setText(slice->labelFont().toString());
340 m_sliceLabelVisible->blockSignals(b: true);
341 m_sliceLabelVisible->setChecked(slice->isLabelVisible());
342 m_sliceLabelVisible->blockSignals(b: false);
343 m_sliceLabelArmFactor->blockSignals(b: true);
344 m_sliceLabelArmFactor->setValue(slice->labelArmLengthFactor());
345 m_sliceLabelArmFactor->blockSignals(b: false);
346 m_labelPosition->blockSignals(b: true);
347 // We assume that label position index is in sync with the enum
348 m_labelPosition->setCurrentIndex(slice->labelPosition());
349 m_labelPosition->blockSignals(b: false);
350
351 // exploded
352 m_sliceExploded->blockSignals(b: true);
353 m_sliceExploded->setChecked(slice->isExploded());
354 m_sliceExploded->blockSignals(b: false);
355 m_sliceExplodedFactor->blockSignals(b: true);
356 m_sliceExplodedFactor->setValue(slice->explodeDistanceFactor());
357 m_sliceExplodedFactor->blockSignals(b: false);
358}
359
360void MainWidget::showFontDialog()
361{
362 if (!m_slice)
363 return;
364
365 QFontDialog dialog(m_slice->labelFont());
366 dialog.show();
367 dialog.exec();
368
369 m_slice->setLabelFont(dialog.currentFont());
370 m_font->setText(dialog.currentFont().toString());
371}
372
373void MainWidget::appendSlice()
374{
375 *m_series << new CustomSlice("Slice " + QString::number(m_series->count() + 1), 10.0);
376}
377
378void MainWidget::insertSlice()
379{
380 if (!m_slice)
381 return;
382
383 int i = m_series->slices().indexOf(t: m_slice);
384
385 m_series->insert(index: i, slice: new CustomSlice("Slice " + QString::number(m_series->count() + 1), 10.0));
386}
387
388void MainWidget::removeSlice()
389{
390 if (!m_slice)
391 return;
392
393 m_sliceName->setText("<click a slice>");
394
395 m_series->remove(slice: m_slice);
396 m_slice = 0;
397}
398
399#include "moc_mainwidget.cpp"
400

source code of qtcharts/examples/charts/piechartcustomization/mainwidget.cpp