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 "mainwindow.h"
31#include "chartview.h"
32#include <QtCharts/QScatterSeries>
33#include <QtCharts/QLineSeries>
34#include <QtCharts/QValueAxis>
35#include <QtCharts/QLogValueAxis>
36#include <QtCharts/QDateTimeAxis>
37#include <QtCharts/QCategoryAxis>
38#include <QtCharts/QChart>
39#include <QtCore/QRandomGenerator>
40#include <QtCore/QDebug>
41#include <QtCore/QDateTime>
42
43QT_CHARTS_USE_NAMESPACE
44#include "ui_mainwindow.h"
45
46MainWindow::MainWindow(QWidget *parent) :
47 QMainWindow(parent),
48 ui(new Ui::MainWindow),
49 m_xMin(0.0),
50 m_xMax(20.0),
51 m_yMin(0.0),
52 m_yMax(10.0),
53 m_backgroundBrush(new QBrush(Qt::white)),
54 m_plotAreaBackgroundBrush(new QBrush(Qt::NoBrush)),
55 m_backgroundPen(new QPen(Qt::NoPen)),
56 m_plotAreaBackgroundPen(new QPen(Qt::NoPen)),
57 m_animationOptions(QChart::NoAnimation),
58 m_chart(0),
59 m_xAxis(0),
60 m_yAxis(0),
61 m_xAxisMode(AxisModeValue),
62 m_yAxisMode(AxisModeValue),
63 m_pointCount(100)
64{
65 ui->setupUi(this);
66
67 ui->yMinSpin->setValue(m_yMin);
68 ui->yMaxSpin->setValue(m_yMax);
69 ui->xMinSpin->setValue(m_xMin);
70 ui->xMaxSpin->setValue(m_xMax);
71
72 initXYValueChart();
73 setXAxis(AxisModeValue);
74 setYAxis(AxisModeValue);
75
76 connect(sender: ui->yMinSpin, SIGNAL(valueChanged(double)),
77 receiver: this, SLOT(yMinChanged(double)));
78 connect(sender: ui->yMaxSpin, SIGNAL(valueChanged(double)),
79 receiver: this, SLOT(yMaxChanged(double)));
80 connect(sender: ui->xMinSpin, SIGNAL(valueChanged(double)),
81 receiver: this, SLOT(xMinChanged(double)));
82 connect(sender: ui->xMaxSpin, SIGNAL(valueChanged(double)),
83 receiver: this, SLOT(xMaxChanged(double)));
84 connect(sender: ui->animationsComboBox, SIGNAL(currentIndexChanged(int)),
85 receiver: this, SLOT(animationIndexChanged(int)));
86 connect(sender: ui->xAxisComboBox, SIGNAL(currentIndexChanged(int)),
87 receiver: this, SLOT(xAxisIndexChanged(int)));
88 connect(sender: ui->yAxisComboBox, SIGNAL(currentIndexChanged(int)),
89 receiver: this, SLOT(yAxisIndexChanged(int)));
90 connect(sender: ui->backgroundComboBox, SIGNAL(currentIndexChanged(int)),
91 receiver: this, SLOT(backgroundIndexChanged(int)));
92 connect(sender: ui->plotAreaComboBox, SIGNAL(currentIndexChanged(int)),
93 receiver: this, SLOT(plotAreaIndexChanged(int)));
94 connect(sender: ui->themeComboBox, SIGNAL(currentIndexChanged(int)),
95 receiver: this, SLOT(themeIndexChanged(int)));
96 connect(sender: ui->addSeriesButton, SIGNAL(clicked(bool)),
97 receiver: this, SLOT(addSeriesClicked()));
98 connect(sender: ui->addGLSeriesButton, SIGNAL(clicked(bool)),
99 receiver: this, SLOT(addGLSeriesClicked()));
100 connect(sender: ui->removeSeriesButton, SIGNAL(clicked(bool)),
101 receiver: this, SLOT(removeSeriesClicked()));
102 connect(sender: ui->countComboBox, SIGNAL(currentIndexChanged(int)),
103 receiver: this, SLOT(countIndexChanged(int)));
104 connect(sender: ui->colorsComboBox, SIGNAL(currentIndexChanged(int)),
105 receiver: this, SLOT(colorIndexChanged(int)));
106 connect(sender: ui->widthComboBox, SIGNAL(currentIndexChanged(int)),
107 receiver: this, SLOT(widthIndexChanged(int)));
108 connect(sender: ui->antiAliasCheckBox, SIGNAL(clicked(bool)),
109 receiver: this, SLOT(antiAliasCheckBoxClicked(bool)));
110 connect(sender: ui->intervalSpinbox, SIGNAL(valueChanged(int)),
111 receiver: &m_dataSource, SLOT(setInterval(int)));
112
113 ui->chartView->setChart(m_chart);
114 ui->chartView->setRenderHint(hint: QPainter::Antialiasing);
115
116 QObject::connect(sender: m_chart->scene(), signal: &QGraphicsScene::changed,
117 receiver: &m_dataSource, slot: &DataSource::handleSceneChanged);
118
119 m_dataSource.startUpdates(seriesList&: m_seriesList, fpsLabel: ui->fpsLabel, interval: ui->intervalSpinbox->value());
120}
121
122MainWindow::~MainWindow()
123{
124 delete ui;
125}
126
127void MainWindow::initXYValueChart()
128{
129 m_chart = new QChart();
130 m_chart->setTitle("Use arrow keys to scroll and +/- to zoom");
131 m_chart->setAnimationOptions(m_animationOptions);
132 m_chart->setBackgroundBrush(*m_backgroundBrush);
133 m_chart->setBackgroundPen(*m_backgroundPen);
134 m_chart->setPlotAreaBackgroundBrush(*m_plotAreaBackgroundBrush);
135 m_chart->setPlotAreaBackgroundPen(*m_plotAreaBackgroundPen);
136}
137
138void MainWindow::setXAxis(MainWindow::AxisMode mode)
139{
140 if (m_xAxis) {
141 m_chart->removeAxis(axis: m_xAxis);
142 delete m_xAxis;
143 m_xAxis = 0;
144 }
145
146 m_xAxisMode = mode;
147
148 switch (m_xAxisMode) {
149 case AxisModeNone:
150 return;
151 case AxisModeValue:
152 m_xAxis = new QValueAxis();
153 break;
154 case AxisModeLogValue:
155 m_xAxis = new QLogValueAxis();
156 break;
157 case AxisModeDateTime:
158 m_xAxis = new QDateTimeAxis();
159 break;
160 case AxisModeCategory:
161 m_xAxis = new QCategoryAxis();
162 applyCategories();
163 break;
164 default:
165 qWarning() << "Unsupported AxisMode";
166 return;
167 }
168
169 m_chart->addAxis(axis: m_xAxis, alignment: Qt::AlignBottom);
170
171 foreach (QAbstractSeries *series, m_seriesList)
172 series->attachAxis(axis: m_xAxis);
173
174 applyRanges();
175}
176
177void MainWindow::setYAxis(MainWindow::AxisMode mode)
178{
179 if (m_yAxis) {
180 m_chart->removeAxis(axis: m_yAxis);
181 delete m_yAxis;
182 m_yAxis = 0;
183 }
184
185 m_yAxisMode = mode;
186
187 switch (m_yAxisMode) {
188 case AxisModeNone:
189 return;
190 case AxisModeValue:
191 m_yAxis = new QValueAxis();
192 break;
193 case AxisModeLogValue:
194 m_yAxis = new QLogValueAxis();
195 break;
196 case AxisModeDateTime:
197 m_yAxis = new QDateTimeAxis();
198 break;
199 case AxisModeCategory:
200 m_yAxis = new QCategoryAxis();
201 applyCategories();
202 break;
203 default:
204 qWarning() << "Unsupported AxisMode";
205 return;
206 }
207
208 m_chart->addAxis(axis: m_yAxis, alignment: Qt::AlignLeft);
209
210 foreach (QAbstractSeries *series, m_seriesList)
211 series->attachAxis(axis: m_yAxis);
212
213 applyRanges();
214}
215
216void MainWindow::applyRanges()
217{
218 if (m_xAxis) {
219 if (m_xAxisMode == AxisModeLogValue) {
220 if (m_xMin <= 0)
221 m_xMin = 1.0;
222 if (m_xMax <= m_xMin)
223 m_xMax = m_xMin + 1.0;
224 }
225 if (m_xAxisMode == AxisModeDateTime) {
226 QDateTime dateTimeMin;
227 QDateTime dateTimeMax;
228 dateTimeMin.setMSecsSinceEpoch(qint64(m_xMin));
229 dateTimeMax.setMSecsSinceEpoch(qint64(m_xMax));
230 m_xAxis->setRange(min: dateTimeMin, max: dateTimeMax);
231 } else {
232 m_xAxis->setRange(min: m_xMin, max: m_xMax);
233 }
234 ui->xMinSpin->setValue(m_xMin);
235 ui->xMaxSpin->setValue(m_xMax);
236 }
237 if (m_yAxis) {
238 if (m_yAxisMode == AxisModeLogValue) {
239 if (m_yMin <= 0)
240 m_yMin = 1.0;
241 if (m_yMax <= m_yMin)
242 m_yMax = m_yMin + 1.0;
243 }
244 if (m_yAxisMode == AxisModeDateTime) {
245 QDateTime dateTimeMin;
246 QDateTime dateTimeMax;
247 dateTimeMin.setMSecsSinceEpoch(qint64(m_yMin));
248 dateTimeMax.setMSecsSinceEpoch(qint64(m_yMax));
249 m_yAxis->setRange(min: dateTimeMin, max: dateTimeMax);
250 } else {
251 m_yAxis->setRange(min: m_yMin, max: m_yMax);
252 }
253 ui->yMinSpin->setValue(m_yMin);
254 ui->yMaxSpin->setValue(m_yMax);
255 }
256}
257
258void MainWindow::xMinChanged(double value)
259{
260 m_xMin = value;
261 applyRanges();
262}
263
264void MainWindow::xMaxChanged(double value)
265{
266 m_xMax = value;
267 applyRanges();
268}
269
270void MainWindow::yMinChanged(double value)
271{
272 m_yMin = value;
273 applyRanges();
274}
275
276void MainWindow::yMaxChanged(double value)
277{
278 m_yMax = value;
279 applyRanges();
280}
281
282void MainWindow::animationIndexChanged(int index)
283{
284 switch (index) {
285 case 0:
286 m_animationOptions = QChart::NoAnimation;
287 break;
288 case 1:
289 m_animationOptions = QChart::SeriesAnimations;
290 break;
291 case 2:
292 m_animationOptions = QChart::GridAxisAnimations;
293 break;
294 case 3:
295 m_animationOptions = QChart::AllAnimations;
296 break;
297 default:
298 break;
299 }
300
301 m_chart->setAnimationOptions(m_animationOptions);
302}
303
304void MainWindow::xRangeChanged(qreal min, qreal max)
305{
306 if (!qFuzzyCompare(p1: qreal(ui->xMinSpin->value()), p2: min))
307 ui->xMinSpin->setValue(min);
308 if (!qFuzzyCompare(p1: qreal(ui->xMaxSpin->value()), p2: max))
309 ui->xMaxSpin->setValue(max);
310}
311
312void MainWindow::yRangeChanged(qreal min, qreal max)
313{
314 if (!qFuzzyCompare(p1: qreal(ui->yMinSpin->value()), p2: min))
315 ui->yMinSpin->setValue(min);
316 if (!qFuzzyCompare(p1: qreal(ui->yMaxSpin->value()), p2: max))
317 ui->yMaxSpin->setValue(max);
318}
319
320void MainWindow::xAxisIndexChanged(int index)
321{
322 switch (index) {
323 case 0:
324 setXAxis(AxisModeNone);
325 break;
326 case 1:
327 setXAxis(AxisModeValue);
328 break;
329 case 2:
330 setXAxis(AxisModeLogValue);
331 break;
332 case 3:
333 setXAxis(AxisModeDateTime);
334 break;
335 case 4:
336 setXAxis(AxisModeCategory);
337 break;
338 default:
339 qWarning(msg: "Invalid Index!");
340 }
341}
342
343void MainWindow::yAxisIndexChanged(int index)
344{
345 switch (index) {
346 case 0:
347 setYAxis(AxisModeNone);
348 break;
349 case 1:
350 setYAxis(AxisModeValue);
351 break;
352 case 2:
353 setYAxis(AxisModeLogValue);
354 break;
355 case 3:
356 setYAxis(AxisModeDateTime);
357 break;
358 case 4:
359 setYAxis(AxisModeCategory);
360 break;
361 default:
362 qWarning(msg: "Invalid Index!");
363 }
364}
365
366void MainWindow::themeIndexChanged(int index)
367{
368 m_chart->setTheme(QChart::ChartTheme(index));
369}
370
371void MainWindow::addSeriesClicked()
372{
373 addSeries(gl: false);
374}
375
376void MainWindow::removeSeriesClicked()
377{
378 if (m_seriesList.size()) {
379 QXYSeries *series = m_seriesList.takeAt(i: m_seriesList.size() - 1);
380 m_chart->removeSeries(series);
381 delete series;
382 }
383}
384
385void MainWindow::addGLSeriesClicked()
386{
387 addSeries(gl: true);
388}
389
390void MainWindow::countIndexChanged(int index)
391{
392 m_pointCount = ui->countComboBox->itemText(index).toInt();
393 for (int i = 0; i < m_seriesList.size(); i++) {
394 m_dataSource.generateData(seriesIndex: i, rowCount: 2, colCount: m_pointCount);
395 }
396}
397
398void MainWindow::colorIndexChanged(int index)
399{
400 QColor color = QColor(ui->colorsComboBox->itemText(index).toLower());
401 foreach (QXYSeries *series, m_seriesList) {
402 if (series->type() == QAbstractSeries::SeriesTypeScatter) {
403 QScatterSeries *scatterSeries = static_cast<QScatterSeries *>(series);
404 scatterSeries->setBorderColor(color);
405 scatterSeries->setColor(color);
406 } else {
407 series->setColor(color);
408 }
409 }
410}
411
412void MainWindow::widthIndexChanged(int index)
413{
414 int width = ui->widthComboBox->itemText(index).toInt();
415 foreach (QXYSeries *series, m_seriesList) {
416 if (series->type() == QAbstractSeries::SeriesTypeScatter) {
417 QScatterSeries *scatterSeries = static_cast<QScatterSeries *>(series);
418 scatterSeries->setMarkerSize(width);
419 } else {
420 QColor color = QColor(ui->colorsComboBox->itemText(
421 index: ui->colorsComboBox->currentIndex()).toLower());
422 series->setPen(QPen(QBrush(color), width));
423 }
424 }
425}
426
427void MainWindow::antiAliasCheckBoxClicked(bool checked)
428{
429 ui->chartView->setRenderHint(hint: QPainter::Antialiasing, enabled: checked);
430}
431
432void MainWindow::handleHovered(const QPointF &point, bool state)
433{
434 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: sender());
435 if (series) {
436 qDebug() << __FUNCTION__ << series->name() << point << state;
437 const QString labelTemplate = QStringLiteral("%3: %1 x %2 - %4");
438 ui->coordinatesLabel->setText(
439 labelTemplate.arg(a: point.x()).arg(a: point.y()).arg(a: series->name())
440 .arg(a: state ? QStringLiteral("enter") : QStringLiteral("leave")));
441 }
442}
443
444void MainWindow::handleClicked(const QPointF &point)
445{
446 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: sender());
447 qDebug() << __FUNCTION__ << series->name() << point;
448}
449
450void MainWindow::handlePressed(const QPointF &point)
451{
452 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: sender());
453 qDebug() << __FUNCTION__ << series->name() << point;
454}
455
456void MainWindow::handleReleased(const QPointF &point)
457{
458 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: sender());
459 qDebug() << __FUNCTION__ << series->name() << point;
460}
461
462void MainWindow::handleDoubleClicked(const QPointF &point)
463{
464 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: sender());
465 qDebug() << __FUNCTION__ << series->name() << point;
466}
467
468void MainWindow::backgroundIndexChanged(int index)
469{
470 delete m_backgroundBrush;
471 delete m_backgroundPen;
472
473 switch (index) {
474 case 0:
475 m_backgroundBrush = new QBrush(Qt::white);
476 m_backgroundPen = new QPen(Qt::NoPen);
477 break;
478 case 1:
479 m_backgroundBrush = new QBrush(Qt::blue);
480 m_backgroundPen = new QPen(Qt::NoPen);
481 break;
482 case 2:
483 m_backgroundBrush = new QBrush(Qt::yellow);
484 m_backgroundPen = new QPen(Qt::black, 2);
485 break;
486 default:
487 break;
488 }
489 m_chart->setBackgroundBrush(*m_backgroundBrush);
490 m_chart->setBackgroundPen(*m_backgroundPen);
491}
492
493void MainWindow::plotAreaIndexChanged(int index)
494{
495 delete m_plotAreaBackgroundBrush;
496 delete m_plotAreaBackgroundPen;
497
498 switch (index) {
499 case 0:
500 m_plotAreaBackgroundBrush = new QBrush(Qt::green);
501 m_plotAreaBackgroundPen = new QPen(Qt::green);
502 m_chart->setPlotAreaBackgroundVisible(false);
503 break;
504 case 1:
505 m_plotAreaBackgroundBrush = new QBrush(Qt::magenta);
506 m_plotAreaBackgroundPen = new QPen(Qt::NoPen);
507 m_chart->setPlotAreaBackgroundVisible(true);
508 break;
509 case 2:
510 m_plotAreaBackgroundBrush = new QBrush(Qt::lightGray);
511 m_plotAreaBackgroundPen = new QPen(Qt::red, 6);
512 m_chart->setPlotAreaBackgroundVisible(true);
513 break;
514 default:
515 break;
516 }
517 m_chart->setPlotAreaBackgroundBrush(*m_plotAreaBackgroundBrush);
518 m_chart->setPlotAreaBackgroundPen(*m_plotAreaBackgroundPen);
519}
520
521void MainWindow::applyCategories()
522{
523 // Basic layout is three categories, extended has five
524 if (m_xAxisMode == AxisModeCategory) {
525 QCategoryAxis *angCatAxis = static_cast<QCategoryAxis *>(m_xAxis);
526 if (angCatAxis->count() == 0) {
527 angCatAxis->setStartValue(2);
528 angCatAxis->append(label: "Category A", categoryEndValue: 6);
529 angCatAxis->append(label: "Category B", categoryEndValue: 12);
530 angCatAxis->append(label: "Category C", categoryEndValue: 18);
531 }
532 }
533
534 if (m_yAxisMode == AxisModeCategory) {
535 QCategoryAxis *radCatAxis = static_cast<QCategoryAxis *>(m_yAxis);
536 if (radCatAxis->count() == 0) {
537 radCatAxis->setStartValue(1);
538 radCatAxis->append(label: "Category 1", categoryEndValue: 3);
539 radCatAxis->append(label: "Category 2", categoryEndValue: 4);
540 radCatAxis->append(label: "Category 3", categoryEndValue: 8);
541 }
542 }
543}
544
545void MainWindow::addSeries(bool gl)
546{
547 QColor color = QColor(ui->colorsComboBox->itemText(index: ui->colorsComboBox->currentIndex()).toLower());
548 int width = ui->widthComboBox->itemText(index: ui->widthComboBox->currentIndex()).toInt();
549
550 if (m_seriesList.size() < maxSeriesCount) {
551 QXYSeries *series;
552 if (QRandomGenerator::global()->bounded(highest: 2)) {
553 series = new QLineSeries;
554 series->setPen(QPen(QBrush(color), width));
555 } else {
556 QScatterSeries *scatterSeries = new QScatterSeries;
557 scatterSeries->setMarkerSize(width);
558 scatterSeries->setBorderColor(color);
559 scatterSeries->setBrush(QBrush(color));
560 series = scatterSeries;
561 }
562 series->setUseOpenGL(gl);
563 const QString glNameTemplate = QStringLiteral("GL_%1");
564 const QString rasterNameTemplate = QStringLiteral("Raster_%1");
565 if (gl)
566 series->setName(glNameTemplate.arg(a: m_seriesList.size()));
567 else
568 series->setName(rasterNameTemplate.arg(a: m_seriesList.size()));
569 m_dataSource.generateData(seriesIndex: m_seriesList.size(), rowCount: 2, colCount: m_pointCount);
570 m_seriesList.append(t: series);
571 m_chart->addSeries(series);
572 if (m_xAxis)
573 series->attachAxis(axis: m_xAxis);
574 if (m_yAxis)
575 series->attachAxis(axis: m_yAxis);
576 applyRanges();
577
578 QObject::connect(sender: series, signal: &QXYSeries::hovered, receiver: this, slot: &MainWindow::handleHovered);
579 QObject::connect(sender: series, signal: &QXYSeries::clicked, receiver: this, slot: &MainWindow::handleClicked);
580 QObject::connect(sender: series, signal: &QXYSeries::pressed, receiver: this, slot: &MainWindow::handlePressed);
581 QObject::connect(sender: series, signal: &QXYSeries::released, receiver: this, slot: &MainWindow::handleReleased);
582 QObject::connect(sender: series, signal: &QXYSeries::doubleClicked, receiver: this, slot: &MainWindow::handleDoubleClicked);
583 }
584}
585

source code of qtcharts/tests/manual/openglseriestest/mainwindow.cpp