@@ -0,0 +1,118 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #include "datasource.h" | |||
|
20 | #include <QtCore/QtMath> | |||
|
21 | ||||
|
22 | QT_CHARTS_USE_NAMESPACE | |||
|
23 | ||||
|
24 | DataSource::DataSource(QObject *parent) : | |||
|
25 | QObject(parent), | |||
|
26 | m_index(-1) | |||
|
27 | { | |||
|
28 | generateData(0, 0, 0); | |||
|
29 | } | |||
|
30 | ||||
|
31 | void DataSource::update(QAbstractSeries *series, int seriesIndex) | |||
|
32 | { | |||
|
33 | if (series) { | |||
|
34 | QXYSeries *xySeries = static_cast<QXYSeries *>(series); | |||
|
35 | const QVector<QVector<QPointF> > &seriesData = m_data.at(seriesIndex); | |||
|
36 | if (seriesIndex == 0) | |||
|
37 | m_index++; | |||
|
38 | if (m_index > seriesData.count() - 1) | |||
|
39 | m_index = 0; | |||
|
40 | ||||
|
41 | QVector<QPointF> points = seriesData.at(m_index); | |||
|
42 | // Use replace instead of clear + append, it's optimized for performance | |||
|
43 | xySeries->replace(points); | |||
|
44 | } | |||
|
45 | } | |||
|
46 | ||||
|
47 | void DataSource::handleSceneChanged() | |||
|
48 | { | |||
|
49 | m_dataUpdater.start(); | |||
|
50 | } | |||
|
51 | ||||
|
52 | void DataSource::updateAllSeries() | |||
|
53 | { | |||
|
54 | static int frameCount = 0; | |||
|
55 | static QString labelText = QStringLiteral("FPS: %1"); | |||
|
56 | ||||
|
57 | for (int i = 0; i < m_seriesList.size(); i++) | |||
|
58 | update(m_seriesList[i], i); | |||
|
59 | ||||
|
60 | frameCount++; | |||
|
61 | int elapsed = m_fpsTimer.elapsed(); | |||
|
62 | if (elapsed >= 1000) { | |||
|
63 | elapsed = m_fpsTimer.restart(); | |||
|
64 | qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed)))); | |||
|
65 | m_fpsLabel->setText(labelText.arg(QString::number(fps, 'f', 1))); | |||
|
66 | frameCount = 0; | |||
|
67 | } | |||
|
68 | } | |||
|
69 | ||||
|
70 | void DataSource::startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel) | |||
|
71 | { | |||
|
72 | m_seriesList = seriesList; | |||
|
73 | m_fpsLabel = fpsLabel; | |||
|
74 | ||||
|
75 | m_dataUpdater.setInterval(0); | |||
|
76 | m_dataUpdater.setSingleShot(true); | |||
|
77 | QObject::connect(&m_dataUpdater, &QTimer::timeout, | |||
|
78 | this, &DataSource::updateAllSeries); | |||
|
79 | ||||
|
80 | m_fpsTimer.start(); | |||
|
81 | updateAllSeries(); | |||
|
82 | } | |||
|
83 | ||||
|
84 | void DataSource::generateData(int seriesCount, int rowCount, int colCount) | |||
|
85 | { | |||
|
86 | // Remove previous data | |||
|
87 | foreach (QVector<QVector<QPointF> > seriesData, m_data) { | |||
|
88 | foreach (QVector<QPointF> row, seriesData) | |||
|
89 | row.clear(); | |||
|
90 | } | |||
|
91 | ||||
|
92 | m_data.clear(); | |||
|
93 | ||||
|
94 | qreal xAdjustment = 20.0 / (colCount * rowCount); | |||
|
95 | qreal yMultiplier = 3.0 / qreal(seriesCount); | |||
|
96 | ||||
|
97 | // Append the new data depending on the type | |||
|
98 | for (int k(0); k < seriesCount; k++) { | |||
|
99 | QVector<QVector<QPointF> > seriesData; | |||
|
100 | qreal height = qreal(k) * (10.0 / qreal(seriesCount)) + 0.3; | |||
|
101 | for (int i(0); i < rowCount; i++) { | |||
|
102 | QVector<QPointF> points; | |||
|
103 | points.reserve(colCount); | |||
|
104 | for (int j(0); j < colCount; j++) { | |||
|
105 | qreal x(0); | |||
|
106 | qreal y(0); | |||
|
107 | // data with sin + random component | |||
|
108 | y = height + (yMultiplier * qSin(3.14159265358979 / 50 * j) | |||
|
109 | + (yMultiplier * (qreal) rand() / (qreal) RAND_MAX)); | |||
|
110 | // 0.000001 added to make values logaxis compatible | |||
|
111 | x = 0.000001 + 20.0 * (qreal(j) / qreal(colCount)) + (xAdjustment * qreal(i)); | |||
|
112 | points.append(QPointF(x, y)); | |||
|
113 | } | |||
|
114 | seriesData.append(points); | |||
|
115 | } | |||
|
116 | m_data.append(seriesData); | |||
|
117 | } | |||
|
118 | } |
@@ -0,0 +1,53 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #ifndef DATASOURCE_H | |||
|
20 | #define DATASOURCE_H | |||
|
21 | ||||
|
22 | #include <QtCore/QObject> | |||
|
23 | #include <QtCharts/QXYSeries> | |||
|
24 | #include <QtWidgets/QLabel> | |||
|
25 | #include <QtCore/QElapsedTimer> | |||
|
26 | #include <QtCore/QTimer> | |||
|
27 | ||||
|
28 | QT_CHARTS_USE_NAMESPACE | |||
|
29 | ||||
|
30 | class DataSource : public QObject | |||
|
31 | { | |||
|
32 | Q_OBJECT | |||
|
33 | public: | |||
|
34 | explicit DataSource(QObject *parent = 0); | |||
|
35 | ||||
|
36 | void startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel); | |||
|
37 | ||||
|
38 | public slots: | |||
|
39 | void generateData(int seriesCount, int rowCount, int colCount); | |||
|
40 | void update(QAbstractSeries *series, int seriesIndex); | |||
|
41 | void handleSceneChanged(); | |||
|
42 | void updateAllSeries(); | |||
|
43 | ||||
|
44 | private: | |||
|
45 | QVector<QVector<QVector<QPointF> > > m_data; | |||
|
46 | int m_index; | |||
|
47 | QList<QXYSeries *> m_seriesList; | |||
|
48 | QLabel *m_fpsLabel; | |||
|
49 | QElapsedTimer m_fpsTimer; | |||
|
50 | QTimer m_dataUpdater; | |||
|
51 | }; | |||
|
52 | ||||
|
53 | #endif // DATASOURCE_H |
@@ -0,0 +1,167 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #include "datasource.h" | |||
|
20 | #include <QtWidgets/QApplication> | |||
|
21 | #include <QtWidgets/QMainWindow> | |||
|
22 | #include <QtCharts/QChartView> | |||
|
23 | #include <QtCharts/QLineSeries> | |||
|
24 | #include <QtCharts/QScatterSeries> | |||
|
25 | #include <QtCharts/QValueAxis> | |||
|
26 | #include <QtCharts/QLogValueAxis> | |||
|
27 | #include <QtWidgets/QLabel> | |||
|
28 | ||||
|
29 | // Uncomment to use logarithmic axes instead of regular value axes | |||
|
30 | //#define USE_LOG_AXIS | |||
|
31 | ||||
|
32 | // Uncomment to use regular series instead of OpenGL accelerated series | |||
|
33 | //#define DONT_USE_GL_SERIES | |||
|
34 | ||||
|
35 | // Uncomment to add a simple regular series (thick line) and a matching OpenGL series (thinner line) | |||
|
36 | // to verify the series have same visible geometry. | |||
|
37 | //#define ADD_SIMPLE_SERIES | |||
|
38 | ||||
|
39 | QT_CHARTS_USE_NAMESPACE | |||
|
40 | ||||
|
41 | int main(int argc, char *argv[]) | |||
|
42 | { | |||
|
43 | QApplication a(argc, argv); | |||
|
44 | QStringList colors; | |||
|
45 | colors << "red" << "blue" << "green" << "black"; | |||
|
46 | ||||
|
47 | QChart *chart = new QChart(); | |||
|
48 | chart->legend()->hide(); | |||
|
49 | ||||
|
50 | #ifdef USE_LOG_AXIS | |||
|
51 | QLogValueAxis *axisX = new QLogValueAxis; | |||
|
52 | QLogValueAxis *axisY = new QLogValueAxis; | |||
|
53 | #else | |||
|
54 | QValueAxis *axisX = new QValueAxis; | |||
|
55 | QValueAxis *axisY = new QValueAxis; | |||
|
56 | #endif | |||
|
57 | ||||
|
58 | chart->addAxis(axisX, Qt::AlignBottom); | |||
|
59 | chart->addAxis(axisY, Qt::AlignLeft); | |||
|
60 | ||||
|
61 | const int seriesCount = 10; | |||
|
62 | #ifdef DONT_USE_GL_SERIES | |||
|
63 | const int pointCount = 100; | |||
|
64 | chart->setTitle("Unaccelerated Series"); | |||
|
65 | #else | |||
|
66 | const int pointCount = 10000; | |||
|
67 | chart->setTitle("OpenGL Accelerated Series"); | |||
|
68 | #endif | |||
|
69 | ||||
|
70 | QList<QXYSeries *> seriesList; | |||
|
71 | for (int i = 0; i < seriesCount; i++) { | |||
|
72 | QXYSeries *series = 0; | |||
|
73 | int colorIndex = i % colors.size(); | |||
|
74 | if (i % 2) { | |||
|
75 | series = new QScatterSeries; | |||
|
76 | QScatterSeries *scatter = static_cast<QScatterSeries *>(series); | |||
|
77 | scatter->setColor(QColor(colors.at(colorIndex))); | |||
|
78 | scatter->setMarkerSize(qreal(colorIndex + 2) / 2.0); | |||
|
79 | // Scatter pen doesn't have affect in OpenGL drawing, but if you disable OpenGL drawing | |||
|
80 | // this makes the marker border visible and gives comparable marker size to OpenGL | |||
|
81 | // scatter points. | |||
|
82 | scatter->setPen(QPen("black")); | |||
|
83 | } else { | |||
|
84 | series = new QLineSeries; | |||
|
85 | series->setPen(QPen(QBrush(QColor(colors.at(colorIndex))), | |||
|
86 | qreal(colorIndex + 2) / 2.0)); | |||
|
87 | } | |||
|
88 | seriesList.append(series); | |||
|
89 | #ifdef DONT_USE_GL_SERIES | |||
|
90 | series->setUseOpenGL(false); | |||
|
91 | #else | |||
|
92 | //![1] | |||
|
93 | series->setUseOpenGL(true); | |||
|
94 | //![1] | |||
|
95 | #endif | |||
|
96 | chart->addSeries(series); | |||
|
97 | series->attachAxis(axisX); | |||
|
98 | series->attachAxis(axisY); | |||
|
99 | } | |||
|
100 | ||||
|
101 | if (axisX->type() == QAbstractAxis::AxisTypeLogValue) | |||
|
102 | axisX->setRange(0.1, 20.0); | |||
|
103 | else | |||
|
104 | axisX->setRange(0, 20.0); | |||
|
105 | ||||
|
106 | if (axisY->type() == QAbstractAxis::AxisTypeLogValue) | |||
|
107 | axisY->setRange(0.1, 10.0); | |||
|
108 | else | |||
|
109 | axisY->setRange(0, 10.0); | |||
|
110 | ||||
|
111 | #ifdef ADD_SIMPLE_SERIES | |||
|
112 | QLineSeries *simpleRasterSeries = new QLineSeries; | |||
|
113 | *simpleRasterSeries << QPointF(0.001, 0.001) | |||
|
114 | << QPointF(2.5, 8.0) | |||
|
115 | << QPointF(5.0, 4.0) | |||
|
116 | << QPointF(7.5, 9.0) | |||
|
117 | << QPointF(10.0, 0.001) | |||
|
118 | << QPointF(12.5, 2.0) | |||
|
119 | << QPointF(15.0, 1.0) | |||
|
120 | << QPointF(17.5, 6.0) | |||
|
121 | << QPointF(20.0, 10.0); | |||
|
122 | simpleRasterSeries->setUseOpenGL(false); | |||
|
123 | simpleRasterSeries->setPen(QPen(QBrush("magenta"), 8)); | |||
|
124 | chart->addSeries(simpleRasterSeries); | |||
|
125 | simpleRasterSeries->attachAxis(axisX); | |||
|
126 | simpleRasterSeries->attachAxis(axisY); | |||
|
127 | ||||
|
128 | QLineSeries *simpleGLSeries = new QLineSeries; | |||
|
129 | simpleGLSeries->setUseOpenGL(true); | |||
|
130 | simpleGLSeries->setPen(QPen(QBrush("black"), 2)); | |||
|
131 | simpleGLSeries->replace(simpleRasterSeries->points()); | |||
|
132 | chart->addSeries(simpleGLSeries); | |||
|
133 | simpleGLSeries->attachAxis(axisX); | |||
|
134 | simpleGLSeries->attachAxis(axisY); | |||
|
135 | #endif | |||
|
136 | ||||
|
137 | QChartView *chartView = new QChartView(chart); | |||
|
138 | ||||
|
139 | QMainWindow window; | |||
|
140 | window.setCentralWidget(chartView); | |||
|
141 | window.resize(600, 400); | |||
|
142 | window.show(); | |||
|
143 | ||||
|
144 | DataSource dataSource; | |||
|
145 | dataSource.generateData(seriesCount, 10, pointCount); | |||
|
146 | ||||
|
147 | QLabel *fpsLabel = new QLabel(&window); | |||
|
148 | QLabel *countLabel = new QLabel(&window); | |||
|
149 | QString countText = QStringLiteral("Total point count: %1"); | |||
|
150 | countLabel->setText(countText.arg(pointCount * seriesCount)); | |||
|
151 | countLabel->resize(window.width(), countLabel->height()); | |||
|
152 | fpsLabel->move(10,2); | |||
|
153 | fpsLabel->raise(); | |||
|
154 | fpsLabel->show(); | |||
|
155 | countLabel->move(10, 14); | |||
|
156 | fpsLabel->raise(); | |||
|
157 | countLabel->show(); | |||
|
158 | ||||
|
159 | // We can get more than one changed event per frame, so do async update. | |||
|
160 | // This also allows the application to be responsive. | |||
|
161 | QObject::connect(chart->scene(), &QGraphicsScene::changed, | |||
|
162 | &dataSource, &DataSource::handleSceneChanged); | |||
|
163 | ||||
|
164 | dataSource.startUpdates(seriesList, fpsLabel); | |||
|
165 | ||||
|
166 | return a.exec(); | |||
|
167 | } |
@@ -0,0 +1,9 | |||||
|
1 | !include( ../examples.pri ) { | |||
|
2 | error( "Couldn't find the examples.pri file!" ) | |||
|
3 | } | |||
|
4 | ||||
|
5 | TARGET = openglseries | |||
|
6 | SOURCES += main.cpp \ | |||
|
7 | datasource.cpp | |||
|
8 | HEADERS += datasource.h | |||
|
9 |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,37 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | /*! | |||
|
20 | \example openglseries | |||
|
21 | \title OpenGL Accelerated Series Example | |||
|
22 | \ingroup qtcharts_examples | |||
|
23 | ||||
|
24 | \brief The example shows how to enable OpenGL acceleration for QLineSeries and QScatterSeries. | |||
|
25 | ||||
|
26 | \image examples_openglseries.png | |||
|
27 | ||||
|
28 | To create an OpenGL accelerated series, all you have to do compared to a regular | |||
|
29 | series is to set QAbstractSeries::useOpenGL property to \c{true}: | |||
|
30 | ||||
|
31 | \snippet openglseries/main.cpp 1 | |||
|
32 | ||||
|
33 | This makes the chart to instantiate a transparent QOpenGLWidget that is used to draw | |||
|
34 | the accelerated series on top of the chart. | |||
|
35 | ||||
|
36 | \note The OpenGL acceleration is only supported for QLineSeries and QScatterSeries. | |||
|
37 | */ |
@@ -0,0 +1,222 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #ifndef QT_NO_OPENGL | |||
|
20 | ||||
|
21 | #include "private/glwidget_p.h" | |||
|
22 | #include "private/glxyseriesdata_p.h" | |||
|
23 | #include <QtGui/QOpenGLShaderProgram> | |||
|
24 | #include <QtGui/QOpenGLContext> | |||
|
25 | #include <QtGui/QOpenGLBuffer> | |||
|
26 | ||||
|
27 | //#define QDEBUG_TRACE_GL_FPS | |||
|
28 | #ifdef QDEBUG_TRACE_GL_FPS | |||
|
29 | # include <QElapsedTimer> | |||
|
30 | #endif | |||
|
31 | ||||
|
32 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
33 | ||||
|
34 | GLWidget::GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent) | |||
|
35 | : QOpenGLWidget(parent), | |||
|
36 | m_program(0), | |||
|
37 | m_shaderAttribLoc(-1), | |||
|
38 | m_colorUniformLoc(-1), | |||
|
39 | m_minUniformLoc(-1), | |||
|
40 | m_deltaUniformLoc(-1), | |||
|
41 | m_pointSizeUniformLoc(-1), | |||
|
42 | m_xyDataManager(xyDataManager) | |||
|
43 | { | |||
|
44 | setAttribute(Qt::WA_TranslucentBackground); | |||
|
45 | setAttribute(Qt::WA_AlwaysStackOnTop); | |||
|
46 | setAttribute(Qt::WA_TransparentForMouseEvents); | |||
|
47 | ||||
|
48 | QSurfaceFormat surfaceFormat; | |||
|
49 | surfaceFormat.setDepthBufferSize(0); | |||
|
50 | surfaceFormat.setStencilBufferSize(0); | |||
|
51 | surfaceFormat.setRedBufferSize(8); | |||
|
52 | surfaceFormat.setGreenBufferSize(8); | |||
|
53 | surfaceFormat.setBlueBufferSize(8); | |||
|
54 | surfaceFormat.setAlphaBufferSize(8); | |||
|
55 | surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); | |||
|
56 | surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); | |||
|
57 | setFormat(surfaceFormat); | |||
|
58 | ||||
|
59 | connect(xyDataManager, &GLXYSeriesDataManager::seriesRemoved, | |||
|
60 | this, &GLWidget::cleanXYSeriesResources); | |||
|
61 | } | |||
|
62 | ||||
|
63 | GLWidget::~GLWidget() | |||
|
64 | { | |||
|
65 | cleanup(); | |||
|
66 | } | |||
|
67 | ||||
|
68 | void GLWidget::cleanup() | |||
|
69 | { | |||
|
70 | makeCurrent(); | |||
|
71 | ||||
|
72 | delete m_program; | |||
|
73 | m_program = 0; | |||
|
74 | ||||
|
75 | foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values()) | |||
|
76 | delete buffer; | |||
|
77 | m_seriesBufferMap.clear(); | |||
|
78 | ||||
|
79 | doneCurrent(); | |||
|
80 | } | |||
|
81 | ||||
|
82 | void GLWidget::cleanXYSeriesResources(const QXYSeries *series) | |||
|
83 | { | |||
|
84 | makeCurrent(); | |||
|
85 | if (series) { | |||
|
86 | delete m_seriesBufferMap.take(series); | |||
|
87 | } else { | |||
|
88 | // Null series means all series were removed | |||
|
89 | foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values()) | |||
|
90 | delete buffer; | |||
|
91 | m_seriesBufferMap.clear(); | |||
|
92 | } | |||
|
93 | doneCurrent(); | |||
|
94 | } | |||
|
95 | ||||
|
96 | static const char *vertexSource = | |||
|
97 | "attribute highp vec2 points;\n" | |||
|
98 | "uniform highp vec2 min;\n" | |||
|
99 | "uniform highp vec2 delta;\n" | |||
|
100 | "uniform highp float pointSize;\n" | |||
|
101 | "void main() {\n" | |||
|
102 | " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n" | |||
|
103 | " gl_Position = vec4(normalPoint, 0, 1);\n" | |||
|
104 | " gl_PointSize = pointSize;\n" | |||
|
105 | "}"; | |||
|
106 | static const char *fragmentSource = | |||
|
107 | "uniform highp vec3 color;\n" | |||
|
108 | "void main() {\n" | |||
|
109 | " gl_FragColor = vec4(color,1);\n" | |||
|
110 | "}\n"; | |||
|
111 | ||||
|
112 | void GLWidget::initializeGL() | |||
|
113 | { | |||
|
114 | connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup); | |||
|
115 | ||||
|
116 | initializeOpenGLFunctions(); | |||
|
117 | glClearColor(0, 0, 0, 0); | |||
|
118 | ||||
|
119 | m_program = new QOpenGLShaderProgram; | |||
|
120 | m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource); | |||
|
121 | m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource); | |||
|
122 | m_program->bindAttributeLocation("points", 0); | |||
|
123 | m_program->link(); | |||
|
124 | ||||
|
125 | m_program->bind(); | |||
|
126 | m_colorUniformLoc = m_program->uniformLocation("color"); | |||
|
127 | m_minUniformLoc = m_program->uniformLocation("min"); | |||
|
128 | m_deltaUniformLoc = m_program->uniformLocation("delta"); | |||
|
129 | m_pointSizeUniformLoc = m_program->uniformLocation("pointSize"); | |||
|
130 | ||||
|
131 | ||||
|
132 | // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x | |||
|
133 | // implementations this is optional and support may not be present | |||
|
134 | // at all. Nonetheless the below code works in all cases and makes | |||
|
135 | // sure there is a VAO when one is needed. | |||
|
136 | m_vao.create(); | |||
|
137 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); | |||
|
138 | ||||
|
139 | glEnableVertexAttribArray(0); | |||
|
140 | ||||
|
141 | glDisable(GL_DEPTH_TEST); | |||
|
142 | glDisable(GL_STENCIL_TEST); | |||
|
143 | ||||
|
144 | #if !defined(QT_OPENGL_ES_2) | |||
|
145 | if (!QOpenGLContext::currentContext()->isOpenGLES()) { | |||
|
146 | // Make it possible to change point primitive size and use textures with them in | |||
|
147 | // the shaders. These are implicitly enabled in ES2. | |||
|
148 | glEnable(GL_PROGRAM_POINT_SIZE); | |||
|
149 | } | |||
|
150 | #endif | |||
|
151 | ||||
|
152 | m_program->release(); | |||
|
153 | } | |||
|
154 | ||||
|
155 | void GLWidget::paintGL() | |||
|
156 | { | |||
|
157 | glClear(GL_COLOR_BUFFER_BIT); | |||
|
158 | ||||
|
159 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); | |||
|
160 | m_program->bind(); | |||
|
161 | ||||
|
162 | GLXYDataMapIterator i(m_xyDataManager->dataMap()); | |||
|
163 | while (i.hasNext()) { | |||
|
164 | i.next(); | |||
|
165 | QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); | |||
|
166 | GLXYSeriesData *data = i.value(); | |||
|
167 | ||||
|
168 | m_program->setUniformValue(m_colorUniformLoc, data->color); | |||
|
169 | m_program->setUniformValue(m_minUniformLoc, data->min); | |||
|
170 | m_program->setUniformValue(m_deltaUniformLoc, data->delta); | |||
|
171 | ||||
|
172 | if (!vbo) { | |||
|
173 | vbo = new QOpenGLBuffer; | |||
|
174 | m_seriesBufferMap.insert(i.key(), vbo); | |||
|
175 | vbo->create(); | |||
|
176 | } | |||
|
177 | vbo->bind(); | |||
|
178 | if (data->dirty) { | |||
|
179 | vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); | |||
|
180 | data->dirty = false; | |||
|
181 | } | |||
|
182 | ||||
|
183 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); | |||
|
184 | if (data->type == QAbstractSeries::SeriesTypeLine) { | |||
|
185 | glLineWidth(data->width); | |||
|
186 | glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); | |||
|
187 | } else { // Scatter | |||
|
188 | m_program->setUniformValue(m_pointSizeUniformLoc, data->width); | |||
|
189 | glDrawArrays(GL_POINTS, 0, data->array.size() / 2); | |||
|
190 | } | |||
|
191 | vbo->release(); | |||
|
192 | } | |||
|
193 | ||||
|
194 | #ifdef QDEBUG_TRACE_GL_FPS | |||
|
195 | static QElapsedTimer stopWatch; | |||
|
196 | static int frameCount = -1; | |||
|
197 | if (frameCount == -1) { | |||
|
198 | stopWatch.start(); | |||
|
199 | frameCount = 0; | |||
|
200 | } | |||
|
201 | frameCount++; | |||
|
202 | int elapsed = stopWatch.elapsed(); | |||
|
203 | if (elapsed >= 1000) { | |||
|
204 | elapsed = stopWatch.restart(); | |||
|
205 | qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed)))); | |||
|
206 | qDebug() << "FPS:" << fps; | |||
|
207 | frameCount = 0; | |||
|
208 | } | |||
|
209 | #endif | |||
|
210 | ||||
|
211 | m_program->release(); | |||
|
212 | } | |||
|
213 | ||||
|
214 | void GLWidget::resizeGL(int w, int h) | |||
|
215 | { | |||
|
216 | Q_UNUSED(w) | |||
|
217 | Q_UNUSED(h) | |||
|
218 | } | |||
|
219 | ||||
|
220 | QT_CHARTS_END_NAMESPACE | |||
|
221 | ||||
|
222 | #endif |
@@ -0,0 +1,80 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | // W A R N I N G | |||
|
20 | // ------------- | |||
|
21 | // | |||
|
22 | // This file is not part of the Qt Chart API. It exists purely as an | |||
|
23 | // implementation detail. This header file may change from version to | |||
|
24 | // version without notice, or even be removed. | |||
|
25 | // | |||
|
26 | // We mean it. | |||
|
27 | ||||
|
28 | #ifndef GLWIDGET_H | |||
|
29 | #define GLWIDGET_H | |||
|
30 | ||||
|
31 | #ifndef QT_NO_OPENGL | |||
|
32 | ||||
|
33 | #include <QtWidgets/QOpenGLWidget> | |||
|
34 | #include <QtGui/QOpenGLFunctions> | |||
|
35 | #include <QtGui/QOpenGLVertexArrayObject> | |||
|
36 | #include <QtCore/QHash> | |||
|
37 | #include <QtCharts/QAbstractSeries> | |||
|
38 | #include <QtCharts/QXYSeries> | |||
|
39 | ||||
|
40 | QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) | |||
|
41 | ||||
|
42 | class QOpenGLBuffer; | |||
|
43 | ||||
|
44 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
45 | ||||
|
46 | class GLXYSeriesDataManager; | |||
|
47 | ||||
|
48 | class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions | |||
|
49 | { | |||
|
50 | Q_OBJECT | |||
|
51 | ||||
|
52 | public: | |||
|
53 | GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent = 0); | |||
|
54 | ~GLWidget(); | |||
|
55 | ||||
|
56 | public Q_SLOTS: | |||
|
57 | void cleanup(); | |||
|
58 | void cleanXYSeriesResources(const QXYSeries *series); | |||
|
59 | ||||
|
60 | protected: | |||
|
61 | void initializeGL() Q_DECL_OVERRIDE; | |||
|
62 | void paintGL() Q_DECL_OVERRIDE; | |||
|
63 | void resizeGL(int width, int height) Q_DECL_OVERRIDE; | |||
|
64 | ||||
|
65 | private: | |||
|
66 | QOpenGLShaderProgram *m_program; | |||
|
67 | int m_shaderAttribLoc; | |||
|
68 | int m_colorUniformLoc; | |||
|
69 | int m_minUniformLoc; | |||
|
70 | int m_deltaUniformLoc; | |||
|
71 | int m_pointSizeUniformLoc; | |||
|
72 | QOpenGLVertexArrayObject m_vao; | |||
|
73 | ||||
|
74 | QHash<const QAbstractSeries *, QOpenGLBuffer *> m_seriesBufferMap; | |||
|
75 | GLXYSeriesDataManager *m_xyDataManager; | |||
|
76 | }; | |||
|
77 | ||||
|
78 | QT_CHARTS_END_NAMESPACE | |||
|
79 | #endif | |||
|
80 | #endif |
@@ -0,0 +1,147 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #include "private/glxyseriesdata_p.h" | |||
|
20 | #include "private/abstractdomain_p.h" | |||
|
21 | #include <QtCharts/QScatterSeries> | |||
|
22 | ||||
|
23 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
24 | ||||
|
25 | GLXYSeriesDataManager::GLXYSeriesDataManager(QObject *parent) | |||
|
26 | : QObject(parent), | |||
|
27 | m_mapDirty(false) | |||
|
28 | { | |||
|
29 | } | |||
|
30 | ||||
|
31 | GLXYSeriesDataManager::~GLXYSeriesDataManager() | |||
|
32 | { | |||
|
33 | cleanup(); | |||
|
34 | } | |||
|
35 | ||||
|
36 | void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *domain) | |||
|
37 | { | |||
|
38 | GLXYSeriesData *data = m_seriesDataMap.value(series); | |||
|
39 | if (!data) { | |||
|
40 | data = new GLXYSeriesData; | |||
|
41 | data->type = series->type(); | |||
|
42 | QColor sc; | |||
|
43 | if (data->type == QAbstractSeries::SeriesTypeScatter) { | |||
|
44 | QScatterSeries *scatter = static_cast<QScatterSeries *>(series); | |||
|
45 | data->width = float(scatter->markerSize()); | |||
|
46 | sc = scatter->color(); // Scatter overwrites color property | |||
|
47 | } else { | |||
|
48 | data->width = float(series->pen().widthF()); | |||
|
49 | sc = series->color(); | |||
|
50 | } | |||
|
51 | data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF())); | |||
|
52 | connect(series, &QXYSeries::penChanged, this, | |||
|
53 | &GLXYSeriesDataManager::handleSeriesPenChange); | |||
|
54 | connect(series, &QXYSeries::useOpenGLChanged, this, | |||
|
55 | &GLXYSeriesDataManager::handleSeriesOpenGLChange); | |||
|
56 | m_seriesDataMap.insert(series, data); | |||
|
57 | m_mapDirty = true; | |||
|
58 | } | |||
|
59 | QVector<float> &array = data->array; | |||
|
60 | ||||
|
61 | bool logAxis = false; | |||
|
62 | foreach (QAbstractAxis* axis, series->attachedAxes()) { | |||
|
63 | if (axis->type() == QAbstractAxis::AxisTypeLogValue) { | |||
|
64 | logAxis = true; | |||
|
65 | break; | |||
|
66 | } | |||
|
67 | } | |||
|
68 | ||||
|
69 | int count = series->count(); | |||
|
70 | int index = 0; | |||
|
71 | array.resize(count * 2); | |||
|
72 | if (logAxis) { | |||
|
73 | // Use domain to resolve geometry points. Not as fast as shaders, but simpler that way | |||
|
74 | QVector<QPointF> geometryPoints = domain->calculateGeometryPoints(series->pointsVector()); | |||
|
75 | const float height = domain->size().height(); | |||
|
76 | if (geometryPoints.size()) { | |||
|
77 | for (int i = 0; i < count; i++) { | |||
|
78 | const QPointF &point = geometryPoints.at(i); | |||
|
79 | array[index++] = float(point.x()); | |||
|
80 | array[index++] = float(height - point.y()); | |||
|
81 | } | |||
|
82 | } else { | |||
|
83 | // If there are invalid log values, geometry points generation fails | |||
|
84 | for (int i = 0; i < count; i++) { | |||
|
85 | array[index++] = 0.0f; | |||
|
86 | array[index++] = 0.0f; | |||
|
87 | } | |||
|
88 | } | |||
|
89 | data->min = QVector2D(0, 0); | |||
|
90 | data->delta = QVector2D(domain->size().width() / 2.0f, domain->size().height() / 2.0f); | |||
|
91 | } else { | |||
|
92 | // Regular value axes, so we can do the math easily on shaders. | |||
|
93 | QVector<QPointF> seriesPoints = series->pointsVector(); | |||
|
94 | for (int i = 0; i < count; i++) { | |||
|
95 | const QPointF &point = seriesPoints.at(i); | |||
|
96 | array[index++] = float(point.x()); | |||
|
97 | array[index++] = float(point.y()); | |||
|
98 | } | |||
|
99 | data->min = QVector2D(domain->minX(), domain->minY()); | |||
|
100 | data->delta = QVector2D((domain->maxX() - domain->minX()) / 2.0f, | |||
|
101 | (domain->maxY() - domain->minY()) / 2.0f); | |||
|
102 | } | |||
|
103 | data->dirty = true; | |||
|
104 | } | |||
|
105 | ||||
|
106 | void GLXYSeriesDataManager::removeSeries(const QXYSeries *series) | |||
|
107 | { | |||
|
108 | GLXYSeriesData *data = m_seriesDataMap.take(series); | |||
|
109 | if (data) { | |||
|
110 | disconnect(series, 0, this, 0); | |||
|
111 | delete data; | |||
|
112 | emit seriesRemoved(series); | |||
|
113 | m_mapDirty = true; | |||
|
114 | } | |||
|
115 | } | |||
|
116 | ||||
|
117 | void GLXYSeriesDataManager::cleanup() | |||
|
118 | { | |||
|
119 | foreach (GLXYSeriesData *data, m_seriesDataMap.values()) | |||
|
120 | delete data; | |||
|
121 | m_seriesDataMap.clear(); | |||
|
122 | m_mapDirty = true; | |||
|
123 | // Signal all series removal by using zero as parameter | |||
|
124 | emit seriesRemoved(0); | |||
|
125 | } | |||
|
126 | ||||
|
127 | void GLXYSeriesDataManager::handleSeriesPenChange() | |||
|
128 | { | |||
|
129 | QXYSeries *series = qobject_cast<QXYSeries *>(sender()); | |||
|
130 | if (series) { | |||
|
131 | GLXYSeriesData *data = m_seriesDataMap.value(series); | |||
|
132 | if (data) { | |||
|
133 | QColor sc = series->color(); | |||
|
134 | data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF())); | |||
|
135 | data->width = float(series->pen().widthF()); | |||
|
136 | } | |||
|
137 | } | |||
|
138 | } | |||
|
139 | ||||
|
140 | void GLXYSeriesDataManager::handleSeriesOpenGLChange() | |||
|
141 | { | |||
|
142 | QXYSeries *series = qobject_cast<QXYSeries *>(sender()); | |||
|
143 | if (!series->useOpenGL()) | |||
|
144 | removeSeries(series); | |||
|
145 | } | |||
|
146 | ||||
|
147 | QT_CHARTS_END_NAMESPACE |
@@ -0,0 +1,91 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | // W A R N I N G | |||
|
20 | // ------------- | |||
|
21 | // | |||
|
22 | // This file is not part of the Qt Chart API. It exists purely as an | |||
|
23 | // implementation detail. This header file may change from version to | |||
|
24 | // version without notice, or even be removed. | |||
|
25 | // | |||
|
26 | // We mean it. | |||
|
27 | ||||
|
28 | #ifndef GLXYSERIESDATA_H | |||
|
29 | #define GLXYSERIESDATA_H | |||
|
30 | ||||
|
31 | #include <QtCore/QMap> | |||
|
32 | #include <QtCharts/QAbstractSeries> | |||
|
33 | #include <QtCharts/QXYSeries> | |||
|
34 | #include <QtGui/QVector3D> | |||
|
35 | #include <QtGui/QVector2D> | |||
|
36 | ||||
|
37 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
38 | ||||
|
39 | class AbstractDomain; | |||
|
40 | ||||
|
41 | struct GLXYSeriesData { | |||
|
42 | QVector<float> array; | |||
|
43 | bool dirty; | |||
|
44 | QVector3D color; | |||
|
45 | float width; | |||
|
46 | QAbstractSeries::SeriesType type; | |||
|
47 | QVector2D min; | |||
|
48 | QVector2D delta; | |||
|
49 | }; | |||
|
50 | ||||
|
51 | typedef QMap<const QXYSeries *, GLXYSeriesData *> GLXYDataMap; | |||
|
52 | typedef QMapIterator<const QXYSeries *, GLXYSeriesData *> GLXYDataMapIterator; | |||
|
53 | ||||
|
54 | class GLXYSeriesDataManager : public QObject | |||
|
55 | { | |||
|
56 | Q_OBJECT | |||
|
57 | ||||
|
58 | public: | |||
|
59 | GLXYSeriesDataManager(QObject *parent = 0); | |||
|
60 | ~GLXYSeriesDataManager(); | |||
|
61 | ||||
|
62 | void setPoints(QXYSeries *series, const AbstractDomain *domain); | |||
|
63 | ||||
|
64 | void removeSeries(const QXYSeries *series); | |||
|
65 | ||||
|
66 | GLXYDataMap &dataMap() { return m_seriesDataMap; } | |||
|
67 | ||||
|
68 | // These functions are needed by qml side, so they must be inline | |||
|
69 | bool mapDirty() const { return m_mapDirty; } | |||
|
70 | void clearAllDirty() { | |||
|
71 | m_mapDirty = false; | |||
|
72 | foreach (GLXYSeriesData *data, m_seriesDataMap.values()) | |||
|
73 | data->dirty = false; | |||
|
74 | } | |||
|
75 | ||||
|
76 | public Q_SLOTS: | |||
|
77 | void cleanup(); | |||
|
78 | void handleSeriesPenChange(); | |||
|
79 | void handleSeriesOpenGLChange(); | |||
|
80 | ||||
|
81 | Q_SIGNALS: | |||
|
82 | void seriesRemoved(const QXYSeries *series); | |||
|
83 | ||||
|
84 | private: | |||
|
85 | GLXYDataMap m_seriesDataMap; | |||
|
86 | bool m_mapDirty; | |||
|
87 | }; | |||
|
88 | ||||
|
89 | QT_CHARTS_END_NAMESPACE | |||
|
90 | ||||
|
91 | #endif |
@@ -0,0 +1,78 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #include "declarativechartnode.h" | |||
|
20 | #include "declarativerendernode.h" | |||
|
21 | #include <QtGui/QOpenGLContext> | |||
|
22 | #include <QtGui/QOpenGLFunctions> | |||
|
23 | #include <QtGui/QOpenGLFramebufferObjectFormat> | |||
|
24 | #include <QtGui/QOpenGLFramebufferObject> | |||
|
25 | #include <QOpenGLShaderProgram> | |||
|
26 | #include <QtGui/QOpenGLBuffer> | |||
|
27 | ||||
|
28 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
29 | ||||
|
30 | // This node handles displaying of the chart itself | |||
|
31 | DeclarativeChartNode::DeclarativeChartNode(QQuickWindow *window) : | |||
|
32 | QSGSimpleTextureNode(), | |||
|
33 | m_texture(0), | |||
|
34 | m_window(window), | |||
|
35 | m_textureOptions(0), | |||
|
36 | m_textureSize(1, 1), | |||
|
37 | m_glRenderNode(0) | |||
|
38 | { | |||
|
39 | initializeOpenGLFunctions(); | |||
|
40 | ||||
|
41 | // Our texture node must have a texture, so use a default one pixel texture | |||
|
42 | GLuint defaultTexture = 0; | |||
|
43 | glGenTextures(1, &defaultTexture); | |||
|
44 | glBindTexture(GL_TEXTURE_2D, defaultTexture); | |||
|
45 | uchar buf[4] = { 0, 0, 0, 0 }; | |||
|
46 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf); | |||
|
47 | ||||
|
48 | QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions( | |||
|
49 | QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture); | |||
|
50 | m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions); | |||
|
51 | ||||
|
52 | setTexture(m_texture); | |||
|
53 | setFiltering(QSGTexture::Linear); | |||
|
54 | ||||
|
55 | // Create child node for rendering GL graphics | |||
|
56 | m_glRenderNode = new DeclarativeRenderNode(m_window); | |||
|
57 | m_glRenderNode->setFlag(OwnedByParent); | |||
|
58 | appendChildNode(m_glRenderNode); | |||
|
59 | m_glRenderNode->setRect(0, 0, 0, 0); // Hide child node by default | |||
|
60 | } | |||
|
61 | ||||
|
62 | DeclarativeChartNode::~DeclarativeChartNode() | |||
|
63 | { | |||
|
64 | delete m_texture; | |||
|
65 | } | |||
|
66 | ||||
|
67 | // Must be called on render thread and in context | |||
|
68 | void DeclarativeChartNode::createTextureFromImage(const QImage &chartImage) | |||
|
69 | { | |||
|
70 | if (chartImage.size() != m_textureSize) | |||
|
71 | m_textureSize = chartImage.size(); | |||
|
72 | ||||
|
73 | delete m_texture; | |||
|
74 | m_texture = m_window->createTextureFromImage(chartImage, m_textureOptions); | |||
|
75 | setTexture(m_texture); | |||
|
76 | } | |||
|
77 | ||||
|
78 | QT_CHARTS_END_NAMESPACE |
@@ -0,0 +1,50 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #ifndef DECLARATIVECHARTNODE_P_H | |||
|
20 | #define DECLARATIVECHARTNODE_P_H | |||
|
21 | ||||
|
22 | #include <QtCharts/QChartGlobal> | |||
|
23 | #include <QtQuick/QSGSimpleTextureNode> | |||
|
24 | #include <QtQuick/QQuickWindow> | |||
|
25 | #include <QtGui/QOpenGLFunctions> | |||
|
26 | ||||
|
27 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
28 | ||||
|
29 | class DeclarativeRenderNode; | |||
|
30 | ||||
|
31 | class DeclarativeChartNode : public QSGSimpleTextureNode, QOpenGLFunctions | |||
|
32 | { | |||
|
33 | public: | |||
|
34 | DeclarativeChartNode(QQuickWindow *window); | |||
|
35 | ~DeclarativeChartNode(); | |||
|
36 | ||||
|
37 | void createTextureFromImage(const QImage &chartImage); | |||
|
38 | DeclarativeRenderNode *glRenderNode() const { return m_glRenderNode; } | |||
|
39 | ||||
|
40 | private: | |||
|
41 | QSGTexture *m_texture; | |||
|
42 | QQuickWindow *m_window; | |||
|
43 | QQuickWindow::CreateTextureOptions m_textureOptions; | |||
|
44 | QSize m_textureSize; | |||
|
45 | DeclarativeRenderNode *m_glRenderNode; | |||
|
46 | }; | |||
|
47 | ||||
|
48 | QT_CHARTS_END_NAMESPACE | |||
|
49 | ||||
|
50 | #endif // DECLARATIVECHARTNODE_P_H |
@@ -0,0 +1,317 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #include "declarativerendernode.h" | |||
|
20 | ||||
|
21 | #include <QtGui/QOpenGLContext> | |||
|
22 | #include <QtGui/QOpenGLFunctions> | |||
|
23 | #include <QtGui/QOpenGLFramebufferObjectFormat> | |||
|
24 | #include <QtGui/QOpenGLFramebufferObject> | |||
|
25 | #include <QOpenGLShaderProgram> | |||
|
26 | #include <QtGui/QOpenGLBuffer> | |||
|
27 | ||||
|
28 | //#define QDEBUG_TRACE_GL_FPS | |||
|
29 | #ifdef QDEBUG_TRACE_GL_FPS | |||
|
30 | # include <QElapsedTimer> | |||
|
31 | #endif | |||
|
32 | ||||
|
33 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
34 | ||||
|
35 | // This node draws the xy series data on a transparent background using OpenGL. | |||
|
36 | // It is used as a child node of the chart node. | |||
|
37 | DeclarativeRenderNode::DeclarativeRenderNode(QQuickWindow *window) : | |||
|
38 | QObject(), | |||
|
39 | QSGSimpleTextureNode(), | |||
|
40 | m_texture(0), | |||
|
41 | m_window(window), | |||
|
42 | m_textureOptions(QQuickWindow::TextureHasAlphaChannel), | |||
|
43 | m_textureSize(1, 1), | |||
|
44 | m_recreateFbo(false), | |||
|
45 | m_fbo(0), | |||
|
46 | m_program(0), | |||
|
47 | m_shaderAttribLoc(-1), | |||
|
48 | m_colorUniformLoc(-1), | |||
|
49 | m_minUniformLoc(-1), | |||
|
50 | m_deltaUniformLoc(-1), | |||
|
51 | m_pointSizeUniformLoc(-1), | |||
|
52 | m_renderNeeded(true) | |||
|
53 | { | |||
|
54 | initializeOpenGLFunctions(); | |||
|
55 | ||||
|
56 | // Our texture node must have a texture, so use a default one pixel texture | |||
|
57 | GLuint defaultTexture = 0; | |||
|
58 | glGenTextures(1, &defaultTexture); | |||
|
59 | glBindTexture(GL_TEXTURE_2D, defaultTexture); | |||
|
60 | uchar buf[4] = { 0, 0, 0, 0 }; | |||
|
61 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf); | |||
|
62 | ||||
|
63 | QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions( | |||
|
64 | QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture); | |||
|
65 | m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions); | |||
|
66 | ||||
|
67 | setTexture(m_texture); | |||
|
68 | setFiltering(QSGTexture::Linear); | |||
|
69 | setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically); | |||
|
70 | } | |||
|
71 | ||||
|
72 | DeclarativeRenderNode::~DeclarativeRenderNode() | |||
|
73 | { | |||
|
74 | delete m_texture; | |||
|
75 | delete m_fbo; | |||
|
76 | ||||
|
77 | delete m_program; | |||
|
78 | m_program = 0; | |||
|
79 | ||||
|
80 | cleanXYSeriesResources(0); | |||
|
81 | } | |||
|
82 | ||||
|
83 | static const char *vertexSource = | |||
|
84 | "attribute highp vec2 points;\n" | |||
|
85 | "uniform highp vec2 min;\n" | |||
|
86 | "uniform highp vec2 delta;\n" | |||
|
87 | "uniform highp float pointSize;\n" | |||
|
88 | "void main() {\n" | |||
|
89 | " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n" | |||
|
90 | " gl_Position = vec4(normalPoint, 0, 1);\n" | |||
|
91 | " gl_PointSize = pointSize;\n" | |||
|
92 | "}"; | |||
|
93 | static const char *fragmentSource = | |||
|
94 | "uniform highp vec3 color;\n" | |||
|
95 | "void main() {\n" | |||
|
96 | " gl_FragColor = vec4(color,1);\n" | |||
|
97 | "}\n"; | |||
|
98 | ||||
|
99 | // Must be called on render thread and in context | |||
|
100 | void DeclarativeRenderNode::initGL() | |||
|
101 | { | |||
|
102 | recreateFBO(); | |||
|
103 | ||||
|
104 | m_program = new QOpenGLShaderProgram; | |||
|
105 | m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource); | |||
|
106 | m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource); | |||
|
107 | m_program->bindAttributeLocation("points", 0); | |||
|
108 | m_program->link(); | |||
|
109 | ||||
|
110 | m_program->bind(); | |||
|
111 | m_colorUniformLoc = m_program->uniformLocation("color"); | |||
|
112 | m_minUniformLoc = m_program->uniformLocation("min"); | |||
|
113 | m_deltaUniformLoc = m_program->uniformLocation("delta"); | |||
|
114 | m_pointSizeUniformLoc = m_program->uniformLocation("pointSize"); | |||
|
115 | ||||
|
116 | // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x | |||
|
117 | // implementations this is optional and support may not be present | |||
|
118 | // at all. Nonetheless the below code works in all cases and makes | |||
|
119 | // sure there is a VAO when one is needed. | |||
|
120 | m_vao.create(); | |||
|
121 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); | |||
|
122 | ||||
|
123 | #if !defined(QT_OPENGL_ES_2) | |||
|
124 | if (!QOpenGLContext::currentContext()->isOpenGLES()) { | |||
|
125 | // Make it possible to change point primitive size and use textures with them in | |||
|
126 | // the shaders. These are implicitly enabled in ES2. | |||
|
127 | // Qt Quick doesn't change these flags, so it should be safe to just enable them | |||
|
128 | // at initialization. | |||
|
129 | glEnable(GL_PROGRAM_POINT_SIZE); | |||
|
130 | } | |||
|
131 | #endif | |||
|
132 | ||||
|
133 | m_program->release(); | |||
|
134 | } | |||
|
135 | ||||
|
136 | void DeclarativeRenderNode::recreateFBO() | |||
|
137 | { | |||
|
138 | QOpenGLFramebufferObjectFormat fboFormat; | |||
|
139 | fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment); | |||
|
140 | delete m_fbo; | |||
|
141 | m_fbo = new QOpenGLFramebufferObject(m_textureSize.width(), | |||
|
142 | m_textureSize.height(), | |||
|
143 | fboFormat); | |||
|
144 | ||||
|
145 | delete m_texture; | |||
|
146 | m_texture = m_window->createTextureFromId(m_fbo->texture(), m_textureSize, m_textureOptions); | |||
|
147 | setTexture(m_texture); | |||
|
148 | ||||
|
149 | m_recreateFbo = false; | |||
|
150 | } | |||
|
151 | ||||
|
152 | // Must be called on render thread and in context | |||
|
153 | void DeclarativeRenderNode::setTextureSize(const QSize &size) | |||
|
154 | { | |||
|
155 | m_textureSize = size; | |||
|
156 | m_recreateFbo = true; | |||
|
157 | m_renderNeeded = true; | |||
|
158 | } | |||
|
159 | ||||
|
160 | // Must be called on render thread while gui thread is blocked, and in context | |||
|
161 | void DeclarativeRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap &dataMap) | |||
|
162 | { | |||
|
163 | if (mapDirty) { | |||
|
164 | // Series have changed, recreate map, but utilize old data where feasible | |||
|
165 | GLXYDataMap oldMap = m_xyDataMap; | |||
|
166 | m_xyDataMap.clear(); | |||
|
167 | ||||
|
168 | GLXYDataMapIterator i(dataMap); | |||
|
169 | while (i.hasNext()) { | |||
|
170 | i.next(); | |||
|
171 | GLXYSeriesData *data = oldMap.take(i.key()); | |||
|
172 | const GLXYSeriesData *newData = i.value(); | |||
|
173 | if (!data || newData->dirty) { | |||
|
174 | data = new GLXYSeriesData; | |||
|
175 | data->array = newData->array; | |||
|
176 | data->color = newData->color; | |||
|
177 | data->dirty = newData->dirty; | |||
|
178 | data->width = newData->width; | |||
|
179 | data->type = newData->type; | |||
|
180 | data->min = newData->min; | |||
|
181 | data->delta = newData->delta; | |||
|
182 | } | |||
|
183 | m_xyDataMap.insert(i.key(), data); | |||
|
184 | } | |||
|
185 | // Delete remaining old data | |||
|
186 | i = oldMap; | |||
|
187 | while (i.hasNext()) { | |||
|
188 | i.next(); | |||
|
189 | delete i.value(); | |||
|
190 | cleanXYSeriesResources(i.key()); | |||
|
191 | } | |||
|
192 | } else { | |||
|
193 | // Series have not changed, so just copy dirty data over | |||
|
194 | GLXYDataMapIterator i(dataMap); | |||
|
195 | while (i.hasNext()) { | |||
|
196 | i.next(); | |||
|
197 | const GLXYSeriesData *newData = i.value(); | |||
|
198 | if (i.value()->dirty) { | |||
|
199 | GLXYSeriesData *data = m_xyDataMap.value(i.key()); | |||
|
200 | if (data) { | |||
|
201 | data->array = newData->array; | |||
|
202 | data->color = newData->color; | |||
|
203 | data->dirty = newData->dirty; | |||
|
204 | data->width = newData->width; | |||
|
205 | data->type = newData->type; | |||
|
206 | data->min = newData->min; | |||
|
207 | data->delta = newData->delta; | |||
|
208 | } | |||
|
209 | } | |||
|
210 | } | |||
|
211 | } | |||
|
212 | markDirty(DirtyMaterial); | |||
|
213 | m_renderNeeded = true; | |||
|
214 | } | |||
|
215 | ||||
|
216 | void DeclarativeRenderNode::renderGL() | |||
|
217 | { | |||
|
218 | glClearColor(0, 0, 0, 0); | |||
|
219 | ||||
|
220 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); | |||
|
221 | m_program->bind(); | |||
|
222 | m_fbo->bind(); | |||
|
223 | ||||
|
224 | glClear(GL_COLOR_BUFFER_BIT); | |||
|
225 | glEnableVertexAttribArray(0); | |||
|
226 | ||||
|
227 | glViewport(0, 0, m_textureSize.width(), m_textureSize.height()); | |||
|
228 | ||||
|
229 | GLXYDataMapIterator i(m_xyDataMap); | |||
|
230 | while (i.hasNext()) { | |||
|
231 | i.next(); | |||
|
232 | QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); | |||
|
233 | GLXYSeriesData *data = i.value(); | |||
|
234 | ||||
|
235 | m_program->setUniformValue(m_colorUniformLoc, data->color); | |||
|
236 | m_program->setUniformValue(m_minUniformLoc, data->min); | |||
|
237 | m_program->setUniformValue(m_deltaUniformLoc, data->delta); | |||
|
238 | ||||
|
239 | if (!vbo) { | |||
|
240 | vbo = new QOpenGLBuffer; | |||
|
241 | m_seriesBufferMap.insert(i.key(), vbo); | |||
|
242 | vbo->create(); | |||
|
243 | } | |||
|
244 | vbo->bind(); | |||
|
245 | if (data->dirty) { | |||
|
246 | vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); | |||
|
247 | data->dirty = false; | |||
|
248 | } | |||
|
249 | ||||
|
250 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); | |||
|
251 | if (data->type == QAbstractSeries::SeriesTypeLine) { | |||
|
252 | glLineWidth(data->width); | |||
|
253 | glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); | |||
|
254 | } else { // Scatter | |||
|
255 | m_program->setUniformValue(m_pointSizeUniformLoc, data->width); | |||
|
256 | glDrawArrays(GL_POINTS, 0, data->array.size() / 2); | |||
|
257 | } | |||
|
258 | vbo->release(); | |||
|
259 | } | |||
|
260 | ||||
|
261 | #ifdef QDEBUG_TRACE_GL_FPS | |||
|
262 | static QElapsedTimer stopWatch; | |||
|
263 | static int frameCount = -1; | |||
|
264 | if (frameCount == -1) { | |||
|
265 | stopWatch.start(); | |||
|
266 | frameCount = 0; | |||
|
267 | } | |||
|
268 | frameCount++; | |||
|
269 | int elapsed = stopWatch.elapsed(); | |||
|
270 | if (elapsed >= 1000) { | |||
|
271 | elapsed = stopWatch.restart(); | |||
|
272 | qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed)))); | |||
|
273 | qDebug() << "FPS:" << fps; | |||
|
274 | frameCount = 0; | |||
|
275 | } | |||
|
276 | #endif | |||
|
277 | ||||
|
278 | markDirty(DirtyMaterial); | |||
|
279 | m_window->resetOpenGLState(); | |||
|
280 | } | |||
|
281 | ||||
|
282 | // Must be called on render thread as response to beforeRendering signal | |||
|
283 | void DeclarativeRenderNode::render() | |||
|
284 | { | |||
|
285 | if (m_renderNeeded) { | |||
|
286 | if (m_xyDataMap.size()) { | |||
|
287 | if (!m_program) | |||
|
288 | initGL(); | |||
|
289 | if (m_recreateFbo) | |||
|
290 | recreateFBO(); | |||
|
291 | renderGL(); | |||
|
292 | } else { | |||
|
293 | if (rect() != QRectF()) { | |||
|
294 | glClearColor(0, 0, 0, 0); | |||
|
295 | m_fbo->bind(); | |||
|
296 | glClear(GL_COLOR_BUFFER_BIT); | |||
|
297 | ||||
|
298 | // If last series was removed, zero out the node rect | |||
|
299 | setRect(QRectF()); | |||
|
300 | } | |||
|
301 | } | |||
|
302 | m_renderNeeded = false; | |||
|
303 | } | |||
|
304 | } | |||
|
305 | ||||
|
306 | void DeclarativeRenderNode::cleanXYSeriesResources(const QXYSeries *series) | |||
|
307 | { | |||
|
308 | if (series) { | |||
|
309 | delete m_seriesBufferMap.take(series); | |||
|
310 | } else { | |||
|
311 | foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values()) | |||
|
312 | delete buffer; | |||
|
313 | m_seriesBufferMap.clear(); | |||
|
314 | } | |||
|
315 | } | |||
|
316 | ||||
|
317 | QT_CHARTS_END_NAMESPACE |
@@ -0,0 +1,74 | |||||
|
1 | /**************************************************************************** | |||
|
2 | ** | |||
|
3 | ** Copyright (C) 2015 The Qt Company Ltd | |||
|
4 | ** All rights reserved. | |||
|
5 | ** For any questions to The Qt Company, please use contact form at http://qt.io | |||
|
6 | ** | |||
|
7 | ** This file is part of the Qt Charts module. | |||
|
8 | ** | |||
|
9 | ** Licensees holding valid commercial license for Qt may use this file in | |||
|
10 | ** accordance with the Qt License Agreement provided with the Software | |||
|
11 | ** or, alternatively, in accordance with the terms contained in a written | |||
|
12 | ** agreement between you and The Qt Company. | |||
|
13 | ** | |||
|
14 | ** If you have questions regarding the use of this file, please use | |||
|
15 | ** contact form at http://qt.io | |||
|
16 | ** | |||
|
17 | ****************************************************************************/ | |||
|
18 | ||||
|
19 | #ifndef DECLARATIVERENDERNODE_P_H | |||
|
20 | #define DECLARATIVERENDERNODE_P_H | |||
|
21 | ||||
|
22 | #include <QtCharts/QChartGlobal> | |||
|
23 | #include <private/glxyseriesdata_p.h> | |||
|
24 | #include <QtQuick/QSGSimpleTextureNode> | |||
|
25 | #include <QtQuick/QQuickWindow> | |||
|
26 | #include <QtGui/QOpenGLVertexArrayObject> | |||
|
27 | #include <QtGui/QOpenGLFunctions> | |||
|
28 | ||||
|
29 | class QOpenGLFramebufferObject; | |||
|
30 | class QOpenGLBuffer; | |||
|
31 | ||||
|
32 | QT_CHARTS_BEGIN_NAMESPACE | |||
|
33 | ||||
|
34 | class DeclarativeRenderNode : public QObject, public QSGSimpleTextureNode, QOpenGLFunctions | |||
|
35 | { | |||
|
36 | Q_OBJECT | |||
|
37 | public: | |||
|
38 | DeclarativeRenderNode(QQuickWindow *window); | |||
|
39 | ~DeclarativeRenderNode(); | |||
|
40 | ||||
|
41 | void initGL(); | |||
|
42 | QSize textureSize() const { return m_textureSize; } | |||
|
43 | void setTextureSize(const QSize &size); | |||
|
44 | void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap); | |||
|
45 | ||||
|
46 | public Q_SLOTS: | |||
|
47 | void render(); | |||
|
48 | ||||
|
49 | private: | |||
|
50 | void renderGL(); | |||
|
51 | void recreateFBO(); | |||
|
52 | void cleanXYSeriesResources(const QXYSeries *series); | |||
|
53 | ||||
|
54 | QSGTexture *m_texture; | |||
|
55 | QQuickWindow *m_window; | |||
|
56 | QQuickWindow::CreateTextureOptions m_textureOptions; | |||
|
57 | QSize m_textureSize; | |||
|
58 | bool m_recreateFbo; | |||
|
59 | GLXYDataMap m_xyDataMap; | |||
|
60 | QOpenGLFramebufferObject *m_fbo; | |||
|
61 | QOpenGLShaderProgram *m_program; | |||
|
62 | int m_shaderAttribLoc; | |||
|
63 | int m_colorUniformLoc; | |||
|
64 | int m_minUniformLoc; | |||
|
65 | int m_deltaUniformLoc; | |||
|
66 | int m_pointSizeUniformLoc; | |||
|
67 | QOpenGLVertexArrayObject m_vao; | |||
|
68 | QHash<const QAbstractSeries *, QOpenGLBuffer *> m_seriesBufferMap; | |||
|
69 | bool m_renderNeeded; | |||
|
70 | }; | |||
|
71 | ||||
|
72 | QT_CHARTS_END_NAMESPACE | |||
|
73 | ||||
|
74 | #endif // DECLARATIVERENDERNODE_P_H |
@@ -35,12 +35,12 qint64 XYSeriesIODevice::readData(char * data, qint64 maxSize) | |||||
35 | qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize) |
|
35 | qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize) | |
36 | { |
|
36 | { | |
37 | qint64 range = 2000; |
|
37 | qint64 range = 2000; | |
38 |
Q |
|
38 | QVector<QPointF> oldPoints = m_series->pointsVector(); | |
39 |
Q |
|
39 | QVector<QPointF> points; | |
40 | int resolution = 4; |
|
40 | int resolution = 4; | |
41 |
|
41 | |||
42 | if (oldPoints.count() < range) { |
|
42 | if (oldPoints.count() < range) { | |
43 | points = m_series->points(); |
|
43 | points = m_series->pointsVector(); | |
44 | } else { |
|
44 | } else { | |
45 | for (int i = maxSize/resolution; i < oldPoints.count(); i++) |
|
45 | for (int i = maxSize/resolution; i < oldPoints.count(); i++) | |
46 | points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y())); |
|
46 | points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y())); |
@@ -50,7 +50,13 qtHaveModule(quick) { | |||||
50 | qtHaveModule(multimedia) { |
|
50 | qtHaveModule(multimedia) { | |
51 | SUBDIRS += audio |
|
51 | SUBDIRS += audio | |
52 | } else { |
|
52 | } else { | |
53 | message("QtMultimedia library not available. Some examples are disabled") |
|
53 | message("QtMultimedia library not available. Some examples are disabled.") | |
|
54 | } | |||
|
55 | ||||
|
56 | contains(QT_CONFIG, opengl) { | |||
|
57 | SUBDIRS += openglseries | |||
|
58 | } else { | |||
|
59 | message("OpenGL not available. Some examples are disabled.") | |||
54 | } |
|
60 | } | |
55 |
|
61 | |||
56 | !linux-arm*: { |
|
62 | !linux-arm*: { |
@@ -189,7 +189,7 QChart *ThemeWidget::createAreaChart() const | |||||
189 | for (int j(0); j < m_dataTable[i].count(); j++) { |
|
189 | for (int j(0); j < m_dataTable[i].count(); j++) { | |
190 | Data data = m_dataTable[i].at(j); |
|
190 | Data data = m_dataTable[i].at(j); | |
191 | if (lowerSeries) { |
|
191 | if (lowerSeries) { | |
192 |
const Q |
|
192 | const QVector<QPointF>& points = lowerSeries->pointsVector(); | |
193 | upperSeries->append(QPointF(j, points[i].y() + data.first.y())); |
|
193 | upperSeries->append(QPointF(j, points[i].y() + data.first.y())); | |
194 | } else { |
|
194 | } else { | |
195 | upperSeries->append(QPointF(j, data.first.y())); |
|
195 | upperSeries->append(QPointF(j, data.first.y())); |
@@ -16,10 +16,11 | |||||
16 | ** |
|
16 | ** | |
17 | ****************************************************************************/ |
|
17 | ****************************************************************************/ | |
18 |
|
18 | |||
19 |
import QtQuick 2. |
|
19 | import QtQuick 2.1 | |
20 | import QtQuick.Layouts 1.0 |
|
20 | import QtQuick.Layouts 1.0 | |
21 |
|
21 | |||
22 | ColumnLayout { |
|
22 | ColumnLayout { | |
|
23 | property alias openGLButton: openGLButton | |||
23 | spacing: 8 |
|
24 | spacing: 8 | |
24 | Layout.fillHeight: true |
|
25 | Layout.fillHeight: true | |
25 | signal animationsEnabled(bool enabled) |
|
26 | signal animationsEnabled(bool enabled) | |
@@ -27,6 +28,7 ColumnLayout { | |||||
27 | signal refreshRateChanged(variant rate); |
|
28 | signal refreshRateChanged(variant rate); | |
28 | signal signalSourceChanged(string source, int signalCount, int sampleCount); |
|
29 | signal signalSourceChanged(string source, int signalCount, int sampleCount); | |
29 | signal antialiasingEnabled(bool enabled) |
|
30 | signal antialiasingEnabled(bool enabled) | |
|
31 | signal openGlChanged(bool enabled) | |||
30 |
|
32 | |||
31 | Text { |
|
33 | Text { | |
32 | text: "Scope" |
|
34 | text: "Scope" | |
@@ -35,6 +37,14 ColumnLayout { | |||||
35 | } |
|
37 | } | |
36 |
|
38 | |||
37 | MultiButton { |
|
39 | MultiButton { | |
|
40 | id: openGLButton | |||
|
41 | text: "OpenGL: " | |||
|
42 | items: ["false", "true"] | |||
|
43 | currentSelection: 0 | |||
|
44 | onSelectionChanged: openGlChanged(currentSelection == 1); | |||
|
45 | } | |||
|
46 | ||||
|
47 | MultiButton { | |||
38 | text: "Graph: " |
|
48 | text: "Graph: " | |
39 | items: ["line", "spline", "scatter"] |
|
49 | items: ["line", "spline", "scatter"] | |
40 | currentSelection: 0 |
|
50 | currentSelection: 0 |
@@ -17,13 +17,18 | |||||
17 | ****************************************************************************/ |
|
17 | ****************************************************************************/ | |
18 |
|
18 | |||
19 | import QtQuick 2.0 |
|
19 | import QtQuick 2.0 | |
20 |
import QtCharts 2. |
|
20 | import QtCharts 2.1 | |
21 |
|
21 | |||
22 | //![1] |
|
22 | //![1] | |
23 | ChartView { |
|
23 | ChartView { | |
24 | id: chartView |
|
24 | id: chartView | |
25 | animationOptions: ChartView.NoAnimation |
|
25 | animationOptions: ChartView.NoAnimation | |
26 | theme: ChartView.ChartThemeDark |
|
26 | theme: ChartView.ChartThemeDark | |
|
27 | property bool openGL: false | |||
|
28 | onOpenGLChanged: { | |||
|
29 | series("signal 1").useOpenGL = openGL; | |||
|
30 | series("signal 2").useOpenGL = openGL; | |||
|
31 | } | |||
27 |
|
32 | |||
28 | ValueAxis { |
|
33 | ValueAxis { | |
29 | id: axisY1 |
|
34 | id: axisY1 | |
@@ -40,7 +45,7 ChartView { | |||||
40 | ValueAxis { |
|
45 | ValueAxis { | |
41 | id: axisX |
|
46 | id: axisX | |
42 | min: 0 |
|
47 | min: 0 | |
43 |
max: 10 |
|
48 | max: 1024 | |
44 | } |
|
49 | } | |
45 |
|
50 | |||
46 | LineSeries { |
|
51 | LineSeries { | |
@@ -48,12 +53,14 ChartView { | |||||
48 | name: "signal 1" |
|
53 | name: "signal 1" | |
49 | axisX: axisX |
|
54 | axisX: axisX | |
50 | axisY: axisY1 |
|
55 | axisY: axisY1 | |
|
56 | useOpenGL: chartView.openGL | |||
51 | } |
|
57 | } | |
52 | LineSeries { |
|
58 | LineSeries { | |
53 | id: lineSeries2 |
|
59 | id: lineSeries2 | |
54 | name: "signal 2" |
|
60 | name: "signal 2" | |
55 | axisX: axisX |
|
61 | axisX: axisX | |
56 | axisYRight: axisY2 |
|
62 | axisYRight: axisY2 | |
|
63 | useOpenGL: chartView.openGL | |||
57 | } |
|
64 | } | |
58 | //![1] |
|
65 | //![1] | |
59 |
|
66 | |||
@@ -78,18 +85,28 ChartView { | |||||
78 | // but the series have their own y-axes to make it possible to control the y-offset |
|
85 | // but the series have their own y-axes to make it possible to control the y-offset | |
79 | // of the "signal sources". |
|
86 | // of the "signal sources". | |
80 | if (type == "line") { |
|
87 | if (type == "line") { | |
81 |
chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", |
|
88 | var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", | |
82 | chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2); |
|
89 | axisX, axisY1); | |
|
90 | series1.useOpenGL = chartView.openGL | |||
|
91 | ||||
|
92 | var series2 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", | |||
|
93 | axisX, axisY2); | |||
|
94 | series2.useOpenGL = chartView.openGL | |||
83 | } else if (type == "spline") { |
|
95 | } else if (type == "spline") { | |
84 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1); |
|
96 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1); | |
85 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2); |
|
97 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2); | |
86 | } else { |
|
98 | } else { | |
87 |
var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", |
|
99 | var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", | |
|
100 | axisX, axisY1); | |||
88 | series1.markerSize = 3; |
|
101 | series1.markerSize = 3; | |
89 | series1.borderColor = "transparent"; |
|
102 | series1.borderColor = "transparent"; | |
90 | var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2", axisX, axisY2); |
|
103 | series1.useOpenGL = chartView.openGL | |
|
104 | ||||
|
105 | var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2", | |||
|
106 | axisX, axisY2); | |||
91 | series2.markerSize = 3; |
|
107 | series2.markerSize = 3; | |
92 | series2.borderColor = "transparent"; |
|
108 | series2.borderColor = "transparent"; | |
|
109 | series2.useOpenGL = chartView.openGL | |||
93 | } |
|
110 | } | |
94 | } |
|
111 | } | |
95 |
|
112 |
@@ -21,8 +21,8 import QtQuick 2.0 | |||||
21 | //![1] |
|
21 | //![1] | |
22 | Rectangle { |
|
22 | Rectangle { | |
23 | id: main |
|
23 | id: main | |
24 |
width: |
|
24 | width: 600 | |
25 |
height: |
|
25 | height: 400 | |
26 | color: "#404040" |
|
26 | color: "#404040" | |
27 |
|
27 | |||
28 | ControlPanel { |
|
28 | ControlPanel { | |
@@ -39,11 +39,22 Rectangle { | |||||
39 | dataSource.generateData(0, signalCount, sampleCount); |
|
39 | dataSource.generateData(0, signalCount, sampleCount); | |
40 | else |
|
40 | else | |
41 | dataSource.generateData(1, signalCount, sampleCount); |
|
41 | dataSource.generateData(1, signalCount, sampleCount); | |
|
42 | scopeView.axisX().max = sampleCount; | |||
42 | } |
|
43 | } | |
43 | onAnimationsEnabled: scopeView.setAnimations(enabled); |
|
44 | onAnimationsEnabled: scopeView.setAnimations(enabled); | |
44 |
onSeriesTypeChanged: |
|
45 | onSeriesTypeChanged: { | |
|
46 | scopeView.changeSeriesType(type); | |||
|
47 | if (type === "spline") { | |||
|
48 | controlPanel.openGLButton.currentSelection = 0; | |||
|
49 | controlPanel.openGLButton.enabled = false; | |||
|
50 | scopeView.openGL = false; | |||
|
51 | } else { | |||
|
52 | controlPanel.openGLButton.enabled = true; | |||
|
53 | } | |||
|
54 | } | |||
45 | onRefreshRateChanged: scopeView.changeRefreshRate(rate); |
|
55 | onRefreshRateChanged: scopeView.changeRefreshRate(rate); | |
46 | onAntialiasingEnabled: scopeView.antialiasing = enabled; |
|
56 | onAntialiasingEnabled: scopeView.antialiasing = enabled; | |
|
57 | onOpenGlChanged: scopeView.openGL = enabled; | |||
47 | } |
|
58 | } | |
48 |
|
59 | |||
49 | //![2] |
|
60 | //![2] | |
@@ -56,4 +67,5 Rectangle { | |||||
56 | height: main.height |
|
67 | height: main.height | |
57 | } |
|
68 | } | |
58 | //![2] |
|
69 | //![2] | |
|
70 | ||||
59 | } |
|
71 | } |
@@ -345,7 +345,7 QT_CHARTS_BEGIN_NAMESPACE | |||||
345 | \sa pointLabelsVisible |
|
345 | \sa pointLabelsVisible | |
346 | */ |
|
346 | */ | |
347 | /*! |
|
347 | /*! | |
348 |
\fn void QAreaSeries::pointLabelsClippin |
|
348 | \fn void QAreaSeries::pointLabelsClippingChanged(bool clipping) | |
349 | The clipping of the data point labels is changed to \a clipping. |
|
349 | The clipping of the data point labels is changed to \a clipping. | |
350 | */ |
|
350 | */ | |
351 | /*! |
|
351 | /*! | |
@@ -395,9 +395,11 QAbstractSeries::SeriesType QAreaSeries::type() const | |||||
395 | void QAreaSeries::setUpperSeries(QLineSeries *series) |
|
395 | void QAreaSeries::setUpperSeries(QLineSeries *series) | |
396 | { |
|
396 | { | |
397 | Q_D(QAreaSeries); |
|
397 | Q_D(QAreaSeries); | |
398 | if (d->m_upperSeries != series) |
|
398 | if (d->m_upperSeries != series) { | |
|
399 | series->d_ptr->setBlockOpenGL(true); | |||
399 | d->m_upperSeries = series; |
|
400 | d->m_upperSeries = series; | |
400 | } |
|
401 | } | |
|
402 | } | |||
401 |
|
403 | |||
402 | QLineSeries *QAreaSeries::upperSeries() const |
|
404 | QLineSeries *QAreaSeries::upperSeries() const | |
403 | { |
|
405 | { | |
@@ -411,6 +413,7 QLineSeries *QAreaSeries::upperSeries() const | |||||
411 | void QAreaSeries::setLowerSeries(QLineSeries *series) |
|
413 | void QAreaSeries::setLowerSeries(QLineSeries *series) | |
412 | { |
|
414 | { | |
413 | Q_D(QAreaSeries); |
|
415 | Q_D(QAreaSeries); | |
|
416 | series->d_ptr->setBlockOpenGL(true); | |||
414 | d->m_lowerSeries = series; |
|
417 | d->m_lowerSeries = series; | |
415 | } |
|
418 | } | |
416 |
|
419 | |||
@@ -624,7 +627,7 void QAreaSeriesPrivate::initializeDomain() | |||||
624 | QLineSeries *lowerSeries = q->lowerSeries(); |
|
627 | QLineSeries *lowerSeries = q->lowerSeries(); | |
625 |
|
628 | |||
626 | if (upperSeries) { |
|
629 | if (upperSeries) { | |
627 |
const Q |
|
630 | const QVector<QPointF> &points = upperSeries->pointsVector(); | |
628 |
|
631 | |||
629 | for (int i = 0; i < points.count(); i++) { |
|
632 | for (int i = 0; i < points.count(); i++) { | |
630 | qreal x = points[i].x(); |
|
633 | qreal x = points[i].x(); | |
@@ -636,8 +639,7 void QAreaSeriesPrivate::initializeDomain() | |||||
636 | } |
|
639 | } | |
637 | } |
|
640 | } | |
638 | if (lowerSeries) { |
|
641 | if (lowerSeries) { | |
639 |
|
642 | const QVector<QPointF> &points = lowerSeries->pointsVector(); | ||
640 | const QList<QPointF>& points = lowerSeries->points(); |
|
|||
641 |
|
643 | |||
642 | for (int i = 0; i < points.count(); i++) { |
|
644 | for (int i = 0; i < points.count(); i++) { | |
643 | qreal x = points[i].x(); |
|
645 | qreal x = points[i].x(); |
@@ -883,7 +883,7 bool QAbstractAxis::isVisible() const | |||||
883 | } |
|
883 | } | |
884 |
|
884 | |||
885 | /*! |
|
885 | /*! | |
886 |
Sets axis, shades, labels and grid lines |
|
886 | Sets axis, shades, labels and grid lines visibility to \a visible. | |
887 | */ |
|
887 | */ | |
888 | void QAbstractAxis::setVisible(bool visible) |
|
888 | void QAbstractAxis::setVisible(bool visible) | |
889 | { |
|
889 | { |
@@ -546,7 +546,7 QColor QBarSet::borderColor() | |||||
546 | } |
|
546 | } | |
547 |
|
547 | |||
548 | /*! |
|
548 | /*! | |
549 | Sets the color of pen for this bar set. |
|
549 | Sets the \a color of pen for this bar set. | |
550 | */ |
|
550 | */ | |
551 | void QBarSet::setBorderColor(QColor color) |
|
551 | void QBarSet::setBorderColor(QColor color) | |
552 | { |
|
552 | { | |
@@ -567,7 +567,7 QColor QBarSet::labelColor() | |||||
567 | } |
|
567 | } | |
568 |
|
568 | |||
569 | /*! |
|
569 | /*! | |
570 | Sets the color of labels for this bar set. |
|
570 | Sets the \a color of labels for this bar set. | |
571 | */ |
|
571 | */ | |
572 | void QBarSet::setLabelColor(QColor color) |
|
572 | void QBarSet::setLabelColor(QColor color) | |
573 | { |
|
573 | { |
@@ -38,6 +38,7 | |||||
38 | #include <private/xlogypolardomain_p.h> |
|
38 | #include <private/xlogypolardomain_p.h> | |
39 | #include <private/logxypolardomain_p.h> |
|
39 | #include <private/logxypolardomain_p.h> | |
40 | #include <private/logxlogypolardomain_p.h> |
|
40 | #include <private/logxlogypolardomain_p.h> | |
|
41 | #include <private/glxyseriesdata_p.h> | |||
41 |
|
42 | |||
42 | #ifndef QT_ON_ARM |
|
43 | #ifndef QT_ON_ARM | |
43 | #include <QtCharts/QDateTimeAxis> |
|
44 | #include <QtCharts/QDateTimeAxis> | |
@@ -47,7 +48,8 QT_CHARTS_BEGIN_NAMESPACE | |||||
47 |
|
48 | |||
48 | ChartDataSet::ChartDataSet(QChart *chart) |
|
49 | ChartDataSet::ChartDataSet(QChart *chart) | |
49 | : QObject(chart), |
|
50 | : QObject(chart), | |
50 | m_chart(chart) |
|
51 | m_chart(chart), | |
|
52 | m_glXYSeriesDataManager(new GLXYSeriesDataManager(this)) | |||
51 | { |
|
53 | { | |
52 |
|
54 | |||
53 | } |
|
55 | } | |
@@ -77,6 +79,8 void ChartDataSet::addSeries(QAbstractSeries *series) | |||||
77 | qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart."); |
|
79 | qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart."); | |
78 | return; |
|
80 | return; | |
79 | } |
|
81 | } | |
|
82 | // Disable OpenGL for series in polar charts | |||
|
83 | series->setUseOpenGL(false); | |||
80 | series->d_ptr->setDomain(new XYPolarDomain()); |
|
84 | series->d_ptr->setDomain(new XYPolarDomain()); | |
81 | // Set the correct domain for upper and lower series too |
|
85 | // Set the correct domain for upper and lower series too | |
82 | if (series->type() == QAbstractSeries::SeriesTypeArea) { |
|
86 | if (series->type() == QAbstractSeries::SeriesTypeArea) { | |
@@ -157,6 +161,10 void ChartDataSet::removeSeries(QAbstractSeries *series) | |||||
157 | series->d_ptr->setDomain(new XYDomain()); |
|
161 | series->d_ptr->setDomain(new XYDomain()); | |
158 | series->setParent(0); |
|
162 | series->setParent(0); | |
159 | series->d_ptr->m_chart = 0; |
|
163 | series->d_ptr->m_chart = 0; | |
|
164 | ||||
|
165 | QXYSeries *xySeries = qobject_cast<QXYSeries *>(series); | |||
|
166 | if (xySeries) | |||
|
167 | m_glXYSeriesDataManager->removeSeries(xySeries); | |||
160 | } |
|
168 | } | |
161 |
|
169 | |||
162 | /* |
|
170 | /* |
@@ -37,6 +37,7 QT_CHARTS_BEGIN_NAMESPACE | |||||
37 |
|
37 | |||
38 | class QAbstractAxis; |
|
38 | class QAbstractAxis; | |
39 | class ChartPresenter; |
|
39 | class ChartPresenter; | |
|
40 | class GLXYSeriesDataManager; | |||
40 |
|
41 | |||
41 | class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject |
|
42 | class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject | |
42 | { |
|
43 | { | |
@@ -67,6 +68,8 public: | |||||
67 | QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0); |
|
68 | QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0); | |
68 | QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0); |
|
69 | QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0); | |
69 |
|
70 | |||
|
71 | GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; } | |||
|
72 | ||||
70 | Q_SIGNALS: |
|
73 | Q_SIGNALS: | |
71 | void axisAdded(QAbstractAxis* axis); |
|
74 | void axisAdded(QAbstractAxis* axis); | |
72 | void axisRemoved(QAbstractAxis* axis); |
|
75 | void axisRemoved(QAbstractAxis* axis); | |
@@ -85,6 +88,7 private: | |||||
85 | QList<QAbstractSeries *> m_seriesList; |
|
88 | QList<QAbstractSeries *> m_seriesList; | |
86 | QList<QAbstractAxis *> m_axisList; |
|
89 | QList<QAbstractAxis *> m_axisList; | |
87 | QChart* m_chart; |
|
90 | QChart* m_chart; | |
|
91 | GLXYSeriesDataManager *m_glXYSeriesDataManager; | |||
88 | }; |
|
92 | }; | |
89 |
|
93 | |||
90 | QT_CHARTS_END_NAMESPACE |
|
94 | QT_CHARTS_END_NAMESPACE |
@@ -19,13 +19,15 | |||||
19 | #include <private/chartelement_p.h> |
|
19 | #include <private/chartelement_p.h> | |
20 | #include <private/chartpresenter_p.h> |
|
20 | #include <private/chartpresenter_p.h> | |
21 | #include <private/abstractdomain_p.h> |
|
21 | #include <private/abstractdomain_p.h> | |
|
22 | #include <private/chartdataset_p.h> | |||
22 |
|
23 | |||
23 | QT_CHARTS_BEGIN_NAMESPACE |
|
24 | QT_CHARTS_BEGIN_NAMESPACE | |
24 |
|
25 | |||
25 | ChartElement::ChartElement(QGraphicsItem* item): |
|
26 | ChartElement::ChartElement(QGraphicsItem* item): | |
26 | QGraphicsObject(item), |
|
27 | QGraphicsObject(item), | |
27 | m_presenter(0), |
|
28 | m_presenter(0), | |
28 | m_themeManager(0) |
|
29 | m_themeManager(0), | |
|
30 | m_dataSet(0) | |||
29 | { |
|
31 | { | |
30 |
|
32 | |||
31 | } |
|
33 | } | |
@@ -50,4 +52,14 ChartThemeManager* ChartElement::themeManager() const | |||||
50 | return m_themeManager; |
|
52 | return m_themeManager; | |
51 | } |
|
53 | } | |
52 |
|
54 | |||
|
55 | void ChartElement::setDataSet(ChartDataSet *dataSet) | |||
|
56 | { | |||
|
57 | m_dataSet = dataSet; | |||
|
58 | } | |||
|
59 | ||||
|
60 | ChartDataSet *ChartElement::dataSet() const | |||
|
61 | { | |||
|
62 | return m_dataSet; | |||
|
63 | } | |||
|
64 | ||||
53 | QT_CHARTS_END_NAMESPACE |
|
65 | QT_CHARTS_END_NAMESPACE |
@@ -40,6 +40,7 class ChartPresenter; | |||||
40 | class ChartAnimation; |
|
40 | class ChartAnimation; | |
41 | class ChartThemeManager; |
|
41 | class ChartThemeManager; | |
42 | class AbstractDomain; |
|
42 | class AbstractDomain; | |
|
43 | class ChartDataSet; | |||
43 |
|
44 | |||
44 | class ChartElement: public QGraphicsObject |
|
45 | class ChartElement: public QGraphicsObject | |
45 | { |
|
46 | { | |
@@ -52,10 +53,13 public: | |||||
52 | ChartPresenter *presenter() const; |
|
53 | ChartPresenter *presenter() const; | |
53 | virtual void setThemeManager(ChartThemeManager *manager); |
|
54 | virtual void setThemeManager(ChartThemeManager *manager); | |
54 | ChartThemeManager* themeManager() const; |
|
55 | ChartThemeManager* themeManager() const; | |
|
56 | virtual void setDataSet(ChartDataSet *dataSet); | |||
|
57 | ChartDataSet *dataSet() const; | |||
55 |
|
58 | |||
56 | private: |
|
59 | private: | |
57 | ChartPresenter *m_presenter; |
|
60 | ChartPresenter *m_presenter; | |
58 | ChartThemeManager *m_themeManager; |
|
61 | ChartThemeManager *m_themeManager; | |
|
62 | ChartDataSet *m_dataSet; | |||
59 | }; |
|
63 | }; | |
60 |
|
64 | |||
61 | QT_CHARTS_END_NAMESPACE |
|
65 | QT_CHARTS_END_NAMESPACE |
@@ -46,6 +46,10 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) | |||||
46 | m_plotAreaBackground(0), |
|
46 | m_plotAreaBackground(0), | |
47 | m_title(0), |
|
47 | m_title(0), | |
48 | m_localizeNumbers(false) |
|
48 | m_localizeNumbers(false) | |
|
49 | #ifndef QT_NO_OPENGL | |||
|
50 | , m_glWidget(0) | |||
|
51 | , m_glUseWidget(true) | |||
|
52 | #endif | |||
49 | { |
|
53 | { | |
50 | if (type == QChart::ChartTypeCartesian) |
|
54 | if (type == QChart::ChartTypeCartesian) | |
51 | m_layout = new CartesianChartLayout(this); |
|
55 | m_layout = new CartesianChartLayout(this); | |
@@ -56,7 +60,9 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) | |||||
56 |
|
60 | |||
57 | ChartPresenter::~ChartPresenter() |
|
61 | ChartPresenter::~ChartPresenter() | |
58 | { |
|
62 | { | |
59 |
|
63 | #ifndef QT_NO_OPENGL | ||
|
64 | delete m_glWidget.data(); | |||
|
65 | #endif | |||
60 | } |
|
66 | } | |
61 |
|
67 | |||
62 | void ChartPresenter::setGeometry(const QRectF rect) |
|
68 | void ChartPresenter::setGeometry(const QRectF rect) | |
@@ -67,6 +73,10 void ChartPresenter::setGeometry(const QRectF rect) | |||||
67 | chart->domain()->setSize(rect.size()); |
|
73 | chart->domain()->setSize(rect.size()); | |
68 | chart->setPos(rect.topLeft()); |
|
74 | chart->setPos(rect.topLeft()); | |
69 | } |
|
75 | } | |
|
76 | #ifndef QT_NO_OPENGL | |||
|
77 | if (!m_glWidget.isNull()) | |||
|
78 | m_glWidget->setGeometry(m_rect.toRect()); | |||
|
79 | #endif | |||
70 | emit plotAreaChanged(m_rect); |
|
80 | emit plotAreaChanged(m_rect); | |
71 | } |
|
81 | } | |
72 | } |
|
82 | } | |
@@ -108,6 +118,7 void ChartPresenter::handleSeriesAdded(QAbstractSeries *series) | |||||
108 | ChartItem *chart = series->d_ptr->chartItem(); |
|
118 | ChartItem *chart = series->d_ptr->chartItem(); | |
109 | chart->setPresenter(this); |
|
119 | chart->setPresenter(this); | |
110 | chart->setThemeManager(m_chart->d_ptr->m_themeManager); |
|
120 | chart->setThemeManager(m_chart->d_ptr->m_themeManager); | |
|
121 | chart->setDataSet(m_chart->d_ptr->m_dataset); | |||
111 | chart->domain()->setSize(m_rect.size()); |
|
122 | chart->domain()->setSize(m_rect.size()); | |
112 | chart->setPos(m_rect.topLeft()); |
|
123 | chart->setPos(m_rect.topLeft()); | |
113 | chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored |
|
124 | chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored | |
@@ -531,6 +542,28 QString ChartPresenter::numberToString(int value) | |||||
531 | return QString::number(value); |
|
542 | return QString::number(value); | |
532 | } |
|
543 | } | |
533 |
|
544 | |||
|
545 | void ChartPresenter::ensureGLWidget() | |||
|
546 | { | |||
|
547 | #ifndef QT_NO_OPENGL | |||
|
548 | // GLWidget pointer is wrapped in QPointer as its parent is not in our control, and therefore | |||
|
549 | // can potentially get deleted unexpectedly. | |||
|
550 | if (m_glWidget.isNull() && m_glUseWidget && m_chart->scene()) { | |||
|
551 | QObject *parent = m_chart->scene()->parent(); | |||
|
552 | while (parent) { | |||
|
553 | QWidget *parentWidget = qobject_cast<QWidget *>(parent); | |||
|
554 | if (parentWidget) { | |||
|
555 | m_glWidget = new GLWidget(m_chart->d_ptr->m_dataset->glXYSeriesDataManager(), | |||
|
556 | parentWidget); | |||
|
557 | m_glWidget->setGeometry(m_rect.toRect()); | |||
|
558 | m_glWidget->show(); | |||
|
559 | break; | |||
|
560 | } | |||
|
561 | parent = parent->parent(); | |||
|
562 | } | |||
|
563 | } | |||
|
564 | #endif | |||
|
565 | } | |||
|
566 | ||||
534 | #include "moc_chartpresenter_p.cpp" |
|
567 | #include "moc_chartpresenter_p.cpp" | |
535 |
|
568 | |||
536 | QT_CHARTS_END_NAMESPACE |
|
569 | QT_CHARTS_END_NAMESPACE |
@@ -30,9 +30,11 | |||||
30 |
|
30 | |||
31 | #include <QtCharts/QChartGlobal> |
|
31 | #include <QtCharts/QChartGlobal> | |
32 | #include <QtCharts/QChart> //because of QChart::ChartThemeId |
|
32 | #include <QtCharts/QChart> //because of QChart::ChartThemeId | |
|
33 | #include <private/glwidget_p.h> | |||
33 | #include <QtCore/QRectF> |
|
34 | #include <QtCore/QRectF> | |
34 | #include <QtCore/QMargins> |
|
35 | #include <QtCore/QMargins> | |
35 | #include <QtCore/QLocale> |
|
36 | #include <QtCore/QLocale> | |
|
37 | #include <QtCore/QPointer> | |||
36 |
|
38 | |||
37 | QT_CHARTS_BEGIN_NAMESPACE |
|
39 | QT_CHARTS_BEGIN_NAMESPACE | |
38 |
|
40 | |||
@@ -160,6 +162,9 public: | |||||
160 | QString numberToString(double value, char f = 'g', int prec = 6); |
|
162 | QString numberToString(double value, char f = 'g', int prec = 6); | |
161 | QString numberToString(int value); |
|
163 | QString numberToString(int value); | |
162 |
|
164 | |||
|
165 | void ensureGLWidget(); | |||
|
166 | void glSetUseWidget(bool enable) { m_glUseWidget = enable; } | |||
|
167 | ||||
163 | private: |
|
168 | private: | |
164 | void createBackgroundItem(); |
|
169 | void createBackgroundItem(); | |
165 | void createPlotAreaBackgroundItem(); |
|
170 | void createPlotAreaBackgroundItem(); | |
@@ -192,6 +197,10 private: | |||||
192 | QRectF m_rect; |
|
197 | QRectF m_rect; | |
193 | bool m_localizeNumbers; |
|
198 | bool m_localizeNumbers; | |
194 | QLocale m_locale; |
|
199 | QLocale m_locale; | |
|
200 | #ifndef QT_NO_OPENGL | |||
|
201 | QPointer<GLWidget> m_glWidget; | |||
|
202 | #endif | |||
|
203 | bool m_glUseWidget; | |||
195 | }; |
|
204 | }; | |
196 |
|
205 | |||
197 | QT_CHARTS_END_NAMESPACE |
|
206 | QT_CHARTS_END_NAMESPACE |
@@ -32,6 +32,9 SOURCES += \ | |||||
32 | $$PWD/scroller.cpp \ |
|
32 | $$PWD/scroller.cpp \ | |
33 | $$PWD/charttitle.cpp \ |
|
33 | $$PWD/charttitle.cpp \ | |
34 | $$PWD/qpolarchart.cpp |
|
34 | $$PWD/qpolarchart.cpp | |
|
35 | ||||
|
36 | contains(QT_CONFIG, opengl): SOURCES += $$PWD/glwidget.cpp | |||
|
37 | ||||
35 | PRIVATE_HEADERS += \ |
|
38 | PRIVATE_HEADERS += \ | |
36 | $$PWD/chartdataset_p.h \ |
|
39 | $$PWD/chartdataset_p.h \ | |
37 | $$PWD/chartitem_p.h \ |
|
40 | $$PWD/chartitem_p.h \ | |
@@ -46,6 +49,9 PRIVATE_HEADERS += \ | |||||
46 | $$PWD/qabstractseries_p.h \ |
|
49 | $$PWD/qabstractseries_p.h \ | |
47 | $$PWD/charttitle_p.h \ |
|
50 | $$PWD/charttitle_p.h \ | |
48 | $$PWD/charthelpers_p.h |
|
51 | $$PWD/charthelpers_p.h | |
|
52 | ||||
|
53 | contains(QT_CONFIG, opengl): PRIVATE_HEADERS += $$PWD/glwidget_p.h | |||
|
54 | ||||
49 | PUBLIC_HEADERS += \ |
|
55 | PUBLIC_HEADERS += \ | |
50 | $$PWD/qchart.h \ |
|
56 | $$PWD/qchart.h \ | |
51 | $$PWD/qchartglobal.h \ |
|
57 | $$PWD/qchartglobal.h \ |
@@ -92,7 +92,7 public: | |||||
92 |
|
92 | |||
93 | virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0; |
|
93 | virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0; | |
94 | virtual QPointF calculateDomainPoint(const QPointF &point) const = 0; |
|
94 | virtual QPointF calculateDomainPoint(const QPointF &point) const = 0; | |
95 |
virtual QVector<QPointF> calculateGeometryPoints(const Q |
|
95 | virtual QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const = 0; | |
96 |
|
96 | |||
97 | virtual bool attachAxis(QAbstractAxis *axis); |
|
97 | virtual bool attachAxis(QAbstractAxis *axis); | |
98 | virtual bool detachAxis(QAbstractAxis *axis); |
|
98 | virtual bool detachAxis(QAbstractAxis *axis); |
@@ -162,7 +162,7 QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c | |||||
162 | return QPointF(x, y); |
|
162 | return QPointF(x, y); | |
163 | } |
|
163 | } | |
164 |
|
164 | |||
165 |
QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const Q |
|
165 | QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const | |
166 | { |
|
166 | { | |
167 | const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX); |
|
167 | const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX); | |
168 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
|
168 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
@@ -54,7 +54,7 public: | |||||
54 |
|
54 | |||
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; | |
56 | QPointF calculateDomainPoint(const QPointF &point) const; |
|
56 | QPointF calculateDomainPoint(const QPointF &point) const; | |
57 |
QVector<QPointF> calculateGeometryPoints(const Q |
|
57 | QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const; | |
58 |
|
58 | |||
59 | bool attachAxis(QAbstractAxis *axis); |
|
59 | bool attachAxis(QAbstractAxis *axis); | |
60 | bool detachAxis(QAbstractAxis *axis); |
|
60 | bool detachAxis(QAbstractAxis *axis); |
@@ -146,7 +146,7 QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons | |||||
146 | return QPointF(x, y); |
|
146 | return QPointF(x, y); | |
147 | } |
|
147 | } | |
148 |
|
148 | |||
149 |
QVector<QPointF> LogXYDomain::calculateGeometryPoints(const Q |
|
149 | QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const | |
150 | { |
|
150 | { | |
151 | const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX); |
|
151 | const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX); | |
152 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
|
152 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
@@ -54,7 +54,7 public: | |||||
54 |
|
54 | |||
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; | |
56 | QPointF calculateDomainPoint(const QPointF &point) const; |
|
56 | QPointF calculateDomainPoint(const QPointF &point) const; | |
57 |
QVector<QPointF> calculateGeometryPoints(const Q |
|
57 | QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const; | |
58 |
|
58 | |||
59 | bool attachAxis(QAbstractAxis *axis); |
|
59 | bool attachAxis(QAbstractAxis *axis); | |
60 | bool detachAxis(QAbstractAxis *axis); |
|
60 | bool detachAxis(QAbstractAxis *axis); |
@@ -53,7 +53,7 QPointF PolarDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons | |||||
53 | } |
|
53 | } | |
54 | } |
|
54 | } | |
55 |
|
55 | |||
56 |
QVector<QPointF> PolarDomain::calculateGeometryPoints(const Q |
|
56 | QVector<QPointF> PolarDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const | |
57 | { |
|
57 | { | |
58 | QVector<QPointF> result; |
|
58 | QVector<QPointF> result; | |
59 | result.resize(vector.count()); |
|
59 | result.resize(vector.count()); |
@@ -43,7 +43,7 public: | |||||
43 | void setSize(const QSizeF &size); |
|
43 | void setSize(const QSizeF &size); | |
44 |
|
44 | |||
45 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
45 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; | |
46 |
QVector<QPointF> calculateGeometryPoints(const Q |
|
46 | QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const; | |
47 |
|
47 | |||
48 | virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0; |
|
48 | virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0; | |
49 | virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0; |
|
49 | virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0; |
@@ -146,7 +146,7 QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons | |||||
146 | return QPointF(x, y); |
|
146 | return QPointF(x, y); | |
147 | } |
|
147 | } | |
148 |
|
148 | |||
149 |
QVector<QPointF> XLogYDomain::calculateGeometryPoints(const Q |
|
149 | QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const | |
150 | { |
|
150 | { | |
151 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); |
|
151 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); | |
152 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
|
152 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
@@ -54,7 +54,7 public: | |||||
54 |
|
54 | |||
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; | |
56 | QPointF calculateDomainPoint(const QPointF &point) const; |
|
56 | QPointF calculateDomainPoint(const QPointF &point) const; | |
57 |
QVector<QPointF> calculateGeometryPoints(const Q |
|
57 | QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const; | |
58 |
|
58 | |||
59 | bool attachAxis(QAbstractAxis *axis); |
|
59 | bool attachAxis(QAbstractAxis *axis); | |
60 | bool detachAxis(QAbstractAxis *axis); |
|
60 | bool detachAxis(QAbstractAxis *axis); |
@@ -144,7 +144,7 QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const | |||||
144 | return QPointF(x, y); |
|
144 | return QPointF(x, y); | |
145 | } |
|
145 | } | |
146 |
|
146 | |||
147 |
QVector<QPointF> XYDomain::calculateGeometryPoints(const Q |
|
147 | QVector<QPointF> XYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const | |
148 | { |
|
148 | { | |
149 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); |
|
149 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); | |
150 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
|
150 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
@@ -54,7 +54,7 public: | |||||
54 |
|
54 | |||
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; | |
56 | QPointF calculateDomainPoint(const QPointF &point) const; |
|
56 | QPointF calculateDomainPoint(const QPointF &point) const; | |
57 |
QVector<QPointF> calculateGeometryPoints(const Q |
|
57 | QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const; | |
58 | }; |
|
58 | }; | |
59 |
|
59 | |||
60 | QT_CHARTS_END_NAMESPACE |
|
60 | QT_CHARTS_END_NAMESPACE |
@@ -70,6 +70,17 QPainterPath LineChartItem::shape() const | |||||
70 |
|
70 | |||
71 | void LineChartItem::updateGeometry() |
|
71 | void LineChartItem::updateGeometry() | |
72 | { |
|
72 | { | |
|
73 | static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001); | |||
|
74 | if (m_series->useOpenGL()) { | |||
|
75 | // Fake a miniscule region, so we trigger changed signal. | |||
|
76 | if (m_rect.width() != dummyRect.width()) { | |||
|
77 | prepareGeometryChange(); | |||
|
78 | m_rect = dummyRect; | |||
|
79 | } | |||
|
80 | update(); | |||
|
81 | return; | |||
|
82 | } | |||
|
83 | ||||
73 | // Store the points to a local variable so that the old line gets properly cleared |
|
84 | // Store the points to a local variable so that the old line gets properly cleared | |
74 | // when animation starts. |
|
85 | // when animation starts. | |
75 | m_linePoints = geometryPoints(); |
|
86 | m_linePoints = geometryPoints(); | |
@@ -345,6 +356,9 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt | |||||
345 | Q_UNUSED(widget) |
|
356 | Q_UNUSED(widget) | |
346 | Q_UNUSED(option) |
|
357 | Q_UNUSED(option) | |
347 |
|
358 | |||
|
359 | if (m_series->useOpenGL()) | |||
|
360 | return; | |||
|
361 | ||||
348 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); |
|
362 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); | |
349 |
|
363 | |||
350 | painter->save(); |
|
364 | painter->save(); |
@@ -135,6 +135,69 QT_CHARTS_BEGIN_NAMESPACE | |||||
135 | */ |
|
135 | */ | |
136 |
|
136 | |||
137 | /*! |
|
137 | /*! | |
|
138 | \property QAbstractSeries::useOpenGL | |||
|
139 | \brief Specifies whether or not the series drawing is accelerated with OpenGL. | |||
|
140 | ||||
|
141 | Drawing series with OpenGL is supported only for QLineSeries and QScatterSeries. | |||
|
142 | Line series used as edge series for a QAreaSeries cannot use OpenGL acceleration. | |||
|
143 | When a chart contains any series that are drawn with OpenGL, a transparent QOpenGLWidget | |||
|
144 | is created on top of the chart plot area. Specified series are not drawn on the underlying | |||
|
145 | QGraphicsView, but are instead drawn on the created QOpenGLWidget. | |||
|
146 | ||||
|
147 | Performance gained from using OpenGL to accelerate series drawing depends on the underlying | |||
|
148 | hardware, but in most cases it is significant. For example, on a standard desktop computer, | |||
|
149 | enabling OpenGL acceleration for a series typically allows rendering at least hundred times | |||
|
150 | more points without reduction on the frame rate. | |||
|
151 | Chart size also has less effect on the frame rate. | |||
|
152 | ||||
|
153 | The OpenGL acceleration of series drawing is meant for use cases that need fast drawing of | |||
|
154 | large numbers of points. It is optimized for efficiency, and therefore the series using | |||
|
155 | it lack support for some features available to non-accelerated series. | |||
|
156 | ||||
|
157 | There are the following restrictions imposed on charts and series when using OpenGL | |||
|
158 | acceleration: | |||
|
159 | ||||
|
160 | \list | |||
|
161 | \li Series animations are not supported for accelerated series. | |||
|
162 | \li Antialiasing is not supported for accelerated series. | |||
|
163 | \li Pen styles and marker shapes are ignored for accelerated series. | |||
|
164 | Only solid lines and plain scatter dots are supported. | |||
|
165 | The scatter dots may be circular or rectangular, depending on the underlying graphics | |||
|
166 | hardware and drivers. | |||
|
167 | \li Polar charts are not supported for accelerated series. | |||
|
168 | \li Since the accelerated series are drawn on top of the entire graphics view, they get drawn | |||
|
169 | on top of any other graphics items that you may have on top chart in the same scene. | |||
|
170 | \li To enable QOpenGLWidget to be partially transparent, it needs to be stacked on top of | |||
|
171 | all other widgets. This means you cannot have other widgets partially covering the | |||
|
172 | chart. | |||
|
173 | \endlist | |||
|
174 | ||||
|
175 | The default value is \c{false}. | |||
|
176 | */ | |||
|
177 | /*! | |||
|
178 | \qmlproperty bool AbstractSeries::useOpenGL | |||
|
179 | Specifies whether or not the series is drawn with OpenGL. | |||
|
180 | ||||
|
181 | Drawing series with OpenGL is supported only for LineSeries and ScatterSeries. | |||
|
182 | ||||
|
183 | For more details, see QAbstractSeries::useOpenGL documentation. QML applications have similar | |||
|
184 | restrictions as those listed in QAbstractSeries::useOpenGL documentation, | |||
|
185 | except there is no restriction about covering the ChartView partially with other | |||
|
186 | items due to different rendering mechanism. | |||
|
187 | ||||
|
188 | The default value is \c{false}. | |||
|
189 | */ | |||
|
190 | ||||
|
191 | /*! | |||
|
192 | \fn void QAbstractSeries::useOpenGLChanged() | |||
|
193 | Emitted when the useOpenGL property value changes. | |||
|
194 | */ | |||
|
195 | /*! | |||
|
196 | \qmlsignal AbstractSeries::onUseOpenGLChanged() | |||
|
197 | Emitted when the useOpenGL property value changes. | |||
|
198 | */ | |||
|
199 | ||||
|
200 | /*! | |||
138 | \internal |
|
201 | \internal | |
139 | \brief Constructs QAbstractSeries object with \a parent. |
|
202 | \brief Constructs QAbstractSeries object with \a parent. | |
140 | */ |
|
203 | */ | |
@@ -192,6 +255,28 void QAbstractSeries::setOpacity(qreal opacity) | |||||
192 | } |
|
255 | } | |
193 | } |
|
256 | } | |
194 |
|
257 | |||
|
258 | void QAbstractSeries::setUseOpenGL(bool enable) | |||
|
259 | { | |||
|
260 | #ifdef QT_NO_OPENGL | |||
|
261 | Q_UNUSED(enable) | |||
|
262 | #else | |||
|
263 | bool polarChart = d_ptr->m_chart && d_ptr->m_chart->chartType() == QChart::ChartTypePolar; | |||
|
264 | bool supportedSeries = (type() == SeriesTypeLine || type() == SeriesTypeScatter); | |||
|
265 | if ((!enable || !d_ptr->m_blockOpenGL) | |||
|
266 | && supportedSeries | |||
|
267 | && enable != d_ptr->m_useOpenGL | |||
|
268 | && (!enable || !polarChart)) { | |||
|
269 | d_ptr->m_useOpenGL = enable; | |||
|
270 | emit useOpenGLChanged(); | |||
|
271 | } | |||
|
272 | #endif | |||
|
273 | } | |||
|
274 | ||||
|
275 | bool QAbstractSeries::useOpenGL() const | |||
|
276 | { | |||
|
277 | return d_ptr->m_useOpenGL; | |||
|
278 | } | |||
|
279 | ||||
195 | /*! |
|
280 | /*! | |
196 | \brief Returns the chart where series belongs to. |
|
281 | \brief Returns the chart where series belongs to. | |
197 |
|
282 | |||
@@ -274,7 +359,9 QAbstractSeriesPrivate::QAbstractSeriesPrivate(QAbstractSeries *q) | |||||
274 | m_item(0), |
|
359 | m_item(0), | |
275 | m_domain(new XYDomain()), |
|
360 | m_domain(new XYDomain()), | |
276 | m_visible(true), |
|
361 | m_visible(true), | |
277 | m_opacity(1.0) |
|
362 | m_opacity(1.0), | |
|
363 | m_useOpenGL(false), | |||
|
364 | m_blockOpenGL(false) | |||
278 | { |
|
365 | { | |
279 | } |
|
366 | } | |
280 |
|
367 | |||
@@ -354,6 +441,15 bool QAbstractSeriesPrivate::reverseYAxis() | |||||
354 | return reverseYAxis; |
|
441 | return reverseYAxis; | |
355 | } |
|
442 | } | |
356 |
|
443 | |||
|
444 | // This function can be used to explicitly block OpenGL use from some otherwise supported series, | |||
|
445 | // such as the line series used as edge series of an area series. | |||
|
446 | void QAbstractSeriesPrivate::setBlockOpenGL(bool enable) | |||
|
447 | { | |||
|
448 | m_blockOpenGL = enable; | |||
|
449 | if (enable) | |||
|
450 | q_ptr->setUseOpenGL(false); | |||
|
451 | } | |||
|
452 | ||||
357 | #include "moc_qabstractseries.cpp" |
|
453 | #include "moc_qabstractseries.cpp" | |
358 | #include "moc_qabstractseries_p.cpp" |
|
454 | #include "moc_qabstractseries_p.cpp" | |
359 |
|
455 |
@@ -36,6 +36,7 class QT_CHARTS_EXPORT QAbstractSeries : public QObject | |||||
36 | Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) |
|
36 | Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) | |
37 | Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) |
|
37 | Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) | |
38 | Q_PROPERTY(SeriesType type READ type) |
|
38 | Q_PROPERTY(SeriesType type READ type) | |
|
39 | Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged) | |||
39 | Q_ENUMS(SeriesType) |
|
40 | Q_ENUMS(SeriesType) | |
40 |
|
41 | |||
41 | public: |
|
42 | public: | |
@@ -67,6 +68,8 public: | |||||
67 | bool isVisible() const; |
|
68 | bool isVisible() const; | |
68 | qreal opacity() const; |
|
69 | qreal opacity() const; | |
69 | void setOpacity(qreal opacity); |
|
70 | void setOpacity(qreal opacity); | |
|
71 | void setUseOpenGL(bool enable = true); | |||
|
72 | bool useOpenGL() const; | |||
70 |
|
73 | |||
71 | QChart *chart() const; |
|
74 | QChart *chart() const; | |
72 |
|
75 | |||
@@ -81,6 +84,7 Q_SIGNALS: | |||||
81 | void nameChanged(); |
|
84 | void nameChanged(); | |
82 | void visibleChanged(); |
|
85 | void visibleChanged(); | |
83 | void opacityChanged(); |
|
86 | void opacityChanged(); | |
|
87 | void useOpenGLChanged(); | |||
84 |
|
88 | |||
85 | protected: |
|
89 | protected: | |
86 | QScopedPointer<QAbstractSeriesPrivate> d_ptr; |
|
90 | QScopedPointer<QAbstractSeriesPrivate> d_ptr; | |
@@ -89,6 +93,7 protected: | |||||
89 | friend class ChartThemeManager; |
|
93 | friend class ChartThemeManager; | |
90 | friend class QLegendPrivate; |
|
94 | friend class QLegendPrivate; | |
91 | friend class DeclarativeChart; |
|
95 | friend class DeclarativeChart; | |
|
96 | friend class QAreaSeries; | |||
92 | }; |
|
97 | }; | |
93 |
|
98 | |||
94 | QT_CHARTS_END_NAMESPACE |
|
99 | QT_CHARTS_END_NAMESPACE |
@@ -81,6 +81,8 public: | |||||
81 | bool reverseXAxis(); |
|
81 | bool reverseXAxis(); | |
82 | bool reverseYAxis(); |
|
82 | bool reverseYAxis(); | |
83 |
|
83 | |||
|
84 | void setBlockOpenGL(bool enable); | |||
|
85 | ||||
84 | Q_SIGNALS: |
|
86 | Q_SIGNALS: | |
85 | void countChanged(); |
|
87 | void countChanged(); | |
86 |
|
88 | |||
@@ -96,6 +98,8 private: | |||||
96 | bool m_visible; |
|
98 | bool m_visible; | |
97 | qreal m_opacity; |
|
99 | qreal m_opacity; | |
98 | ChartPresenter *m_presenter; |
|
100 | ChartPresenter *m_presenter; | |
|
101 | bool m_useOpenGL; | |||
|
102 | bool m_blockOpenGL; | |||
99 |
|
103 | |||
100 | friend class QAbstractSeries; |
|
104 | friend class QAbstractSeries; | |
101 | friend class ChartDataSet; |
|
105 | friend class ChartDataSet; |
@@ -130,6 +130,18 void ScatterChartItem::markerDoubleClicked(QGraphicsItem *marker) | |||||
130 |
|
130 | |||
131 | void ScatterChartItem::updateGeometry() |
|
131 | void ScatterChartItem::updateGeometry() | |
132 | { |
|
132 | { | |
|
133 | static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001); | |||
|
134 | if (m_series->useOpenGL()) { | |||
|
135 | if (m_items.childItems().count()) | |||
|
136 | deletePoints(m_items.childItems().count()); | |||
|
137 | // Fake a miniscule region, so we trigger changed signal. | |||
|
138 | if (m_rect.width() != dummyRect.width()) { | |||
|
139 | prepareGeometryChange(); | |||
|
140 | m_rect = dummyRect; | |||
|
141 | } | |||
|
142 | update(); | |||
|
143 | return; | |||
|
144 | } | |||
133 |
|
145 | |||
134 | const QVector<QPointF>& points = geometryPoints(); |
|
146 | const QVector<QPointF>& points = geometryPoints(); | |
135 |
|
147 | |||
@@ -199,6 +211,9 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * | |||||
199 | Q_UNUSED(option) |
|
211 | Q_UNUSED(option) | |
200 | Q_UNUSED(widget) |
|
212 | Q_UNUSED(widget) | |
201 |
|
213 | |||
|
214 | if (m_series->useOpenGL()) | |||
|
215 | return; | |||
|
216 | ||||
202 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); |
|
217 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); | |
203 |
|
218 | |||
204 | painter->save(); |
|
219 | painter->save(); |
@@ -238,7 +238,7 QT_CHARTS_BEGIN_NAMESPACE | |||||
238 | \sa pointLabelsVisible |
|
238 | \sa pointLabelsVisible | |
239 | */ |
|
239 | */ | |
240 | /*! |
|
240 | /*! | |
241 |
\fn void QXYSeries::pointLabelsClippin |
|
241 | \fn void QXYSeries::pointLabelsClippingChanged(bool clipping) | |
242 | The clipping of the data point labels is changed to \a clipping. |
|
242 | The clipping of the data point labels is changed to \a clipping. | |
243 | */ |
|
243 | */ | |
244 | /*! |
|
244 | /*! | |
@@ -398,6 +398,11 QT_CHARTS_BEGIN_NAMESPACE | |||||
398 | */ |
|
398 | */ | |
399 |
|
399 | |||
400 | /*! |
|
400 | /*! | |
|
401 | \fn void QXYSeries::penChanged(const QPen &pen) | |||
|
402 | \brief Signal is emitted when the line pen has changed to \a pen. | |||
|
403 | */ | |||
|
404 | ||||
|
405 | /*! | |||
401 | \fn void QXYSeriesPrivate::updated() |
|
406 | \fn void QXYSeriesPrivate::updated() | |
402 | \brief \internal |
|
407 | \brief \internal | |
403 | */ |
|
408 | */ | |
@@ -539,7 +544,7 void QXYSeries::replace(int index, const QPointF &newPoint) | |||||
539 | \note This is much faster than replacing data points one by one, |
|
544 | \note This is much faster than replacing data points one by one, | |
540 | or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() |
|
545 | or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() | |
541 | when the points have been replaced. However, note that using the overload that takes |
|
546 | when the points have been replaced. However, note that using the overload that takes | |
542 |
\c{QVector<QPointF>} as parameter is |
|
547 | \c{QVector<QPointF>} as parameter is faster than using this overload. | |
543 | \sa pointsReplaced() |
|
548 | \sa pointsReplaced() | |
544 | */ |
|
549 | */ | |
545 | void QXYSeries::replace(QList<QPointF> points) |
|
550 | void QXYSeries::replace(QList<QPointF> points) | |
@@ -634,7 +639,8 void QXYSeries::clear() | |||||
634 | } |
|
639 | } | |
635 |
|
640 | |||
636 | /*! |
|
641 | /*! | |
637 |
Returns |
|
642 | Returns the points in the series as a list. | |
|
643 | Use QXYSeries::pointsVector() for better performance. | |||
638 | */ |
|
644 | */ | |
639 | QList<QPointF> QXYSeries::points() const |
|
645 | QList<QPointF> QXYSeries::points() const | |
640 | { |
|
646 | { | |
@@ -643,6 +649,16 QList<QPointF> QXYSeries::points() const | |||||
643 | } |
|
649 | } | |
644 |
|
650 | |||
645 | /*! |
|
651 | /*! | |
|
652 | Returns the points in the series as a vector. | |||
|
653 | This is more efficient that calling QXYSeries::points(); | |||
|
654 | */ | |||
|
655 | QVector<QPointF> QXYSeries::pointsVector() const | |||
|
656 | { | |||
|
657 | Q_D(const QXYSeries); | |||
|
658 | return d->m_points; | |||
|
659 | } | |||
|
660 | ||||
|
661 | /*! | |||
646 | Returns point at \a index in internal points vector. |
|
662 | Returns point at \a index in internal points vector. | |
647 | */ |
|
663 | */ | |
648 | const QPointF &QXYSeries::at(int index) const |
|
664 | const QPointF &QXYSeries::at(int index) const | |
@@ -675,6 +691,7 void QXYSeries::setPen(const QPen &pen) | |||||
675 | emit d->updated(); |
|
691 | emit d->updated(); | |
676 | if (emitColorChanged) |
|
692 | if (emitColorChanged) | |
677 | emit colorChanged(pen.color()); |
|
693 | emit colorChanged(pen.color()); | |
|
694 | emit penChanged(pen); | |||
678 | } |
|
695 | } | |
679 | } |
|
696 | } | |
680 |
|
697 | |||
@@ -864,7 +881,7 void QXYSeriesPrivate::initializeDomain() | |||||
864 |
|
881 | |||
865 | Q_Q(QXYSeries); |
|
882 | Q_Q(QXYSeries); | |
866 |
|
883 | |||
867 |
const Q |
|
884 | const QVector<QPointF> &points = q->pointsVector(); | |
868 |
|
885 | |||
869 | if (!points.isEmpty()) { |
|
886 | if (!points.isEmpty()) { | |
870 | minX = points[0].x(); |
|
887 | minX = points[0].x(); |
@@ -65,6 +65,7 public: | |||||
65 |
|
65 | |||
66 | int count() const; |
|
66 | int count() const; | |
67 | QList<QPointF> points() const; |
|
67 | QList<QPointF> points() const; | |
|
68 | QVector<QPointF> pointsVector() const; | |||
68 | const QPointF &at(int index) const; |
|
69 | const QPointF &at(int index) const; | |
69 |
|
70 | |||
70 | QXYSeries &operator << (const QPointF &point); |
|
71 | QXYSeries &operator << (const QPointF &point); | |
@@ -117,6 +118,7 Q_SIGNALS: | |||||
117 | void pointLabelsColorChanged(const QColor &color); |
|
118 | void pointLabelsColorChanged(const QColor &color); | |
118 | void pointLabelsClippingChanged(bool clipping); |
|
119 | void pointLabelsClippingChanged(bool clipping); | |
119 | void pointsRemoved(int index, int count); |
|
120 | void pointsRemoved(int index, int count); | |
|
121 | void penChanged(const QPen &pen); | |||
120 |
|
122 | |||
121 | private: |
|
123 | private: | |
122 | Q_DECLARE_PRIVATE(QXYSeries) |
|
124 | Q_DECLARE_PRIVATE(QXYSeries) |
@@ -21,6 +21,8 | |||||
21 | #include <private/qxyseries_p.h> |
|
21 | #include <private/qxyseries_p.h> | |
22 | #include <private/chartpresenter_p.h> |
|
22 | #include <private/chartpresenter_p.h> | |
23 | #include <private/abstractdomain_p.h> |
|
23 | #include <private/abstractdomain_p.h> | |
|
24 | #include <private/chartdataset_p.h> | |||
|
25 | #include <private/glxyseriesdata_p.h> | |||
24 | #include <QtCharts/QXYModelMapper> |
|
26 | #include <QtCharts/QXYModelMapper> | |
25 | #include <private/qabstractaxis_p.h> |
|
27 | #include <private/qabstractaxis_p.h> | |
26 | #include <QtGui/QPainter> |
|
28 | #include <QtGui/QPainter> | |
@@ -106,6 +108,13 void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoin | |||||
106 | } |
|
108 | } | |
107 | } |
|
109 | } | |
108 |
|
110 | |||
|
111 | void XYChart::updateGlChart() | |||
|
112 | { | |||
|
113 | presenter()->ensureGLWidget(); | |||
|
114 | dataSet()->glXYSeriesDataManager()->setPoints(m_series, domain()); | |||
|
115 | updateGeometry(); | |||
|
116 | } | |||
|
117 | ||||
109 | //handlers |
|
118 | //handlers | |
110 |
|
119 | |||
111 | void XYChart::handlePointAdded(int index) |
|
120 | void XYChart::handlePointAdded(int index) | |
@@ -113,90 +122,108 void XYChart::handlePointAdded(int index) | |||||
113 | Q_ASSERT(index < m_series->count()); |
|
122 | Q_ASSERT(index < m_series->count()); | |
114 | Q_ASSERT(index >= 0); |
|
123 | Q_ASSERT(index >= 0); | |
115 |
|
124 | |||
|
125 | if (m_series->useOpenGL()) { | |||
|
126 | updateGlChart(); | |||
|
127 | } else { | |||
116 | QVector<QPointF> points; |
|
128 | QVector<QPointF> points; | |
117 |
|
||||
118 | if (m_dirty || m_points.isEmpty()) { |
|
129 | if (m_dirty || m_points.isEmpty()) { | |
119 | points = domain()->calculateGeometryPoints(m_series->points()); |
|
130 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
120 | } else { |
|
131 | } else { | |
121 | points = m_points; |
|
132 | points = m_points; | |
122 |
QPointF point = domain()->calculateGeometryPoint(m_series->points() |
|
133 | QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), | |
|
134 | m_validData); | |||
123 | if (!m_validData) |
|
135 | if (!m_validData) | |
124 | m_points.clear(); |
|
136 | m_points.clear(); | |
125 | else |
|
137 | else | |
126 | points.insert(index, point); |
|
138 | points.insert(index, point); | |
127 | } |
|
139 | } | |
128 |
|
||||
129 | updateChart(m_points, points, index); |
|
140 | updateChart(m_points, points, index); | |
130 | } |
|
141 | } | |
|
142 | } | |||
131 |
|
143 | |||
132 | void XYChart::handlePointRemoved(int index) |
|
144 | void XYChart::handlePointRemoved(int index) | |
133 | { |
|
145 | { | |
134 | Q_ASSERT(index <= m_series->count()); |
|
146 | Q_ASSERT(index <= m_series->count()); | |
135 | Q_ASSERT(index >= 0); |
|
147 | Q_ASSERT(index >= 0); | |
136 |
|
148 | |||
|
149 | if (m_series->useOpenGL()) { | |||
|
150 | updateGlChart(); | |||
|
151 | } else { | |||
137 | QVector<QPointF> points; |
|
152 | QVector<QPointF> points; | |
138 |
|
||||
139 | if (m_dirty || m_points.isEmpty()) { |
|
153 | if (m_dirty || m_points.isEmpty()) { | |
140 | points = domain()->calculateGeometryPoints(m_series->points()); |
|
154 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
141 | } else { |
|
155 | } else { | |
142 | points = m_points; |
|
156 | points = m_points; | |
143 | points.remove(index); |
|
157 | points.remove(index); | |
144 | } |
|
158 | } | |
145 |
|
||||
146 | updateChart(m_points, points, index); |
|
159 | updateChart(m_points, points, index); | |
147 | } |
|
160 | } | |
|
161 | } | |||
148 |
|
162 | |||
149 | void XYChart::handlePointsRemoved(int index, int count) |
|
163 | void XYChart::handlePointsRemoved(int index, int count) | |
150 | { |
|
164 | { | |
151 | Q_ASSERT(index <= m_series->count()); |
|
165 | Q_ASSERT(index <= m_series->count()); | |
152 | Q_ASSERT(index >= 0); |
|
166 | Q_ASSERT(index >= 0); | |
153 |
|
167 | |||
|
168 | if (m_series->useOpenGL()) { | |||
|
169 | updateGlChart(); | |||
|
170 | } else { | |||
154 | QVector<QPointF> points; |
|
171 | QVector<QPointF> points; | |
155 |
|
||||
156 | if (m_dirty || m_points.isEmpty()) { |
|
172 | if (m_dirty || m_points.isEmpty()) { | |
157 | points = domain()->calculateGeometryPoints(m_series->points()); |
|
173 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
158 | } else { |
|
174 | } else { | |
159 | points = m_points; |
|
175 | points = m_points; | |
160 | points.remove(index, count); |
|
176 | points.remove(index, count); | |
161 | } |
|
177 | } | |
162 |
|
||||
163 | updateChart(m_points, points, index); |
|
178 | updateChart(m_points, points, index); | |
164 | } |
|
179 | } | |
|
180 | } | |||
165 |
|
181 | |||
166 | void XYChart::handlePointReplaced(int index) |
|
182 | void XYChart::handlePointReplaced(int index) | |
167 | { |
|
183 | { | |
168 | Q_ASSERT(index < m_series->count()); |
|
184 | Q_ASSERT(index < m_series->count()); | |
169 | Q_ASSERT(index >= 0); |
|
185 | Q_ASSERT(index >= 0); | |
170 |
|
186 | |||
|
187 | if (m_series->useOpenGL()) { | |||
|
188 | updateGlChart(); | |||
|
189 | } else { | |||
171 | QVector<QPointF> points; |
|
190 | QVector<QPointF> points; | |
172 |
|
||||
173 | if (m_dirty || m_points.isEmpty()) { |
|
191 | if (m_dirty || m_points.isEmpty()) { | |
174 | points = domain()->calculateGeometryPoints(m_series->points()); |
|
192 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
175 | } else { |
|
193 | } else { | |
176 |
QPointF point = domain()->calculateGeometryPoint(m_series->points() |
|
194 | QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), | |
|
195 | m_validData); | |||
177 | if (!m_validData) |
|
196 | if (!m_validData) | |
178 | m_points.clear(); |
|
197 | m_points.clear(); | |
179 | points = m_points; |
|
198 | points = m_points; | |
180 | if (m_validData) |
|
199 | if (m_validData) | |
181 | points.replace(index, point); |
|
200 | points.replace(index, point); | |
182 | } |
|
201 | } | |
183 |
|
||||
184 | updateChart(m_points, points, index); |
|
202 | updateChart(m_points, points, index); | |
185 | } |
|
203 | } | |
|
204 | } | |||
186 |
|
205 | |||
187 | void XYChart::handlePointsReplaced() |
|
206 | void XYChart::handlePointsReplaced() | |
188 | { |
|
207 | { | |
|
208 | if (m_series->useOpenGL()) { | |||
|
209 | updateGlChart(); | |||
|
210 | } else { | |||
189 | // All the points were replaced -> recalculate |
|
211 | // All the points were replaced -> recalculate | |
190 |
QVector<QPointF> points = |
|
212 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
191 | updateChart(m_points, points, -1); |
|
213 | updateChart(m_points, points, -1); | |
192 | } |
|
214 | } | |
|
215 | } | |||
193 |
|
216 | |||
194 | void XYChart::handleDomainUpdated() |
|
217 | void XYChart::handleDomainUpdated() | |
195 | { |
|
218 | { | |
|
219 | if (m_series->useOpenGL()) { | |||
|
220 | updateGlChart(); | |||
|
221 | } else { | |||
196 | if (isEmpty()) return; |
|
222 | if (isEmpty()) return; | |
197 |
QVector<QPointF> points = |
|
223 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
198 | updateChart(m_points, points); |
|
224 | updateChart(m_points, points); | |
199 | } |
|
225 | } | |
|
226 | } | |||
200 |
|
227 | |||
201 | bool XYChart::isEmpty() |
|
228 | bool XYChart::isEmpty() | |
202 | { |
|
229 | { |
@@ -6,12 +6,14 SOURCES += \ | |||||
6 | $$PWD/qxyseries.cpp \ |
|
6 | $$PWD/qxyseries.cpp \ | |
7 | $$PWD/qxymodelmapper.cpp \ |
|
7 | $$PWD/qxymodelmapper.cpp \ | |
8 | $$PWD/qvxymodelmapper.cpp \ |
|
8 | $$PWD/qvxymodelmapper.cpp \ | |
9 | $$PWD/qhxymodelmapper.cpp |
|
9 | $$PWD/qhxymodelmapper.cpp \ | |
|
10 | $$PWD/glxyseriesdata.cpp | |||
10 |
|
11 | |||
11 | PRIVATE_HEADERS += \ |
|
12 | PRIVATE_HEADERS += \ | |
12 | $$PWD/xychart_p.h \ |
|
13 | $$PWD/xychart_p.h \ | |
13 | $$PWD/qxyseries_p.h \ |
|
14 | $$PWD/qxyseries_p.h \ | |
14 | $$PWD/qxymodelmapper_p.h |
|
15 | $$PWD/qxymodelmapper_p.h \ | |
|
16 | $$PWD/glxyseriesdata_p.h | |||
15 |
|
17 | |||
16 | PUBLIC_HEADERS += \ |
|
18 | PUBLIC_HEADERS += \ | |
17 | $$PWD/qxyseries.h \ |
|
19 | $$PWD/qxyseries.h \ |
@@ -76,6 +76,7 Q_SIGNALS: | |||||
76 |
|
76 | |||
77 | protected: |
|
77 | protected: | |
78 | virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1); |
|
78 | virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1); | |
|
79 | virtual void updateGlChart(); | |||
79 |
|
80 | |||
80 | private: |
|
81 | private: | |
81 | inline bool isEmpty(); |
|
82 | inline bool isEmpty(); |
@@ -33,7 +33,9 SOURCES += \ | |||||
33 | declarativemargins.cpp \ |
|
33 | declarativemargins.cpp \ | |
34 | declarativeaxes.cpp \ |
|
34 | declarativeaxes.cpp \ | |
35 | declarativepolarchart.cpp \ |
|
35 | declarativepolarchart.cpp \ | |
36 | declarativeboxplotseries.cpp |
|
36 | declarativeboxplotseries.cpp \ | |
|
37 | declarativechartnode.cpp \ | |||
|
38 | declarativerendernode.cpp | |||
37 |
|
39 | |||
38 | HEADERS += \ |
|
40 | HEADERS += \ | |
39 | declarativechart.h \ |
|
41 | declarativechart.h \ | |
@@ -49,7 +51,9 HEADERS += \ | |||||
49 | declarativemargins.h \ |
|
51 | declarativemargins.h \ | |
50 | declarativeaxes.h \ |
|
52 | declarativeaxes.h \ | |
51 | declarativepolarchart.h \ |
|
53 | declarativepolarchart.h \ | |
52 | declarativeboxplotseries.h |
|
54 | declarativeboxplotseries.h \ | |
|
55 | declarativechartnode.h \ | |||
|
56 | declarativerendernode.h | |||
53 |
|
57 | |||
54 | OTHER_FILES = qmldir |
|
58 | OTHER_FILES = qmldir | |
55 |
|
59 |
@@ -25,6 +25,8 | |||||
25 | #include "declarativesplineseries.h" |
|
25 | #include "declarativesplineseries.h" | |
26 | #include "declarativeboxplotseries.h" |
|
26 | #include "declarativeboxplotseries.h" | |
27 | #include "declarativescatterseries.h" |
|
27 | #include "declarativescatterseries.h" | |
|
28 | #include "declarativechartnode.h" | |||
|
29 | #include "declarativerendernode.h" | |||
28 | #include <QtCharts/QBarCategoryAxis> |
|
30 | #include <QtCharts/QBarCategoryAxis> | |
29 | #include <QtCharts/QValueAxis> |
|
31 | #include <QtCharts/QValueAxis> | |
30 | #include <QtCharts/QLogValueAxis> |
|
32 | #include <QtCharts/QLogValueAxis> | |
@@ -34,6 +36,7 | |||||
34 | #include <private/chartdataset_p.h> |
|
36 | #include <private/chartdataset_p.h> | |
35 | #include "declarativeaxes.h" |
|
37 | #include "declarativeaxes.h" | |
36 | #include <private/qchart_p.h> |
|
38 | #include <private/qchart_p.h> | |
|
39 | #include <private/chartpresenter_p.h> | |||
37 | #include <QtCharts/QPolarChart> |
|
40 | #include <QtCharts/QPolarChart> | |
38 |
|
41 | |||
39 | #ifndef QT_ON_ARM |
|
42 | #ifndef QT_ON_ARM | |
@@ -88,6 +91,7 QT_CHARTS_BEGIN_NAMESPACE | |||||
88 | /*! |
|
91 | /*! | |
89 | \qmlproperty easing ChartView::animationEasingCurve |
|
92 | \qmlproperty easing ChartView::animationEasingCurve | |
90 | The easing curve of the animation for the chart. |
|
93 | The easing curve of the animation for the chart. | |
|
94 | */ | |||
91 |
|
95 | |||
92 | /*! |
|
96 | /*! | |
93 | \qmlproperty Font ChartView::titleFont |
|
97 | \qmlproperty Font ChartView::titleFont | |
@@ -312,34 +316,42 QT_CHARTS_BEGIN_NAMESPACE | |||||
312 | */ |
|
316 | */ | |
313 |
|
317 | |||
314 | DeclarativeChart::DeclarativeChart(QQuickItem *parent) |
|
318 | DeclarativeChart::DeclarativeChart(QQuickItem *parent) | |
315 |
: QQuick |
|
319 | : QQuickItem(parent) | |
316 | { |
|
320 | { | |
317 | initChart(QChart::ChartTypeCartesian); |
|
321 | initChart(QChart::ChartTypeCartesian); | |
318 | } |
|
322 | } | |
319 |
|
323 | |||
320 | DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent) |
|
324 | DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent) | |
321 |
: QQuick |
|
325 | : QQuickItem(parent) | |
322 | { |
|
326 | { | |
323 | initChart(type); |
|
327 | initChart(type); | |
324 | } |
|
328 | } | |
325 |
|
329 | |||
326 | void DeclarativeChart::initChart(QChart::ChartType type) |
|
330 | void DeclarativeChart::initChart(QChart::ChartType type) | |
327 | { |
|
331 | { | |
328 |
m_ |
|
332 | m_sceneImage = 0; | |
|
333 | m_sceneImageDirty = false; | |||
329 | m_guiThreadId = QThread::currentThreadId(); |
|
334 | m_guiThreadId = QThread::currentThreadId(); | |
330 | m_paintThreadId = 0; |
|
335 | m_paintThreadId = 0; | |
331 | m_updatePending = false; |
|
336 | m_updatePending = false; | |
332 |
|
337 | |||
|
338 | setFlag(ItemHasContents, true); | |||
|
339 | ||||
333 | if (type == QChart::ChartTypePolar) |
|
340 | if (type == QChart::ChartTypePolar) | |
334 | m_chart = new QPolarChart(); |
|
341 | m_chart = new QPolarChart(); | |
335 | else |
|
342 | else | |
336 | m_chart = new QChart(); |
|
343 | m_chart = new QChart(); | |
337 |
|
344 | |||
|
345 | m_chart->d_ptr->m_presenter->glSetUseWidget(false); | |||
|
346 | m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager(); | |||
|
347 | ||||
338 | m_scene = new QGraphicsScene(this); |
|
348 | m_scene = new QGraphicsScene(this); | |
339 | m_scene->addItem(m_chart); |
|
349 | m_scene->addItem(m_chart); | |
340 |
|
350 | |||
341 | setAntialiasing(QQuickItem::antialiasing()); |
|
351 | setAntialiasing(QQuickItem::antialiasing()); | |
342 | connect(m_scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(sceneChanged(QList<QRectF>))); |
|
352 | connect(m_scene, &QGraphicsScene::changed, this, &DeclarativeChart::sceneChanged); | |
|
353 | connect(this, &DeclarativeChart::needRender, this, &DeclarativeChart::renderScene, | |||
|
354 | Qt::QueuedConnection); | |||
343 | connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool))); |
|
355 | connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool))); | |
344 |
|
356 | |||
345 | setAcceptedMouseButtons(Qt::AllButtons); |
|
357 | setAcceptedMouseButtons(Qt::AllButtons); | |
@@ -377,10 +389,7 void DeclarativeChart::changeMargins(int top, int bottom, int left, int right) | |||||
377 | DeclarativeChart::~DeclarativeChart() |
|
389 | DeclarativeChart::~DeclarativeChart() | |
378 | { |
|
390 | { | |
379 | delete m_chart; |
|
391 | delete m_chart; | |
380 |
m_sceneImage |
|
392 | delete m_sceneImage; | |
381 | delete m_currentSceneImage; |
|
|||
382 | m_currentSceneImage = 0; |
|
|||
383 | m_sceneImageLock.unlock(); |
|
|||
384 | } |
|
393 | } | |
385 |
|
394 | |||
386 | void DeclarativeChart::childEvent(QChildEvent *event) |
|
395 | void DeclarativeChart::childEvent(QChildEvent *event) | |
@@ -493,65 +502,102 void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF & | |||||
493 | QQuickItem::geometryChanged(newGeometry, oldGeometry); |
|
502 | QQuickItem::geometryChanged(newGeometry, oldGeometry); | |
494 | } |
|
503 | } | |
495 |
|
504 | |||
496 | void DeclarativeChart::sceneChanged(QList<QRectF> region) |
|
505 | QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) | |
497 | { |
|
506 | { | |
498 | Q_UNUSED(region); |
|
507 | DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode); | |
499 |
|
508 | |||
500 | if (m_guiThreadId == m_paintThreadId) { |
|
509 | if (!node) { | |
501 | // Rendering in gui thread, no need for shenannigans, just update |
|
510 | node = new DeclarativeChartNode(window()); | |
502 | update(); |
|
511 | connect(window(), &QQuickWindow::beforeRendering, | |
503 | } else { |
|
512 | node->glRenderNode(), &DeclarativeRenderNode::render); | |
504 | // Multi-threaded rendering, need to ensure scene is actually rendered in gui thread |
|
|||
505 | if (!m_updatePending) { |
|
|||
506 | m_updatePending = true; |
|
|||
507 | // Do async render to avoid some unnecessary renders. |
|
|||
508 | QTimer::singleShot(0, this, SLOT(renderScene())); |
|
|||
509 |
|
|
513 | } | |
|
514 | ||||
|
515 | const QRectF &bRect = boundingRect(); | |||
|
516 | ||||
|
517 | // Update GL data | |||
|
518 | if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) { | |||
|
519 | const QRectF &plotArea = m_chart->plotArea(); | |||
|
520 | const QSizeF &chartAreaSize = m_chart->size(); | |||
|
521 | ||||
|
522 | // We can't use chart's plot area directly, as graphicscene has some internal minimum size | |||
|
523 | const qreal normalizedX = plotArea.x() / chartAreaSize.width(); | |||
|
524 | const qreal normalizedY = plotArea.y() / chartAreaSize.height(); | |||
|
525 | const qreal normalizedWidth = plotArea.width() / chartAreaSize.width(); | |||
|
526 | const qreal normalizedHeight = plotArea.height() / chartAreaSize.height(); | |||
|
527 | ||||
|
528 | QRectF adjustedPlotArea(normalizedX * bRect.width(), | |||
|
529 | normalizedY * bRect.height(), | |||
|
530 | normalizedWidth * bRect.width(), | |||
|
531 | normalizedHeight * bRect.height()); | |||
|
532 | ||||
|
533 | const QSize &adjustedPlotSize = adjustedPlotArea.size().toSize(); | |||
|
534 | if (adjustedPlotSize != node->glRenderNode()->textureSize()) | |||
|
535 | node->glRenderNode()->setTextureSize(adjustedPlotSize); | |||
|
536 | ||||
|
537 | node->glRenderNode()->setRect(adjustedPlotArea); | |||
|
538 | node->glRenderNode()->setSeriesData(m_glXYDataManager->mapDirty(), | |||
|
539 | m_glXYDataManager->dataMap()); | |||
|
540 | ||||
|
541 | // Clear dirty flags from original xy data | |||
|
542 | m_glXYDataManager->clearAllDirty(); | |||
510 | } |
|
543 | } | |
|
544 | ||||
|
545 | // Copy chart (if dirty) to chart node | |||
|
546 | if (m_sceneImageDirty) { | |||
|
547 | node->createTextureFromImage(*m_sceneImage); | |||
|
548 | m_sceneImageDirty = false; | |||
511 | } |
|
549 | } | |
512 |
|
550 | |||
513 | void DeclarativeChart::renderScene() |
|
551 | node->setRect(bRect); | |
514 | { |
|
|||
515 | m_updatePending = false; |
|
|||
516 | m_sceneImageLock.lock(); |
|
|||
517 | delete m_currentSceneImage; |
|
|||
518 | m_currentSceneImage = new QImage(m_chart->size().toSize(), QImage::Format_ARGB32); |
|
|||
519 | m_currentSceneImage->fill(Qt::transparent); |
|
|||
520 | QPainter painter(m_currentSceneImage); |
|
|||
521 | if (antialiasing()) |
|
|||
522 | painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); |
|
|||
523 | QRect renderRect(QPoint(0, 0), m_chart->size().toSize()); |
|
|||
524 | m_scene->render(&painter, renderRect, renderRect); |
|
|||
525 | m_sceneImageLock.unlock(); |
|
|||
526 |
|
552 | |||
527 | update(); |
|
553 | return node; | |
528 | } |
|
554 | } | |
529 |
|
555 | |||
530 | void DeclarativeChart::paint(QPainter *painter) |
|
556 | void DeclarativeChart::sceneChanged(QList<QRectF> region) | |
531 | { |
|
557 | { | |
532 | if (!m_paintThreadId) { |
|
558 | const int count = region.size(); | |
533 | m_paintThreadId = QThread::currentThreadId(); |
|
559 | const qreal limitSize = 0.01; | |
534 | if (m_guiThreadId == m_paintThreadId) { |
|
560 | if (count && !m_updatePending) { | |
535 | // No need for scene image in single threaded rendering, so delete |
|
561 | qreal totalSize = 0.0; | |
536 | // the one that got made by default before the rendering type was |
|
562 | for (int i = 0; i < count; i++) { | |
537 | // detected. |
|
563 | const QRectF ® = region.at(i); | |
538 | delete m_currentSceneImage; |
|
564 | totalSize += (reg.height() * reg.width()); | |
539 | m_currentSceneImage = 0; |
|
565 | if (totalSize >= limitSize) | |
|
566 | break; | |||
|
567 | } | |||
|
568 | // Ignore region updates that change less than small fraction of a pixel, as there is | |||
|
569 | // little point regenerating the image in these cases. These are typically cases | |||
|
570 | // where OpenGL series are drawn to otherwise static chart. | |||
|
571 | if (totalSize >= limitSize) { | |||
|
572 | m_updatePending = true; | |||
|
573 | // Do async render to avoid some unnecessary renders. | |||
|
574 | emit needRender(); | |||
|
575 | } else { | |||
|
576 | // We do want to call update to trigger possible gl series updates. | |||
|
577 | update(); | |||
|
578 | } | |||
540 |
|
|
579 | } | |
541 | } |
|
580 | } | |
542 |
|
581 | |||
543 | if (m_guiThreadId == m_paintThreadId) { |
|
582 | void DeclarativeChart::renderScene() | |
544 | QRectF renderRect(QPointF(0, 0), m_chart->size()); |
|
583 | { | |
545 | m_scene->render(painter, renderRect, renderRect); |
|
584 | m_updatePending = false; | |
546 | } else { |
|
585 | m_sceneImageDirty = true; | |
547 | m_sceneImageLock.lock(); |
|
586 | QSize chartSize = m_chart->size().toSize(); | |
548 | if (m_currentSceneImage) { |
|
587 | if (!m_sceneImage || chartSize != m_sceneImage->size()) { | |
549 | QRect imageRect(QPoint(0, 0), m_currentSceneImage->size()); |
|
588 | delete m_sceneImage; | |
550 | QRect itemRect(QPoint(0, 0), QSize(width(), height())); |
|
589 | m_sceneImage = new QImage(chartSize, QImage::Format_ARGB32); | |
551 | painter->drawImage(itemRect, *m_currentSceneImage, imageRect); |
|
590 | m_sceneImage->fill(Qt::transparent); | |
552 |
|
|
591 | } | |
553 | m_sceneImageLock.unlock(); |
|
592 | ||
|
593 | QPainter painter(m_sceneImage); | |||
|
594 | if (antialiasing()) { | |||
|
595 | painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | |||
|
596 | | QPainter::SmoothPixmapTransform); | |||
554 | } |
|
597 | } | |
|
598 | QRect renderRect(QPoint(0, 0), chartSize); | |||
|
599 | m_scene->render(&painter, renderRect, renderRect); | |||
|
600 | update(); | |||
555 | } |
|
601 | } | |
556 |
|
602 | |||
557 | void DeclarativeChart::mousePressEvent(QMouseEvent *event) |
|
603 | void DeclarativeChart::mousePressEvent(QMouseEvent *event) |
@@ -19,11 +19,11 | |||||
19 | #ifndef DECLARATIVECHART_H |
|
19 | #ifndef DECLARATIVECHART_H | |
20 | #define DECLARATIVECHART_H |
|
20 | #define DECLARATIVECHART_H | |
21 |
|
21 | |||
|
22 | #include <private/glxyseriesdata_p.h> | |||
|
23 | ||||
22 | #include <QtCore/QtGlobal> |
|
24 | #include <QtCore/QtGlobal> | |
23 | #include <QtQuick/QQuickItem> |
|
25 | #include <QtQuick/QQuickItem> | |
24 | #include <QtQuick/QQuickPaintedItem> |
|
|||
25 | #include <QtWidgets/QGraphicsScene> |
|
26 | #include <QtWidgets/QGraphicsScene> | |
26 | #include <QtCore/QMutex> |
|
|||
27 |
|
27 | |||
28 | #include <QtCharts/QChart> |
|
28 | #include <QtCharts/QChart> | |
29 | #include <QtCore/QLocale> |
|
29 | #include <QtCore/QLocale> | |
@@ -34,7 +34,7 class DeclarativeMargins; | |||||
34 | class Domain; |
|
34 | class Domain; | |
35 | class DeclarativeAxes; |
|
35 | class DeclarativeAxes; | |
36 |
|
36 | |||
37 |
class DeclarativeChart : public QQuick |
|
37 | class DeclarativeChart : public QQuickItem | |
38 | { |
|
38 | { | |
39 | Q_OBJECT |
|
39 | Q_OBJECT | |
40 | Q_PROPERTY(Theme theme READ theme WRITE setTheme) |
|
40 | Q_PROPERTY(Theme theme READ theme WRITE setTheme) | |
@@ -102,7 +102,7 public: // From parent classes | |||||
102 | void childEvent(QChildEvent *event); |
|
102 | void childEvent(QChildEvent *event); | |
103 | void componentComplete(); |
|
103 | void componentComplete(); | |
104 | void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); |
|
104 | void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); | |
105 | void paint(QPainter *painter); |
|
105 | QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); | |
106 | protected: |
|
106 | protected: | |
107 | void mousePressEvent(QMouseEvent *event); |
|
107 | void mousePressEvent(QMouseEvent *event); | |
108 | void mouseReleaseEvent(QMouseEvent *event); |
|
108 | void mouseReleaseEvent(QMouseEvent *event); | |
@@ -199,6 +199,7 Q_SIGNALS: | |||||
199 | Q_REVISION(4) void localeChanged(); |
|
199 | Q_REVISION(4) void localeChanged(); | |
200 | Q_REVISION(5) void animationDurationChanged(int msecs); |
|
200 | Q_REVISION(5) void animationDurationChanged(int msecs); | |
201 | Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve); |
|
201 | Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve); | |
|
202 | void needRender(); | |||
202 |
|
203 | |||
203 | private Q_SLOTS: |
|
204 | private Q_SLOTS: | |
204 | void changeMargins(int top, int bottom, int left, int right); |
|
205 | void changeMargins(int top, int bottom, int left, int right); | |
@@ -227,12 +228,13 private: | |||||
227 | QPoint m_lastMouseMoveScreenPoint; |
|
228 | QPoint m_lastMouseMoveScreenPoint; | |
228 | Qt::MouseButton m_mousePressButton; |
|
229 | Qt::MouseButton m_mousePressButton; | |
229 | Qt::MouseButtons m_mousePressButtons; |
|
230 | Qt::MouseButtons m_mousePressButtons; | |
230 |
Q |
|
231 | QImage *m_sceneImage; | |
231 | QImage *m_currentSceneImage; |
|
232 | bool m_sceneImageDirty; | |
232 | bool m_updatePending; |
|
233 | bool m_updatePending; | |
233 | Qt::HANDLE m_paintThreadId; |
|
234 | Qt::HANDLE m_paintThreadId; | |
234 | Qt::HANDLE m_guiThreadId; |
|
235 | Qt::HANDLE m_guiThreadId; | |
235 | DeclarativeMargins *m_margins; |
|
236 | DeclarativeMargins *m_margins; | |
|
237 | GLXYSeriesDataManager *m_glXYDataManager; | |||
236 | }; |
|
238 | }; | |
237 |
|
239 | |||
238 | QT_CHARTS_END_NAMESPACE |
|
240 | QT_CHARTS_END_NAMESPACE |
@@ -19,8 +19,8 | |||||
19 | #include "../qxyseries/tst_qxyseries.h" |
|
19 | #include "../qxyseries/tst_qxyseries.h" | |
20 | #include <QtCharts/QLineSeries> |
|
20 | #include <QtCharts/QLineSeries> | |
21 |
|
21 | |||
22 |
|
||||
23 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
22 | Q_DECLARE_METATYPE(QList<QPointF>) | |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |||
24 |
|
24 | |||
25 | class tst_QLineSeries : public tst_QXYSeries |
|
25 | class tst_QLineSeries : public tst_QXYSeries | |
26 | { |
|
26 | { | |
@@ -76,6 +76,7 void tst_QLineSeries::qlineseries() | |||||
76 | QCOMPARE(series.count(),0); |
|
76 | QCOMPARE(series.count(),0); | |
77 | QCOMPARE(series.brush(), QBrush()); |
|
77 | QCOMPARE(series.brush(), QBrush()); | |
78 | QCOMPARE(series.points(), QList<QPointF>()); |
|
78 | QCOMPARE(series.points(), QList<QPointF>()); | |
|
79 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |||
79 | QCOMPARE(series.pen(), QPen()); |
|
80 | QCOMPARE(series.pen(), QPen()); | |
80 | QCOMPARE(series.pointsVisible(), false); |
|
81 | QCOMPARE(series.pointsVisible(), false); | |
81 | QCOMPARE(series.pointLabelsVisible(), false); |
|
82 | QCOMPARE(series.pointLabelsVisible(), false); |
@@ -20,6 +20,7 | |||||
20 | #include <QtCharts/QScatterSeries> |
|
20 | #include <QtCharts/QScatterSeries> | |
21 |
|
21 | |||
22 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
22 | Q_DECLARE_METATYPE(QList<QPointF>) | |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |||
23 |
|
24 | |||
24 | class tst_QScatterSeries : public tst_QXYSeries |
|
25 | class tst_QScatterSeries : public tst_QXYSeries | |
25 | { |
|
26 | { | |
@@ -75,6 +76,7 void tst_QScatterSeries::qscatterseries() | |||||
75 | QCOMPARE(series.count(),0); |
|
76 | QCOMPARE(series.count(),0); | |
76 | QCOMPARE(series.brush(), QBrush()); |
|
77 | QCOMPARE(series.brush(), QBrush()); | |
77 | QCOMPARE(series.points(), QList<QPointF>()); |
|
78 | QCOMPARE(series.points(), QList<QPointF>()); | |
|
79 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |||
78 | QCOMPARE(series.pen(), QPen()); |
|
80 | QCOMPARE(series.pen(), QPen()); | |
79 | QCOMPARE(series.pointsVisible(), false); |
|
81 | QCOMPARE(series.pointsVisible(), false); | |
80 |
|
82 |
@@ -20,6 +20,7 | |||||
20 | #include <QtCharts/QSplineSeries> |
|
20 | #include <QtCharts/QSplineSeries> | |
21 |
|
21 | |||
22 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
22 | Q_DECLARE_METATYPE(QList<QPointF>) | |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |||
23 |
|
24 | |||
24 | class tst_QSplineSeries : public tst_QXYSeries |
|
25 | class tst_QSplineSeries : public tst_QXYSeries | |
25 | { |
|
26 | { | |
@@ -73,6 +74,7 void tst_QSplineSeries::qsplineseries() | |||||
73 | QCOMPARE(series.count(),0); |
|
74 | QCOMPARE(series.count(),0); | |
74 | QCOMPARE(series.brush(), QBrush()); |
|
75 | QCOMPARE(series.brush(), QBrush()); | |
75 | QCOMPARE(series.points(), QList<QPointF>()); |
|
76 | QCOMPARE(series.points(), QList<QPointF>()); | |
|
77 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |||
76 | QCOMPARE(series.pen(), QPen()); |
|
78 | QCOMPARE(series.pen(), QPen()); | |
77 | QCOMPARE(series.pointsVisible(), false); |
|
79 | QCOMPARE(series.pointsVisible(), false); | |
78 |
|
80 |
@@ -198,6 +198,7 void tst_QXYSeries::append_raw() | |||||
198 | TRY_COMPARE(spy0.count(), 0); |
|
198 | TRY_COMPARE(spy0.count(), 0); | |
199 | TRY_COMPARE(addedSpy.count(), points.count()); |
|
199 | TRY_COMPARE(addedSpy.count(), points.count()); | |
200 | QCOMPARE(m_series->points(), points); |
|
200 | QCOMPARE(m_series->points(), points); | |
|
201 | QCOMPARE(m_series->pointsVector(), points.toVector()); | |||
201 |
|
202 | |||
202 | // Process events between appends |
|
203 | // Process events between appends | |
203 | foreach (const QPointF &point, otherPoints) { |
|
204 | foreach (const QPointF &point, otherPoints) { |
General Comments 0
You need to be logged in to leave comments.
Login now