| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Charts module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <private/xychart_p.h> |
| #include <QtCharts/QXYSeries> |
| #include <private/qxyseries_p.h> |
| #include <private/chartpresenter_p.h> |
| #include <private/abstractdomain_p.h> |
| #include <private/chartdataset_p.h> |
| #include <private/glxyseriesdata_p.h> |
| #include <QtCharts/QXYModelMapper> |
| #include <private/qabstractaxis_p.h> |
| #include <QtGui/QPainter> |
| #include <QtCore/QAbstractItemModel> |
| |
| |
| QT_CHARTS_BEGIN_NAMESPACE |
| |
| XYChart::XYChart(QXYSeries *series, QGraphicsItem *item): |
| ChartItem(series->d_func(),item), |
| m_series(series), |
| m_animation(0), |
| m_dirty(true) |
| { |
| QObject::connect(series, SIGNAL(pointReplaced(int)), this, SLOT(handlePointReplaced(int))); |
| QObject::connect(series, SIGNAL(pointsReplaced()), this, SLOT(handlePointsReplaced())); |
| QObject::connect(series, SIGNAL(pointAdded(int)), this, SLOT(handlePointAdded(int))); |
| QObject::connect(series, SIGNAL(pointRemoved(int)), this, SLOT(handlePointRemoved(int))); |
| QObject::connect(series, SIGNAL(pointsRemoved(int, int)), this, SLOT(handlePointsRemoved(int, int))); |
| QObject::connect(this, SIGNAL(clicked(QPointF)), series, SIGNAL(clicked(QPointF))); |
| QObject::connect(this, SIGNAL(hovered(QPointF,bool)), series, SIGNAL(hovered(QPointF,bool))); |
| QObject::connect(this, SIGNAL(pressed(QPointF)), series, SIGNAL(pressed(QPointF))); |
| QObject::connect(this, SIGNAL(released(QPointF)), series, SIGNAL(released(QPointF))); |
| QObject::connect(this, SIGNAL(doubleClicked(QPointF)), series, SIGNAL(doubleClicked(QPointF))); |
| QObject::connect(series, &QAbstractSeries::useOpenGLChanged, |
| this, &XYChart::handleDomainUpdated); |
| } |
| |
| void XYChart::setGeometryPoints(const QVector<QPointF> &points) |
| { |
| m_points = points; |
| } |
| |
| void XYChart::setAnimation(XYAnimation *animation) |
| { |
| m_animation = animation; |
| } |
| |
| void XYChart::setDirty(bool dirty) |
| { |
| m_dirty = dirty; |
| } |
| |
| // Returns a vector with same size as geometryPoints vector, indicating |
| // the off grid status of points. |
| QVector<bool> XYChart::offGridStatusVector() |
| { |
| qreal minX = domain()->minX(); |
| qreal maxX = domain()->maxX(); |
| qreal minY = domain()->minY(); |
| qreal maxY = domain()->maxY(); |
| |
| QVector<bool> returnVector; |
| returnVector.resize(m_points.size()); |
| // During remove animation series may have different number of points, |
| // so ensure we don't go over the index. No need to check for zero points, this |
| // will not be called in such a situation. |
| const int seriesLastIndex = m_series->count() - 1; |
| |
| for (int i = 0; i < m_points.size(); i++) { |
| const QPointF &seriesPoint = m_series->at(qMin(seriesLastIndex, i)); |
| if (seriesPoint.x() < minX |
| || seriesPoint.x() > maxX |
| || seriesPoint.y() < minY |
| || seriesPoint.y() > maxY) { |
| returnVector[i] = true; |
| } else { |
| returnVector[i] = false; |
| } |
| } |
| return returnVector; |
| } |
| |
| void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index) |
| { |
| |
| if (m_animation) { |
| m_animation->setup(oldPoints, newPoints, index); |
| m_points = newPoints; |
| setDirty(false); |
| presenter()->startAnimation(m_animation); |
| } else { |
| m_points = newPoints; |
| updateGeometry(); |
| } |
| } |
| |
| void XYChart::updateGlChart() |
| { |
| dataSet()->glXYSeriesDataManager()->setPoints(m_series, domain()); |
| presenter()->updateGLWidget(); |
| updateGeometry(); |
| } |
| |
| // Doesn't update gl geometry, but refreshes the chart |
| void XYChart::refreshGlChart() |
| { |
| if (presenter()) |
| presenter()->updateGLWidget(); |
| } |
| |
| //handlers |
| |
| void XYChart::handlePointAdded(int index) |
| { |
| Q_ASSERT(index < m_series->count()); |
| Q_ASSERT(index >= 0); |
| |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| QVector<QPointF> points; |
| if (m_dirty || m_points.isEmpty()) { |
| points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| } else { |
| points = m_points; |
| QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), |
| m_validData); |
| if (!m_validData) |
| m_points.clear(); |
| else |
| points.insert(index, point); |
| } |
| updateChart(m_points, points, index); |
| } |
| } |
| |
| void XYChart::handlePointRemoved(int index) |
| { |
| Q_ASSERT(index <= m_series->count()); |
| Q_ASSERT(index >= 0); |
| |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| QVector<QPointF> points; |
| if (m_dirty || m_points.isEmpty()) { |
| points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| } else { |
| points = m_points; |
| points.remove(index); |
| } |
| updateChart(m_points, points, index); |
| } |
| } |
| |
| void XYChart::handlePointsRemoved(int index, int count) |
| { |
| Q_ASSERT(index <= m_series->count()); |
| Q_ASSERT(index >= 0); |
| |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| QVector<QPointF> points; |
| if (m_dirty || m_points.isEmpty()) { |
| points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| } else { |
| points = m_points; |
| points.remove(index, count); |
| } |
| updateChart(m_points, points, index); |
| } |
| } |
| |
| void XYChart::handlePointReplaced(int index) |
| { |
| Q_ASSERT(index < m_series->count()); |
| Q_ASSERT(index >= 0); |
| |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| QVector<QPointF> points; |
| if (m_dirty || m_points.isEmpty()) { |
| points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| } else { |
| QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index), |
| m_validData); |
| if (!m_validData) |
| m_points.clear(); |
| points = m_points; |
| if (m_validData) |
| points.replace(index, point); |
| } |
| updateChart(m_points, points, index); |
| } |
| } |
| |
| void XYChart::handlePointsReplaced() |
| { |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| // All the points were replaced -> recalculate |
| QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| updateChart(m_points, points, -1); |
| } |
| } |
| |
| void XYChart::handleDomainUpdated() |
| { |
| if (m_series->useOpenGL()) { |
| updateGlChart(); |
| } else { |
| if (isEmpty()) return; |
| QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector()); |
| updateChart(m_points, points); |
| } |
| } |
| |
| bool XYChart::isEmpty() |
| { |
| return domain()->isEmpty() || m_series->points().isEmpty(); |
| } |
| |
| QT_CHARTS_END_NAMESPACE |
| |
| #include "moc_xychart_p.cpp" |