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 | |
30 | #include "themewidget.h" |
31 | #include "ui_themewidget.h" |
32 | |
33 | #include <QtCharts/QChartView> |
34 | #include <QtCharts/QPieSeries> |
35 | #include <QtCharts/QPieSlice> |
36 | #include <QtCharts/QAbstractBarSeries> |
37 | #include <QtCharts/QPercentBarSeries> |
38 | #include <QtCharts/QStackedBarSeries> |
39 | #include <QtCharts/QBarSeries> |
40 | #include <QtCharts/QBarSet> |
41 | #include <QtCharts/QLineSeries> |
42 | #include <QtCharts/QSplineSeries> |
43 | #include <QtCharts/QScatterSeries> |
44 | #include <QtCharts/QAreaSeries> |
45 | #include <QtCharts/QLegend> |
46 | #include <QtWidgets/QGridLayout> |
47 | #include <QtWidgets/QFormLayout> |
48 | #include <QtWidgets/QComboBox> |
49 | #include <QtWidgets/QSpinBox> |
50 | #include <QtWidgets/QCheckBox> |
51 | #include <QtWidgets/QGroupBox> |
52 | #include <QtWidgets/QLabel> |
53 | #include <QtCore/QRandomGenerator> |
54 | #include <QtCharts/QBarCategoryAxis> |
55 | #include <QtWidgets/QApplication> |
56 | #include <QtCharts/QValueAxis> |
57 | |
58 | ThemeWidget::ThemeWidget(QWidget *parent) : |
59 | QWidget(parent), |
60 | m_listCount(3), |
61 | m_valueMax(10), |
62 | m_valueCount(7), |
63 | m_dataTable(generateRandomData(listCount: m_listCount, valueMax: m_valueMax, valueCount: m_valueCount)), |
64 | m_ui(new Ui_ThemeWidgetForm) |
65 | { |
66 | m_ui->setupUi(this); |
67 | populateThemeBox(); |
68 | populateAnimationBox(); |
69 | populateLegendBox(); |
70 | |
71 | //create charts |
72 | |
73 | QChartView *chartView; |
74 | |
75 | chartView = new QChartView(createAreaChart()); |
76 | m_ui->gridLayout->addWidget(chartView, row: 1, column: 0); |
77 | m_charts << chartView; |
78 | |
79 | chartView = new QChartView(createPieChart()); |
80 | // Funny things happen if the pie slice labels do not fit the screen, so we ignore size policy |
81 | chartView->setSizePolicy(hor: QSizePolicy::Ignored, ver: QSizePolicy::Ignored); |
82 | m_ui->gridLayout->addWidget(chartView, row: 1, column: 1); |
83 | m_charts << chartView; |
84 | |
85 | //![5] |
86 | chartView = new QChartView(createLineChart()); |
87 | m_ui->gridLayout->addWidget(chartView, row: 1, column: 2); |
88 | //![5] |
89 | m_charts << chartView; |
90 | |
91 | chartView = new QChartView(createBarChart(valueCount: m_valueCount)); |
92 | m_ui->gridLayout->addWidget(chartView, row: 2, column: 0); |
93 | m_charts << chartView; |
94 | |
95 | chartView = new QChartView(createSplineChart()); |
96 | m_ui->gridLayout->addWidget(chartView, row: 2, column: 1); |
97 | m_charts << chartView; |
98 | |
99 | chartView = new QChartView(createScatterChart()); |
100 | m_ui->gridLayout->addWidget(chartView, row: 2, column: 2); |
101 | m_charts << chartView; |
102 | |
103 | // Set defaults |
104 | m_ui->antialiasCheckBox->setChecked(true); |
105 | |
106 | // Set the colors from the light theme as default ones |
107 | QPalette pal = qApp->palette(); |
108 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0xf0f0f0)); |
109 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
110 | qApp->setPalette(pal); |
111 | |
112 | updateUI(); |
113 | } |
114 | |
115 | ThemeWidget::~ThemeWidget() |
116 | { |
117 | delete m_ui; |
118 | } |
119 | |
120 | DataTable ThemeWidget::generateRandomData(int listCount, int valueMax, int valueCount) const |
121 | { |
122 | DataTable dataTable; |
123 | |
124 | // generate random data |
125 | for (int i(0); i < listCount; i++) { |
126 | DataList dataList; |
127 | qreal yValue(0); |
128 | for (int j(0); j < valueCount; j++) { |
129 | yValue = yValue + QRandomGenerator::global()->bounded(highest: valueMax / (qreal) valueCount); |
130 | QPointF value((j + QRandomGenerator::global()->generateDouble()) * ((qreal) m_valueMax / (qreal) valueCount), |
131 | yValue); |
132 | QString label = "Slice " + QString::number(i) + ":" + QString::number(j); |
133 | dataList << Data(value, label); |
134 | } |
135 | dataTable << dataList; |
136 | } |
137 | |
138 | return dataTable; |
139 | } |
140 | |
141 | void ThemeWidget::populateThemeBox() |
142 | { |
143 | // add items to theme combobox |
144 | m_ui->themeComboBox->addItem(atext: "Light" , auserData: QChart::ChartThemeLight); |
145 | m_ui->themeComboBox->addItem(atext: "Blue Cerulean" , auserData: QChart::ChartThemeBlueCerulean); |
146 | m_ui->themeComboBox->addItem(atext: "Dark" , auserData: QChart::ChartThemeDark); |
147 | m_ui->themeComboBox->addItem(atext: "Brown Sand" , auserData: QChart::ChartThemeBrownSand); |
148 | m_ui->themeComboBox->addItem(atext: "Blue NCS" , auserData: QChart::ChartThemeBlueNcs); |
149 | m_ui->themeComboBox->addItem(atext: "High Contrast" , auserData: QChart::ChartThemeHighContrast); |
150 | m_ui->themeComboBox->addItem(atext: "Blue Icy" , auserData: QChart::ChartThemeBlueIcy); |
151 | m_ui->themeComboBox->addItem(atext: "Qt" , auserData: QChart::ChartThemeQt); |
152 | } |
153 | |
154 | void ThemeWidget::populateAnimationBox() |
155 | { |
156 | // add items to animation combobox |
157 | m_ui->animatedComboBox->addItem(atext: "No Animations" , auserData: QChart::NoAnimation); |
158 | m_ui->animatedComboBox->addItem(atext: "GridAxis Animations" , auserData: QChart::GridAxisAnimations); |
159 | m_ui->animatedComboBox->addItem(atext: "Series Animations" , auserData: QChart::SeriesAnimations); |
160 | m_ui->animatedComboBox->addItem(atext: "All Animations" , auserData: QChart::AllAnimations); |
161 | } |
162 | |
163 | void ThemeWidget::populateLegendBox() |
164 | { |
165 | // add items to legend combobox |
166 | m_ui->legendComboBox->addItem(atext: "No Legend " , auserData: 0); |
167 | m_ui->legendComboBox->addItem(atext: "Legend Top" , auserData: Qt::AlignTop); |
168 | m_ui->legendComboBox->addItem(atext: "Legend Bottom" , auserData: Qt::AlignBottom); |
169 | m_ui->legendComboBox->addItem(atext: "Legend Left" , auserData: Qt::AlignLeft); |
170 | m_ui->legendComboBox->addItem(atext: "Legend Right" , auserData: Qt::AlignRight); |
171 | } |
172 | |
173 | QChart *ThemeWidget::createAreaChart() const |
174 | { |
175 | QChart *chart = new QChart(); |
176 | chart->setTitle("Area chart" ); |
177 | |
178 | // The lower series initialized to zero values |
179 | QLineSeries *lowerSeries = 0; |
180 | QString name("Series " ); |
181 | int nameIndex = 0; |
182 | for (int i(0); i < m_dataTable.count(); i++) { |
183 | QLineSeries *upperSeries = new QLineSeries(chart); |
184 | for (int j(0); j < m_dataTable[i].count(); j++) { |
185 | Data data = m_dataTable[i].at(i: j); |
186 | if (lowerSeries) { |
187 | const QVector<QPointF>& points = lowerSeries->pointsVector(); |
188 | upperSeries->append(point: QPointF(j, points[i].y() + data.first.y())); |
189 | } else { |
190 | upperSeries->append(point: QPointF(j, data.first.y())); |
191 | } |
192 | } |
193 | QAreaSeries *area = new QAreaSeries(upperSeries, lowerSeries); |
194 | area->setName(name + QString::number(nameIndex)); |
195 | nameIndex++; |
196 | chart->addSeries(series: area); |
197 | lowerSeries = upperSeries; |
198 | } |
199 | |
200 | chart->createDefaultAxes(); |
201 | chart->axes(orientation: Qt::Horizontal).first()->setRange(min: 0, max: m_valueCount - 1); |
202 | chart->axes(orientation: Qt::Vertical).first()->setRange(min: 0, max: m_valueMax); |
203 | // Add space to label to add space between labels and axis |
204 | QValueAxis *axisY = qobject_cast<QValueAxis*>(object: chart->axes(orientation: Qt::Vertical).first()); |
205 | Q_ASSERT(axisY); |
206 | axisY->setLabelFormat("%.1f " ); |
207 | |
208 | return chart; |
209 | } |
210 | |
211 | QChart *ThemeWidget::createBarChart(int valueCount) const |
212 | { |
213 | Q_UNUSED(valueCount); |
214 | QChart *chart = new QChart(); |
215 | chart->setTitle("Bar chart" ); |
216 | |
217 | QStackedBarSeries *series = new QStackedBarSeries(chart); |
218 | for (int i(0); i < m_dataTable.count(); i++) { |
219 | QBarSet *set = new QBarSet("Bar set " + QString::number(i)); |
220 | for (const Data &data : m_dataTable[i]) |
221 | *set << data.first.y(); |
222 | series->append(set); |
223 | } |
224 | chart->addSeries(series); |
225 | |
226 | chart->createDefaultAxes(); |
227 | chart->axes(orientation: Qt::Vertical).first()->setRange(min: 0, max: m_valueMax * 2); |
228 | // Add space to label to add space between labels and axis |
229 | QValueAxis *axisY = qobject_cast<QValueAxis*>(object: chart->axes(orientation: Qt::Vertical).first()); |
230 | Q_ASSERT(axisY); |
231 | axisY->setLabelFormat("%.1f " ); |
232 | |
233 | return chart; |
234 | } |
235 | |
236 | QChart *ThemeWidget::createLineChart() const |
237 | { |
238 | //![1] |
239 | QChart *chart = new QChart(); |
240 | chart->setTitle("Line chart" ); |
241 | //![1] |
242 | |
243 | //![2] |
244 | QString name("Series " ); |
245 | int nameIndex = 0; |
246 | for (const DataList &list : m_dataTable) { |
247 | QLineSeries *series = new QLineSeries(chart); |
248 | for (const Data &data : list) |
249 | series->append(point: data.first); |
250 | series->setName(name + QString::number(nameIndex)); |
251 | nameIndex++; |
252 | chart->addSeries(series); |
253 | } |
254 | //![2] |
255 | |
256 | //![3] |
257 | chart->createDefaultAxes(); |
258 | chart->axes(orientation: Qt::Horizontal).first()->setRange(min: 0, max: m_valueMax); |
259 | chart->axes(orientation: Qt::Vertical).first()->setRange(min: 0, max: m_valueCount); |
260 | //![3] |
261 | //![4] |
262 | // Add space to label to add space between labels and axis |
263 | QValueAxis *axisY = qobject_cast<QValueAxis*>(object: chart->axes(orientation: Qt::Vertical).first()); |
264 | Q_ASSERT(axisY); |
265 | axisY->setLabelFormat("%.1f " ); |
266 | //![4] |
267 | |
268 | return chart; |
269 | } |
270 | |
271 | QChart *ThemeWidget::createPieChart() const |
272 | { |
273 | QChart *chart = new QChart(); |
274 | chart->setTitle("Pie chart" ); |
275 | |
276 | QPieSeries *series = new QPieSeries(chart); |
277 | for (const Data &data : m_dataTable[0]) { |
278 | QPieSlice *slice = series->append(label: data.second, value: data.first.y()); |
279 | if (data == m_dataTable[0].first()) { |
280 | // Show the first slice exploded with label |
281 | slice->setLabelVisible(); |
282 | slice->setExploded(); |
283 | slice->setExplodeDistanceFactor(0.5); |
284 | } |
285 | } |
286 | series->setPieSize(0.4); |
287 | chart->addSeries(series); |
288 | |
289 | return chart; |
290 | } |
291 | |
292 | QChart *ThemeWidget::createSplineChart() const |
293 | { |
294 | QChart *chart = new QChart(); |
295 | chart->setTitle("Spline chart" ); |
296 | QString name("Series " ); |
297 | int nameIndex = 0; |
298 | for (const DataList &list : m_dataTable) { |
299 | QSplineSeries *series = new QSplineSeries(chart); |
300 | for (const Data &data : list) |
301 | series->append(point: data.first); |
302 | series->setName(name + QString::number(nameIndex)); |
303 | nameIndex++; |
304 | chart->addSeries(series); |
305 | } |
306 | |
307 | chart->createDefaultAxes(); |
308 | chart->axes(orientation: Qt::Horizontal).first()->setRange(min: 0, max: m_valueMax); |
309 | chart->axes(orientation: Qt::Vertical).first()->setRange(min: 0, max: m_valueCount); |
310 | |
311 | // Add space to label to add space between labels and axis |
312 | QValueAxis *axisY = qobject_cast<QValueAxis*>(object: chart->axes(orientation: Qt::Vertical).first()); |
313 | Q_ASSERT(axisY); |
314 | axisY->setLabelFormat("%.1f " ); |
315 | return chart; |
316 | } |
317 | |
318 | QChart *ThemeWidget::createScatterChart() const |
319 | { |
320 | // scatter chart |
321 | QChart *chart = new QChart(); |
322 | chart->setTitle("Scatter chart" ); |
323 | QString name("Series " ); |
324 | int nameIndex = 0; |
325 | for (const DataList &list : m_dataTable) { |
326 | QScatterSeries *series = new QScatterSeries(chart); |
327 | for (const Data &data : list) |
328 | series->append(point: data.first); |
329 | series->setName(name + QString::number(nameIndex)); |
330 | nameIndex++; |
331 | chart->addSeries(series); |
332 | } |
333 | |
334 | chart->createDefaultAxes(); |
335 | chart->axes(orientation: Qt::Horizontal).first()->setRange(min: 0, max: m_valueMax); |
336 | chart->axes(orientation: Qt::Vertical).first()->setRange(min: 0, max: m_valueCount); |
337 | // Add space to label to add space between labels and axis |
338 | QValueAxis *axisY = qobject_cast<QValueAxis*>(object: chart->axes(orientation: Qt::Vertical).first()); |
339 | Q_ASSERT(axisY); |
340 | axisY->setLabelFormat("%.1f " ); |
341 | return chart; |
342 | } |
343 | |
344 | void ThemeWidget::updateUI() |
345 | { |
346 | //![6] |
347 | QChart::ChartTheme theme = static_cast<QChart::ChartTheme>( |
348 | m_ui->themeComboBox->itemData(index: m_ui->themeComboBox->currentIndex()).toInt()); |
349 | //![6] |
350 | const auto charts = m_charts; |
351 | if (!m_charts.isEmpty() && m_charts.at(i: 0)->chart()->theme() != theme) { |
352 | for (QChartView *chartView : charts) { |
353 | //![7] |
354 | chartView->chart()->setTheme(theme); |
355 | //![7] |
356 | } |
357 | |
358 | // Set palette colors based on selected theme |
359 | //![8] |
360 | QPalette pal = window()->palette(); |
361 | if (theme == QChart::ChartThemeLight) { |
362 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0xf0f0f0)); |
363 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
364 | //![8] |
365 | } else if (theme == QChart::ChartThemeDark) { |
366 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0x121218)); |
367 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0xd6d6d6)); |
368 | } else if (theme == QChart::ChartThemeBlueCerulean) { |
369 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0x40434a)); |
370 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0xd6d6d6)); |
371 | } else if (theme == QChart::ChartThemeBrownSand) { |
372 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0x9e8965)); |
373 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
374 | } else if (theme == QChart::ChartThemeBlueNcs) { |
375 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0x018bba)); |
376 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
377 | } else if (theme == QChart::ChartThemeHighContrast) { |
378 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0xffab03)); |
379 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x181818)); |
380 | } else if (theme == QChart::ChartThemeBlueIcy) { |
381 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0xcee7f0)); |
382 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
383 | } else { |
384 | pal.setColor(acr: QPalette::Window, acolor: QRgb(0xf0f0f0)); |
385 | pal.setColor(acr: QPalette::WindowText, acolor: QRgb(0x404044)); |
386 | } |
387 | window()->setPalette(pal); |
388 | } |
389 | |
390 | // Update antialiasing |
391 | //![11] |
392 | bool checked = m_ui->antialiasCheckBox->isChecked(); |
393 | for (QChartView *chart : charts) |
394 | chart->setRenderHint(hint: QPainter::Antialiasing, enabled: checked); |
395 | //![11] |
396 | |
397 | // Update animation options |
398 | //![9] |
399 | QChart::AnimationOptions options( |
400 | m_ui->animatedComboBox->itemData(index: m_ui->animatedComboBox->currentIndex()).toInt()); |
401 | if (!m_charts.isEmpty() && m_charts.at(i: 0)->chart()->animationOptions() != options) { |
402 | for (QChartView *chartView : charts) |
403 | chartView->chart()->setAnimationOptions(options); |
404 | } |
405 | //![9] |
406 | |
407 | // Update legend alignment |
408 | //![10] |
409 | Qt::Alignment alignment( |
410 | m_ui->legendComboBox->itemData(index: m_ui->legendComboBox->currentIndex()).toInt()); |
411 | |
412 | if (!alignment) { |
413 | for (QChartView *chartView : charts) |
414 | chartView->chart()->legend()->hide(); |
415 | } else { |
416 | for (QChartView *chartView : charts) { |
417 | chartView->chart()->legend()->setAlignment(alignment); |
418 | chartView->chart()->legend()->show(); |
419 | } |
420 | } |
421 | //![10] |
422 | } |
423 | |
424 | |