@@ -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 |
@@ -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 | 35 | qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize) |
|
36 | 36 | { |
|
37 | 37 | qint64 range = 2000; |
|
38 |
Q |
|
|
39 |
Q |
|
|
38 | QVector<QPointF> oldPoints = m_series->pointsVector(); | |
|
39 | QVector<QPointF> points; | |
|
40 | 40 | int resolution = 4; |
|
41 | 41 | |
|
42 | 42 | if (oldPoints.count() < range) { |
|
43 | points = m_series->points(); | |
|
43 | points = m_series->pointsVector(); | |
|
44 | 44 | } else { |
|
45 | 45 | for (int i = maxSize/resolution; i < oldPoints.count(); i++) |
|
46 | 46 | points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y())); |
@@ -50,7 +50,13 qtHaveModule(quick) { | |||
|
50 | 50 | qtHaveModule(multimedia) { |
|
51 | 51 | SUBDIRS += audio |
|
52 | 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 | 62 | !linux-arm*: { |
@@ -189,7 +189,7 QChart *ThemeWidget::createAreaChart() const | |||
|
189 | 189 | for (int j(0); j < m_dataTable[i].count(); j++) { |
|
190 | 190 | Data data = m_dataTable[i].at(j); |
|
191 | 191 | if (lowerSeries) { |
|
192 |
const Q |
|
|
192 | const QVector<QPointF>& points = lowerSeries->pointsVector(); | |
|
193 | 193 | upperSeries->append(QPointF(j, points[i].y() + data.first.y())); |
|
194 | 194 | } else { |
|
195 | 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 | 20 | import QtQuick.Layouts 1.0 |
|
21 | 21 | |
|
22 | 22 | ColumnLayout { |
|
23 | property alias openGLButton: openGLButton | |
|
23 | 24 | spacing: 8 |
|
24 | 25 | Layout.fillHeight: true |
|
25 | 26 | signal animationsEnabled(bool enabled) |
@@ -27,6 +28,7 ColumnLayout { | |||
|
27 | 28 | signal refreshRateChanged(variant rate); |
|
28 | 29 | signal signalSourceChanged(string source, int signalCount, int sampleCount); |
|
29 | 30 | signal antialiasingEnabled(bool enabled) |
|
31 | signal openGlChanged(bool enabled) | |
|
30 | 32 | |
|
31 | 33 | Text { |
|
32 | 34 | text: "Scope" |
@@ -35,6 +37,14 ColumnLayout { | |||
|
35 | 37 | } |
|
36 | 38 | |
|
37 | 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 | 48 | text: "Graph: " |
|
39 | 49 | items: ["line", "spline", "scatter"] |
|
40 | 50 | currentSelection: 0 |
@@ -17,13 +17,18 | |||
|
17 | 17 | ****************************************************************************/ |
|
18 | 18 | |
|
19 | 19 | import QtQuick 2.0 |
|
20 |
import QtCharts 2. |
|
|
20 | import QtCharts 2.1 | |
|
21 | 21 | |
|
22 | 22 | //![1] |
|
23 | 23 | ChartView { |
|
24 | 24 | id: chartView |
|
25 | 25 | animationOptions: ChartView.NoAnimation |
|
26 | 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 | 33 | ValueAxis { |
|
29 | 34 | id: axisY1 |
@@ -40,7 +45,7 ChartView { | |||
|
40 | 45 | ValueAxis { |
|
41 | 46 | id: axisX |
|
42 | 47 | min: 0 |
|
43 |
max: 10 |
|
|
48 | max: 1024 | |
|
44 | 49 | } |
|
45 | 50 | |
|
46 | 51 | LineSeries { |
@@ -48,12 +53,14 ChartView { | |||
|
48 | 53 | name: "signal 1" |
|
49 | 54 | axisX: axisX |
|
50 | 55 | axisY: axisY1 |
|
56 | useOpenGL: chartView.openGL | |
|
51 | 57 | } |
|
52 | 58 | LineSeries { |
|
53 | 59 | id: lineSeries2 |
|
54 | 60 | name: "signal 2" |
|
55 | 61 | axisX: axisX |
|
56 | 62 | axisYRight: axisY2 |
|
63 | useOpenGL: chartView.openGL | |
|
57 | 64 | } |
|
58 | 65 | //![1] |
|
59 | 66 | |
@@ -78,18 +85,28 ChartView { | |||
|
78 | 85 | // but the series have their own y-axes to make it possible to control the y-offset |
|
79 | 86 | // of the "signal sources". |
|
80 | 87 | if (type == "line") { |
|
81 |
chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", |
|
|
82 | chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2); | |
|
88 | var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", | |
|
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 | 95 | } else if (type == "spline") { |
|
84 | 96 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1); |
|
85 | 97 | chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2); |
|
86 | 98 | } else { |
|
87 |
var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", |
|
|
99 | var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", | |
|
100 | axisX, axisY1); | |
|
88 | 101 | series1.markerSize = 3; |
|
89 | 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 | 107 | series2.markerSize = 3; |
|
92 | 108 | series2.borderColor = "transparent"; |
|
109 | series2.useOpenGL = chartView.openGL | |
|
93 | 110 | } |
|
94 | 111 | } |
|
95 | 112 |
@@ -21,8 +21,8 import QtQuick 2.0 | |||
|
21 | 21 | //![1] |
|
22 | 22 | Rectangle { |
|
23 | 23 | id: main |
|
24 |
width: |
|
|
25 |
height: |
|
|
24 | width: 600 | |
|
25 | height: 400 | |
|
26 | 26 | color: "#404040" |
|
27 | 27 | |
|
28 | 28 | ControlPanel { |
@@ -39,11 +39,22 Rectangle { | |||
|
39 | 39 | dataSource.generateData(0, signalCount, sampleCount); |
|
40 | 40 | else |
|
41 | 41 | dataSource.generateData(1, signalCount, sampleCount); |
|
42 | scopeView.axisX().max = sampleCount; | |
|
42 | 43 | } |
|
43 | 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 | 55 | onRefreshRateChanged: scopeView.changeRefreshRate(rate); |
|
46 | 56 | onAntialiasingEnabled: scopeView.antialiasing = enabled; |
|
57 | onOpenGlChanged: scopeView.openGL = enabled; | |
|
47 | 58 | } |
|
48 | 59 | |
|
49 | 60 | //![2] |
@@ -56,4 +67,5 Rectangle { | |||
|
56 | 67 | height: main.height |
|
57 | 68 | } |
|
58 | 69 | //![2] |
|
70 | ||
|
59 | 71 | } |
@@ -345,7 +345,7 QT_CHARTS_BEGIN_NAMESPACE | |||
|
345 | 345 | \sa pointLabelsVisible |
|
346 | 346 | */ |
|
347 | 347 | /*! |
|
348 |
\fn void QAreaSeries::pointLabelsClippin |
|
|
348 | \fn void QAreaSeries::pointLabelsClippingChanged(bool clipping) | |
|
349 | 349 | The clipping of the data point labels is changed to \a clipping. |
|
350 | 350 | */ |
|
351 | 351 | /*! |
@@ -395,8 +395,10 QAbstractSeries::SeriesType QAreaSeries::type() const | |||
|
395 | 395 | void QAreaSeries::setUpperSeries(QLineSeries *series) |
|
396 | 396 | { |
|
397 | 397 | Q_D(QAreaSeries); |
|
398 | if (d->m_upperSeries != series) | |
|
398 | if (d->m_upperSeries != series) { | |
|
399 | series->d_ptr->setBlockOpenGL(true); | |
|
399 | 400 | d->m_upperSeries = series; |
|
401 | } | |
|
400 | 402 | } |
|
401 | 403 | |
|
402 | 404 | QLineSeries *QAreaSeries::upperSeries() const |
@@ -411,6 +413,7 QLineSeries *QAreaSeries::upperSeries() const | |||
|
411 | 413 | void QAreaSeries::setLowerSeries(QLineSeries *series) |
|
412 | 414 | { |
|
413 | 415 | Q_D(QAreaSeries); |
|
416 | series->d_ptr->setBlockOpenGL(true); | |
|
414 | 417 | d->m_lowerSeries = series; |
|
415 | 418 | } |
|
416 | 419 | |
@@ -624,7 +627,7 void QAreaSeriesPrivate::initializeDomain() | |||
|
624 | 627 | QLineSeries *lowerSeries = q->lowerSeries(); |
|
625 | 628 | |
|
626 | 629 | if (upperSeries) { |
|
627 |
const Q |
|
|
630 | const QVector<QPointF> &points = upperSeries->pointsVector(); | |
|
628 | 631 | |
|
629 | 632 | for (int i = 0; i < points.count(); i++) { |
|
630 | 633 | qreal x = points[i].x(); |
@@ -636,8 +639,7 void QAreaSeriesPrivate::initializeDomain() | |||
|
636 | 639 | } |
|
637 | 640 | } |
|
638 | 641 | if (lowerSeries) { |
|
639 | ||
|
640 | const QList<QPointF>& points = lowerSeries->points(); | |
|
642 | const QVector<QPointF> &points = lowerSeries->pointsVector(); | |
|
641 | 643 | |
|
642 | 644 | for (int i = 0; i < points.count(); i++) { |
|
643 | 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 | 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 | 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 | 572 | void QBarSet::setLabelColor(QColor color) |
|
573 | 573 | { |
@@ -38,6 +38,7 | |||
|
38 | 38 | #include <private/xlogypolardomain_p.h> |
|
39 | 39 | #include <private/logxypolardomain_p.h> |
|
40 | 40 | #include <private/logxlogypolardomain_p.h> |
|
41 | #include <private/glxyseriesdata_p.h> | |
|
41 | 42 | |
|
42 | 43 | #ifndef QT_ON_ARM |
|
43 | 44 | #include <QtCharts/QDateTimeAxis> |
@@ -47,7 +48,8 QT_CHARTS_BEGIN_NAMESPACE | |||
|
47 | 48 | |
|
48 | 49 | ChartDataSet::ChartDataSet(QChart *chart) |
|
49 | 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 | 79 | qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart."); |
|
78 | 80 | return; |
|
79 | 81 | } |
|
82 | // Disable OpenGL for series in polar charts | |
|
83 | series->setUseOpenGL(false); | |
|
80 | 84 | series->d_ptr->setDomain(new XYPolarDomain()); |
|
81 | 85 | // Set the correct domain for upper and lower series too |
|
82 | 86 | if (series->type() == QAbstractSeries::SeriesTypeArea) { |
@@ -157,6 +161,10 void ChartDataSet::removeSeries(QAbstractSeries *series) | |||
|
157 | 161 | series->d_ptr->setDomain(new XYDomain()); |
|
158 | 162 | series->setParent(0); |
|
159 | 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 | 38 | class QAbstractAxis; |
|
39 | 39 | class ChartPresenter; |
|
40 | class GLXYSeriesDataManager; | |
|
40 | 41 | |
|
41 | 42 | class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject |
|
42 | 43 | { |
@@ -67,6 +68,8 public: | |||
|
67 | 68 | QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0); |
|
68 | 69 | QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0); |
|
69 | 70 | |
|
71 | GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; } | |
|
72 | ||
|
70 | 73 | Q_SIGNALS: |
|
71 | 74 | void axisAdded(QAbstractAxis* axis); |
|
72 | 75 | void axisRemoved(QAbstractAxis* axis); |
@@ -85,6 +88,7 private: | |||
|
85 | 88 | QList<QAbstractSeries *> m_seriesList; |
|
86 | 89 | QList<QAbstractAxis *> m_axisList; |
|
87 | 90 | QChart* m_chart; |
|
91 | GLXYSeriesDataManager *m_glXYSeriesDataManager; | |
|
88 | 92 | }; |
|
89 | 93 | |
|
90 | 94 | QT_CHARTS_END_NAMESPACE |
@@ -19,13 +19,15 | |||
|
19 | 19 | #include <private/chartelement_p.h> |
|
20 | 20 | #include <private/chartpresenter_p.h> |
|
21 | 21 | #include <private/abstractdomain_p.h> |
|
22 | #include <private/chartdataset_p.h> | |
|
22 | 23 | |
|
23 | 24 | QT_CHARTS_BEGIN_NAMESPACE |
|
24 | 25 | |
|
25 | 26 | ChartElement::ChartElement(QGraphicsItem* item): |
|
26 | 27 | QGraphicsObject(item), |
|
27 | 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 | 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 | 65 | QT_CHARTS_END_NAMESPACE |
@@ -40,6 +40,7 class ChartPresenter; | |||
|
40 | 40 | class ChartAnimation; |
|
41 | 41 | class ChartThemeManager; |
|
42 | 42 | class AbstractDomain; |
|
43 | class ChartDataSet; | |
|
43 | 44 | |
|
44 | 45 | class ChartElement: public QGraphicsObject |
|
45 | 46 | { |
@@ -52,10 +53,13 public: | |||
|
52 | 53 | ChartPresenter *presenter() const; |
|
53 | 54 | virtual void setThemeManager(ChartThemeManager *manager); |
|
54 | 55 | ChartThemeManager* themeManager() const; |
|
56 | virtual void setDataSet(ChartDataSet *dataSet); | |
|
57 | ChartDataSet *dataSet() const; | |
|
55 | 58 | |
|
56 | 59 | private: |
|
57 | 60 | ChartPresenter *m_presenter; |
|
58 | 61 | ChartThemeManager *m_themeManager; |
|
62 | ChartDataSet *m_dataSet; | |
|
59 | 63 | }; |
|
60 | 64 | |
|
61 | 65 | QT_CHARTS_END_NAMESPACE |
@@ -46,6 +46,10 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) | |||
|
46 | 46 | m_plotAreaBackground(0), |
|
47 | 47 | m_title(0), |
|
48 | 48 | m_localizeNumbers(false) |
|
49 | #ifndef QT_NO_OPENGL | |
|
50 | , m_glWidget(0) | |
|
51 | , m_glUseWidget(true) | |
|
52 | #endif | |
|
49 | 53 | { |
|
50 | 54 | if (type == QChart::ChartTypeCartesian) |
|
51 | 55 | m_layout = new CartesianChartLayout(this); |
@@ -56,7 +60,9 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) | |||
|
56 | 60 | |
|
57 | 61 | ChartPresenter::~ChartPresenter() |
|
58 | 62 | { |
|
59 | ||
|
63 | #ifndef QT_NO_OPENGL | |
|
64 | delete m_glWidget.data(); | |
|
65 | #endif | |
|
60 | 66 | } |
|
61 | 67 | |
|
62 | 68 | void ChartPresenter::setGeometry(const QRectF rect) |
@@ -67,6 +73,10 void ChartPresenter::setGeometry(const QRectF rect) | |||
|
67 | 73 | chart->domain()->setSize(rect.size()); |
|
68 | 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 | 80 | emit plotAreaChanged(m_rect); |
|
71 | 81 | } |
|
72 | 82 | } |
@@ -108,6 +118,7 void ChartPresenter::handleSeriesAdded(QAbstractSeries *series) | |||
|
108 | 118 | ChartItem *chart = series->d_ptr->chartItem(); |
|
109 | 119 | chart->setPresenter(this); |
|
110 | 120 | chart->setThemeManager(m_chart->d_ptr->m_themeManager); |
|
121 | chart->setDataSet(m_chart->d_ptr->m_dataset); | |
|
111 | 122 | chart->domain()->setSize(m_rect.size()); |
|
112 | 123 | chart->setPos(m_rect.topLeft()); |
|
113 | 124 | chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored |
@@ -531,6 +542,28 QString ChartPresenter::numberToString(int value) | |||
|
531 | 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 | 567 | #include "moc_chartpresenter_p.cpp" |
|
535 | 568 | |
|
536 | 569 | QT_CHARTS_END_NAMESPACE |
@@ -30,9 +30,11 | |||
|
30 | 30 | |
|
31 | 31 | #include <QtCharts/QChartGlobal> |
|
32 | 32 | #include <QtCharts/QChart> //because of QChart::ChartThemeId |
|
33 | #include <private/glwidget_p.h> | |
|
33 | 34 | #include <QtCore/QRectF> |
|
34 | 35 | #include <QtCore/QMargins> |
|
35 | 36 | #include <QtCore/QLocale> |
|
37 | #include <QtCore/QPointer> | |
|
36 | 38 | |
|
37 | 39 | QT_CHARTS_BEGIN_NAMESPACE |
|
38 | 40 | |
@@ -160,6 +162,9 public: | |||
|
160 | 162 | QString numberToString(double value, char f = 'g', int prec = 6); |
|
161 | 163 | QString numberToString(int value); |
|
162 | 164 | |
|
165 | void ensureGLWidget(); | |
|
166 | void glSetUseWidget(bool enable) { m_glUseWidget = enable; } | |
|
167 | ||
|
163 | 168 | private: |
|
164 | 169 | void createBackgroundItem(); |
|
165 | 170 | void createPlotAreaBackgroundItem(); |
@@ -192,6 +197,10 private: | |||
|
192 | 197 | QRectF m_rect; |
|
193 | 198 | bool m_localizeNumbers; |
|
194 | 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 | 206 | QT_CHARTS_END_NAMESPACE |
@@ -32,6 +32,9 SOURCES += \ | |||
|
32 | 32 | $$PWD/scroller.cpp \ |
|
33 | 33 | $$PWD/charttitle.cpp \ |
|
34 | 34 | $$PWD/qpolarchart.cpp |
|
35 | ||
|
36 | contains(QT_CONFIG, opengl): SOURCES += $$PWD/glwidget.cpp | |
|
37 | ||
|
35 | 38 | PRIVATE_HEADERS += \ |
|
36 | 39 | $$PWD/chartdataset_p.h \ |
|
37 | 40 | $$PWD/chartitem_p.h \ |
@@ -46,6 +49,9 PRIVATE_HEADERS += \ | |||
|
46 | 49 | $$PWD/qabstractseries_p.h \ |
|
47 | 50 | $$PWD/charttitle_p.h \ |
|
48 | 51 | $$PWD/charthelpers_p.h |
|
52 | ||
|
53 | contains(QT_CONFIG, opengl): PRIVATE_HEADERS += $$PWD/glwidget_p.h | |
|
54 | ||
|
49 | 55 | PUBLIC_HEADERS += \ |
|
50 | 56 | $$PWD/qchart.h \ |
|
51 | 57 | $$PWD/qchartglobal.h \ |
@@ -92,7 +92,7 public: | |||
|
92 | 92 | |
|
93 | 93 | virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0; |
|
94 | 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 | 97 | virtual bool attachAxis(QAbstractAxis *axis); |
|
98 | 98 | virtual bool detachAxis(QAbstractAxis *axis); |
@@ -162,7 +162,7 QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c | |||
|
162 | 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 | 167 | const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX); |
|
168 | 168 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
@@ -54,7 +54,7 public: | |||
|
54 | 54 | |
|
55 | 55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
56 | 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 | bool attachAxis(QAbstractAxis *axis); |
|
60 | 60 | bool detachAxis(QAbstractAxis *axis); |
@@ -146,7 +146,7 QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons | |||
|
146 | 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 | 151 | const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX); |
|
152 | 152 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
@@ -54,7 +54,7 public: | |||
|
54 | 54 | |
|
55 | 55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
56 | 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 | bool attachAxis(QAbstractAxis *axis); |
|
60 | 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 | 58 | QVector<QPointF> result; |
|
59 | 59 | result.resize(vector.count()); |
@@ -43,7 +43,7 public: | |||
|
43 | 43 | void setSize(const QSizeF &size); |
|
44 | 44 | |
|
45 | 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 | 48 | virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0; |
|
49 | 49 | virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0; |
@@ -146,7 +146,7 QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons | |||
|
146 | 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 | 151 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); |
|
152 | 152 | const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); |
@@ -54,7 +54,7 public: | |||
|
54 | 54 | |
|
55 | 55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
56 | 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 | bool attachAxis(QAbstractAxis *axis); |
|
60 | 60 | bool detachAxis(QAbstractAxis *axis); |
@@ -144,7 +144,7 QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const | |||
|
144 | 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 | 149 | const qreal deltaX = m_size.width() / (m_maxX - m_minX); |
|
150 | 150 | const qreal deltaY = m_size.height() / (m_maxY - m_minY); |
@@ -54,7 +54,7 public: | |||
|
54 | 54 | |
|
55 | 55 | QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; |
|
56 | 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 | 60 | QT_CHARTS_END_NAMESPACE |
@@ -70,6 +70,17 QPainterPath LineChartItem::shape() const | |||
|
70 | 70 | |
|
71 | 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 | 84 | // Store the points to a local variable so that the old line gets properly cleared |
|
74 | 85 | // when animation starts. |
|
75 | 86 | m_linePoints = geometryPoints(); |
@@ -345,6 +356,9 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt | |||
|
345 | 356 | Q_UNUSED(widget) |
|
346 | 357 | Q_UNUSED(option) |
|
347 | 358 | |
|
359 | if (m_series->useOpenGL()) | |
|
360 | return; | |
|
361 | ||
|
348 | 362 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); |
|
349 | 363 | |
|
350 | 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 | 201 | \internal |
|
139 | 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 | 281 | \brief Returns the chart where series belongs to. |
|
197 | 282 | |
@@ -274,7 +359,9 QAbstractSeriesPrivate::QAbstractSeriesPrivate(QAbstractSeries *q) | |||
|
274 | 359 | m_item(0), |
|
275 | 360 | m_domain(new XYDomain()), |
|
276 | 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 | 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 | 453 | #include "moc_qabstractseries.cpp" |
|
358 | 454 | #include "moc_qabstractseries_p.cpp" |
|
359 | 455 |
@@ -36,6 +36,7 class QT_CHARTS_EXPORT QAbstractSeries : public QObject | |||
|
36 | 36 | Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) |
|
37 | 37 | Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) |
|
38 | 38 | Q_PROPERTY(SeriesType type READ type) |
|
39 | Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged) | |
|
39 | 40 | Q_ENUMS(SeriesType) |
|
40 | 41 | |
|
41 | 42 | public: |
@@ -67,6 +68,8 public: | |||
|
67 | 68 | bool isVisible() const; |
|
68 | 69 | qreal opacity() const; |
|
69 | 70 | void setOpacity(qreal opacity); |
|
71 | void setUseOpenGL(bool enable = true); | |
|
72 | bool useOpenGL() const; | |
|
70 | 73 | |
|
71 | 74 | QChart *chart() const; |
|
72 | 75 | |
@@ -81,6 +84,7 Q_SIGNALS: | |||
|
81 | 84 | void nameChanged(); |
|
82 | 85 | void visibleChanged(); |
|
83 | 86 | void opacityChanged(); |
|
87 | void useOpenGLChanged(); | |
|
84 | 88 | |
|
85 | 89 | protected: |
|
86 | 90 | QScopedPointer<QAbstractSeriesPrivate> d_ptr; |
@@ -89,6 +93,7 protected: | |||
|
89 | 93 | friend class ChartThemeManager; |
|
90 | 94 | friend class QLegendPrivate; |
|
91 | 95 | friend class DeclarativeChart; |
|
96 | friend class QAreaSeries; | |
|
92 | 97 | }; |
|
93 | 98 | |
|
94 | 99 | QT_CHARTS_END_NAMESPACE |
@@ -81,6 +81,8 public: | |||
|
81 | 81 | bool reverseXAxis(); |
|
82 | 82 | bool reverseYAxis(); |
|
83 | 83 | |
|
84 | void setBlockOpenGL(bool enable); | |
|
85 | ||
|
84 | 86 | Q_SIGNALS: |
|
85 | 87 | void countChanged(); |
|
86 | 88 | |
@@ -96,6 +98,8 private: | |||
|
96 | 98 | bool m_visible; |
|
97 | 99 | qreal m_opacity; |
|
98 | 100 | ChartPresenter *m_presenter; |
|
101 | bool m_useOpenGL; | |
|
102 | bool m_blockOpenGL; | |
|
99 | 103 | |
|
100 | 104 | friend class QAbstractSeries; |
|
101 | 105 | friend class ChartDataSet; |
@@ -130,6 +130,18 void ScatterChartItem::markerDoubleClicked(QGraphicsItem *marker) | |||
|
130 | 130 | |
|
131 | 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 | 146 | const QVector<QPointF>& points = geometryPoints(); |
|
135 | 147 | |
@@ -199,6 +211,9 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * | |||
|
199 | 211 | Q_UNUSED(option) |
|
200 | 212 | Q_UNUSED(widget) |
|
201 | 213 | |
|
214 | if (m_series->useOpenGL()) | |
|
215 | return; | |
|
216 | ||
|
202 | 217 | QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); |
|
203 | 218 | |
|
204 | 219 | painter->save(); |
@@ -238,7 +238,7 QT_CHARTS_BEGIN_NAMESPACE | |||
|
238 | 238 | \sa pointLabelsVisible |
|
239 | 239 | */ |
|
240 | 240 | /*! |
|
241 |
\fn void QXYSeries::pointLabelsClippin |
|
|
241 | \fn void QXYSeries::pointLabelsClippingChanged(bool clipping) | |
|
242 | 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 | 406 | \fn void QXYSeriesPrivate::updated() |
|
402 | 407 | \brief \internal |
|
403 | 408 | */ |
@@ -539,7 +544,7 void QXYSeries::replace(int index, const QPointF &newPoint) | |||
|
539 | 544 | \note This is much faster than replacing data points one by one, |
|
540 | 545 | or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() |
|
541 | 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 | 548 | \sa pointsReplaced() |
|
544 | 549 | */ |
|
545 | 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 | 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 | 662 | Returns point at \a index in internal points vector. |
|
647 | 663 | */ |
|
648 | 664 | const QPointF &QXYSeries::at(int index) const |
@@ -675,6 +691,7 void QXYSeries::setPen(const QPen &pen) | |||
|
675 | 691 | emit d->updated(); |
|
676 | 692 | if (emitColorChanged) |
|
677 | 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 | 882 | Q_Q(QXYSeries); |
|
866 | 883 | |
|
867 |
const Q |
|
|
884 | const QVector<QPointF> &points = q->pointsVector(); | |
|
868 | 885 | |
|
869 | 886 | if (!points.isEmpty()) { |
|
870 | 887 | minX = points[0].x(); |
@@ -65,6 +65,7 public: | |||
|
65 | 65 | |
|
66 | 66 | int count() const; |
|
67 | 67 | QList<QPointF> points() const; |
|
68 | QVector<QPointF> pointsVector() const; | |
|
68 | 69 | const QPointF &at(int index) const; |
|
69 | 70 | |
|
70 | 71 | QXYSeries &operator << (const QPointF &point); |
@@ -117,6 +118,7 Q_SIGNALS: | |||
|
117 | 118 | void pointLabelsColorChanged(const QColor &color); |
|
118 | 119 | void pointLabelsClippingChanged(bool clipping); |
|
119 | 120 | void pointsRemoved(int index, int count); |
|
121 | void penChanged(const QPen &pen); | |
|
120 | 122 | |
|
121 | 123 | private: |
|
122 | 124 | Q_DECLARE_PRIVATE(QXYSeries) |
@@ -21,6 +21,8 | |||
|
21 | 21 | #include <private/qxyseries_p.h> |
|
22 | 22 | #include <private/chartpresenter_p.h> |
|
23 | 23 | #include <private/abstractdomain_p.h> |
|
24 | #include <private/chartdataset_p.h> | |
|
25 | #include <private/glxyseriesdata_p.h> | |
|
24 | 26 | #include <QtCharts/QXYModelMapper> |
|
25 | 27 | #include <private/qabstractaxis_p.h> |
|
26 | 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 | 118 | //handlers |
|
110 | 119 | |
|
111 | 120 | void XYChart::handlePointAdded(int index) |
@@ -113,20 +122,23 void XYChart::handlePointAdded(int index) | |||
|
113 | 122 | Q_ASSERT(index < m_series->count()); |
|
114 | 123 | Q_ASSERT(index >= 0); |
|
115 | 124 | |
|
116 | QVector<QPointF> points; | |
|
117 | ||
|
118 | if (m_dirty || m_points.isEmpty()) { | |
|
119 | points = domain()->calculateGeometryPoints(m_series->points()); | |
|
125 | if (m_series->useOpenGL()) { | |
|
126 | updateGlChart(); | |
|
120 | 127 | } else { |
|
121 |
points |
|
|
122 | QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData); | |
|
123 | if (!m_validData) | |
|
124 | m_points.clear(); | |
|
125 | else | |
|
126 | points.insert(index, point); | |
|
128 | QVector<QPointF> points; | |
|
129 | if (m_dirty || m_points.isEmpty()) { | |
|
130 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
131 | } else { | |
|
132 | points = m_points; | |
|
133 | QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), | |
|
134 | m_validData); | |
|
135 | if (!m_validData) | |
|
136 | m_points.clear(); | |
|
137 | else | |
|
138 | points.insert(index, point); | |
|
139 | } | |
|
140 | updateChart(m_points, points, index); | |
|
127 | 141 | } |
|
128 | ||
|
129 | updateChart(m_points, points, index); | |
|
130 | 142 | } |
|
131 | 143 | |
|
132 | 144 | void XYChart::handlePointRemoved(int index) |
@@ -134,16 +146,18 void XYChart::handlePointRemoved(int index) | |||
|
134 | 146 | Q_ASSERT(index <= m_series->count()); |
|
135 | 147 | Q_ASSERT(index >= 0); |
|
136 | 148 | |
|
137 | QVector<QPointF> points; | |
|
138 | ||
|
139 | if (m_dirty || m_points.isEmpty()) { | |
|
140 | points = domain()->calculateGeometryPoints(m_series->points()); | |
|
149 | if (m_series->useOpenGL()) { | |
|
150 | updateGlChart(); | |
|
141 | 151 | } else { |
|
142 |
points |
|
|
143 | points.remove(index); | |
|
152 | QVector<QPointF> points; | |
|
153 | if (m_dirty || m_points.isEmpty()) { | |
|
154 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
155 | } else { | |
|
156 | points = m_points; | |
|
157 | points.remove(index); | |
|
158 | } | |
|
159 | updateChart(m_points, points, index); | |
|
144 | 160 | } |
|
145 | ||
|
146 | updateChart(m_points, points, index); | |
|
147 | 161 | } |
|
148 | 162 | |
|
149 | 163 | void XYChart::handlePointsRemoved(int index, int count) |
@@ -151,16 +165,18 void XYChart::handlePointsRemoved(int index, int count) | |||
|
151 | 165 | Q_ASSERT(index <= m_series->count()); |
|
152 | 166 | Q_ASSERT(index >= 0); |
|
153 | 167 | |
|
154 | QVector<QPointF> points; | |
|
155 | ||
|
156 | if (m_dirty || m_points.isEmpty()) { | |
|
157 | points = domain()->calculateGeometryPoints(m_series->points()); | |
|
168 | if (m_series->useOpenGL()) { | |
|
169 | updateGlChart(); | |
|
158 | 170 | } else { |
|
159 |
points |
|
|
160 | points.remove(index, count); | |
|
171 | QVector<QPointF> points; | |
|
172 | if (m_dirty || m_points.isEmpty()) { | |
|
173 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
174 | } else { | |
|
175 | points = m_points; | |
|
176 | points.remove(index, count); | |
|
177 | } | |
|
178 | updateChart(m_points, points, index); | |
|
161 | 179 | } |
|
162 | ||
|
163 | updateChart(m_points, points, index); | |
|
164 | 180 | } |
|
165 | 181 | |
|
166 | 182 | void XYChart::handlePointReplaced(int index) |
@@ -168,34 +184,45 void XYChart::handlePointReplaced(int index) | |||
|
168 | 184 | Q_ASSERT(index < m_series->count()); |
|
169 | 185 | Q_ASSERT(index >= 0); |
|
170 | 186 | |
|
171 | QVector<QPointF> points; | |
|
172 | ||
|
173 | if (m_dirty || m_points.isEmpty()) { | |
|
174 | points = domain()->calculateGeometryPoints(m_series->points()); | |
|
187 | if (m_series->useOpenGL()) { | |
|
188 | updateGlChart(); | |
|
175 | 189 | } else { |
|
176 | QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData); | |
|
177 | if (!m_validData) | |
|
178 | m_points.clear(); | |
|
179 | points = m_points; | |
|
180 | if (m_validData) | |
|
181 | points.replace(index, point); | |
|
190 | QVector<QPointF> points; | |
|
191 | if (m_dirty || m_points.isEmpty()) { | |
|
192 | points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
193 | } else { | |
|
194 | QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), | |
|
195 | m_validData); | |
|
196 | if (!m_validData) | |
|
197 | m_points.clear(); | |
|
198 | points = m_points; | |
|
199 | if (m_validData) | |
|
200 | points.replace(index, point); | |
|
201 | } | |
|
202 | updateChart(m_points, points, index); | |
|
182 | 203 | } |
|
183 | ||
|
184 | updateChart(m_points, points, index); | |
|
185 | 204 | } |
|
186 | 205 | |
|
187 | 206 | void XYChart::handlePointsReplaced() |
|
188 | 207 | { |
|
189 | // All the points were replaced -> recalculate | |
|
190 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points()); | |
|
191 | updateChart(m_points, points, -1); | |
|
208 | if (m_series->useOpenGL()) { | |
|
209 | updateGlChart(); | |
|
210 | } else { | |
|
211 | // All the points were replaced -> recalculate | |
|
212 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
213 | updateChart(m_points, points, -1); | |
|
214 | } | |
|
192 | 215 | } |
|
193 | 216 | |
|
194 | 217 | void XYChart::handleDomainUpdated() |
|
195 | 218 | { |
|
196 | if (isEmpty()) return; | |
|
197 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points()); | |
|
198 | updateChart(m_points, points); | |
|
219 | if (m_series->useOpenGL()) { | |
|
220 | updateGlChart(); | |
|
221 | } else { | |
|
222 | if (isEmpty()) return; | |
|
223 | QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); | |
|
224 | updateChart(m_points, points); | |
|
225 | } | |
|
199 | 226 | } |
|
200 | 227 | |
|
201 | 228 | bool XYChart::isEmpty() |
@@ -6,12 +6,14 SOURCES += \ | |||
|
6 | 6 | $$PWD/qxyseries.cpp \ |
|
7 | 7 | $$PWD/qxymodelmapper.cpp \ |
|
8 | 8 | $$PWD/qvxymodelmapper.cpp \ |
|
9 | $$PWD/qhxymodelmapper.cpp | |
|
9 | $$PWD/qhxymodelmapper.cpp \ | |
|
10 | $$PWD/glxyseriesdata.cpp | |
|
10 | 11 | |
|
11 | 12 | PRIVATE_HEADERS += \ |
|
12 | 13 | $$PWD/xychart_p.h \ |
|
13 | 14 | $$PWD/qxyseries_p.h \ |
|
14 | $$PWD/qxymodelmapper_p.h | |
|
15 | $$PWD/qxymodelmapper_p.h \ | |
|
16 | $$PWD/glxyseriesdata_p.h | |
|
15 | 17 | |
|
16 | 18 | PUBLIC_HEADERS += \ |
|
17 | 19 | $$PWD/qxyseries.h \ |
@@ -76,6 +76,7 Q_SIGNALS: | |||
|
76 | 76 | |
|
77 | 77 | protected: |
|
78 | 78 | virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1); |
|
79 | virtual void updateGlChart(); | |
|
79 | 80 | |
|
80 | 81 | private: |
|
81 | 82 | inline bool isEmpty(); |
@@ -33,7 +33,9 SOURCES += \ | |||
|
33 | 33 | declarativemargins.cpp \ |
|
34 | 34 | declarativeaxes.cpp \ |
|
35 | 35 | declarativepolarchart.cpp \ |
|
36 | declarativeboxplotseries.cpp | |
|
36 | declarativeboxplotseries.cpp \ | |
|
37 | declarativechartnode.cpp \ | |
|
38 | declarativerendernode.cpp | |
|
37 | 39 | |
|
38 | 40 | HEADERS += \ |
|
39 | 41 | declarativechart.h \ |
@@ -49,7 +51,9 HEADERS += \ | |||
|
49 | 51 | declarativemargins.h \ |
|
50 | 52 | declarativeaxes.h \ |
|
51 | 53 | declarativepolarchart.h \ |
|
52 | declarativeboxplotseries.h | |
|
54 | declarativeboxplotseries.h \ | |
|
55 | declarativechartnode.h \ | |
|
56 | declarativerendernode.h | |
|
53 | 57 | |
|
54 | 58 | OTHER_FILES = qmldir |
|
55 | 59 |
@@ -25,6 +25,8 | |||
|
25 | 25 | #include "declarativesplineseries.h" |
|
26 | 26 | #include "declarativeboxplotseries.h" |
|
27 | 27 | #include "declarativescatterseries.h" |
|
28 | #include "declarativechartnode.h" | |
|
29 | #include "declarativerendernode.h" | |
|
28 | 30 | #include <QtCharts/QBarCategoryAxis> |
|
29 | 31 | #include <QtCharts/QValueAxis> |
|
30 | 32 | #include <QtCharts/QLogValueAxis> |
@@ -34,6 +36,7 | |||
|
34 | 36 | #include <private/chartdataset_p.h> |
|
35 | 37 | #include "declarativeaxes.h" |
|
36 | 38 | #include <private/qchart_p.h> |
|
39 | #include <private/chartpresenter_p.h> | |
|
37 | 40 | #include <QtCharts/QPolarChart> |
|
38 | 41 | |
|
39 | 42 | #ifndef QT_ON_ARM |
@@ -88,6 +91,7 QT_CHARTS_BEGIN_NAMESPACE | |||
|
88 | 91 | /*! |
|
89 | 92 | \qmlproperty easing ChartView::animationEasingCurve |
|
90 | 93 | The easing curve of the animation for the chart. |
|
94 | */ | |
|
91 | 95 | |
|
92 | 96 | /*! |
|
93 | 97 | \qmlproperty Font ChartView::titleFont |
@@ -312,34 +316,42 QT_CHARTS_BEGIN_NAMESPACE | |||
|
312 | 316 | */ |
|
313 | 317 | |
|
314 | 318 | DeclarativeChart::DeclarativeChart(QQuickItem *parent) |
|
315 |
: QQuick |
|
|
319 | : QQuickItem(parent) | |
|
316 | 320 | { |
|
317 | 321 | initChart(QChart::ChartTypeCartesian); |
|
318 | 322 | } |
|
319 | 323 | |
|
320 | 324 | DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent) |
|
321 |
: QQuick |
|
|
325 | : QQuickItem(parent) | |
|
322 | 326 | { |
|
323 | 327 | initChart(type); |
|
324 | 328 | } |
|
325 | 329 | |
|
326 | 330 | void DeclarativeChart::initChart(QChart::ChartType type) |
|
327 | 331 | { |
|
328 |
m_ |
|
|
332 | m_sceneImage = 0; | |
|
333 | m_sceneImageDirty = false; | |
|
329 | 334 | m_guiThreadId = QThread::currentThreadId(); |
|
330 | 335 | m_paintThreadId = 0; |
|
331 | 336 | m_updatePending = false; |
|
332 | 337 | |
|
338 | setFlag(ItemHasContents, true); | |
|
339 | ||
|
333 | 340 | if (type == QChart::ChartTypePolar) |
|
334 | 341 | m_chart = new QPolarChart(); |
|
335 | 342 | else |
|
336 | 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 | 348 | m_scene = new QGraphicsScene(this); |
|
339 | 349 | m_scene->addItem(m_chart); |
|
340 | 350 | |
|
341 | 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 | 355 | connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool))); |
|
344 | 356 | |
|
345 | 357 | setAcceptedMouseButtons(Qt::AllButtons); |
@@ -377,10 +389,7 void DeclarativeChart::changeMargins(int top, int bottom, int left, int right) | |||
|
377 | 389 | DeclarativeChart::~DeclarativeChart() |
|
378 | 390 | { |
|
379 | 391 | delete m_chart; |
|
380 |
m_sceneImage |
|
|
381 | delete m_currentSceneImage; | |
|
382 | m_currentSceneImage = 0; | |
|
383 | m_sceneImageLock.unlock(); | |
|
392 | delete m_sceneImage; | |
|
384 | 393 | } |
|
385 | 394 | |
|
386 | 395 | void DeclarativeChart::childEvent(QChildEvent *event) |
@@ -493,19 +502,79 void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF & | |||
|
493 | 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) { | |
|
501 | // Rendering in gui thread, no need for shenannigans, just update | |
|
502 | update(); | |
|
503 | } else { | |
|
504 | // Multi-threaded rendering, need to ensure scene is actually rendered in gui thread | |
|
505 | if (!m_updatePending) { | |
|
509 | if (!node) { | |
|
510 | node = new DeclarativeChartNode(window()); | |
|
511 | connect(window(), &QQuickWindow::beforeRendering, | |
|
512 | node->glRenderNode(), &DeclarativeRenderNode::render); | |
|
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(); | |
|
543 | } | |
|
544 | ||
|
545 | // Copy chart (if dirty) to chart node | |
|
546 | if (m_sceneImageDirty) { | |
|
547 | node->createTextureFromImage(*m_sceneImage); | |
|
548 | m_sceneImageDirty = false; | |
|
549 | } | |
|
550 | ||
|
551 | node->setRect(bRect); | |
|
552 | ||
|
553 | return node; | |
|
554 | } | |
|
555 | ||
|
556 | void DeclarativeChart::sceneChanged(QList<QRectF> region) | |
|
557 | { | |
|
558 | const int count = region.size(); | |
|
559 | const qreal limitSize = 0.01; | |
|
560 | if (count && !m_updatePending) { | |
|
561 | qreal totalSize = 0.0; | |
|
562 | for (int i = 0; i < count; i++) { | |
|
563 | const QRectF ® = region.at(i); | |
|
564 | totalSize += (reg.height() * reg.width()); | |
|
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) { | |
|
506 | 572 | m_updatePending = true; |
|
507 | 573 | // Do async render to avoid some unnecessary renders. |
|
508 | QTimer::singleShot(0, this, SLOT(renderScene())); | |
|
574 | emit needRender(); | |
|
575 | } else { | |
|
576 | // We do want to call update to trigger possible gl series updates. | |
|
577 | update(); | |
|
509 | 578 | } |
|
510 | 579 | } |
|
511 | 580 | } |
@@ -513,45 +582,22 void DeclarativeChart::sceneChanged(QList<QRectF> region) | |||
|
513 | 582 | void DeclarativeChart::renderScene() |
|
514 | 583 | { |
|
515 | 584 | 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 | ||
|
527 | update(); | |
|
528 | } | |
|
529 | ||
|
530 | void DeclarativeChart::paint(QPainter *painter) | |
|
531 | { | |
|
532 | if (!m_paintThreadId) { | |
|
533 | m_paintThreadId = QThread::currentThreadId(); | |
|
534 | if (m_guiThreadId == m_paintThreadId) { | |
|
535 | // No need for scene image in single threaded rendering, so delete | |
|
536 | // the one that got made by default before the rendering type was | |
|
537 | // detected. | |
|
538 | delete m_currentSceneImage; | |
|
539 | m_currentSceneImage = 0; | |
|
540 | } | |
|
585 | m_sceneImageDirty = true; | |
|
586 | QSize chartSize = m_chart->size().toSize(); | |
|
587 | if (!m_sceneImage || chartSize != m_sceneImage->size()) { | |
|
588 | delete m_sceneImage; | |
|
589 | m_sceneImage = new QImage(chartSize, QImage::Format_ARGB32); | |
|
590 | m_sceneImage->fill(Qt::transparent); | |
|
541 | 591 | } |
|
542 | 592 | |
|
543 | if (m_guiThreadId == m_paintThreadId) { | |
|
544 | QRectF renderRect(QPointF(0, 0), m_chart->size()); | |
|
545 | m_scene->render(painter, renderRect, renderRect); | |
|
546 | } else { | |
|
547 | m_sceneImageLock.lock(); | |
|
548 | if (m_currentSceneImage) { | |
|
549 | QRect imageRect(QPoint(0, 0), m_currentSceneImage->size()); | |
|
550 | QRect itemRect(QPoint(0, 0), QSize(width(), height())); | |
|
551 | painter->drawImage(itemRect, *m_currentSceneImage, imageRect); | |
|
552 | } | |
|
553 | m_sceneImageLock.unlock(); | |
|
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 | 603 | void DeclarativeChart::mousePressEvent(QMouseEvent *event) |
@@ -19,11 +19,11 | |||
|
19 | 19 | #ifndef DECLARATIVECHART_H |
|
20 | 20 | #define DECLARATIVECHART_H |
|
21 | 21 | |
|
22 | #include <private/glxyseriesdata_p.h> | |
|
23 | ||
|
22 | 24 | #include <QtCore/QtGlobal> |
|
23 | 25 | #include <QtQuick/QQuickItem> |
|
24 | #include <QtQuick/QQuickPaintedItem> | |
|
25 | 26 | #include <QtWidgets/QGraphicsScene> |
|
26 | #include <QtCore/QMutex> | |
|
27 | 27 | |
|
28 | 28 | #include <QtCharts/QChart> |
|
29 | 29 | #include <QtCore/QLocale> |
@@ -34,7 +34,7 class DeclarativeMargins; | |||
|
34 | 34 | class Domain; |
|
35 | 35 | class DeclarativeAxes; |
|
36 | 36 | |
|
37 |
class DeclarativeChart : public QQuick |
|
|
37 | class DeclarativeChart : public QQuickItem | |
|
38 | 38 | { |
|
39 | 39 | Q_OBJECT |
|
40 | 40 | Q_PROPERTY(Theme theme READ theme WRITE setTheme) |
@@ -102,7 +102,7 public: // From parent classes | |||
|
102 | 102 | void childEvent(QChildEvent *event); |
|
103 | 103 | void componentComplete(); |
|
104 | 104 | void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); |
|
105 | void paint(QPainter *painter); | |
|
105 | QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); | |
|
106 | 106 | protected: |
|
107 | 107 | void mousePressEvent(QMouseEvent *event); |
|
108 | 108 | void mouseReleaseEvent(QMouseEvent *event); |
@@ -199,6 +199,7 Q_SIGNALS: | |||
|
199 | 199 | Q_REVISION(4) void localeChanged(); |
|
200 | 200 | Q_REVISION(5) void animationDurationChanged(int msecs); |
|
201 | 201 | Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve); |
|
202 | void needRender(); | |
|
202 | 203 | |
|
203 | 204 | private Q_SLOTS: |
|
204 | 205 | void changeMargins(int top, int bottom, int left, int right); |
@@ -227,12 +228,13 private: | |||
|
227 | 228 | QPoint m_lastMouseMoveScreenPoint; |
|
228 | 229 | Qt::MouseButton m_mousePressButton; |
|
229 | 230 | Qt::MouseButtons m_mousePressButtons; |
|
230 |
Q |
|
|
231 | QImage *m_currentSceneImage; | |
|
231 | QImage *m_sceneImage; | |
|
232 | bool m_sceneImageDirty; | |
|
232 | 233 | bool m_updatePending; |
|
233 | 234 | Qt::HANDLE m_paintThreadId; |
|
234 | 235 | Qt::HANDLE m_guiThreadId; |
|
235 | 236 | DeclarativeMargins *m_margins; |
|
237 | GLXYSeriesDataManager *m_glXYDataManager; | |
|
236 | 238 | }; |
|
237 | 239 | |
|
238 | 240 | QT_CHARTS_END_NAMESPACE |
@@ -19,8 +19,8 | |||
|
19 | 19 | #include "../qxyseries/tst_qxyseries.h" |
|
20 | 20 | #include <QtCharts/QLineSeries> |
|
21 | 21 | |
|
22 | ||
|
23 | 22 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |
|
24 | 24 | |
|
25 | 25 | class tst_QLineSeries : public tst_QXYSeries |
|
26 | 26 | { |
@@ -76,6 +76,7 void tst_QLineSeries::qlineseries() | |||
|
76 | 76 | QCOMPARE(series.count(),0); |
|
77 | 77 | QCOMPARE(series.brush(), QBrush()); |
|
78 | 78 | QCOMPARE(series.points(), QList<QPointF>()); |
|
79 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |
|
79 | 80 | QCOMPARE(series.pen(), QPen()); |
|
80 | 81 | QCOMPARE(series.pointsVisible(), false); |
|
81 | 82 | QCOMPARE(series.pointLabelsVisible(), false); |
@@ -20,6 +20,7 | |||
|
20 | 20 | #include <QtCharts/QScatterSeries> |
|
21 | 21 | |
|
22 | 22 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |
|
23 | 24 | |
|
24 | 25 | class tst_QScatterSeries : public tst_QXYSeries |
|
25 | 26 | { |
@@ -75,6 +76,7 void tst_QScatterSeries::qscatterseries() | |||
|
75 | 76 | QCOMPARE(series.count(),0); |
|
76 | 77 | QCOMPARE(series.brush(), QBrush()); |
|
77 | 78 | QCOMPARE(series.points(), QList<QPointF>()); |
|
79 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |
|
78 | 80 | QCOMPARE(series.pen(), QPen()); |
|
79 | 81 | QCOMPARE(series.pointsVisible(), false); |
|
80 | 82 |
@@ -20,6 +20,7 | |||
|
20 | 20 | #include <QtCharts/QSplineSeries> |
|
21 | 21 | |
|
22 | 22 | Q_DECLARE_METATYPE(QList<QPointF>) |
|
23 | Q_DECLARE_METATYPE(QVector<QPointF>) | |
|
23 | 24 | |
|
24 | 25 | class tst_QSplineSeries : public tst_QXYSeries |
|
25 | 26 | { |
@@ -73,6 +74,7 void tst_QSplineSeries::qsplineseries() | |||
|
73 | 74 | QCOMPARE(series.count(),0); |
|
74 | 75 | QCOMPARE(series.brush(), QBrush()); |
|
75 | 76 | QCOMPARE(series.points(), QList<QPointF>()); |
|
77 | QCOMPARE(series.pointsVector(), QVector<QPointF>()); | |
|
76 | 78 | QCOMPARE(series.pen(), QPen()); |
|
77 | 79 | QCOMPARE(series.pointsVisible(), false); |
|
78 | 80 |
@@ -198,6 +198,7 void tst_QXYSeries::append_raw() | |||
|
198 | 198 | TRY_COMPARE(spy0.count(), 0); |
|
199 | 199 | TRY_COMPARE(addedSpy.count(), points.count()); |
|
200 | 200 | QCOMPARE(m_series->points(), points); |
|
201 | QCOMPARE(m_series->pointsVector(), points.toVector()); | |
|
201 | 202 | |
|
202 | 203 | // Process events between appends |
|
203 | 204 | foreach (const QPointF &point, otherPoints) { |
General Comments 0
You need to be logged in to leave comments.
Login now