declarativechart.cpp
1250 lines
| 39.8 KiB
| text/x-c
|
CppLexer
|
r2854 | /**************************************************************************** | ||
|
r830 | ** | ||
|
r2854 | ** Copyright (C) 2016 The Qt Company Ltd. | ||
** Contact: https://www.qt.io/licensing/ | ||||
|
r830 | ** | ||
|
r2854 | ** This file is part of the Qt Charts module of the Qt Toolkit. | ||
|
r830 | ** | ||
|
r2854 | ** $QT_BEGIN_LICENSE:GPL$ | ||
|
r2845 | ** 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 | ||||
|
r2854 | ** 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. | ||||
|
r830 | ** | ||
|
r2845 | ** $QT_END_LICENSE$ | ||
** | ||||
|
r2854 | ****************************************************************************/ | ||
|
r830 | |||
|
r1 | #include "declarativechart.h" | ||
|
r2714 | #include <QtGui/QPainter> | ||
|
r1240 | #include "declarativelineseries.h" | ||
#include "declarativeareaseries.h" | ||||
#include "declarativebarseries.h" | ||||
#include "declarativepieseries.h" | ||||
#include "declarativesplineseries.h" | ||||
|
r2548 | #include "declarativeboxplotseries.h" | ||
|
r1240 | #include "declarativescatterseries.h" | ||
|
r2820 | #include "declarativechartnode.h" | ||
#include "declarativerendernode.h" | ||||
|
r2714 | #include <QtCharts/QBarCategoryAxis> | ||
#include <QtCharts/QValueAxis> | ||||
#include <QtCharts/QLogValueAxis> | ||||
#include <QtCharts/QCategoryAxis> | ||||
#include <private/qabstractseries_p.h> | ||||
|
r1928 | #include "declarativemargins.h" | ||
|
r2714 | #include <private/chartdataset_p.h> | ||
|
r2296 | #include "declarativeaxes.h" | ||
|
r2714 | #include <private/qchart_p.h> | ||
|
r2820 | #include <private/chartpresenter_p.h> | ||
|
r2714 | #include <QtCharts/QPolarChart> | ||
|
r1 | |||
|
r2861 | #ifndef QT_QREAL_IS_FLOAT | ||
|
r2714 | #include <QtCharts/QDateTimeAxis> | ||
|
r1867 | #endif | ||
|
r2714 | #include <QtWidgets/QGraphicsSceneMouseEvent> | ||
#include <QtWidgets/QGraphicsSceneHoverEvent> | ||||
#include <QtWidgets/QApplication> | ||||
#include <QtCore/QTimer> | ||||
#include <QtCore/QThread> | ||||
|
r2495 | |||
|
r2712 | QT_CHARTS_BEGIN_NAMESPACE | ||
|
r120 | |||
|
r1443 | /*! | ||
|
r2639 | \qmltype ChartView | ||
\instantiates DeclarativeChart | ||||
|
r2712 | \inqmlmodule QtCharts | ||
|
r1443 | |||
|
r2727 | \brief Chart element. | ||
|
r2712 | ChartView element is the parent that is responsible for showing different chart series types. | ||
|
r1443 | |||
|
r2712 | The following QML shows how to create a simple chart with one pie series: | ||
\snippet qmlpiechart/qml/qmlpiechart/main.qml 1 | ||||
\snippet qmlpiechart/qml/qmlpiechart/main.qml 2 | ||||
\snippet qmlpiechart/qml/qmlpiechart/main.qml 3 | ||||
\beginfloatleft | ||||
\image examples_qmlpiechart.png | ||||
\endfloat | ||||
\clearfloat | ||||
|
r1443 | */ | ||
|
r1462 | /*! | ||
\qmlproperty Theme ChartView::theme | ||||
Theme defines the visual appearance of the chart, including for example colors, fonts, line | ||||
widths and chart background. | ||||
*/ | ||||
/*! | ||||
|
r2113 | \qmlproperty Animation ChartView::animationOptions | ||
|
r1462 | Animation configuration of the chart. One of ChartView.NoAnimation, ChartView.GridAxisAnimations, | ||
ChartView.SeriesAnimations or ChartView.AllAnimations. | ||||
*/ | ||||
|
r2804 | /*! | ||
\qmlproperty int ChartView::animationDuration | ||||
The duration of the animation for the chart. | ||||
*/ | ||||
/*! | ||||
\qmlproperty easing ChartView::animationEasingCurve | ||||
The easing curve of the animation for the chart. | ||||
|
r2820 | */ | ||
|
r2804 | |||
|
r1517 | /*! | ||
\qmlproperty Font ChartView::titleFont | ||||
|
r2639 | The title font of the chart. | ||
|
r1517 | |||
|
r2639 | See the Qt documentation for more details of Font. | ||
|
r1517 | */ | ||
|
r1462 | /*! | ||
\qmlproperty string ChartView::title | ||||
The title of the chart, shown on top of the chart. | ||||
|
r1473 | \sa ChartView::titleColor | ||
|
r1462 | */ | ||
/*! | ||||
|
r2113 | \qmlproperty color ChartView::titleColor | ||
|
r1462 | The color of the title text. | ||
*/ | ||||
|
r1473 | /*! | ||
\qmlproperty Legend ChartView::legend | ||||
The legend of the chart. Legend lists all the series, pie slices and bar sets added on the chart. | ||||
*/ | ||||
/*! | ||||
\qmlproperty int ChartView::count | ||||
The count of series added to the chart. | ||||
*/ | ||||
/*! | ||||
\qmlproperty color ChartView::backgroundColor | ||||
The color of the chart's background. By default background color is defined by chart theme. | ||||
\sa ChartView::theme | ||||
*/ | ||||
|
r2549 | /*! | ||
\qmlproperty real ChartView::backgroundRoundness | ||||
|
r2785 | The diameter of the rounding circle at the corners of the chart background. | ||
|
r2549 | */ | ||
|
r2498 | /*! | ||
\qmlproperty color ChartView::plotAreaColor | ||||
The color of the background of the chart's plot area. By default plot area background uses chart's | ||||
background color. | ||||
\sa ChartView::backgroundColor | ||||
*/ | ||||
|
r2770 | /*! | ||
\qmlproperty list<AbstractAxis> ChartView::axes | ||||
The axes of the ChartView. | ||||
*/ | ||||
|
r1473 | /*! | ||
\qmlproperty bool ChartView::dropShadowEnabled | ||||
The chart's border drop shadow. Set to true to enable drop shadow. | ||||
*/ | ||||
|
r1929 | /*! | ||
\qmlproperty rect ChartView::plotArea | ||||
The area on the ChartView that is used for drawing series. This is the ChartView rect without the | ||||
margins. | ||||
|
r2742 | \sa ChartView::margins | ||
|
r1524 | */ | ||
|
r2369 | /*! | ||
\qmlproperty Margins ChartView::margins | ||||
The minimum margins allowed between the outer bounds and the plotArea of the ChartView. Margins | ||||
area of ChartView is used for drawing title, axes and legend. | ||||
*/ | ||||
|
r2707 | /*! | ||
\qmlproperty bool ChartView::localizeNumbers | ||||
\since QtCharts 2.0 | ||||
When \c{true}, all generated numbers appearing in various series and axis labels will be | ||||
localized using the default QLocale of the application, which defaults to the system locale. | ||||
When \c{false}, the "C" locale is always used. | ||||
|
r2710 | Defaults to \c{false}. | ||
\sa locale | ||||
*/ | ||||
/*! | ||||
\qmlproperty locale ChartView::locale | ||||
\since QtCharts 2.0 | ||||
Sets the locale used to format various chart labels when localizeNumbers is \c{true}. | ||||
This also determines the locale used to format DateTimeAxis labels regardless of | ||||
localizeNumbers property. | ||||
Defaults to application default locale at the time the chart is constructed. | ||||
\sa localizeNumbers | ||||
|
r2707 | */ | ||
|
r1521 | /*! | ||
\qmlmethod AbstractSeries ChartView::series(int index) | ||||
Returns the series with \a index on the chart. This allows you to loop through the series of a chart together with | ||||
the count property of the chart. | ||||
*/ | ||||
/*! | ||||
\qmlmethod AbstractSeries ChartView::series(string name) | ||||
Returns the first series on the chart with \a name. If there is no series with that name, returns null. | ||||
*/ | ||||
/*! | ||||
|
r1960 | \qmlmethod AbstractSeries ChartView::createSeries(SeriesType type, string name, AbstractAxis axisX, AbstractAxis axisY) | ||
Creates a series object of \a type to the chart with name \a name, optional axis \a axisX and | ||||
optional axis \a axisY. For example: | ||||
|
r1521 | \code | ||
|
r1960 | // lineSeries is a LineSeries object that has already been added to the ChartView; re-use it's axes | ||
var myAxisX = chartView.axisX(lineSeries); | ||||
var myAxisY = chartView.axisY(lineSeries); | ||||
var scatter = chartView.createSeries(ChartView.SeriesTypeScatter, "scatter series", myAxisX, myAxisY); | ||||
|
r1521 | \endcode | ||
*/ | ||||
|
r1948 | /*! | ||
\qmlmethod ChartView::removeSeries(AbstractSeries series) | ||||
Removes the \a series from the chart. The series object is also destroyed. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::removeAllSeries() | ||||
Removes all series from the chart. All the series objects are also destroyed. | ||||
*/ | ||||
|
r1960 | /*! | ||
|
r2068 | \qmlmethod Axis ChartView::axisX(AbstractSeries series) | ||
|
r1960 | The x-axis of the series. | ||
*/ | ||||
|
r2770 | /*! | ||
\qmlmethod ChartView::setAxisX(AbstractAxis axis, AbstractSeries series) | ||||
Set the x-axis of the series. | ||||
*/ | ||||
|
r1462 | /*! | ||
|
r2068 | \qmlmethod Axis ChartView::axisY(AbstractSeries series) | ||
|
r1960 | The y-axis of the series. | ||
|
r1521 | */ | ||
/*! | ||||
|
r2770 | \qmlmethod ChartView::setAxisY(AbstractAxis axis, AbstractSeries series) | ||
Set the y-axis of the series. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::zoom(real factor) | ||||
|
r1521 | Zooms in by \a factor on the center of the chart. | ||
|
r2788 | |||
A factor over 1.0 zooms the view in and factor between 0.0 and 1.0 zooms out. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::zoomIn() | ||||
Zooms in the view by a factor of two. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::zoomIn(rect rectangle) | ||||
Zooms in the view to a maximum level at which \a rectangle is still fully visible. | ||||
\note This is not supported for polar charts. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::zoomOut() | ||||
Zooms out the view by a factor of two. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::zoomReset() | ||||
Resets the series domains to what they were before any zoom method was called. | ||||
Note that this will also reset any scrolls and explicit axis range settings done between | ||||
the first zoom operation and calling this method. If no zoom operation has been | ||||
done, this method does nothing. | ||||
|
r1521 | */ | ||
|
r2813 | /*! | ||
\qmlmethod ChartView::isZoomed() | ||||
Returns true if any series has a zoomed domain. | ||||
*/ | ||||
|
r1521 | /*! | ||
\qmlmethod ChartView::scrollLeft(real pixels) | ||||
Scrolls to left by \a pixels. This is a convenience function that suits for example for key navigation. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::scrollRight(real pixels) | ||||
Scrolls to right by \a pixels. This is a convenience function that suits for example for key navigation. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::scrollUp(real pixels) | ||||
Scrolls up by \a pixels. This is a convenience function that suits for example for key navigation. | ||||
*/ | ||||
/*! | ||||
\qmlmethod ChartView::scrollDown(real pixels) | ||||
Scrolls down by \a pixels. This is a convenience function that suits for example for key navigation. | ||||
|
r1462 | */ | ||
|
r2786 | /*! | ||
\qmlmethod point ChartView::mapToValue(point position, AbstractSeries series) | ||||
Returns the value in the \a series domain that corresponds to the \a position relative to the | ||||
chart. | ||||
*/ | ||||
/*! | ||||
\qmlmethod point ChartView::mapToPosition(point value, AbstractSeries series) | ||||
Returns the position on the chart that corresponds to the \a value in the \a series domain. | ||||
*/ | ||||
|
r2068 | /*! | ||
\qmlsignal ChartView::seriesAdded(AbstractSeries series) | ||||
The \a series has been added to the chart. | ||||
*/ | ||||
/*! | ||||
\qmlsignal ChartView::seriesRemoved(AbstractSeries series) | ||||
The \a series has been removed from the chart. Please note that \a series is no longer a valid | ||||
object after the signal handler has completed. | ||||
*/ | ||||
|
r2712 | DeclarativeChart::DeclarativeChart(QQuickItem *parent) | ||
|
r2820 | : QQuickItem(parent) | ||
|
r1 | { | ||
|
r2483 | initChart(QChart::ChartTypeCartesian); | ||
} | ||||
|
r2712 | DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent) | ||
|
r2820 | : QQuickItem(parent) | ||
|
r2483 | { | ||
initChart(type); | ||||
} | ||||
void DeclarativeChart::initChart(QChart::ChartType type) | ||||
{ | ||||
|
r2820 | m_sceneImage = 0; | ||
m_sceneImageDirty = false; | ||||
|
r2850 | m_sceneImageNeedsClear = false; | ||
|
r2506 | m_guiThreadId = QThread::currentThreadId(); | ||
m_paintThreadId = 0; | ||||
|
r2594 | m_updatePending = false; | ||
|
r2505 | |||
|
r2820 | setFlag(ItemHasContents, true); | ||
|
r2488 | if (type == QChart::ChartTypePolar) | ||
m_chart = new QPolarChart(); | ||||
else | ||||
m_chart = new QChart(); | ||||
|
r2820 | m_chart->d_ptr->m_presenter->glSetUseWidget(false); | ||
m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager(); | ||||
|
r2488 | m_scene = new QGraphicsScene(this); | ||
m_scene->addItem(m_chart); | ||||
setAntialiasing(QQuickItem::antialiasing()); | ||||
|
r2820 | connect(m_scene, &QGraphicsScene::changed, this, &DeclarativeChart::sceneChanged); | ||
connect(this, &DeclarativeChart::needRender, this, &DeclarativeChart::renderScene, | ||||
Qt::QueuedConnection); | ||||
|
r2488 | connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool))); | ||
|
r2495 | |||
setAcceptedMouseButtons(Qt::AllButtons); | ||||
setAcceptHoverEvents(true); | ||||
|
r2488 | |||
|
r2090 | m_margins = new DeclarativeMargins(this); | ||
m_margins->setTop(m_chart->margins().top()); | ||||
m_margins->setLeft(m_chart->margins().left()); | ||||
m_margins->setRight(m_chart->margins().right()); | ||||
m_margins->setBottom(m_chart->margins().bottom()); | ||||
|
r2742 | connect(m_margins, SIGNAL(topChanged(int,int,int,int)), | ||
this, SLOT(changeMargins(int,int,int,int))); | ||||
connect(m_margins, SIGNAL(bottomChanged(int,int,int,int)), | ||||
this, SLOT(changeMargins(int,int,int,int))); | ||||
connect(m_margins, SIGNAL(leftChanged(int,int,int,int)), | ||||
this, SLOT(changeMargins(int,int,int,int))); | ||||
connect(m_margins, SIGNAL(rightChanged(int,int,int,int)), | ||||
this, SLOT(changeMargins(int,int,int,int))); | ||||
|
r2277 | connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), this, SLOT(handleSeriesAdded(QAbstractSeries*))); | ||
|
r2110 | connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), this, SIGNAL(seriesRemoved(QAbstractSeries*))); | ||
|
r2716 | connect(m_chart, &QChart::plotAreaChanged, this, &DeclarativeChart::plotAreaChanged); | ||
|
r2068 | } | ||
|
r2277 | void DeclarativeChart::handleSeriesAdded(QAbstractSeries *series) | ||
|
r2068 | { | ||
emit seriesAdded(series); | ||||
|
r1524 | } | ||
|
r2742 | void DeclarativeChart::changeMargins(int top, int bottom, int left, int right) | ||
|
r1524 | { | ||
|
r1965 | m_chart->setMargins(QMargins(left, top, right, bottom)); | ||
|
r2742 | emit marginsChanged(); | ||
|
r1 | } | ||
|
r722 | DeclarativeChart::~DeclarativeChart() | ||
{ | ||||
delete m_chart; | ||||
|
r2820 | delete m_sceneImage; | ||
|
r722 | } | ||
|
r1117 | void DeclarativeChart::childEvent(QChildEvent *event) | ||
{ | ||||
if (event->type() == QEvent::ChildAdded) { | ||||
if (qobject_cast<QAbstractSeries *>(event->child())) { | ||||
m_chart->addSeries(qobject_cast<QAbstractSeries *>(event->child())); | ||||
} | ||||
} | ||||
} | ||||
void DeclarativeChart::componentComplete() | ||||
{ | ||||
|
r2100 | foreach (QObject *child, children()) { | ||
|
r1117 | if (qobject_cast<QAbstractSeries *>(child)) { | ||
|
r1903 | // Add series to the chart | ||
QAbstractSeries *series = qobject_cast<QAbstractSeries *>(child); | ||||
m_chart->addSeries(series); | ||||
|
r2296 | // Connect to axis changed signals (unless this is a pie series) | ||
if (!qobject_cast<DeclarativePieSeries *>(series)) { | ||||
connect(series, SIGNAL(axisXChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*))); | ||||
|
r2576 | connect(series, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SLOT(handleAxisXTopSet(QAbstractAxis*))); | ||
|
r2296 | connect(series, SIGNAL(axisYChanged(QAbstractAxis*)), this, SLOT(handleAxisYSet(QAbstractAxis*))); | ||
connect(series, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SLOT(handleAxisYRightSet(QAbstractAxis*))); | ||||
|
r1813 | } | ||
|
r2296 | |||
initializeAxes(series); | ||||
|
r1117 | } | ||
} | ||||
|
r1813 | |||
|
r2712 | QQuickItem::componentComplete(); | ||
|
r1117 | } | ||
|
r2718 | void DeclarativeChart::seriesAxisAttachHelper(QAbstractSeries *series, QAbstractAxis *axis, | ||
Qt::Orientations orientation, | ||||
Qt::Alignment alignment) | ||||
{ | ||||
if (!series->attachedAxes().contains(axis)) { | ||||
// Remove & delete old axes that are not attached to any other series | ||||
foreach (QAbstractAxis* oldAxis, m_chart->axes(orientation, series)) { | ||||
bool otherAttachments = false; | ||||
if (oldAxis != axis) { | ||||
foreach (QAbstractSeries *oldSeries, m_chart->series()) { | ||||
if (oldSeries != series && oldSeries->attachedAxes().contains(oldAxis)) { | ||||
otherAttachments = true; | ||||
break; | ||||
} | ||||
} | ||||
if (!otherAttachments) { | ||||
m_chart->removeAxis(oldAxis); | ||||
delete oldAxis; | ||||
} | ||||
} | ||||
} | ||||
if (!m_chart->axes(orientation).contains(axis)) | ||||
m_chart->addAxis(axis, alignment); | ||||
series->attachAxis(axis); | ||||
} | ||||
} | ||||
|
r2101 | void DeclarativeChart::handleAxisXSet(QAbstractAxis *axis) | ||
|
r1813 | { | ||
|
r2296 | QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender()); | ||
if (axis && s) { | ||||
|
r2718 | seriesAxisAttachHelper(s, axis, Qt::Horizontal, Qt::AlignBottom); | ||
|
r2296 | } else { | ||
|
r1947 | qWarning() << "Trying to set axisX to null."; | ||
|
r2296 | } | ||
} | ||||
void DeclarativeChart::handleAxisXTopSet(QAbstractAxis *axis) | ||||
{ | ||||
QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender()); | ||||
if (axis && s) { | ||||
|
r2718 | seriesAxisAttachHelper(s, axis, Qt::Horizontal, Qt::AlignTop); | ||
|
r2296 | } else { | ||
qWarning() << "Trying to set axisXTop to null."; | ||||
} | ||||
|
r1813 | } | ||
|
r2101 | void DeclarativeChart::handleAxisYSet(QAbstractAxis *axis) | ||
|
r1813 | { | ||
|
r2296 | QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender()); | ||
if (axis && s) { | ||||
|
r2718 | seriesAxisAttachHelper(s, axis, Qt::Vertical, Qt::AlignLeft); | ||
|
r2296 | } else { | ||
|
r1947 | qWarning() << "Trying to set axisY to null."; | ||
|
r2296 | } | ||
} | ||||
void DeclarativeChart::handleAxisYRightSet(QAbstractAxis *axis) | ||||
{ | ||||
QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender()); | ||||
if (axis && s) { | ||||
|
r2718 | seriesAxisAttachHelper(s, axis, Qt::Vertical, Qt::AlignRight); | ||
|
r2296 | } else { | ||
qWarning() << "Trying to set axisYRight to null."; | ||||
} | ||||
|
r1813 | } | ||
|
r120 | void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | ||
|
r1 | { | ||
|
r200 | if (newGeometry.isValid()) { | ||
if (newGeometry.width() > 0 && newGeometry.height() > 0) { | ||||
|
r642 | m_chart->resize(newGeometry.width(), newGeometry.height()); | ||
|
r200 | } | ||
} | ||||
|
r2712 | QQuickItem::geometryChanged(newGeometry, oldGeometry); | ||
|
r1 | } | ||
|
r2820 | QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) | ||
|
r2505 | { | ||
|
r2820 | DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode); | ||
|
r2505 | |||
|
r2820 | if (!node) { | ||
node = new DeclarativeChartNode(window()); | ||||
connect(window(), &QQuickWindow::beforeRendering, | ||||
node->glRenderNode(), &DeclarativeRenderNode::render); | ||||
} | ||||
const QRectF &bRect = boundingRect(); | ||||
// Update GL data | ||||
if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) { | ||||
const QRectF &plotArea = m_chart->plotArea(); | ||||
const QSizeF &chartAreaSize = m_chart->size(); | ||||
// We can't use chart's plot area directly, as graphicscene has some internal minimum size | ||||
const qreal normalizedX = plotArea.x() / chartAreaSize.width(); | ||||
const qreal normalizedY = plotArea.y() / chartAreaSize.height(); | ||||
const qreal normalizedWidth = plotArea.width() / chartAreaSize.width(); | ||||
const qreal normalizedHeight = plotArea.height() / chartAreaSize.height(); | ||||
QRectF adjustedPlotArea(normalizedX * bRect.width(), | ||||
normalizedY * bRect.height(), | ||||
normalizedWidth * bRect.width(), | ||||
normalizedHeight * bRect.height()); | ||||
const QSize &adjustedPlotSize = adjustedPlotArea.size().toSize(); | ||||
if (adjustedPlotSize != node->glRenderNode()->textureSize()) | ||||
node->glRenderNode()->setTextureSize(adjustedPlotSize); | ||||
node->glRenderNode()->setRect(adjustedPlotArea); | ||||
node->glRenderNode()->setSeriesData(m_glXYDataManager->mapDirty(), | ||||
m_glXYDataManager->dataMap()); | ||||
// Clear dirty flags from original xy data | ||||
m_glXYDataManager->clearAllDirty(); | ||||
} | ||||
// Copy chart (if dirty) to chart node | ||||
if (m_sceneImageDirty) { | ||||
node->createTextureFromImage(*m_sceneImage); | ||||
m_sceneImageDirty = false; | ||||
} | ||||
node->setRect(bRect); | ||||
return node; | ||||
} | ||||
void DeclarativeChart::sceneChanged(QList<QRectF> region) | ||||
{ | ||||
const int count = region.size(); | ||||
const qreal limitSize = 0.01; | ||||
if (count && !m_updatePending) { | ||||
qreal totalSize = 0.0; | ||||
for (int i = 0; i < count; i++) { | ||||
const QRectF ® = region.at(i); | ||||
totalSize += (reg.height() * reg.width()); | ||||
if (totalSize >= limitSize) | ||||
break; | ||||
} | ||||
// Ignore region updates that change less than small fraction of a pixel, as there is | ||||
// little point regenerating the image in these cases. These are typically cases | ||||
// where OpenGL series are drawn to otherwise static chart. | ||||
if (totalSize >= limitSize) { | ||||
|
r2506 | m_updatePending = true; | ||
// Do async render to avoid some unnecessary renders. | ||||
|
r2820 | emit needRender(); | ||
} else { | ||||
// We do want to call update to trigger possible gl series updates. | ||||
update(); | ||||
|
r2506 | } | ||
|
r2505 | } | ||
} | ||||
void DeclarativeChart::renderScene() | ||||
{ | ||||
m_updatePending = false; | ||||
|
r2820 | m_sceneImageDirty = true; | ||
QSize chartSize = m_chart->size().toSize(); | ||||
if (!m_sceneImage || chartSize != m_sceneImage->size()) { | ||||
delete m_sceneImage; | ||||
|
r2859 | qreal dpr = window() ? window()->devicePixelRatio() : 1.0; | ||
m_sceneImage = new QImage(chartSize * dpr, QImage::Format_ARGB32); | ||||
m_sceneImage->setDevicePixelRatio(dpr); | ||||
|
r2850 | m_sceneImageNeedsClear = true; | ||
|
r2506 | } | ||
|
r2850 | if (m_sceneImageNeedsClear) { | ||
m_sceneImage->fill(Qt::transparent); | ||||
// Don't clear the flag if chart background has any transparent element to it | ||||
if (m_chart->backgroundBrush().color().alpha() == 0xff && !m_chart->isDropShadowEnabled()) | ||||
m_sceneImageNeedsClear = false; | ||||
} | ||||
|
r2820 | QPainter painter(m_sceneImage); | ||
if (antialiasing()) { | ||||
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | ||||
| QPainter::SmoothPixmapTransform); | ||||
|
r2505 | } | ||
|
r2820 | QRect renderRect(QPoint(0, 0), chartSize); | ||
m_scene->render(&painter, renderRect, renderRect); | ||||
update(); | ||||
|
r2488 | } | ||
|
r2495 | void DeclarativeChart::mousePressEvent(QMouseEvent *event) | ||
{ | ||||
m_mousePressScenePoint = event->pos(); | ||||
m_mousePressScreenPoint = event->globalPos(); | ||||
m_lastMouseMoveScenePoint = m_mousePressScenePoint; | ||||
m_lastMouseMoveScreenPoint = m_mousePressScreenPoint; | ||||
m_mousePressButton = event->button(); | ||||
m_mousePressButtons = event->buttons(); | ||||
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress); | ||||
mouseEvent.setWidget(0); | ||||
mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint); | ||||
mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint); | ||||
mouseEvent.setScenePos(m_mousePressScenePoint); | ||||
mouseEvent.setScreenPos(m_mousePressScreenPoint); | ||||
mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint); | ||||
mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint); | ||||
mouseEvent.setButtons(m_mousePressButtons); | ||||
mouseEvent.setButton(m_mousePressButton); | ||||
mouseEvent.setModifiers(event->modifiers()); | ||||
mouseEvent.setAccepted(false); | ||||
QApplication::sendEvent(m_scene, &mouseEvent); | ||||
} | ||||
void DeclarativeChart::mouseReleaseEvent(QMouseEvent *event) | ||||
{ | ||||
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease); | ||||
mouseEvent.setWidget(0); | ||||
mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint); | ||||
mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint); | ||||
mouseEvent.setScenePos(event->pos()); | ||||
mouseEvent.setScreenPos(event->globalPos()); | ||||
mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint); | ||||
mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint); | ||||
mouseEvent.setButtons(event->buttons()); | ||||
mouseEvent.setButton(event->button()); | ||||
mouseEvent.setModifiers(event->modifiers()); | ||||
mouseEvent.setAccepted(false); | ||||
QApplication::sendEvent(m_scene, &mouseEvent); | ||||
m_mousePressButtons = event->buttons(); | ||||
m_mousePressButton = Qt::NoButton; | ||||
} | ||||
void DeclarativeChart::hoverMoveEvent(QHoverEvent *event) | ||||
{ | ||||
// Convert hover move to mouse move, since we don't seem to get actual mouse move events. | ||||
// QGraphicsScene generates hover events from mouse move events, so we don't need | ||||
// to pass hover events there. | ||||
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); | ||||
mouseEvent.setWidget(0); | ||||
mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint); | ||||
mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint); | ||||
mouseEvent.setScenePos(event->pos()); | ||||
// Hover events do not have global pos in them, and the screen position doesn't seem to | ||||
// matter anyway in this use case, so just pass event pos instead of trying to | ||||
// calculate the real screen position. | ||||
mouseEvent.setScreenPos(event->pos()); | ||||
mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint); | ||||
mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint); | ||||
mouseEvent.setButtons(m_mousePressButtons); | ||||
mouseEvent.setButton(m_mousePressButton); | ||||
mouseEvent.setModifiers(event->modifiers()); | ||||
m_lastMouseMoveScenePoint = mouseEvent.scenePos(); | ||||
m_lastMouseMoveScreenPoint = mouseEvent.screenPos(); | ||||
mouseEvent.setAccepted(false); | ||||
QApplication::sendEvent(m_scene, &mouseEvent); | ||||
} | ||||
|
r2739 | void DeclarativeChart::mouseDoubleClickEvent(QMouseEvent *event) | ||
{ | ||||
m_mousePressScenePoint = event->pos(); | ||||
m_mousePressScreenPoint = event->globalPos(); | ||||
m_lastMouseMoveScenePoint = m_mousePressScenePoint; | ||||
m_lastMouseMoveScreenPoint = m_mousePressScreenPoint; | ||||
m_mousePressButton = event->button(); | ||||
m_mousePressButtons = event->buttons(); | ||||
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick); | ||||
mouseEvent.setWidget(0); | ||||
mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint); | ||||
mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint); | ||||
mouseEvent.setScenePos(m_mousePressScenePoint); | ||||
mouseEvent.setScreenPos(m_mousePressScreenPoint); | ||||
mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint); | ||||
mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint); | ||||
mouseEvent.setButtons(m_mousePressButtons); | ||||
mouseEvent.setButton(m_mousePressButton); | ||||
mouseEvent.setModifiers(event->modifiers()); | ||||
mouseEvent.setAccepted(false); | ||||
QApplication::sendEvent(m_scene, &mouseEvent); | ||||
} | ||||
|
r2488 | void DeclarativeChart::handleAntialiasingChanged(bool enable) | ||
{ | ||||
setAntialiasing(enable); | ||||
} | ||||
|
r1240 | void DeclarativeChart::setTheme(DeclarativeChart::Theme theme) | ||
|
r1094 | { | ||
|
r1357 | QChart::ChartTheme chartTheme = (QChart::ChartTheme) theme; | ||
|
r1481 | if (chartTheme != m_chart->theme()) | ||
|
r1357 | m_chart->setTheme(chartTheme); | ||
|
r1094 | } | ||
|
r1240 | DeclarativeChart::Theme DeclarativeChart::theme() | ||
{ | ||||
return (DeclarativeChart::Theme) m_chart->theme(); | ||||
} | ||||
void DeclarativeChart::setAnimationOptions(DeclarativeChart::Animation animations) | ||||
{ | ||||
|
r1357 | QChart::AnimationOption animationOptions = (QChart::AnimationOption) animations; | ||
|
r1481 | if (animationOptions != m_chart->animationOptions()) | ||
|
r1357 | m_chart->setAnimationOptions(animationOptions); | ||
|
r1240 | } | ||
DeclarativeChart::Animation DeclarativeChart::animationOptions() | ||||
|
r1094 | { | ||
if (m_chart->animationOptions().testFlag(QChart::AllAnimations)) | ||||
|
r1240 | return DeclarativeChart::AllAnimations; | ||
|
r1094 | else if (m_chart->animationOptions().testFlag(QChart::GridAxisAnimations)) | ||
|
r1240 | return DeclarativeChart::GridAxisAnimations; | ||
|
r1094 | else if (m_chart->animationOptions().testFlag(QChart::SeriesAnimations)) | ||
|
r1240 | return DeclarativeChart::SeriesAnimations; | ||
|
r1094 | else | ||
|
r1240 | return DeclarativeChart::NoAnimation; | ||
|
r1094 | } | ||
|
r2804 | void DeclarativeChart::setAnimationDuration(int msecs) | ||
{ | ||||
if (msecs != m_chart->animationDuration()) { | ||||
m_chart->setAnimationDuration(msecs); | ||||
emit animationDurationChanged(msecs); | ||||
} | ||||
} | ||||
int DeclarativeChart::animationDuration() const | ||||
{ | ||||
return m_chart->animationDuration(); | ||||
} | ||||
void DeclarativeChart::setAnimationEasingCurve(const QEasingCurve &curve) | ||||
{ | ||||
if (curve != m_chart->animationEasingCurve()) { | ||||
m_chart->setAnimationEasingCurve(curve); | ||||
emit animationEasingCurveChanged(curve); | ||||
} | ||||
} | ||||
QEasingCurve DeclarativeChart::animationEasingCurve() const | ||||
{ | ||||
return m_chart->animationEasingCurve(); | ||||
} | ||||
|
r1357 | void DeclarativeChart::setTitle(QString title) | ||
{ | ||||
|
r1481 | if (title != m_chart->title()) | ||
|
r1357 | m_chart->setTitle(title); | ||
|
r1095 | } | ||
|
r1357 | QString DeclarativeChart::title() | ||
|
r1095 | { | ||
|
r1357 | return m_chart->title(); | ||
|
r1095 | } | ||
|
r1550 | QAbstractAxis *DeclarativeChart::axisX(QAbstractSeries *series) | ||
|
r1139 | { | ||
|
r2296 | QList<QAbstractAxis *> axes = m_chart->axes(Qt::Horizontal, series); | ||
if (axes.count()) | ||||
return axes[0]; | ||||
return 0; | ||||
|
r1139 | } | ||
|
r1541 | QAbstractAxis *DeclarativeChart::axisY(QAbstractSeries *series) | ||
|
r1139 | { | ||
|
r2296 | QList<QAbstractAxis *> axes = m_chart->axes(Qt::Vertical, series); | ||
if (axes.count()) | ||||
return axes[0]; | ||||
return 0; | ||||
|
r1139 | } | ||
|
r1357 | QLegend *DeclarativeChart::legend() | ||
{ | ||||
return m_chart->legend(); | ||||
} | ||||
void DeclarativeChart::setTitleColor(QColor color) | ||||
{ | ||||
QBrush b = m_chart->titleBrush(); | ||||
if (color != b.color()) { | ||||
b.setColor(color); | ||||
m_chart->setTitleBrush(b); | ||||
|
r1524 | emit titleColorChanged(color); | ||
|
r1357 | } | ||
} | ||||
|
r1517 | QFont DeclarativeChart::titleFont() const | ||
{ | ||||
return m_chart->titleFont(); | ||||
} | ||||
|
r2101 | void DeclarativeChart::setTitleFont(const QFont &font) | ||
|
r1517 | { | ||
m_chart->setTitleFont(font); | ||||
} | ||||
|
r1357 | QColor DeclarativeChart::titleColor() | ||
{ | ||||
return m_chart->titleBrush().color(); | ||||
} | ||||
void DeclarativeChart::setBackgroundColor(QColor color) | ||||
{ | ||||
QBrush b = m_chart->backgroundBrush(); | ||||
|
r1473 | if (b.style() != Qt::SolidPattern || color != b.color()) { | ||
|
r2850 | if (color.alpha() < 0xff) | ||
m_sceneImageNeedsClear = true; | ||||
|
r1473 | b.setStyle(Qt::SolidPattern); | ||
|
r1357 | b.setColor(color); | ||
m_chart->setBackgroundBrush(b); | ||||
emit backgroundColorChanged(); | ||||
} | ||||
} | ||||
QColor DeclarativeChart::backgroundColor() | ||||
{ | ||||
return m_chart->backgroundBrush().color(); | ||||
|
r1157 | } | ||
|
r2712 | void QtCharts::DeclarativeChart::setPlotAreaColor(QColor color) | ||
|
r2498 | { | ||
QBrush b = m_chart->plotAreaBackgroundBrush(); | ||||
if (b.style() != Qt::SolidPattern || color != b.color()) { | ||||
b.setStyle(Qt::SolidPattern); | ||||
b.setColor(color); | ||||
m_chart->setPlotAreaBackgroundBrush(b); | ||||
m_chart->setPlotAreaBackgroundVisible(true); | ||||
emit plotAreaColorChanged(); | ||||
} | ||||
} | ||||
|
r2712 | QColor QtCharts::DeclarativeChart::plotAreaColor() | ||
|
r2498 | { | ||
return m_chart->plotAreaBackgroundBrush().color(); | ||||
} | ||||
|
r2707 | void DeclarativeChart::setLocalizeNumbers(bool localize) | ||
{ | ||||
if (m_chart->localizeNumbers() != localize) { | ||||
m_chart->setLocalizeNumbers(localize); | ||||
emit localizeNumbersChanged(); | ||||
} | ||||
} | ||||
bool DeclarativeChart::localizeNumbers() const | ||||
{ | ||||
return m_chart->localizeNumbers(); | ||||
} | ||||
|
r2712 | void QtCharts::DeclarativeChart::setLocale(const QLocale &locale) | ||
|
r2710 | { | ||
if (m_chart->locale() != locale) { | ||||
m_chart->setLocale(locale); | ||||
emit localeChanged(); | ||||
} | ||||
} | ||||
|
r2712 | QLocale QtCharts::DeclarativeChart::locale() const | ||
|
r2710 | { | ||
return m_chart->locale(); | ||||
} | ||||
|
r1240 | int DeclarativeChart::count() | ||
{ | ||||
return m_chart->series().count(); | ||||
} | ||||
|
r1461 | void DeclarativeChart::setDropShadowEnabled(bool enabled) | ||
{ | ||||
|
r1462 | if (enabled != m_chart->isDropShadowEnabled()) { | ||
|
r2850 | m_sceneImageNeedsClear = true; | ||
|
r1462 | m_chart->setDropShadowEnabled(enabled); | ||
|
r1461 | dropShadowEnabledChanged(enabled); | ||
} | ||||
} | ||||
bool DeclarativeChart::dropShadowEnabled() | ||||
{ | ||||
|
r1462 | return m_chart->isDropShadowEnabled(); | ||
|
r1461 | } | ||
|
r2549 | qreal DeclarativeChart::backgroundRoundness() const | ||
{ | ||||
return m_chart->backgroundRoundness(); | ||||
} | ||||
void DeclarativeChart::setBackgroundRoundness(qreal diameter) | ||||
{ | ||||
if (m_chart->backgroundRoundness() != diameter) { | ||||
|
r2850 | m_sceneImageNeedsClear = true; | ||
|
r2549 | m_chart->setBackgroundRoundness(diameter); | ||
emit backgroundRoundnessChanged(diameter); | ||||
} | ||||
} | ||||
|
r1461 | void DeclarativeChart::zoom(qreal factor) | ||
{ | ||||
m_chart->zoom(factor); | ||||
} | ||||
|
r2788 | void DeclarativeChart::zoomIn() | ||
{ | ||||
m_chart->zoomIn(); | ||||
} | ||||
void DeclarativeChart::zoomIn(const QRectF &rectangle) | ||||
{ | ||||
m_chart->zoomIn(rectangle); | ||||
} | ||||
void DeclarativeChart::zoomOut() | ||||
{ | ||||
m_chart->zoomOut(); | ||||
} | ||||
void DeclarativeChart::zoomReset() | ||||
{ | ||||
m_chart->zoomReset(); | ||||
} | ||||
|
r2813 | bool DeclarativeChart::isZoomed() | ||
{ | ||||
return m_chart->isZoomed(); | ||||
} | ||||
|
r1461 | void DeclarativeChart::scrollLeft(qreal pixels) | ||
{ | ||||
|
r1955 | m_chart->scroll(-pixels, 0); | ||
|
r1461 | } | ||
void DeclarativeChart::scrollRight(qreal pixels) | ||||
{ | ||||
|
r1955 | m_chart->scroll(pixels, 0); | ||
|
r1461 | } | ||
void DeclarativeChart::scrollUp(qreal pixels) | ||||
{ | ||||
|
r1553 | m_chart->scroll(0, pixels); | ||
|
r1461 | } | ||
void DeclarativeChart::scrollDown(qreal pixels) | ||||
{ | ||||
|
r1553 | m_chart->scroll(0, -pixels); | ||
|
r1461 | } | ||
|
r2712 | QQmlListProperty<QAbstractAxis> DeclarativeChart::axes() | ||
|
r2296 | { | ||
|
r2712 | return QQmlListProperty<QAbstractAxis>(this, 0, | ||
|
r2296 | &DeclarativeChart::axesAppendFunc, | ||
&DeclarativeChart::axesCountFunc, | ||||
|
r2488 | &DeclarativeChart::axesAtFunc, | ||
&DeclarativeChart::axesClearFunc); | ||||
|
r2296 | } | ||
|
r2712 | void DeclarativeChart::axesAppendFunc(QQmlListProperty<QAbstractAxis> *list, QAbstractAxis *element) | ||
|
r2296 | { | ||
// Empty implementation | ||||
Q_UNUSED(list); | ||||
Q_UNUSED(element); | ||||
} | ||||
|
r2712 | int DeclarativeChart::axesCountFunc(QQmlListProperty<QAbstractAxis> *list) | ||
|
r2296 | { | ||
if (qobject_cast<DeclarativeChart *>(list->object)) { | ||||
DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(list->object); | ||||
|
r2300 | return chart->m_chart->axes(Qt::Horizontal | Qt::Vertical).count(); | ||
|
r2296 | } | ||
return 0; | ||||
} | ||||
|
r2712 | QAbstractAxis *DeclarativeChart::axesAtFunc(QQmlListProperty<QAbstractAxis> *list, int index) | ||
|
r2296 | { | ||
if (qobject_cast<DeclarativeChart *>(list->object)) { | ||||
DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(list->object); | ||||
QList<QAbstractAxis *> axes = chart->m_chart->axes(Qt::Horizontal | Qt::Vertical, chart->m_chart->series()[0]); | ||||
return axes.at(index); | ||||
} | ||||
return 0; | ||||
} | ||||
|
r2712 | void DeclarativeChart::axesClearFunc(QQmlListProperty<QAbstractAxis> *list) | ||
|
r2488 | { | ||
// Empty implementation | ||||
Q_UNUSED(list); | ||||
} | ||||
|
r1240 | QAbstractSeries *DeclarativeChart::series(int index) | ||
{ | ||||
if (index < m_chart->series().count()) { | ||||
return m_chart->series().at(index); | ||||
} | ||||
return 0; | ||||
} | ||||
QAbstractSeries *DeclarativeChart::series(QString seriesName) | ||||
{ | ||||
|
r2100 | foreach (QAbstractSeries *series, m_chart->series()) { | ||
|
r1240 | if (series->name() == seriesName) | ||
return series; | ||||
} | ||||
return 0; | ||||
} | ||||
|
r2389 | QAbstractSeries *DeclarativeChart::createSeries(int type, QString name, QAbstractAxis *axisX, QAbstractAxis *axisY) | ||
|
r1240 | { | ||
QAbstractSeries *series = 0; | ||||
|
r1813 | |||
|
r1240 | switch (type) { | ||
case DeclarativeChart::SeriesTypeLine: | ||||
series = new DeclarativeLineSeries(); | ||||
break; | ||||
|
r2114 | case DeclarativeChart::SeriesTypeArea: { | ||
DeclarativeAreaSeries *area = new DeclarativeAreaSeries(); | ||||
|
r2735 | DeclarativeLineSeries *line = new DeclarativeLineSeries(); | ||
line->setParent(area); | ||||
area->setUpperSeries(line); | ||||
|
r2114 | series = area; | ||
|
r1240 | break; | ||
|
r2114 | } | ||
|
r1240 | case DeclarativeChart::SeriesTypeStackedBar: | ||
|
r1690 | series = new DeclarativeStackedBarSeries(); | ||
|
r1240 | break; | ||
case DeclarativeChart::SeriesTypePercentBar: | ||||
|
r1690 | series = new DeclarativePercentBarSeries(); | ||
|
r1240 | break; | ||
|
r1594 | case DeclarativeChart::SeriesTypeBar: | ||
series = new DeclarativeBarSeries(); | ||||
|
r1240 | break; | ||
|
r1811 | case DeclarativeChart::SeriesTypeHorizontalBar: | ||
series = new DeclarativeHorizontalBarSeries(); | ||||
break; | ||||
case DeclarativeChart::SeriesTypeHorizontalPercentBar: | ||||
series = new DeclarativeHorizontalPercentBarSeries(); | ||||
break; | ||||
case DeclarativeChart::SeriesTypeHorizontalStackedBar: | ||||
series = new DeclarativeHorizontalStackedBarSeries(); | ||||
break; | ||||
|
r2548 | case DeclarativeChart::SeriesTypeBoxPlot: | ||
series = new DeclarativeBoxPlotSeries(); | ||||
break; | ||||
|
r1240 | case DeclarativeChart::SeriesTypePie: | ||
series = new DeclarativePieSeries(); | ||||
break; | ||||
case DeclarativeChart::SeriesTypeScatter: | ||||
series = new DeclarativeScatterSeries(); | ||||
break; | ||||
case DeclarativeChart::SeriesTypeSpline: | ||||
series = new DeclarativeSplineSeries(); | ||||
break; | ||||
default: | ||||
qWarning() << "Illegal series type"; | ||||
} | ||||
|
r1813 | |||
if (series) { | ||||
|
r2296 | // Connect to axis changed signals (unless this is a pie series) | ||
if (!qobject_cast<DeclarativePieSeries *>(series)) { | ||||
connect(series, SIGNAL(axisXChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*))); | ||||
connect(series, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*))); | ||||
connect(series, SIGNAL(axisYChanged(QAbstractAxis*)), this, SLOT(handleAxisYSet(QAbstractAxis*))); | ||||
connect(series, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SLOT(handleAxisYRightSet(QAbstractAxis*))); | ||||
} | ||||
|
r1813 | series->setName(name); | ||
m_chart->addSeries(series); | ||||
|
r2296 | |||
|
r2718 | if (!axisX || !axisY) | ||
initializeAxes(series); | ||||
|
r2296 | if (axisX) | ||
setAxisX(axisX, series); | ||||
if (axisY) | ||||
setAxisY(axisY, series); | ||||
|
r1813 | } | ||
|
r1240 | return series; | ||
} | ||||
|
r2068 | void DeclarativeChart::removeSeries(QAbstractSeries *series) | ||
{ | ||||
if (series) | ||||
m_chart->removeSeries(series); | ||||
else | ||||
qWarning("removeSeries: cannot remove null"); | ||||
} | ||||
|
r1813 | void DeclarativeChart::setAxisX(QAbstractAxis *axis, QAbstractSeries *series) | ||
|
r1604 | { | ||
|
r2718 | if (axis && series) | ||
seriesAxisAttachHelper(series, axis, Qt::Horizontal, Qt::AlignBottom); | ||||
|
r1604 | } | ||
|
r1813 | void DeclarativeChart::setAxisY(QAbstractAxis *axis, QAbstractSeries *series) | ||
|
r1604 | { | ||
|
r2718 | if (axis && series) | ||
seriesAxisAttachHelper(series, axis, Qt::Vertical, Qt::AlignLeft); | ||||
|
r1604 | } | ||
|
r2296 | QAbstractAxis *DeclarativeChart::defaultAxis(Qt::Orientation orientation, QAbstractSeries *series) | ||
|
r1813 | { | ||
|
r2296 | if (!series) { | ||
qWarning() << "No axis type defined for null series"; | ||||
return 0; | ||||
|
r1813 | } | ||
|
r2296 | foreach (QAbstractAxis *existingAxis, m_chart->axes(orientation)) { | ||
if (existingAxis->type() == series->d_ptr->defaultAxisType(orientation)) | ||||
return existingAxis; | ||||
|
r1813 | } | ||
|
r2296 | switch (series->d_ptr->defaultAxisType(orientation)) { | ||
case QAbstractAxis::AxisTypeValue: | ||||
return new QValueAxis(this); | ||||
case QAbstractAxis::AxisTypeBarCategory: | ||||
return new QBarCategoryAxis(this); | ||||
case QAbstractAxis::AxisTypeCategory: | ||||
return new QCategoryAxis(this); | ||||
|
r2861 | #ifndef QT_QREAL_IS_FLOAT | ||
|
r2296 | case QAbstractAxis::AxisTypeDateTime: | ||
return new QDateTimeAxis(this); | ||||
|
r1867 | #endif | ||
|
r2493 | case QAbstractAxis::AxisTypeLogValue: | ||
return new QLogValueAxis(this); | ||||
|
r2296 | default: | ||
// assume AxisTypeNoAxis | ||||
return 0; | ||||
|
r1813 | } | ||
|
r2296 | } | ||
|
r1813 | |||
|
r2296 | void DeclarativeChart::initializeAxes(QAbstractSeries *series) | ||
{ | ||||
if (qobject_cast<DeclarativeLineSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeLineSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeScatterSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeScatterSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeSplineSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeSplineSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeAreaSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeAreaSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeBarSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeStackedBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeStackedBarSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativePercentBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativePercentBarSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeHorizontalBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeHorizontalBarSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeHorizontalStackedBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeHorizontalStackedBarSeries *>(series)->m_axes); | ||||
else if (qobject_cast<DeclarativeHorizontalPercentBarSeries *>(series)) | ||||
doInitializeAxes(series, qobject_cast<DeclarativeHorizontalPercentBarSeries *>(series)->m_axes); | ||||
|
r2548 | else if (qobject_cast<DeclarativeBoxPlotSeries *>(series)) | ||
doInitializeAxes(series, qobject_cast<DeclarativeBoxPlotSeries *>(series)->m_axes); | ||||
|
r2296 | // else: do nothing | ||
} | ||||
void DeclarativeChart::doInitializeAxes(QAbstractSeries *series, DeclarativeAxes *axes) | ||||
{ | ||||
|
r2789 | qreal min; | ||
qreal max; | ||||
|
r2296 | // Initialize axis X | ||
|
r2789 | if (axes->axisX()) { | ||
|
r2296 | axes->emitAxisXChanged(); | ||
|
r2789 | } else if (axes->axisXTop()) { | ||
|
r2296 | axes->emitAxisXTopChanged(); | ||
|
r2789 | } else { | ||
|
r2296 | axes->setAxisX(defaultAxis(Qt::Horizontal, series)); | ||
|
r2789 | findMinMaxForSeries(series, Qt::Horizontal, min, max); | ||
axes->axisX()->setRange(min, max); | ||||
} | ||||
|
r2296 | |||
// Initialize axis Y | ||||
|
r2789 | if (axes->axisY()) { | ||
|
r2296 | axes->emitAxisYChanged(); | ||
|
r2789 | } else if (axes->axisYRight()) { | ||
|
r2296 | axes->emitAxisYRightChanged(); | ||
|
r2789 | } else { | ||
|
r2296 | axes->setAxisY(defaultAxis(Qt::Vertical, series)); | ||
|
r2789 | findMinMaxForSeries(series, Qt::Vertical, min, max); | ||
axes->axisY()->setRange(min, max); | ||||
} | ||||
} | ||||
void DeclarativeChart::findMinMaxForSeries(QAbstractSeries *series, Qt::Orientations orientation, | ||||
qreal &min, qreal &max) | ||||
{ | ||||
if (!series) { | ||||
min = 0.5; | ||||
max = 0.5; | ||||
} else { | ||||
AbstractDomain *domain = series->d_ptr->domain(); | ||||
min = (orientation == Qt::Vertical) ? domain->minY() : domain->minX(); | ||||
max = (orientation == Qt::Vertical) ? domain->maxY() : domain->maxX(); | ||||
if (min == max) { | ||||
min -= 0.5; | ||||
max += 0.5; | ||||
} | ||||
} | ||||
|
r1604 | } | ||
|
r2786 | QPointF DeclarativeChart::mapToValue(const QPointF &position, QAbstractSeries *series) | ||
{ | ||||
return m_chart->mapToValue(position, series); | ||||
} | ||||
QPointF DeclarativeChart::mapToPosition(const QPointF &value, QAbstractSeries *series) | ||||
{ | ||||
return m_chart->mapToPosition(value, series); | ||||
} | ||||
|
r120 | #include "moc_declarativechart.cpp" | ||
|
r2712 | QT_CHARTS_END_NAMESPACE | ||