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 "mainwidget.h"
31#include "dataseriedialog.h"
32#include <QtCharts/QChartView>
33#include <QtCharts/QPieSeries>
34#include <QtCharts/QScatterSeries>
35#include <QtCharts/QLineSeries>
36#include <QtCharts/QAreaSeries>
37#include <QtCharts/QSplineSeries>
38#include <QtCharts/QBarSet>
39#include <QtCharts/QBarSeries>
40#include <QtCharts/QStackedBarSeries>
41#include <QtCharts/QPercentBarSeries>
42#include <QtWidgets/QPushButton>
43#include <QtWidgets/QComboBox>
44#include <QtWidgets/QSpinBox>
45#include <QtWidgets/QCheckBox>
46#include <QtWidgets/QGridLayout>
47#include <QtWidgets/QHBoxLayout>
48#include <QtWidgets/QLabel>
49#include <QtWidgets/QSpacerItem>
50#include <QtWidgets/QMessageBox>
51#include <QtCore/QDebug>
52#include <QtCore/QRandomGenerator>
53#include <QtGui/QStandardItemModel>
54#include <QtCharts/QBarCategoryAxis>
55#include <QtWidgets/QOpenGLWidget>
56
57#include <qmath.h>
58
59QT_CHARTS_USE_NAMESPACE
60
61MainWidget::MainWidget(QWidget *parent) :
62 QWidget(parent),
63 m_addSerieDialog(0),
64 m_chart(0)
65{
66 m_chart = new QChart();
67
68 // Grid layout for the controls for configuring the chart widget
69 QGridLayout *grid = new QGridLayout();
70 QPushButton *addSeriesButton = new QPushButton("Add series");
71 connect(sender: addSeriesButton, SIGNAL(clicked()), receiver: this, SLOT(addSeries()));
72 grid->addWidget(addSeriesButton, row: 0, column: 1);
73 initBackroundCombo(grid);
74 initScaleControls(grid);
75 initThemeCombo(grid);
76 initCheckboxes(grid);
77
78 // add row with empty label to make all the other rows static
79 grid->addWidget(new QLabel(""), row: grid->rowCount(), column: 0);
80 grid->setRowStretch(row: grid->rowCount() - 1, stretch: 1);
81
82 // Create chart view with the chart
83 m_chartView = new QChartView(m_chart, this);
84 m_chartView->setRubberBand(QChartView::HorizontalRubberBand);
85
86 // Another grid layout as a main layout
87 QGridLayout *mainLayout = new QGridLayout();
88 mainLayout->addLayout(grid, row: 0, column: 0);
89 mainLayout->addWidget(m_chartView, row: 0, column: 1, rowSpan: 3, columnSpan: 1);
90 setLayout(mainLayout);
91}
92
93// Combo box for selecting the chart's background
94void MainWidget::initBackroundCombo(QGridLayout *grid)
95{
96 QComboBox *backgroundCombo = new QComboBox(this);
97 backgroundCombo->addItem(atext: "Color");
98 backgroundCombo->addItem(atext: "Gradient");
99 backgroundCombo->addItem(atext: "Image");
100 connect(sender: backgroundCombo, SIGNAL(currentIndexChanged(int)),
101 receiver: this, SLOT(backgroundChanged(int)));
102
103 grid->addWidget(new QLabel("Background:"), row: grid->rowCount(), column: 0);
104 grid->addWidget(backgroundCombo, row: grid->rowCount() - 1, column: 1);
105}
106
107// Scale related controls (auto-scale vs. manual min-max values)
108void MainWidget::initScaleControls(QGridLayout *grid)
109{
110 m_autoScaleCheck = new QCheckBox("Automatic scaling");
111 connect(sender: m_autoScaleCheck, SIGNAL(stateChanged(int)), receiver: this, SLOT(autoScaleChanged(int)));
112 // Allow setting also non-sense values (like -2147483648 and 2147483647)
113 m_xMinSpin = new QSpinBox();
114 m_xMinSpin->setMinimum(INT_MIN);
115 m_xMinSpin->setMaximum(INT_MAX);
116 m_xMinSpin->setValue(0);
117 connect(sender: m_xMinSpin, SIGNAL(valueChanged(int)), receiver: this, SLOT(xMinChanged(int)));
118 m_xMaxSpin = new QSpinBox();
119 m_xMaxSpin->setMinimum(INT_MIN);
120 m_xMaxSpin->setMaximum(INT_MAX);
121 m_xMaxSpin->setValue(10);
122 connect(sender: m_xMaxSpin, SIGNAL(valueChanged(int)), receiver: this, SLOT(xMaxChanged(int)));
123 m_yMinSpin = new QSpinBox();
124 m_yMinSpin->setMinimum(INT_MIN);
125 m_yMinSpin->setMaximum(INT_MAX);
126 m_yMinSpin->setValue(0);
127 connect(sender: m_yMinSpin, SIGNAL(valueChanged(int)), receiver: this, SLOT(yMinChanged(int)));
128 m_yMaxSpin = new QSpinBox();
129 m_yMaxSpin->setMinimum(INT_MIN);
130 m_yMaxSpin->setMaximum(INT_MAX);
131 m_yMaxSpin->setValue(10);
132 connect(sender: m_yMaxSpin, SIGNAL(valueChanged(int)), receiver: this, SLOT(yMaxChanged(int)));
133
134 grid->addWidget(m_autoScaleCheck, row: grid->rowCount(), column: 0);
135 grid->addWidget(new QLabel("x min:"), row: grid->rowCount(), column: 0);
136 grid->addWidget(m_xMinSpin, row: grid->rowCount() - 1, column: 1);
137 grid->addWidget(new QLabel("x max:"), row: grid->rowCount(), column: 0);
138 grid->addWidget(m_xMaxSpin, row: grid->rowCount() - 1, column: 1);
139 grid->addWidget(new QLabel("y min:"), row: grid->rowCount(), column: 0);
140 grid->addWidget(m_yMinSpin, row: grid->rowCount() - 1, column: 1);
141 grid->addWidget(new QLabel("y max:"), row: grid->rowCount(), column: 0);
142 grid->addWidget(m_yMaxSpin, row: grid->rowCount() - 1, column: 1);
143
144 m_autoScaleCheck->setChecked(true);
145}
146
147// Combo box for selecting theme
148void MainWidget::initThemeCombo(QGridLayout *grid)
149{
150 QComboBox *chartTheme = new QComboBox();
151 chartTheme->addItem(atext: "Default");
152 chartTheme->addItem(atext: "Light");
153 chartTheme->addItem(atext: "Blue Cerulean");
154 chartTheme->addItem(atext: "Dark");
155 chartTheme->addItem(atext: "Brown Sand");
156 chartTheme->addItem(atext: "Blue NCS");
157 chartTheme->addItem(atext: "High Contrast");
158 chartTheme->addItem(atext: "Blue Icy");
159 chartTheme->addItem(atext: "Qt");
160 connect(sender: chartTheme, SIGNAL(currentIndexChanged(int)),
161 receiver: this, SLOT(changeChartTheme(int)));
162 grid->addWidget(new QLabel("Chart theme:"), row: 8, column: 0);
163 grid->addWidget(chartTheme, row: 8, column: 1);
164}
165
166// Different check boxes for customizing chart
167void MainWidget::initCheckboxes(QGridLayout *grid)
168{
169 // TODO: setZoomEnabled slot has been removed from QChartView -> Re-implement zoom on/off
170 QCheckBox *zoomCheckBox = new QCheckBox("Drag'n drop Zoom");
171// connect(zoomCheckBox, SIGNAL(toggled(bool)), m_chartView, SLOT(setZoomEnabled(bool)));
172 zoomCheckBox->setChecked(true);
173 grid->addWidget(zoomCheckBox, row: grid->rowCount(), column: 0);
174
175 QCheckBox *aliasCheckBox = new QCheckBox("Anti-alias");
176 connect(sender: aliasCheckBox, SIGNAL(toggled(bool)), receiver: this, SLOT(antiAliasToggled(bool)));
177 aliasCheckBox->setChecked(false);
178 grid->addWidget(aliasCheckBox, row: grid->rowCount(), column: 0);
179
180 QCheckBox *openGLCheckBox = new QCheckBox("Use QOpenGLWidget");
181 connect(sender: openGLCheckBox, SIGNAL(toggled(bool)), receiver: this, SLOT(openGLToggled(bool)));
182 openGLCheckBox->setChecked(false);
183 grid->addWidget(openGLCheckBox, row: grid->rowCount(), column: 0);
184}
185
186void MainWidget::antiAliasToggled(bool enabled)
187{
188 m_chartView->setRenderHint(hint: QPainter::Antialiasing, enabled);
189}
190
191void MainWidget::openGLToggled(bool enabled)
192{
193 if (enabled) {
194 QSurfaceFormat f = QSurfaceFormat::defaultFormat();
195 f.setSamples(4);
196 QSurfaceFormat::setDefaultFormat(f);
197 QOpenGLWidget *g = new QOpenGLWidget();
198 m_chartView->setViewport(g);
199 } else {
200 m_chartView->setViewport(0);
201 }
202}
203
204void MainWidget::addSeries()
205{
206 if (!m_addSerieDialog) {
207 m_addSerieDialog = new DataSerieDialog(this);
208 connect(sender: m_addSerieDialog, SIGNAL(accepted(QString,int,int,QString,bool)),
209 receiver: this, SLOT(addSeries(QString,int,int,QString,bool)));
210 }
211 m_addSerieDialog->exec();
212}
213
214QList<RealList> MainWidget::generateTestData(int columnCount, int rowCount, QString dataCharacteristics)
215{
216 QList<RealList> testData;
217 for (int j(0); j < columnCount; j++) {
218 QList <qreal> newColumn;
219 for (int i(0); i < rowCount; i++) {
220 if (dataCharacteristics == "Sin") {
221 newColumn.append(t: std::abs(x: sin(M_PI / 50 * i) * 100));
222 } else if (dataCharacteristics == "Sin + random") {
223 newColumn.append(t: std::abs(x: sin(M_PI / 50 * i) * 100) + QRandomGenerator::global()->bounded(highest: 5));
224 } else if (dataCharacteristics == "Random") {
225 newColumn.append(t: QRandomGenerator::global()->bounded(highest: 11.0));
226 } else if (dataCharacteristics == "Linear") {
227 //newColumn.append(i * (j + 1.0));
228 // TODO: temporary hack to make pie work; prevent zero values:
229 newColumn.append(t: i * (j + 1.0) + 0.1);
230 } else { // "constant"
231 newColumn.append(t: (j + 1.0));
232 }
233 }
234 testData.append(t: newColumn);
235 }
236 return testData;
237}
238
239QStringList MainWidget::generateLabels(int count)
240{
241 QStringList result;
242 for (int i(0); i < count; i++)
243 result.append(t: "label" + QString::number(i));
244 return result;
245}
246
247void MainWidget::addSeries(QString seriesName, int columnCount, int rowCount, QString dataCharacteristics, bool labelsEnabled)
248{
249 qDebug() << "addSeries: " << seriesName
250 << " columnCount: " << columnCount
251 << " rowCount: " << rowCount
252 << " dataCharacteristics: " << dataCharacteristics
253 << " labels enabled: " << labelsEnabled;
254 m_defaultSeriesName = seriesName;
255
256 QList<RealList> data = generateTestData(columnCount, rowCount, dataCharacteristics);
257
258 // Line series and scatter series use similar data
259 if (seriesName == "Line") {
260 for (int j(0); j < data.count(); j ++) {
261 QList<qreal> column = data.at(i: j);
262 QLineSeries *series = new QLineSeries();
263 series->setName("line" + QString::number(j));
264 for (int i(0); i < column.count(); i++)
265 series->append(x: i, y: column.at(i));
266 m_chart->addSeries(series);
267 }
268 } else if (seriesName == "Area") {
269 // TODO: lower series for the area?
270 for (int j(0); j < data.count(); j ++) {
271 QList<qreal> column = data.at(i: j);
272 QLineSeries *lineSeries = new QLineSeries();
273 for (int i(0); i < column.count(); i++)
274 lineSeries->append(x: i, y: column.at(i));
275 QAreaSeries *areaSeries = new QAreaSeries(lineSeries);
276 areaSeries->setName("area" + QString::number(j));
277 m_chart->addSeries(series: areaSeries);
278 }
279 } else if (seriesName == "Scatter") {
280 for (int j(0); j < data.count(); j++) {
281 QList<qreal> column = data.at(i: j);
282 QScatterSeries *series = new QScatterSeries();
283 series->setName("scatter" + QString::number(j));
284 for (int i(0); i < column.count(); i++)
285 series->append(x: i, y: column.at(i));
286 m_chart->addSeries(series);
287 }
288 } else if (seriesName == "Pie") {
289 QStringList labels = generateLabels(count: rowCount);
290 for (int j(0); j < data.count(); j++) {
291 QPieSeries *series = new QPieSeries();
292 QList<qreal> column = data.at(i: j);
293 for (int i(0); i < column.count(); i++)
294 series->append(label: labels.at(i), value: column.at(i));
295 m_chart->addSeries(series);
296 }
297 } else if (seriesName == "Bar"
298 || seriesName == "Stacked bar"
299 || seriesName == "Percent bar") {
300 QStringList category;
301 QStringList labels = generateLabels(count: rowCount);
302 foreach (QString label, labels)
303 category << label;
304 QAbstractBarSeries* series = 0;
305 if (seriesName == "Bar") {
306 series = new QBarSeries(this);
307 QBarCategoryAxis* axis = new QBarCategoryAxis();
308 axis->append(categories: category);
309 m_chart->setAxisX(axis,series);
310 } else if (seriesName == "Stacked bar") {
311 series = new QStackedBarSeries(this);
312 QBarCategoryAxis* axis = new QBarCategoryAxis();
313 axis->append(categories: category);
314 m_chart->setAxisX(axis,series);
315 } else {
316 series = new QPercentBarSeries(this);
317 QBarCategoryAxis* axis = new QBarCategoryAxis();
318 axis->append(categories: category);
319 m_chart->setAxisX(axis,series);
320 }
321
322 for (int j(0); j < data.count(); j++) {
323 QList<qreal> column = data.at(i: j);
324 QBarSet *set = new QBarSet("set" + QString::number(j));
325 for (int i(0); i < column.count(); i++)
326 *set << column.at(i);
327 series->append(set);
328 }
329
330 m_chart->addSeries(series);
331 } else if (seriesName == "Spline") {
332 for (int j(0); j < data.count(); j ++) {
333 QList<qreal> column = data.at(i: j);
334 QSplineSeries *series = new QSplineSeries();
335 series->setName("spline" + QString::number(j));
336 for (int i(0); i < column.count(); i++)
337 series->append(x: i, y: column.at(i));
338 m_chart->addSeries(series);
339 }
340 }
341 m_chart->createDefaultAxes();
342}
343
344void MainWidget::backgroundChanged(int itemIndex)
345{
346 qDebug() << "backgroundChanged: " << itemIndex;
347}
348
349void MainWidget::autoScaleChanged(int value)
350{
351 if (value) {
352 // TODO: enable auto scaling
353 } else {
354 // TODO: set scaling manually (and disable auto scaling)
355 }
356
357 m_xMinSpin->setEnabled(!value);
358 m_xMaxSpin->setEnabled(!value);
359 m_yMinSpin->setEnabled(!value);
360 m_yMaxSpin->setEnabled(!value);
361}
362
363void MainWidget::xMinChanged(int value)
364{
365 qDebug() << "xMinChanged: " << value;
366}
367
368void MainWidget::xMaxChanged(int value)
369{
370 qDebug() << "xMaxChanged: " << value;
371}
372
373void MainWidget::yMinChanged(int value)
374{
375 qDebug() << "yMinChanged: " << value;
376}
377
378void MainWidget::yMaxChanged(int value)
379{
380 qDebug() << "yMaxChanged: " << value;
381}
382
383void MainWidget::changeChartTheme(int themeIndex)
384{
385 qDebug() << "changeChartTheme: " << themeIndex;
386 if (themeIndex == 0)
387 m_chart->setTheme(QChart::ChartThemeLight);
388 else
389 m_chart->setTheme((QChart::ChartTheme) (themeIndex - 1));
390}
391

source code of qtcharts/tests/manual/chartwidgettest/mainwidget.cpp