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 | |
44 | QT_CHARTS_USE_NAMESPACE |
45 | |
46 | MainWidget::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 | |
263 | void 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 | |
281 | void 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 | |
292 | void 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 | |
314 | void 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 | |
360 | void 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 | |
373 | void MainWidget::appendSlice() |
374 | { |
375 | *m_series << new CustomSlice("Slice " + QString::number(m_series->count() + 1), 10.0); |
376 | } |
377 | |
378 | void 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 | |
388 | void 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 | |