summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@theqtcompany.com>2015-09-15 17:39:54 +0300
committerMiikka Heikkinen <miikka.heikkinen@theqtcompany.com>2015-09-25 06:44:35 +0000
commit79a856530b6986ca6d6d7485b2e6cec810c3b7fe (patch)
tree6f3f8dfe76e64da9ec48909fef1d7cccb87c3fc0 /examples
parent3a89e3fee61a52927f836f3b3de4c922c6b954e4 (diff)
Accelerating lineseries with OpenGL
Added support for QAbstractSeries::useOpenGL property. When true, the series in question is drawn on a separate offscreen buffer using OpenGL and then superimposed on the chart. Currently this property is only supported for line and scatter series. Change-Id: I174fec541f9f3c23464270c1fe08f824af16a0fb Reviewed-by: Titta Heikkala <titta.heikkala@theqtcompany.com> Reviewed-by: Tomi Korpipää <tomi.korpipaa@theqtcompany.com>
Diffstat (limited to 'examples')
-rw-r--r--examples/charts/audio/xyseriesiodevice.cpp6
-rw-r--r--examples/charts/charts.pro8
-rw-r--r--examples/charts/chartthemes/themewidget.cpp2
-rw-r--r--examples/charts/openglseries/datasource.cpp118
-rw-r--r--examples/charts/openglseries/datasource.h53
-rw-r--r--examples/charts/openglseries/main.cpp167
-rw-r--r--examples/charts/openglseries/openglseries.pro9
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml12
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml29
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml18
10 files changed, 407 insertions, 15 deletions
diff --git a/examples/charts/audio/xyseriesiodevice.cpp b/examples/charts/audio/xyseriesiodevice.cpp
index ef9789cd..4b7d2f52 100644
--- a/examples/charts/audio/xyseriesiodevice.cpp
+++ b/examples/charts/audio/xyseriesiodevice.cpp
@@ -35,12 +35,12 @@ qint64 XYSeriesIODevice::readData(char * data, qint64 maxSize)
qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize)
{
qint64 range = 2000;
- QList<QPointF> oldPoints = m_series->points();
- QList<QPointF> points;
+ QVector<QPointF> oldPoints = m_series->pointsVector();
+ QVector<QPointF> points;
int resolution = 4;
if (oldPoints.count() < range) {
- points = m_series->points();
+ points = m_series->pointsVector();
} else {
for (int i = maxSize/resolution; i < oldPoints.count(); i++)
points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y()));
diff --git a/examples/charts/charts.pro b/examples/charts/charts.pro
index a104fcdd..3f463e46 100644
--- a/examples/charts/charts.pro
+++ b/examples/charts/charts.pro
@@ -50,7 +50,13 @@ qtHaveModule(quick) {
qtHaveModule(multimedia) {
SUBDIRS += audio
} else {
- message("QtMultimedia library not available. Some examples are disabled")
+ message("QtMultimedia library not available. Some examples are disabled.")
+}
+
+contains(QT_CONFIG, opengl) {
+ SUBDIRS += openglseries
+} else {
+ message("OpenGL not available. Some examples are disabled.")
}
!linux-arm*: {
diff --git a/examples/charts/chartthemes/themewidget.cpp b/examples/charts/chartthemes/themewidget.cpp
index 797356b7..a78ab9b4 100644
--- a/examples/charts/chartthemes/themewidget.cpp
+++ b/examples/charts/chartthemes/themewidget.cpp
@@ -189,7 +189,7 @@ QChart *ThemeWidget::createAreaChart() const
for (int j(0); j < m_dataTable[i].count(); j++) {
Data data = m_dataTable[i].at(j);
if (lowerSeries) {
- const QList<QPointF>& points = lowerSeries->points();
+ const QVector<QPointF>& points = lowerSeries->pointsVector();
upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
} else {
upperSeries->append(QPointF(j, data.first.y()));
diff --git a/examples/charts/openglseries/datasource.cpp b/examples/charts/openglseries/datasource.cpp
new file mode 100644
index 00000000..64b45405
--- /dev/null
+++ b/examples/charts/openglseries/datasource.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+#include "datasource.h"
+#include <QtCore/QtMath>
+
+QT_CHARTS_USE_NAMESPACE
+
+DataSource::DataSource(QObject *parent) :
+ QObject(parent),
+ m_index(-1)
+{
+ generateData(0, 0, 0);
+}
+
+void DataSource::update(QAbstractSeries *series, int seriesIndex)
+{
+ if (series) {
+ QXYSeries *xySeries = static_cast<QXYSeries *>(series);
+ const QVector<QVector<QPointF> > &seriesData = m_data.at(seriesIndex);
+ if (seriesIndex == 0)
+ m_index++;
+ if (m_index > seriesData.count() - 1)
+ m_index = 0;
+
+ QVector<QPointF> points = seriesData.at(m_index);
+ // Use replace instead of clear + append, it's optimized for performance
+ xySeries->replace(points);
+ }
+}
+
+void DataSource::handleSceneChanged()
+{
+ m_dataUpdater.start();
+}
+
+void DataSource::updateAllSeries()
+{
+ static int frameCount = 0;
+ static QString labelText = QStringLiteral("FPS: %1");
+
+ for (int i = 0; i < m_seriesList.size(); i++)
+ update(m_seriesList[i], i);
+
+ frameCount++;
+ int elapsed = m_fpsTimer.elapsed();
+ if (elapsed >= 1000) {
+ elapsed = m_fpsTimer.restart();
+ qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
+ m_fpsLabel->setText(labelText.arg(QString::number(fps, 'f', 1)));
+ frameCount = 0;
+ }
+}
+
+void DataSource::startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel)
+{
+ m_seriesList = seriesList;
+ m_fpsLabel = fpsLabel;
+
+ m_dataUpdater.setInterval(0);
+ m_dataUpdater.setSingleShot(true);
+ QObject::connect(&m_dataUpdater, &QTimer::timeout,
+ this, &DataSource::updateAllSeries);
+
+ m_fpsTimer.start();
+ updateAllSeries();
+}
+
+void DataSource::generateData(int seriesCount, int rowCount, int colCount)
+{
+ // Remove previous data
+ foreach (QVector<QVector<QPointF> > seriesData, m_data) {
+ foreach (QVector<QPointF> row, seriesData)
+ row.clear();
+ }
+
+ m_data.clear();
+
+ qreal xAdjustment = 20.0 / (colCount * rowCount);
+ qreal yMultiplier = 3.0 / qreal(seriesCount);
+
+ // Append the new data depending on the type
+ for (int k(0); k < seriesCount; k++) {
+ QVector<QVector<QPointF> > seriesData;
+ qreal height = qreal(k) * (10.0 / qreal(seriesCount)) + 0.3;
+ for (int i(0); i < rowCount; i++) {
+ QVector<QPointF> points;
+ points.reserve(colCount);
+ for (int j(0); j < colCount; j++) {
+ qreal x(0);
+ qreal y(0);
+ // data with sin + random component
+ y = height + (yMultiplier * qSin(3.14159265358979 / 50 * j)
+ + (yMultiplier * (qreal) rand() / (qreal) RAND_MAX));
+ // 0.000001 added to make values logaxis compatible
+ x = 0.000001 + 20.0 * (qreal(j) / qreal(colCount)) + (xAdjustment * qreal(i));
+ points.append(QPointF(x, y));
+ }
+ seriesData.append(points);
+ }
+ m_data.append(seriesData);
+ }
+}
diff --git a/examples/charts/openglseries/datasource.h b/examples/charts/openglseries/datasource.h
new file mode 100644
index 00000000..295d93a3
--- /dev/null
+++ b/examples/charts/openglseries/datasource.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+#ifndef DATASOURCE_H
+#define DATASOURCE_H
+
+#include <QtCore/QObject>
+#include <QtCharts/QXYSeries>
+#include <QtWidgets/QLabel>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QTimer>
+
+QT_CHARTS_USE_NAMESPACE
+
+class DataSource : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DataSource(QObject *parent = 0);
+
+ void startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel);
+
+public slots:
+ void generateData(int seriesCount, int rowCount, int colCount);
+ void update(QAbstractSeries *series, int seriesIndex);
+ void handleSceneChanged();
+ void updateAllSeries();
+
+private:
+ QVector<QVector<QVector<QPointF> > > m_data;
+ int m_index;
+ QList<QXYSeries *> m_seriesList;
+ QLabel *m_fpsLabel;
+ QElapsedTimer m_fpsTimer;
+ QTimer m_dataUpdater;
+};
+
+#endif // DATASOURCE_H
diff --git a/examples/charts/openglseries/main.cpp b/examples/charts/openglseries/main.cpp
new file mode 100644
index 00000000..7b654a5d
--- /dev/null
+++ b/examples/charts/openglseries/main.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+#include "datasource.h"
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QMainWindow>
+#include <QtCharts/QChartView>
+#include <QtCharts/QLineSeries>
+#include <QtCharts/QScatterSeries>
+#include <QtCharts/QValueAxis>
+#include <QtCharts/QLogValueAxis>
+#include <QtWidgets/QLabel>
+
+// Uncomment to use logarithmic axes instead of regular value axes
+//#define USE_LOG_AXIS
+
+// Uncomment to use regular series instead of OpenGL accelerated series
+//#define DONT_USE_GL_SERIES
+
+// Uncomment to add a simple regular series (thick line) and a matching OpenGL series (thinner line)
+// to verify the series have same visible geometry.
+//#define ADD_SIMPLE_SERIES
+
+QT_CHARTS_USE_NAMESPACE
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QStringList colors;
+ colors << "red" << "blue" << "green" << "black";
+
+ QChart *chart = new QChart();
+ chart->legend()->hide();
+
+#ifdef USE_LOG_AXIS
+ QLogValueAxis *axisX = new QLogValueAxis;
+ QLogValueAxis *axisY = new QLogValueAxis;
+#else
+ QValueAxis *axisX = new QValueAxis;
+ QValueAxis *axisY = new QValueAxis;
+#endif
+
+ chart->addAxis(axisX, Qt::AlignBottom);
+ chart->addAxis(axisY, Qt::AlignLeft);
+
+ const int seriesCount = 10;
+#ifdef DONT_USE_GL_SERIES
+ const int pointCount = 100;
+ chart->setTitle("Unaccelerated Series");
+#else
+ const int pointCount = 10000;
+ chart->setTitle("OpenGL Accelerated Series");
+#endif
+
+ QList<QXYSeries *> seriesList;
+ for (int i = 0; i < seriesCount; i++) {
+ QXYSeries *series = 0;
+ int colorIndex = i % colors.size();
+ if (i % 2) {
+ series = new QScatterSeries;
+ QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
+ scatter->setColor(QColor(colors.at(colorIndex)));
+ scatter->setMarkerSize(qreal(colorIndex + 2) / 2.0);
+ // Scatter pen doesn't have affect in OpenGL drawing, but if you disable OpenGL drawing
+ // this makes the marker border visible and gives comparable marker size to OpenGL
+ // scatter points.
+ scatter->setPen(QPen("black"));
+ } else {
+ series = new QLineSeries;
+ series->setPen(QPen(QBrush(QColor(colors.at(colorIndex))),
+ qreal(colorIndex + 2) / 2.0));
+ }
+ seriesList.append(series);
+#ifdef DONT_USE_GL_SERIES
+ series->setUseOpenGL(false);
+#else
+ //![1]
+ series->setUseOpenGL(true);
+ //![1]
+#endif
+ chart->addSeries(series);
+ series->attachAxis(axisX);
+ series->attachAxis(axisY);
+ }
+
+ if (axisX->type() == QAbstractAxis::AxisTypeLogValue)
+ axisX->setRange(0.1, 20.0);
+ else
+ axisX->setRange(0, 20.0);
+
+ if (axisY->type() == QAbstractAxis::AxisTypeLogValue)
+ axisY->setRange(0.1, 10.0);
+ else
+ axisY->setRange(0, 10.0);
+
+#ifdef ADD_SIMPLE_SERIES
+ QLineSeries *simpleRasterSeries = new QLineSeries;
+ *simpleRasterSeries << QPointF(0.001, 0.001)
+ << QPointF(2.5, 8.0)
+ << QPointF(5.0, 4.0)
+ << QPointF(7.5, 9.0)
+ << QPointF(10.0, 0.001)
+ << QPointF(12.5, 2.0)
+ << QPointF(15.0, 1.0)
+ << QPointF(17.5, 6.0)
+ << QPointF(20.0, 10.0);
+ simpleRasterSeries->setUseOpenGL(false);
+ simpleRasterSeries->setPen(QPen(QBrush("magenta"), 8));
+ chart->addSeries(simpleRasterSeries);
+ simpleRasterSeries->attachAxis(axisX);
+ simpleRasterSeries->attachAxis(axisY);
+
+ QLineSeries *simpleGLSeries = new QLineSeries;
+ simpleGLSeries->setUseOpenGL(true);
+ simpleGLSeries->setPen(QPen(QBrush("black"), 2));
+ simpleGLSeries->replace(simpleRasterSeries->points());
+ chart->addSeries(simpleGLSeries);
+ simpleGLSeries->attachAxis(axisX);
+ simpleGLSeries->attachAxis(axisY);
+#endif
+
+ QChartView *chartView = new QChartView(chart);
+
+ QMainWindow window;
+ window.setCentralWidget(chartView);
+ window.resize(600, 400);
+ window.show();
+
+ DataSource dataSource;
+ dataSource.generateData(seriesCount, 10, pointCount);
+
+ QLabel *fpsLabel = new QLabel(&window);
+ QLabel *countLabel = new QLabel(&window);
+ QString countText = QStringLiteral("Total point count: %1");
+ countLabel->setText(countText.arg(pointCount * seriesCount));
+ countLabel->resize(window.width(), countLabel->height());
+ fpsLabel->move(10,2);
+ fpsLabel->raise();
+ fpsLabel->show();
+ countLabel->move(10, 14);
+ fpsLabel->raise();
+ countLabel->show();
+
+ // We can get more than one changed event per frame, so do async update.
+ // This also allows the application to be responsive.
+ QObject::connect(chart->scene(), &QGraphicsScene::changed,
+ &dataSource, &DataSource::handleSceneChanged);
+
+ dataSource.startUpdates(seriesList, fpsLabel);
+
+ return a.exec();
+}
diff --git a/examples/charts/openglseries/openglseries.pro b/examples/charts/openglseries/openglseries.pro
new file mode 100644
index 00000000..0ae11595
--- /dev/null
+++ b/examples/charts/openglseries/openglseries.pro
@@ -0,0 +1,9 @@
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+TARGET = openglseries
+SOURCES += main.cpp \
+ datasource.cpp
+HEADERS += datasource.h
+
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
index 478a2cc3..ccf53fa1 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
@@ -16,10 +16,11 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.1
import QtQuick.Layouts 1.0
ColumnLayout {
+ property alias openGLButton: openGLButton
spacing: 8
Layout.fillHeight: true
signal animationsEnabled(bool enabled)
@@ -27,6 +28,7 @@ ColumnLayout {
signal refreshRateChanged(variant rate);
signal signalSourceChanged(string source, int signalCount, int sampleCount);
signal antialiasingEnabled(bool enabled)
+ signal openGlChanged(bool enabled)
Text {
text: "Scope"
@@ -35,6 +37,14 @@ ColumnLayout {
}
MultiButton {
+ id: openGLButton
+ text: "OpenGL: "
+ items: ["false", "true"]
+ currentSelection: 0
+ onSelectionChanged: openGlChanged(currentSelection == 1);
+ }
+
+ MultiButton {
text: "Graph: "
items: ["line", "spline", "scatter"]
currentSelection: 0
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
index e422d056..443e515a 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
@@ -17,13 +17,18 @@
****************************************************************************/
import QtQuick 2.0
-import QtCharts 2.0
+import QtCharts 2.1
//![1]
ChartView {
id: chartView
animationOptions: ChartView.NoAnimation
theme: ChartView.ChartThemeDark
+ property bool openGL: false
+ onOpenGLChanged: {
+ series("signal 1").useOpenGL = openGL;
+ series("signal 2").useOpenGL = openGL;
+ }
ValueAxis {
id: axisY1
@@ -40,7 +45,7 @@ ChartView {
ValueAxis {
id: axisX
min: 0
- max: 1000
+ max: 1024
}
LineSeries {
@@ -48,12 +53,14 @@ ChartView {
name: "signal 1"
axisX: axisX
axisY: axisY1
+ useOpenGL: chartView.openGL
}
LineSeries {
id: lineSeries2
name: "signal 2"
axisX: axisX
axisYRight: axisY2
+ useOpenGL: chartView.openGL
}
//![1]
@@ -78,18 +85,28 @@ ChartView {
// but the series have their own y-axes to make it possible to control the y-offset
// of the "signal sources".
if (type == "line") {
- chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", axisX, axisY1);
- chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2);
+ var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1",
+ axisX, axisY1);
+ series1.useOpenGL = chartView.openGL
+
+ var series2 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 2",
+ axisX, axisY2);
+ series2.useOpenGL = chartView.openGL
} else if (type == "spline") {
chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1);
chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2);
} else {
- var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", axisX, axisY1);
+ var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1",
+ axisX, axisY1);
series1.markerSize = 3;
series1.borderColor = "transparent";
- var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2", axisX, axisY2);
+ series1.useOpenGL = chartView.openGL
+
+ var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2",
+ axisX, axisY2);
series2.markerSize = 3;
series2.borderColor = "transparent";
+ series2.useOpenGL = chartView.openGL
}
}
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
index 0ff40c21..5340177e 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
@@ -21,8 +21,8 @@ import QtQuick 2.0
//![1]
Rectangle {
id: main
- width: 400
- height: 300
+ width: 600
+ height: 400
color: "#404040"
ControlPanel {
@@ -39,11 +39,22 @@ Rectangle {
dataSource.generateData(0, signalCount, sampleCount);
else
dataSource.generateData(1, signalCount, sampleCount);
+ scopeView.axisX().max = sampleCount;
}
onAnimationsEnabled: scopeView.setAnimations(enabled);
- onSeriesTypeChanged: scopeView.changeSeriesType(type);
+ onSeriesTypeChanged: {
+ scopeView.changeSeriesType(type);
+ if (type === "spline") {
+ controlPanel.openGLButton.currentSelection = 0;
+ controlPanel.openGLButton.enabled = false;
+ scopeView.openGL = false;
+ } else {
+ controlPanel.openGLButton.enabled = true;
+ }
+ }
onRefreshRateChanged: scopeView.changeRefreshRate(rate);
onAntialiasingEnabled: scopeView.antialiasing = enabled;
+ onOpenGlChanged: scopeView.openGL = enabled;
}
//![2]
@@ -56,4 +67,5 @@ Rectangle {
height: main.height
}
//![2]
+
}