diff options
author | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2013-04-16 10:07:13 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2013-04-17 10:14:43 +0300 |
commit | f494279b6366b06e3eeeb4f8c006ce76b08f10d7 (patch) | |
tree | 26951efa14e26eb0791d13ea32624e9afcf48851 | |
parent | 56fd46a395765db6818f890676e42cc59a9f4a81 (diff) |
Add Polar chart support
This commit also heavily refactors things as polar chart needs
separate implementation of various classes that previously
only needed one, such as ChartAxis and ChartLayout.
Task-number: QTRD-1757
Change-Id: I3d3db23920314987ceef3ae92879960b833b7136
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>
179 files changed, 9957 insertions, 1502 deletions
diff --git a/demos/chartinteractions/chart.cpp b/demos/chartinteractions/chart.cpp index 17ee90f0..6043c9bb 100644 --- a/demos/chartinteractions/chart.cpp +++ b/demos/chartinteractions/chart.cpp @@ -24,7 +24,7 @@ #include <cmath> Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags, QLineSeries *series) - : QChart(parent, wFlags), m_series(series) + : QChart(QChart::ChartTypeCartesian, parent, wFlags), m_series(series) { m_clicked = false; } diff --git a/demos/demos.pro b/demos/demos.pro index 43f6c933..76a53c50 100644 --- a/demos/demos.pro +++ b/demos/demos.pro @@ -15,7 +15,8 @@ SUBDIRS += piechartcustomization \ chartinteractions \ qmlaxes \ qmlcustomlegend \ - callout + callout \ + qmlpolarchart contains(QT_CONFIG, opengl) { SUBDIRS += chartthemes \ diff --git a/demos/dynamicspline/chart.cpp b/demos/dynamicspline/chart.cpp index c7e111b5..28457778 100644 --- a/demos/dynamicspline/chart.cpp +++ b/demos/dynamicspline/chart.cpp @@ -26,7 +26,7 @@ #include <QDebug> Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags): - QChart(parent, wFlags), + QChart(QChart::ChartTypeCartesian, parent, wFlags), m_series(0), m_axis(new QValueAxis), m_step(0), diff --git a/demos/qmlpolarchart/main.cpp b/demos/qmlpolarchart/main.cpp new file mode 100644 index 00000000..f5f00ccd --- /dev/null +++ b/demos/qmlpolarchart/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QtDeclarative/QDeclarativeEngine> +#include "qmlapplicationviewer.h" + +Q_DECL_EXPORT int main(int argc, char *argv[]) +{ + QScopedPointer<QApplication> app(createApplication(argc, argv)); + QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create()); + + // // viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto); + viewer->setSource(QUrl("qrc:/qml/qmlpolarchart/loader.qml")); + viewer->setRenderHint(QPainter::Antialiasing, true); + viewer->showExpanded(); + + return app->exec(); +} diff --git a/demos/qmlpolarchart/qml/qmlpolarchart/View1.qml b/demos/qmlpolarchart/qml/qmlpolarchart/View1.qml new file mode 100644 index 00000000..c3b849b7 --- /dev/null +++ b/demos/qmlpolarchart/qml/qmlpolarchart/View1.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 +import QtCommercial.Chart 1.3 + +Rectangle { + anchors.fill: parent + //![1] + PolarChartView { + title: "Two Series, Common Axes" + anchors.fill: parent + legend.visible: false + + ValueAxis { + id: axisAngular + min: 0 + max: 20 + tickCount: 9 + } + + ValueAxis { + id: axisRadial + min: -0.5 + max: 1.5 + } + + SplineSeries { + id: series1 + axisAngular: axisAngular + axisRadial: axisRadial + pointsVisible: true + } + + ScatterSeries { + id: series2 + axisAngular: axisAngular + axisRadial: axisRadial + markerSize: 10 + } + } + + // Add data dynamically to the series + Component.onCompleted: { + for (var i = 0; i <= 20; i++) { + series1.append(i, Math.random()); + series2.append(i, Math.random()); + } + } + //![1] +} diff --git a/demos/qmlpolarchart/qml/qmlpolarchart/View2.qml b/demos/qmlpolarchart/qml/qmlpolarchart/View2.qml new file mode 100644 index 00000000..ff8ac326 --- /dev/null +++ b/demos/qmlpolarchart/qml/qmlpolarchart/View2.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 +import QtCommercial.Chart 1.3 + +Rectangle { + anchors.fill: parent + + //![1] + PolarChartView { + title: "Historical Area Series" + anchors.fill: parent + legend.visible: false + + DateTimeAxis { + id: axis1 + format: "yyyy MMM" + tickCount: 13 + } + ValueAxis { + id: axis2 + } + LineSeries { + id: lowerLine + axisAngular: axis1 + axisRadial: axis2 + + // Please note that month in JavaScript months are zero based, so 2 means March + XYPoint { x: toMsecsSinceEpoch(new Date(1950, 0, 1)); y: 15 } + XYPoint { x: toMsecsSinceEpoch(new Date(1962, 4, 1)); y: 35 } + XYPoint { x: toMsecsSinceEpoch(new Date(1970, 0, 1)); y: 50 } + XYPoint { x: toMsecsSinceEpoch(new Date(1978, 2, 1)); y: 75 } + XYPoint { x: toMsecsSinceEpoch(new Date(1987, 11, 1)); y: 102 } + XYPoint { x: toMsecsSinceEpoch(new Date(1992, 1, 1)); y: 132 } + XYPoint { x: toMsecsSinceEpoch(new Date(1998, 7, 1)); y: 100 } + XYPoint { x: toMsecsSinceEpoch(new Date(2002, 4, 1)); y: 120 } + XYPoint { x: toMsecsSinceEpoch(new Date(2012, 8, 1)); y: 140 } + XYPoint { x: toMsecsSinceEpoch(new Date(2013, 5, 1)); y: 150 } + } + LineSeries { + id: upperLine + axisAngular: axis1 + axisRadial: axis2 + + // Please note that month in JavaScript months are zero based, so 2 means March + XYPoint { x: toMsecsSinceEpoch(new Date(1950, 0, 1)); y: 30 } + XYPoint { x: toMsecsSinceEpoch(new Date(1962, 4, 1)); y: 55 } + XYPoint { x: toMsecsSinceEpoch(new Date(1970, 0, 1)); y: 80 } + XYPoint { x: toMsecsSinceEpoch(new Date(1978, 2, 1)); y: 105 } + XYPoint { x: toMsecsSinceEpoch(new Date(1987, 11, 1)); y: 125 } + XYPoint { x: toMsecsSinceEpoch(new Date(1992, 1, 1)); y: 160 } + XYPoint { x: toMsecsSinceEpoch(new Date(1998, 7, 1)); y: 140 } + XYPoint { x: toMsecsSinceEpoch(new Date(2002, 4, 1)); y: 140 } + XYPoint { x: toMsecsSinceEpoch(new Date(2012, 8, 1)); y: 170 } + XYPoint { x: toMsecsSinceEpoch(new Date(2013, 5, 1)); y: 200 } + } + AreaSeries { + axisAngular: axis1 + axisRadial: axis2 + lowerSeries: lowerLine + upperSeries: upperLine + } + } + // DateTimeAxis is based on QDateTimes so we must convert our JavaScript dates to + // milliseconds since epoch to make them match the DateTimeAxis values + function toMsecsSinceEpoch(date) { + var msecs = date.getTime(); + return msecs; + } + //![1] +} diff --git a/demos/qmlpolarchart/qml/qmlpolarchart/View3.qml b/demos/qmlpolarchart/qml/qmlpolarchart/View3.qml new file mode 100644 index 00000000..27c12107 --- /dev/null +++ b/demos/qmlpolarchart/qml/qmlpolarchart/View3.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 +import QtCommercial.Chart 1.3 + +Rectangle { + anchors.fill: parent + + //![1] + PolarChartView { + title: "Numerical Data for Dummies" + anchors.fill: parent + legend.visible: false + + LineSeries { + axisRadial: CategoryAxis { + min: 0 + max: 30 + CategoryRange { + label: "critical" + endValue: 2 + } + CategoryRange { + label: "low" + endValue: 4 + } + CategoryRange { + label: "normal" + endValue: 7 + } + CategoryRange { + label: "high" + endValue: 15 + } + CategoryRange { + label: "extremely high" + endValue: 30 + } + } + + axisAngular: ValueAxis { + tickCount: 13 + } + + XYPoint { x: 0; y: 4.3 } + XYPoint { x: 1; y: 4.1 } + XYPoint { x: 2; y: 4.7 } + XYPoint { x: 3; y: 3.9 } + XYPoint { x: 4; y: 5.2 } + XYPoint { x: 5; y: 5.3 } + XYPoint { x: 6; y: 6.1 } + XYPoint { x: 7; y: 7.7 } + XYPoint { x: 8; y: 12.9 } + XYPoint { x: 9; y: 19.2 } + } + } + //![1] +} diff --git a/demos/qmlpolarchart/qml/qmlpolarchart/loader.qml b/demos/qmlpolarchart/qml/qmlpolarchart/loader.qml new file mode 100644 index 00000000..20627ab3 --- /dev/null +++ b/demos/qmlpolarchart/qml/qmlpolarchart/loader.qml @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +Item { + id: container + width: 400 + height: 300 + Component.onCompleted: { + var co = Qt.createComponent("main.qml") + if (co.status == Component.Ready) { + var o = co.createObject(container) + } else { + console.log(co.errorString()) + console.log("QtCommercial.Chart 1.3 not available") + console.log("Please use correct QML_IMPORT_PATH export") + } + } +} diff --git a/demos/qmlpolarchart/qml/qmlpolarchart/main.qml b/demos/qmlpolarchart/qml/qmlpolarchart/main.qml new file mode 100644 index 00000000..e6ce6569 --- /dev/null +++ b/demos/qmlpolarchart/qml/qmlpolarchart/main.qml @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +Rectangle { + width: parent.width + height: parent.height + property int viewNumber: 1 + property int viewCount: 3 + + Loader { + id: loader + anchors.fill: parent + source: "View" + viewNumber + ".qml"; + } + + Rectangle { + id: infoText + anchors.centerIn: parent + width: parent.width + height: 40 + color: "black" + Text { + color: "white" + anchors.centerIn: parent + text: "Use left and right arrow keys to navigate" + } + + Behavior on opacity { + NumberAnimation { duration: 400 } + } + } + + MouseArea { + focus: true + anchors.fill: parent + onClicked: { + if (infoText.opacity > 0) { + infoText.opacity = 0.0; + } else { + nextView(); + } + } + Keys.onPressed: { + if (infoText.opacity > 0) { + infoText.opacity = 0.0; + } else { + if (event.key == Qt.Key_Left) { + previousView(); + } else { + nextView(); + } + } + } + } + + function nextView() { + var i = viewNumber + 1; + if (i > viewCount) + viewNumber = 1; + else + viewNumber = i; + } + + function previousView() { + var i = viewNumber - 1; + if (i <= 0) + viewNumber = viewCount; + else + viewNumber = i; + } +} diff --git a/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.cpp b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.cpp new file mode 100644 index 00000000..0f12265e --- /dev/null +++ b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.cpp @@ -0,0 +1,177 @@ +// checksum 0xbd34 version 0x80016 +/* + This file was generated by the Qt Quick Application wizard of Qt Creator. + QmlApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#include "qmlapplicationviewer.h" + +#include <QDir> +#include <QFileInfo> +#include <QApplication> +#include <QDeclarativeComponent> +#include <QDeclarativeEngine> +#include <QDeclarativeContext> + +#include <qplatformdefs.h> // MEEGO_EDITION_HARMATTAN + +#ifdef HARMATTAN_BOOSTER +#include <MDeclarativeCache> +#endif + +#if defined(QMLJSDEBUGGER) && QT_VERSION < 0x040800 + +#include <qt_private/qdeclarativedebughelper_p.h> + +#if !defined(NO_JSDEBUGGER) +#include <jsdebuggeragent.h> +#endif +#if !defined(NO_QMLOBSERVER) +#include <qdeclarativeviewobserver.h> +#endif + +// Enable debugging before any QDeclarativeEngine is created +struct QmlJsDebuggingEnabler +{ + QmlJsDebuggingEnabler() + { + QDeclarativeDebugHelper::enableDebugging(); + } +}; + +// Execute code in constructor before first QDeclarativeEngine is instantiated +static QmlJsDebuggingEnabler enableDebuggingHelper; + +#endif // QMLJSDEBUGGER + +class QmlApplicationViewerPrivate +{ + QString mainQmlFile; + friend class QmlApplicationViewer; + static QString adjustPath(const QString &path); +}; + +QString QmlApplicationViewerPrivate::adjustPath(const QString &path) +{ +#ifdef Q_OS_MAC + if (!QDir::isAbsolutePath(path)) + return QString::fromLatin1("%1/../Resources/%2") + .arg(QCoreApplication::applicationDirPath(), path); +#elif defined(Q_OS_QNX) + if (!QDir::isAbsolutePath(path)) + return QString::fromLatin1("app/native/%1").arg(path); +#elif !defined(Q_OS_ANDROID) + QString pathInInstallDir = + QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path); + if (QFileInfo(pathInInstallDir).exists()) + return pathInInstallDir; + pathInInstallDir = + QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path); + if (QFileInfo(pathInInstallDir).exists()) + return pathInInstallDir; +#endif + return path; +} + +QmlApplicationViewer::QmlApplicationViewer(QWidget *parent) + : QDeclarativeView(parent) + , d(new QmlApplicationViewerPrivate()) +{ + connect(engine(), SIGNAL(quit()), SLOT(close())); + setResizeMode(QDeclarativeView::SizeRootObjectToView); + + // Qt versions prior to 4.8.0 don't have QML/JS debugging services built in +#if defined(QMLJSDEBUGGER) && QT_VERSION < 0x040800 +#if !defined(NO_JSDEBUGGER) + new QmlJSDebugger::JSDebuggerAgent(engine()); +#endif +#if !defined(NO_QMLOBSERVER) + new QmlJSDebugger::QDeclarativeViewObserver(this, this); +#endif +#endif +} + +QmlApplicationViewer::~QmlApplicationViewer() +{ + delete d; +} + +QmlApplicationViewer *QmlApplicationViewer::create() +{ + return new QmlApplicationViewer(); +} + +void QmlApplicationViewer::setMainQmlFile(const QString &file) +{ + d->mainQmlFile = QmlApplicationViewerPrivate::adjustPath(file); +#ifdef Q_OS_ANDROID + setSource(QUrl(QLatin1String("assets:/")+d->mainQmlFile)); +#else + setSource(QUrl::fromLocalFile(d->mainQmlFile)); +#endif +} + +void QmlApplicationViewer::addImportPath(const QString &path) +{ + engine()->addImportPath(QmlApplicationViewerPrivate::adjustPath(path)); +} + +void QmlApplicationViewer::setOrientation(ScreenOrientation orientation) +{ +#if QT_VERSION < 0x050000 + Qt::WidgetAttribute attribute; + switch (orientation) { +#if QT_VERSION < 0x040702 + // Qt < 4.7.2 does not yet have the Qt::WA_*Orientation attributes + case ScreenOrientationLockPortrait: + attribute = static_cast<Qt::WidgetAttribute>(128); + break; + case ScreenOrientationLockLandscape: + attribute = static_cast<Qt::WidgetAttribute>(129); + break; + default: + case ScreenOrientationAuto: + attribute = static_cast<Qt::WidgetAttribute>(130); + break; +#else // QT_VERSION < 0x040702 + case ScreenOrientationLockPortrait: + attribute = Qt::WA_LockPortraitOrientation; + break; + case ScreenOrientationLockLandscape: + attribute = Qt::WA_LockLandscapeOrientation; + break; + default: + case ScreenOrientationAuto: + attribute = Qt::WA_AutoOrientation; + break; +#endif // QT_VERSION < 0x040702 + }; + setAttribute(attribute, true); +#else // QT_VERSION < 0x050000 + Q_UNUSED(orientation) +#endif // QT_VERSION < 0x050000 +} + +void QmlApplicationViewer::showExpanded() +{ +#if defined(MEEGO_EDITION_HARMATTAN) || defined(Q_WS_SIMULATOR) + showFullScreen(); +#elif defined(Q_WS_MAEMO_5) || defined(Q_OS_QNX) + showMaximized(); +#else + show(); +#endif +} + +QApplication *createApplication(int &argc, char **argv) +{ +#ifdef HARMATTAN_BOOSTER + return MDeclarativeCache::qApplication(argc, argv); +#else + return new QApplication(argc, argv); +#endif +} diff --git a/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.h b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.h new file mode 100644 index 00000000..fba2d52a --- /dev/null +++ b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.h @@ -0,0 +1,46 @@ +// checksum 0xc67a version 0x80016 +/* + This file was generated by the Qt Quick Application wizard of Qt Creator. + QmlApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#ifndef QMLAPPLICATIONVIEWER_H +#define QMLAPPLICATIONVIEWER_H + +#include <QDeclarativeView> + +class QmlApplicationViewer : public QDeclarativeView +{ + Q_OBJECT + +public: + enum ScreenOrientation { + ScreenOrientationLockPortrait, + ScreenOrientationLockLandscape, + ScreenOrientationAuto + }; + + explicit QmlApplicationViewer(QWidget *parent = 0); + virtual ~QmlApplicationViewer(); + + static QmlApplicationViewer *create(); + + void setMainQmlFile(const QString &file); + void addImportPath(const QString &path); + + // Note that this will only have an effect on Fremantle. + void setOrientation(ScreenOrientation orientation); + + void showExpanded(); + +private: + class QmlApplicationViewerPrivate *d; +}; + +QApplication *createApplication(int &argc, char **argv); + +#endif // QMLAPPLICATIONVIEWER_H diff --git a/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.pri b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.pri new file mode 100644 index 00000000..567c6dc6 --- /dev/null +++ b/demos/qmlpolarchart/qmlapplicationviewer/qmlapplicationviewer.pri @@ -0,0 +1,13 @@ +QT += declarative + +SOURCES += $$PWD/qmlapplicationviewer.cpp +HEADERS += $$PWD/qmlapplicationviewer.h +INCLUDEPATH += $$PWD + +# Include JS debugger library if QMLJSDEBUGGER_PATH is set +!isEmpty(QMLJSDEBUGGER_PATH) { + include($$QMLJSDEBUGGER_PATH/qmljsdebugger-lib.pri) +} else { + DEFINES -= QMLJSDEBUGGER +} + diff --git a/demos/qmlpolarchart/qmlpolarchart.pro b/demos/qmlpolarchart/qmlpolarchart.pro new file mode 100644 index 00000000..f661312b --- /dev/null +++ b/demos/qmlpolarchart/qmlpolarchart.pro @@ -0,0 +1,9 @@ +!include( ../demos.pri ) { + error( "Couldn't find the demos.pri file!" ) +} + +RESOURCES += resources.qrc +SOURCES += main.cpp +OTHER_FILES += qml/qmlpolarchart/* + +include(qmlapplicationviewer/qmlapplicationviewer.pri) diff --git a/demos/qmlpolarchart/resources.qrc b/demos/qmlpolarchart/resources.qrc new file mode 100644 index 00000000..854acbef --- /dev/null +++ b/demos/qmlpolarchart/resources.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/"> + <file>qml/qmlpolarchart/loader.qml</file> + <file>qml/qmlpolarchart/main.qml</file> + <file>qml/qmlpolarchart/View1.qml</file> + <file>qml/qmlpolarchart/View2.qml</file> + <file>qml/qmlpolarchart/View3.qml</file> + </qresource> +</RCC> diff --git a/doc/images/demos_qmlpolarchart1.png b/doc/images/demos_qmlpolarchart1.png Binary files differnew file mode 100644 index 00000000..58be80e6 --- /dev/null +++ b/doc/images/demos_qmlpolarchart1.png diff --git a/doc/images/demos_qmlpolarchart2.png b/doc/images/demos_qmlpolarchart2.png Binary files differnew file mode 100644 index 00000000..35c3521a --- /dev/null +++ b/doc/images/demos_qmlpolarchart2.png diff --git a/doc/images/demos_qmlpolarchart3.png b/doc/images/demos_qmlpolarchart3.png Binary files differnew file mode 100644 index 00000000..31a68dc6 --- /dev/null +++ b/doc/images/demos_qmlpolarchart3.png diff --git a/doc/images/examples_polarchart.png b/doc/images/examples_polarchart.png Binary files differnew file mode 100644 index 00000000..f5ea517a --- /dev/null +++ b/doc/images/examples_polarchart.png diff --git a/doc/src/classes.qdoc b/doc/src/classes.qdoc index 89f4d070..07be9dd8 100644 --- a/doc/src/classes.qdoc +++ b/doc/src/classes.qdoc @@ -30,6 +30,7 @@ <td valign="top"> <ul> <li><a href="qchart.html">QChart</a></li> + <li><a href="qpolarchart.html">QPolarChart</a></li> <li><a href="qchartview.html">QChartView</a></li> <li><a href="qabstractaxis.html">QAbstractAxis</a></li> <li><a href="qvalueaxis.html">QValueAxis</a></li> diff --git a/doc/src/demos-qmlpolarchart.qdoc b/doc/src/demos-qmlpolarchart.qdoc new file mode 100644 index 00000000..04c7037e --- /dev/null +++ b/doc/src/demos-qmlpolarchart.qdoc @@ -0,0 +1,33 @@ +/*! + \example demos/qmlpolarchart + \title Qml Polar Chart + \subtitle + + This is a demonstration of how to use polar chart in your QML application. + + \table + \row + \o \br + We begin with a chart that has a spline series and a scatter series with random + data. Both series use the same axes. + \br + \br + \snippet ../demos/qmlpolarchart/qml/qmlpolarchart/View1.qml 1 + \o \inlineimage demos_qmlpolarchart1.png + \row + \o \br + The next example shows a chart with some accurate historical data that makes us to use a DateTimeAxis + and AreaSeries. + \br + \br + \snippet ../demos/qmlpolarchart/qml/qmlpolarchart/View2.qml 1 + \o \inlineimage demos_qmlpolarchart2.png + \row + \o \br + And the final example with a chart that uses a CategoryAxis to make the data easier to understand. + \br + \br + \snippet ../demos/qmlpolarchart/qml/qmlpolarchart/View3.qml 1 + \o \inlineimage demos_qmlpolarchart3.png + \endtable +*/ diff --git a/doc/src/demos.qdoc b/doc/src/demos.qdoc index 573b416a..6316617a 100644 --- a/doc/src/demos.qdoc +++ b/doc/src/demos.qdoc @@ -8,6 +8,14 @@ <table> <tr> + <td><a href="demos-audio.html">Audio</a></td> + <td><a href="demos-callout.html">Callout</a></td> + </tr> + <tr> + <td><a href="demos-audio.html"><img src="images/demos_audio.png" width="300" alt="Audio" /></a></td> + <td><a href="demos-callout.html"><img src="images/demos_callout.png" width="300" alt="Callout" /></a></td> + </tr> + <tr> <td><a href="demos-chartthemes.html">Chart Themes</a></td> <td><a href="demos-dynamicspline.html">Dynamic Spline Chart</a></td> </tr> @@ -15,7 +23,6 @@ <td><a href="demos-chartthemes.html"><img src="images/demo_chartthemes_blue_cerulean.png" width="300" alt="Chart Themes" /></a></td> <td><a href="demos-dynamicspline.html"><img src="images/demos_dynamicspline2.png" width="300" alt="Dynamic Spline" /></a></td> </tr> - <tr> <td><a href="demos-nesteddonuts.html">Nested Donuts Chart</a></td> <td><a href="demos-piechartcustomization.html">Pie Chart Customization</a></td> @@ -24,7 +31,6 @@ <td><a href="demos-nesteddonuts.html"><img src="images/demos_nesteddonuts.png" width="300" alt="Nested Donuts Chart" /></a></td> <td><a href="demos-piechartcustomization.html"><img src="images/piechart_customization.png" width="300" alt="Pie Chart Customization" /></a></td> </tr> - <tr> <td><a href="demos-qmlchart.html">Qml Basic Charts</a></td> <td><a href="demos-qmlaxes.html">Qml Axes</a></td> @@ -33,7 +39,6 @@ <td><a href="demos-qmlchart.html"><img src="images/demos_qmlchart1.png" width="300" alt="Qml Basic Charts" /></a></td> <td><a href="demos-qmlaxes.html"><img src="images/demos_qmlaxes1.png" width="300" alt="Qml Axes" /></a></td> </tr> - <tr> <td><a href="demos-qmlcustomizations.html">Qml Customizations</a></td> <td><a href="demos-qmlcustommodel.html">Qml Custom Model</a></td> @@ -42,7 +47,6 @@ <td><a href="demos-qmlcustomizations.html"><img src="images/demos_qmlcustomizations.png" width="300" alt="Qml Customizations" /></a></td> <td><a href="demos-qmlcustommodel.html"><img src="images/demos_qmlcustommodel.png" width="300" alt="Qml Custom Model" /></a></td> </tr> - <tr> <td><a href="demos-qmlf1legends.html">Qml F1 Legends</a></td> <td><a href="demos-qmloscilloscope.html">Qml Oscilloscope</a></td> @@ -51,7 +55,6 @@ <td><a href="demos-qmlf1legends.html"><img src="images/demos_qmlf1legends.png" width="300" alt="Qml F1 Legends" /></a></td> <td><a href="demos-qmloscilloscope.html"><img src="images/demos_qmloscilloscope.png" width="300" alt="Qml Oscilloscope" /></a></td> </tr> - <tr> <td><a href="demos-qmlweather.html">Qml Weather</a></td> <td><a href="demos-qmlcustomlegend.html">Qml Custom Legend</a></td> @@ -61,14 +64,11 @@ <td><a href="demos-qmlcustomlegend.html"><img src="images/demos-qmlcustomlegend1.png" width="300" alt="Qml Custom Legend" /></a></td> </tr> <tr> - <td><a href="demos-callout.html">Callout</a></td> - <td><a href="demos-audio.html">Audio</a></td> + <td><a href="demos-qmlpolarchart.html">Qml Polar Chart</a></td> </tr> <tr> - <td><a href="demos-callout.html"><img src="images/demos_callout.png" width="300" alt="Callout" /></a></td> - <td><a href="demos-audio.html"><img src="images/demos_audio.png" width="300" alt="Audio" /></a></td> + <td><a href="demos-qmlpolarchart.html"><img src="images/demos_qmlpolarchart1.png" width="300" alt="Qml Polar Chart" /></a></td> </tr> - </table> </div> \endraw diff --git a/doc/src/examples-polarchart.qdoc b/doc/src/examples-polarchart.qdoc new file mode 100644 index 00000000..d9f976d6 --- /dev/null +++ b/doc/src/examples-polarchart.qdoc @@ -0,0 +1,33 @@ +/*! + \example examples/polarchart + \title Polar chart example + \subtitle + + The example shows how to create simple polar chart with multiple different series. + It also shows how to implement scrolling and zooming of the polar chart as well as + visually demonstrate how polar chart and cartesian chart relate to each other. + + \image examples_polarchart.png + + Creating polar chart is done via QPolarChart instance instead of QChart. + + \snippet ../examples/polarchart/main.cpp 1 + + Axes are created similarly to cartesian charts, but when axes are added to the chart, + you can use polar orientations instead of alignments. + + \snippet ../examples/polarchart/main.cpp 2 + + Zooming and scrolling of the polar chart is logically nearly identical to zooming and scrolling of cartesian chart. + The main difference is that when scrolling along X-axis (angular axis), angle is used instead of number of pixels. + Another difference is that zooming to a rectangle cannot be done. + + \snippet ../examples/polarchart/chartview.cpp 1 + + Same axes and series can be used in both cartesian and polar charts, though not simultaneously. + To switch between chart types, you need to first remove series and axes from the old chart and then add + them to the new chart. If you want to preserve axis ranges, those need to be copied, too. + + \snippet ../examples/polarchart/chartview.cpp 2 + +*/ diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index f0588bec..f5c736eb 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -98,36 +98,38 @@ </tr> <tr> + <td><a href="examples-polarchart.html">Polar chart</a></td> <td><a href="examples-scatterchart.html">Scatter chart</a></td> - <td><a href="examples-scatterinteractions.html">Scatter Interactions</a></td> </tr> <tr> + <td><a href="examples-polarchart.html"><img src="images/examples_polarchart.png" width="300" alt="Polar chart" /></a></td> <td><a href="examples-scatterchart.html"><img src="images/examples_scatterchart.png" width="300" alt="Scatter chart" /></a></td> - <td><a href="examples-scatterinteractions.html"><img src="images/examples_scatterinteractions.png" width="300" alt="Scatter Interactions" /></a></td> </tr> <tr> + <td><a href="examples-scatterinteractions.html">Scatter Interactions</a></td> <td><a href="examples-splinechart.html">Spline Chart</a></td> - <td><a href="examples-stackedbarchart.html">Stacked Bar Chart</a></td> </tr> <tr> + <td><a href="examples-scatterinteractions.html"><img src="images/examples_scatterinteractions.png" width="300" alt="Scatter Interactions" /></a></td> <td><a href="examples-splinechart.html"><img src="images/examples_splinechart.png" width="300" alt="Spline Chart" /></a></td> - <td><a href="examples-stackedbarchart.html"><img src="images/examples_stackedbarchart.png" width="300" alt="Stacked Bar Chart" /></a></td> </tr> <tr> + <td><a href="examples-stackedbarchart.html">Stacked Bar Chart</a></td> <td><a href="examples-stackedbarchartdrilldown.html">Stacked Bar Chart Drilldown</a></td> - <td><a href="examples-temperaturerecords.html">Temperature Records</a></td> </tr> <tr> + <td><a href="examples-stackedbarchart.html"><img src="images/examples_stackedbarchart.png" width="300" alt="Stacked Bar Chart" /></a></td> <td><a href="examples-stackedbarchartdrilldown.html"><img src="images/examples_stackedbarchartdrilldown1.png" width="300" alt="Stacked Bar Chart Drilldown" /></a></td> - <td><a href="examples-temperaturerecords.html"><img src="images/examples_temperaturerecords.png" width="300" alt="Temperature Records" /></a></td> </tr> <tr> + <td><a href="examples-temperaturerecords.html">Temperature Records</a></td> <td><a href="examples-zoomlinechart.html">Zoom Line</a></td> </tr> <tr> + <td><a href="examples-temperaturerecords.html"><img src="images/examples_temperaturerecords.png" width="300" alt="Temperature Records" /></a></td> <td><a href="examples-zoomlinechart.html"><img src="images/examples_zoomlinechart1.png" width="300" alt="Zoom Line" /></a></td> </tr> diff --git a/doc/src/qml.qdoc b/doc/src/qml.qdoc index 9fbb9fee..d7fae7e6 100644 --- a/doc/src/qml.qdoc +++ b/doc/src/qml.qdoc @@ -27,6 +27,7 @@ <td valign="top"> <ul> <li><a href="qml-chartview.html">ChartView</a></li> + <li><a href="qml-polarchartview.html">PolarChartView</a></li> <li><a href="qml-abstractaxis.html">AbstractAxis</a></li> <li><a href="qml-valueaxis.html">ValueAxis</a></li> <li><a href="qml-categoryaxis.html">CategoryAxis</a></li> diff --git a/examples/donutbreakdown/donutbreakdownchart.cpp b/examples/donutbreakdown/donutbreakdownchart.cpp index 6509e464..37130f01 100644 --- a/examples/donutbreakdown/donutbreakdownchart.cpp +++ b/examples/donutbreakdown/donutbreakdownchart.cpp @@ -26,7 +26,7 @@ QTCOMMERCIALCHART_USE_NAMESPACE //![1] DonutBreakdownChart::DonutBreakdownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) - : QChart(parent, wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags) { // create the series for main center pie m_mainSeries = new QPieSeries(); diff --git a/examples/examples.pro b/examples/examples.pro index ab406af3..716dce54 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -31,7 +31,8 @@ SUBDIRS += \ donutchart \ multiaxis \ legendmarkers \ - logvalueaxis + logvalueaxis \ + polarchart !linux-arm*: { SUBDIRS += \ diff --git a/examples/piechartdrilldown/drilldownchart.cpp b/examples/piechartdrilldown/drilldownchart.cpp index 96de458c..47d83ade 100644 --- a/examples/piechartdrilldown/drilldownchart.cpp +++ b/examples/piechartdrilldown/drilldownchart.cpp @@ -23,7 +23,7 @@ QTCOMMERCIALCHART_USE_NAMESPACE DrilldownChart::DrilldownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) - : QChart(parent, wFlags), + : QChart(QChart::ChartTypeCartesian, parent, wFlags), m_currentSeries(0) { diff --git a/examples/polarchart/chartview.cpp b/examples/polarchart/chartview.cpp new file mode 100644 index 00000000..fc53a536 --- /dev/null +++ b/examples/polarchart/chartview.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chartview.h" +#include <QMouseEvent> +#include <QDebug> +#include <QAbstractAxis> +#include <QValueAxis> + +QTCOMMERCIALCHART_USE_NAMESPACE + +ChartView::ChartView(QWidget *parent) + : QChartView(parent) +{ +} + +//![1] +void ChartView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + chart()->zoomIn(); + break; + case Qt::Key_Minus: + chart()->zoomOut(); + break; + case Qt::Key_Left: + chart()->scroll(-1.0, 0); + break; + case Qt::Key_Right: + chart()->scroll(1.0, 0); + break; + case Qt::Key_Up: + chart()->scroll(0, 1.0); + break; + case Qt::Key_Down: + chart()->scroll(0, -1.0); + break; + case Qt::Key_Space: + switchChartType(); + break; + default: + QGraphicsView::keyPressEvent(event); + break; + } +} +//![1] + +//![2] +void ChartView::switchChartType() +{ + QChart *newChart; + QChart *oldChart = chart(); + + if (oldChart->chartType() == QChart::ChartTypeCartesian) + newChart = new QPolarChart(); + else + newChart = new QChart(); + + // Move series and axes from old chart to new one + QList<QAbstractSeries *> seriesList = oldChart->series(); + QList<QAbstractAxis *> axisList = oldChart->axes(); + QList<QPair<qreal, qreal>> axisRanges; + + foreach (QAbstractAxis *axis, axisList) { + QValueAxis *valueAxis = static_cast<QValueAxis *>(axis); + axisRanges.append(QPair<qreal, qreal>(valueAxis->min(), valueAxis->max())); + } + + foreach (QAbstractSeries *series, seriesList) + oldChart->removeSeries(series); + + foreach (QAbstractAxis *axis, axisList) { + oldChart->removeAxis(axis); + newChart->addAxis(axis, axis->alignment()); + } + + foreach (QAbstractSeries *series, seriesList) { + newChart->addSeries(series); + foreach (QAbstractAxis *axis, axisList) + series->attachAxis(axis); + } + + int count = 0; + foreach (QAbstractAxis *axis, axisList) { + axis->setRange(axisRanges[count].first, axisRanges[count].second); + count++; + } + + newChart->setTitle(oldChart->title()); + setChart(newChart); + delete oldChart; +} +//![2] diff --git a/examples/polarchart/chartview.h b/examples/polarchart/chartview.h new file mode 100644 index 00000000..c4584ac8 --- /dev/null +++ b/examples/polarchart/chartview.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CHARTVIEW_H +#define CHARTVIEW_H + +#include <QChartView> +#include <QPolarChart> + +QTCOMMERCIALCHART_USE_NAMESPACE + +class ChartView : public QChartView +{ +public: + ChartView(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + void switchChartType(); +}; + +#endif diff --git a/examples/polarchart/main.cpp b/examples/polarchart/main.cpp new file mode 100644 index 00000000..7b3b38d8 --- /dev/null +++ b/examples/polarchart/main.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chartview.h" +#include <QApplication> +#include <QMainWindow> +#include <QScatterSeries> +#include <QLineSeries> +#include <QSplineSeries> +#include <QAreaSeries> +#include <QValueAxis> +#include <QPolarChart> +#include <QDebug> + +QTCOMMERCIALCHART_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + const qreal angularMin = -100; + const qreal angularMax = 100; + + const qreal radialMin = -100; + const qreal radialMax = 100; + + QScatterSeries *series1 = new QScatterSeries(); + series1->setName("scatter"); + for (int i = angularMin; i <= angularMax; i += 10) + series1->append(i, (i / radialMax) * radialMax + 8.0); + + QSplineSeries *series2 = new QSplineSeries(); + series2->setName("spline"); + for (int i = angularMin; i <= angularMax; i += 10) + series2->append(i, (i / radialMax) * radialMax); + + QLineSeries *series3 = new QLineSeries(); + series3->setName("star outer"); + qreal ad = (angularMax - angularMin) / 8; + qreal rd = (radialMax - radialMin) / 3 * 1.3; + series3->append(angularMin, radialMax); + series3->append(angularMin + ad*1, radialMin + rd); + series3->append(angularMin + ad*2, radialMax); + series3->append(angularMin + ad*3, radialMin + rd); + series3->append(angularMin + ad*4, radialMax); + series3->append(angularMin + ad*5, radialMin + rd); + series3->append(angularMin + ad*6, radialMax); + series3->append(angularMin + ad*7, radialMin + rd); + series3->append(angularMin + ad*8, radialMax); + + QLineSeries *series4 = new QLineSeries(); + series4->setName("star inner"); + ad = (angularMax - angularMin) / 8; + rd = (radialMax - radialMin) / 3; + series4->append(angularMin, radialMax); + series4->append(angularMin + ad*1, radialMin + rd); + series4->append(angularMin + ad*2, radialMax); + series4->append(angularMin + ad*3, radialMin + rd); + series4->append(angularMin + ad*4, radialMax); + series4->append(angularMin + ad*5, radialMin + rd); + series4->append(angularMin + ad*6, radialMax); + series4->append(angularMin + ad*7, radialMin + rd); + series4->append(angularMin + ad*8, radialMax); + + QAreaSeries *series5 = new QAreaSeries(); + series5->setName("star area"); + series5->setUpperSeries(series3); + series5->setLowerSeries(series4); + series5->setOpacity(0.5); + + //![1] + QPolarChart *chart = new QPolarChart(); + //![1] + chart->addSeries(series1); + chart->addSeries(series2); + chart->addSeries(series3); + chart->addSeries(series4); + chart->addSeries(series5); + + chart->setTitle("Use arrow keys to scroll, +/- to zoom, and space to switch chart type."); + + //![2] + QValueAxis *angularAxis = new QValueAxis(); + angularAxis->setTickCount(9); // First and last ticks are co-located on 0/360 angle. + angularAxis->setLabelFormat("%.1f"); + angularAxis->setShadesVisible(true); + angularAxis->setShadesBrush(QBrush(QColor(249, 249, 255))); + chart->addAxis(angularAxis, QPolarChart::PolarOrientationAngular); + + QValueAxis *radialAxis = new QValueAxis(); + radialAxis->setTickCount(9); + radialAxis->setLabelFormat("%d"); + chart->addAxis(radialAxis, QPolarChart::PolarOrientationRadial); + //![2] + + series1->attachAxis(radialAxis); + series1->attachAxis(angularAxis); + series2->attachAxis(radialAxis); + series2->attachAxis(angularAxis); + series3->attachAxis(radialAxis); + series3->attachAxis(angularAxis); + series4->attachAxis(radialAxis); + series4->attachAxis(angularAxis); + series5->attachAxis(radialAxis); + series5->attachAxis(angularAxis); + + radialAxis->setRange(radialMin, radialMax); + angularAxis->setRange(angularMin, angularMax); + + ChartView *chartView = new ChartView(); + chartView->setChart(chart); + chartView->setRenderHint(QPainter::Antialiasing); + + QMainWindow window; + window.setCentralWidget(chartView); + window.resize(800, 600); + window.show(); + + return a.exec(); +} diff --git a/examples/polarchart/polarchart.pro b/examples/polarchart/polarchart.pro new file mode 100644 index 00000000..8759c1ce --- /dev/null +++ b/examples/polarchart/polarchart.pro @@ -0,0 +1,6 @@ +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} +TARGET = polarchart +SOURCES += main.cpp chartview.cpp +HEADERS += chartview.h diff --git a/examples/stackedbarchartdrilldown/drilldownchart.cpp b/examples/stackedbarchartdrilldown/drilldownchart.cpp index 51c9147a..b2f5120d 100644 --- a/examples/stackedbarchartdrilldown/drilldownchart.cpp +++ b/examples/stackedbarchartdrilldown/drilldownchart.cpp @@ -24,7 +24,7 @@ QTCOMMERCIALCHART_USE_NAMESPACE DrilldownChart::DrilldownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) - : QChart(parent, wFlags), + : QChart(QChart::ChartTypeCartesian, parent, wFlags), m_currentSeries(0) { } diff --git a/examples/zoomlinechart/chart.cpp b/examples/zoomlinechart/chart.cpp index aa109f2c..40834346 100644 --- a/examples/zoomlinechart/chart.cpp +++ b/examples/zoomlinechart/chart.cpp @@ -24,7 +24,7 @@ #include <QGraphicsView> Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags) - : QChart(parent, wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags) { // Seems that QGraphicsView (QChartView) does not grab gestures. // They can only be grabbed here in the QGraphicsWidget (QChart). diff --git a/plugins/declarative/declarative.pro b/plugins/declarative/declarative.pro index 7641b2b5..12d48832 100644 --- a/plugins/declarative/declarative.pro +++ b/plugins/declarative/declarative.pro @@ -25,7 +25,8 @@ SOURCES += \ declarativebarseries.cpp \ declarativecategoryaxis.cpp \ declarativemargins.cpp \ - declarativeaxes.cpp + declarativeaxes.cpp \ + declarativepolarchart.cpp HEADERS += \ declarativechart.h \ @@ -39,7 +40,8 @@ HEADERS += \ declarativebarseries.h \ declarativecategoryaxis.h \ declarativemargins.h \ - declarativeaxes.h + declarativeaxes.h \ + declarativepolarchart.h TARGETPATH = QtCommercial/Chart target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH diff --git a/plugins/declarative/declarativeareaseries.cpp b/plugins/declarative/declarativeareaseries.cpp index cda4db60..fabbc612 100644 --- a/plugins/declarative/declarativeareaseries.cpp +++ b/plugins/declarative/declarativeareaseries.cpp @@ -31,6 +31,8 @@ DeclarativeAreaSeries::DeclarativeAreaSeries(QObject *parent) : connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisYChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SIGNAL(axisXTopChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SIGNAL(axisYRightChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), this, SIGNAL(axisAngularChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisRadialChanged(QAbstractAxis*))); } void DeclarativeAreaSeries::setUpperSeries(DeclarativeLineSeries *series) diff --git a/plugins/declarative/declarativeareaseries.h b/plugins/declarative/declarativeareaseries.h index 3b3fc118..7c1c0abe 100644 --- a/plugins/declarative/declarativeareaseries.h +++ b/plugins/declarative/declarativeareaseries.h @@ -36,6 +36,8 @@ class DeclarativeAreaSeries : public QAreaSeries Q_PROPERTY(QAbstractAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged REVISION 1) Q_PROPERTY(QAbstractAxis *axisXTop READ axisXTop WRITE setAxisXTop NOTIFY axisXTopChanged REVISION 2) Q_PROPERTY(QAbstractAxis *axisYRight READ axisYRight WRITE setAxisYRight NOTIFY axisYRightChanged REVISION 2) + Q_PROPERTY(QAbstractAxis *axisAngular READ axisAngular WRITE setAxisAngular NOTIFY axisAngularChanged REVISION 3) + Q_PROPERTY(QAbstractAxis *axisRadial READ axisRadial WRITE setAxisRadial NOTIFY axisRadialChanged REVISION 3) Q_PROPERTY(qreal borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged REVISION 1) public: @@ -52,6 +54,10 @@ public: Q_REVISION(2) void setAxisXTop(QAbstractAxis *axis) { m_axes->setAxisXTop(axis); } Q_REVISION(2) QAbstractAxis *axisYRight() { return m_axes->axisYRight(); } Q_REVISION(2) void setAxisYRight(QAbstractAxis *axis) { m_axes->setAxisYRight(axis); } + Q_REVISION(3) QAbstractAxis *axisAngular() { return m_axes->axisX(); } + Q_REVISION(3) void setAxisAngular(QAbstractAxis *axis) { m_axes->setAxisX(axis); } + Q_REVISION(3) QAbstractAxis *axisRadial() { return m_axes->axisY(); } + Q_REVISION(3) void setAxisRadial(QAbstractAxis *axis) { m_axes->setAxisY(axis); } qreal borderWidth() const; void setBorderWidth(qreal borderWidth); @@ -61,6 +67,8 @@ Q_SIGNALS: Q_REVISION(1) void borderWidthChanged(qreal width); Q_REVISION(2) void axisXTopChanged(QAbstractAxis *axis); Q_REVISION(2) void axisYRightChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisAngularChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisRadialChanged(QAbstractAxis *axis); public: DeclarativeAxes *m_axes; diff --git a/plugins/declarative/declarativechart.cpp b/plugins/declarative/declarativechart.cpp index 7e5500b8..344f5e62 100644 --- a/plugins/declarative/declarativechart.cpp +++ b/plugins/declarative/declarativechart.cpp @@ -35,6 +35,7 @@ #include "chartdataset_p.h" #include "declarativeaxes.h" #include "qchart_p.h" +#include "qpolarchart.h" #ifndef QT_ON_ARM #include "qdatetimeaxis.h" @@ -235,9 +236,24 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE */ DeclarativeChart::DeclarativeChart(QDeclarativeItem *parent) - : QDeclarativeItem(parent), - m_chart(new QChart(this)) + : QDeclarativeItem(parent) { + initChart(QChart::ChartTypeCartesian); +} + +DeclarativeChart::DeclarativeChart(QChart::ChartType type, QDeclarativeItem *parent) + : QDeclarativeItem(parent) +{ + initChart(type); +} + +void DeclarativeChart::initChart(QChart::ChartType type) +{ + if (type == QChart::ChartTypePolar) + m_chart = new QPolarChart(this); + else + m_chart = new QChart(this); + setFlag(QGraphicsItem::ItemHasNoContents, false); m_margins = new DeclarativeMargins(this); m_margins->setTop(m_chart->margins().top()); diff --git a/plugins/declarative/declarativechart.h b/plugins/declarative/declarativechart.h index 69116063..cc6295e7 100644 --- a/plugins/declarative/declarativechart.h +++ b/plugins/declarative/declarativechart.h @@ -170,7 +170,11 @@ private Q_SLOTS: void handleAxisYRightSet(QAbstractAxis *axis); void handleSeriesAdded(QAbstractSeries *series); +protected: + explicit DeclarativeChart(QChart::ChartType type, QDeclarativeItem *parent); + private: + void initChart(QChart::ChartType type); // Extending QChart with DeclarativeChart is not possible because QObject does not support // multi inheritance, so we now have a QChart as a member instead QChart *m_chart; diff --git a/plugins/declarative/declarativelineseries.cpp b/plugins/declarative/declarativelineseries.cpp index aae3eedf..735b2962 100644 --- a/plugins/declarative/declarativelineseries.cpp +++ b/plugins/declarative/declarativelineseries.cpp @@ -30,6 +30,8 @@ DeclarativeLineSeries::DeclarativeLineSeries(QObject *parent) : connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisYChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SIGNAL(axisXTopChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SIGNAL(axisYRightChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), this, SIGNAL(axisAngularChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisRadialChanged(QAbstractAxis*))); connect(this, SIGNAL(pointAdded(int)), this, SLOT(handleCountChanged(int))); connect(this, SIGNAL(pointRemoved(int)), this, SLOT(handleCountChanged(int))); } diff --git a/plugins/declarative/declarativelineseries.h b/plugins/declarative/declarativelineseries.h index 21e2ac19..05222a6d 100644 --- a/plugins/declarative/declarativelineseries.h +++ b/plugins/declarative/declarativelineseries.h @@ -38,6 +38,8 @@ class DeclarativeLineSeries : public QLineSeries, public DeclarativeXySeries, pu Q_PROPERTY(QAbstractAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged REVISION 1) Q_PROPERTY(QAbstractAxis *axisXTop READ axisXTop WRITE setAxisXTop NOTIFY axisXTopChanged REVISION 2) Q_PROPERTY(QAbstractAxis *axisYRight READ axisYRight WRITE setAxisYRight NOTIFY axisYRightChanged REVISION 2) + Q_PROPERTY(QAbstractAxis *axisAngular READ axisAngular WRITE setAxisAngular NOTIFY axisAngularChanged REVISION 3) + Q_PROPERTY(QAbstractAxis *axisRadial READ axisRadial WRITE setAxisRadial NOTIFY axisRadialChanged REVISION 3) Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged REVISION 1) Q_PROPERTY(Qt::PenStyle style READ style WRITE setStyle NOTIFY styleChanged REVISION 1) Q_PROPERTY(Qt::PenCapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged REVISION 1) @@ -55,6 +57,10 @@ public: Q_REVISION(2) void setAxisXTop(QAbstractAxis *axis) { m_axes->setAxisXTop(axis); } Q_REVISION(2) QAbstractAxis *axisYRight() { return m_axes->axisYRight(); } Q_REVISION(2) void setAxisYRight(QAbstractAxis *axis) { m_axes->setAxisYRight(axis); } + Q_REVISION(3) QAbstractAxis *axisAngular() { return m_axes->axisX(); } + Q_REVISION(3) void setAxisAngular(QAbstractAxis *axis) { m_axes->setAxisX(axis); } + Q_REVISION(3) QAbstractAxis *axisRadial() { return m_axes->axisY(); } + Q_REVISION(3) void setAxisRadial(QAbstractAxis *axis) { m_axes->setAxisY(axis); } qreal width() const; void setWidth(qreal width); Qt::PenStyle style() const; @@ -81,6 +87,8 @@ Q_SIGNALS: Q_REVISION(1) void axisYChanged(QAbstractAxis *axis); Q_REVISION(2) void axisXTopChanged(QAbstractAxis *axis); Q_REVISION(2) void axisYRightChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisAngularChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisRadialChanged(QAbstractAxis *axis); Q_REVISION(1) void widthChanged(qreal width); Q_REVISION(1) void styleChanged(Qt::PenStyle style); Q_REVISION(1) void capStyleChanged(Qt::PenCapStyle capStyle); diff --git a/plugins/declarative/declarativepolarchart.cpp b/plugins/declarative/declarativepolarchart.cpp new file mode 100644 index 00000000..3be90d78 --- /dev/null +++ b/plugins/declarative/declarativepolarchart.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "declarativepolarchart.h" +#include "qchart.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +/*! + \qmlclass PolarChartView DeclarativePolarChart + + PolarChartView element is the parent that is responsible for showing different chart series types + in a polar chart. + + Polar charts support line, spline, area, and scatter series, and all axis types + supported by those series. + + \note When setting ticks to an angular ValueAxis, keep in mind that the first and last tick + are co-located at 0/360 degree angle. + + \note If the angular distance between two consecutive points in a series is more than 180 degrees, + any line connecting the two points becomes meaningless, so choose the axis ranges accordingly + when displaying line, spline, or area series. + + \note Polar charts do not support multiple axes of same orientation. + + The following QML shows how to create a polar chart with two series: + \snippet ../demos/qmlpolarchart/qml/qmlpolarchart/view1.qml 1 + + \beginfloatleft + \image demos_qmlpolarchart1.png + \endfloat + \clearfloat +*/ + +DeclarativePolarChart::DeclarativePolarChart(QDeclarativeItem *parent) + : DeclarativeChart(QChart::ChartTypePolar, parent) +{ +} + +DeclarativePolarChart::~DeclarativePolarChart() +{ +} + +#include "moc_declarativepolarchart.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/plugins/declarative/declarativepolarchart.h b/plugins/declarative/declarativepolarchart.h new file mode 100644 index 00000000..598de6ea --- /dev/null +++ b/plugins/declarative/declarativepolarchart.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DECLARATIVEPOLARCHART_H +#define DECLARATIVEPOLARCHART_H + +#include <QtCore/QtGlobal> +#include <QtDeclarative/QDeclarativeItem> +#include "declarativechart.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class DeclarativePolarChart : public DeclarativeChart +{ + Q_OBJECT +public: + DeclarativePolarChart(QDeclarativeItem *parent = 0); + ~DeclarativePolarChart(); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // DECLARATIVEPOLARCHART_H diff --git a/plugins/declarative/declarativescatterseries.cpp b/plugins/declarative/declarativescatterseries.cpp index 209333e5..af7b41a6 100644 --- a/plugins/declarative/declarativescatterseries.cpp +++ b/plugins/declarative/declarativescatterseries.cpp @@ -30,6 +30,8 @@ DeclarativeScatterSeries::DeclarativeScatterSeries(QObject *parent) : connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisYChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SIGNAL(axisXTopChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SIGNAL(axisYRightChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), this, SIGNAL(axisAngularChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisRadialChanged(QAbstractAxis*))); connect(this, SIGNAL(pointAdded(int)), this, SLOT(handleCountChanged(int))); connect(this, SIGNAL(pointRemoved(int)), this, SLOT(handleCountChanged(int))); } diff --git a/plugins/declarative/declarativescatterseries.h b/plugins/declarative/declarativescatterseries.h index a6b4b1b8..2400242c 100644 --- a/plugins/declarative/declarativescatterseries.h +++ b/plugins/declarative/declarativescatterseries.h @@ -38,6 +38,8 @@ class DeclarativeScatterSeries : public QScatterSeries, public DeclarativeXySeri Q_PROPERTY(QAbstractAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged REVISION 1) Q_PROPERTY(QAbstractAxis *axisXTop READ axisXTop WRITE setAxisXTop NOTIFY axisXTopChanged REVISION 2) Q_PROPERTY(QAbstractAxis *axisYRight READ axisYRight WRITE setAxisYRight NOTIFY axisYRightChanged REVISION 2) + Q_PROPERTY(QAbstractAxis *axisAngular READ axisAngular WRITE setAxisAngular NOTIFY axisAngularChanged REVISION 3) + Q_PROPERTY(QAbstractAxis *axisRadial READ axisRadial WRITE setAxisRadial NOTIFY axisRadialChanged REVISION 3) Q_PROPERTY(qreal borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged REVISION 1) Q_PROPERTY(QDeclarativeListProperty<QObject> declarativeChildren READ declarativeChildren) Q_CLASSINFO("DefaultProperty", "declarativeChildren") @@ -53,6 +55,10 @@ public: Q_REVISION(2) void setAxisXTop(QAbstractAxis *axis) { m_axes->setAxisXTop(axis); } Q_REVISION(2) QAbstractAxis *axisYRight() { return m_axes->axisYRight(); } Q_REVISION(2) void setAxisYRight(QAbstractAxis *axis) { m_axes->setAxisYRight(axis); } + Q_REVISION(3) QAbstractAxis *axisAngular() { return m_axes->axisX(); } + Q_REVISION(3) void setAxisAngular(QAbstractAxis *axis) { m_axes->setAxisX(axis); } + Q_REVISION(3) QAbstractAxis *axisRadial() { return m_axes->axisY(); } + Q_REVISION(3) void setAxisRadial(QAbstractAxis *axis) { m_axes->setAxisY(axis); } qreal borderWidth() const; void setBorderWidth(qreal borderWidth); QDeclarativeListProperty<QObject> declarativeChildren(); @@ -76,6 +82,8 @@ Q_SIGNALS: Q_REVISION(1) void borderWidthChanged(qreal width); Q_REVISION(2) void axisXTopChanged(QAbstractAxis *axis); Q_REVISION(2) void axisYRightChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisAngularChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisRadialChanged(QAbstractAxis *axis); public Q_SLOTS: static void appendDeclarativeChildren(QDeclarativeListProperty<QObject> *list, QObject *element); diff --git a/plugins/declarative/declarativesplineseries.cpp b/plugins/declarative/declarativesplineseries.cpp index 7825b59b..f3900413 100644 --- a/plugins/declarative/declarativesplineseries.cpp +++ b/plugins/declarative/declarativesplineseries.cpp @@ -30,6 +30,8 @@ DeclarativeSplineSeries::DeclarativeSplineSeries(QObject *parent) : connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisYChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SIGNAL(axisXTopChanged(QAbstractAxis*))); connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SIGNAL(axisYRightChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), this, SIGNAL(axisAngularChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisRadialChanged(QAbstractAxis*))); connect(this, SIGNAL(pointAdded(int)), this, SLOT(handleCountChanged(int))); connect(this, SIGNAL(pointRemoved(int)), this, SLOT(handleCountChanged(int))); } diff --git a/plugins/declarative/declarativesplineseries.h b/plugins/declarative/declarativesplineseries.h index f9e1a3e7..cfa0a7ee 100644 --- a/plugins/declarative/declarativesplineseries.h +++ b/plugins/declarative/declarativesplineseries.h @@ -38,6 +38,8 @@ class DeclarativeSplineSeries : public QSplineSeries, public DeclarativeXySeries Q_PROPERTY(QAbstractAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged REVISION 1) Q_PROPERTY(QAbstractAxis *axisXTop READ axisXTop WRITE setAxisXTop NOTIFY axisXTopChanged REVISION 2) Q_PROPERTY(QAbstractAxis *axisYRight READ axisYRight WRITE setAxisYRight NOTIFY axisYRightChanged REVISION 2) + Q_PROPERTY(QAbstractAxis *axisAngular READ axisAngular WRITE setAxisAngular NOTIFY axisAngularChanged REVISION 3) + Q_PROPERTY(QAbstractAxis *axisRadial READ axisRadial WRITE setAxisRadial NOTIFY axisRadialChanged REVISION 3) Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged REVISION 1) Q_PROPERTY(Qt::PenStyle style READ style WRITE setStyle NOTIFY styleChanged REVISION 1) Q_PROPERTY(Qt::PenCapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged REVISION 1) @@ -55,6 +57,10 @@ public: Q_REVISION(2) void setAxisXTop(QAbstractAxis *axis) { m_axes->setAxisXTop(axis); } Q_REVISION(2) QAbstractAxis *axisYRight() { return m_axes->axisYRight(); } Q_REVISION(2) void setAxisYRight(QAbstractAxis *axis) { m_axes->setAxisYRight(axis); } + Q_REVISION(3) QAbstractAxis *axisAngular() { return m_axes->axisX(); } + Q_REVISION(3) void setAxisAngular(QAbstractAxis *axis) { m_axes->setAxisX(axis); } + Q_REVISION(3) QAbstractAxis *axisRadial() { return m_axes->axisY(); } + Q_REVISION(3) void setAxisRadial(QAbstractAxis *axis) { m_axes->setAxisY(axis); } qreal width() const; void setWidth(qreal width); Qt::PenStyle style() const; @@ -81,6 +87,8 @@ Q_SIGNALS: Q_REVISION(1) void axisYChanged(QAbstractAxis *axis); Q_REVISION(2) void axisXTopChanged(QAbstractAxis *axis); Q_REVISION(2) void axisYRightChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisAngularChanged(QAbstractAxis *axis); + Q_REVISION(3) void axisRadialChanged(QAbstractAxis *axis); Q_REVISION(1) void widthChanged(qreal width); Q_REVISION(1) void styleChanged(Qt::PenStyle style); Q_REVISION(1) void capStyleChanged(Qt::PenCapStyle capStyle); diff --git a/plugins/declarative/plugin.cpp b/plugins/declarative/plugin.cpp index 2b4fd0aa..bf560e59 100644 --- a/plugins/declarative/plugin.cpp +++ b/plugins/declarative/plugin.cpp @@ -24,6 +24,7 @@ #include "declarativecategoryaxis.h" #include "qbarcategoryaxis.h" #include "declarativechart.h" +#include "declarativepolarchart.h" #include "declarativexypoint.h" #include "declarativelineseries.h" #include "declarativesplineseries.h" @@ -62,6 +63,7 @@ Q_DECLARE_METATYPE(QList<QAbstractAxis *>) // These should not be needed or at least they are not needed in Qt4. Q_DECLARE_METATYPE(DeclarativeChart *) +Q_DECLARE_METATYPE(DeclarativePolarChart *) Q_DECLARE_METATYPE(DeclarativeMargins *) Q_DECLARE_METATYPE(DeclarativeAreaSeries *) Q_DECLARE_METATYPE(DeclarativeBarSeries *) @@ -213,6 +215,13 @@ public: qmlRegisterType<DeclarativeHorizontalBarSeries, 2>(uri, 1, 2, "HorizontalBarSeries"); qmlRegisterType<DeclarativeHorizontalStackedBarSeries, 2>(uri, 1, 2, "HorizontalStackedBarSeries"); qmlRegisterType<DeclarativeHorizontalPercentBarSeries, 2>(uri, 1, 2, "HorizontalPercentBarSeries"); + + // QtCommercial.Chart 1.3 + qmlRegisterType<DeclarativePolarChart, 1>(uri, 1, 3, "PolarChartView"); + qmlRegisterType<DeclarativeSplineSeries, 3>(uri, 1, 3, "SplineSeries"); + qmlRegisterType<DeclarativeScatterSeries, 3>(uri, 1, 3, "ScatterSeries"); + qmlRegisterType<DeclarativeLineSeries, 3>(uri, 1, 3, "LineSeries"); + qmlRegisterType<DeclarativeAreaSeries, 3>(uri, 1, 3, "AreaSeries"); } }; diff --git a/src/animations/axisanimation.cpp b/src/animations/axisanimation.cpp index e59d9aae..fab017e1 100644 --- a/src/animations/axisanimation.cpp +++ b/src/animations/axisanimation.cpp @@ -19,14 +19,15 @@ ****************************************************************************/ #include "axisanimation_p.h" -#include "chartaxis_p.h" +#include "chartaxiselement_p.h" +#include "qabstractaxis_p.h" Q_DECLARE_METATYPE(QVector<qreal>) QTCOMMERCIALCHART_BEGIN_NAMESPACE -AxisAnimation::AxisAnimation(ChartAxis *axis) +AxisAnimation::AxisAnimation(ChartAxisElement *axis) : ChartAnimation(axis), m_axis(axis), m_type(DefaultAnimation) @@ -68,13 +69,13 @@ void AxisAnimation::setValues(QVector<qreal> &oldLayout, QVector<qreal> &newLayo oldLayout.resize(newLayout.count()); for (int i = 0, j = oldLayout.count() - 1; i < (oldLayout.count() + 1) / 2; ++i, --j) { - oldLayout[i] = m_axis->orientation() == Qt::Horizontal ? rect.left() : rect.bottom(); - oldLayout[j] = m_axis->orientation() == Qt::Horizontal ? rect.right() : rect.top(); + oldLayout[i] = m_axis->axis()->orientation() == Qt::Horizontal ? rect.left() : rect.bottom(); + oldLayout[j] = m_axis->axis()->orientation() == Qt::Horizontal ? rect.right() : rect.top(); } } break; case ZoomInAnimation: { - int index = qMin(oldLayout.count() * (m_axis->orientation() == Qt::Horizontal ? m_point.x() : (1 - m_point.y())), newLayout.count() - (qreal)1.0); + int index = qMin(oldLayout.count() * (m_axis->axis()->orientation() == Qt::Horizontal ? m_point.x() : (1 - m_point.y())), newLayout.count() - (qreal)1.0); oldLayout.resize(newLayout.count()); for (int i = 0; i < oldLayout.count(); i++) @@ -99,7 +100,7 @@ void AxisAnimation::setValues(QVector<qreal> &oldLayout, QVector<qreal> &newLayo oldLayout.resize(newLayout.count()); QRectF rect = m_axis->gridGeometry(); for (int i = 0, j = oldLayout.count() - 1; i < oldLayout.count(); ++i, --j) - oldLayout[i] = m_axis->orientation() == Qt::Horizontal ? rect.left() : rect.top(); + oldLayout[i] = m_axis->axis()->orientation() == Qt::Horizontal ? rect.left() : rect.top(); } break; } diff --git a/src/animations/axisanimation_p.h b/src/animations/axisanimation_p.h index 6a213eae..d25cceda 100644 --- a/src/animations/axisanimation_p.h +++ b/src/animations/axisanimation_p.h @@ -35,13 +35,13 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -class ChartAxis; +class ChartAxisElement; class AxisAnimation: public ChartAnimation { public: enum Animation { DefaultAnimation, ZoomOutAnimation, ZoomInAnimation, MoveForwardAnimation, MoveBackwordAnimation}; - AxisAnimation(ChartAxis *axis); + AxisAnimation(ChartAxisElement *axis); ~AxisAnimation(); void setAnimationType(Animation type); void setAnimationPoint(const QPointF &point); @@ -50,7 +50,7 @@ protected: QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const; void updateCurrentValue(const QVariant &value); private: - ChartAxis *m_axis; + ChartAxisElement *m_axis; Animation m_type; QPointF m_point; }; diff --git a/src/areachart/areachartitem.cpp b/src/areachart/areachartitem.cpp index 92f89999..10adfa6f 100644 --- a/src/areachart/areachartitem.cpp +++ b/src/areachart/areachartitem.cpp @@ -88,12 +88,26 @@ void AreaChartItem::updatePath() path = m_upper->path(); if (m_lower) { + // Note: Polarcharts always draw area correctly only when both series have equal width or are + // fully displayed. If one series is partally off-chart, the connecting line between + // the series does not attach to the end of the partially hidden series but to the point + // where it intersects the axis line. The problem is especially noticeable when one of the series + // is entirely off-chart, in which case the connecting line connects two ends of the + // visible series. + // This happens because we get the paths from linechart, which omits off-chart segments. + // To properly fix, linechart would need to provide true full path, in right, left, and the rest + // portions to enable proper clipping. However, combining those to single visually unified area + // would be a nightmare, since they would have to be painted separately. path.connectPath(m_lower->path().toReversed()); } else { QPointF first = path.pointAtPercent(0); QPointF last = path.pointAtPercent(1); - path.lineTo(last.x(), rect.bottom()); - path.lineTo(first.x(), rect.bottom()); + if (presenter()->chartType() == QChart::ChartTypeCartesian) { + path.lineTo(last.x(), rect.bottom()); + path.lineTo(first.x(), rect.bottom()); + } else { // polar + path.lineTo(rect.center()); + } } path.closeSubpath(); prepareGeometryChange(); @@ -137,7 +151,11 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt painter->save(); painter->setPen(m_linePen); painter->setBrush(m_brush); - painter->setClipRect(QRectF(QPointF(0,0),domain()->size())); + QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); + if (presenter()->chartType() == QChart::ChartTypePolar) + painter->setClipRegion(QRegion(clipRect.toRect(), QRegion::Ellipse)); + else + painter->setClipRect(clipRect); painter->drawPath(m_path); if (m_pointsVisible) { painter->setPen(m_pointPen); diff --git a/src/areachart/areachartitem_p.h b/src/areachart/areachartitem_p.h index 0c09bade..60d3e07f 100644 --- a/src/areachart/areachartitem_p.h +++ b/src/areachart/areachartitem_p.h @@ -32,11 +32,11 @@ #include "qchartglobal.h" #include "linechartitem_p.h" +#include "qareaseries.h" #include <QPen> QTCOMMERCIALCHART_BEGIN_NAMESPACE -class QAreaSeries; class AreaChartItem; class AreaChartItem : public ChartItem @@ -57,6 +57,8 @@ public: void updatePath(); void setPresenter(ChartPresenter *presenter); + QAreaSeries *series() const { return m_series; } + protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); @@ -89,6 +91,9 @@ public: AreaBoundItem(AreaChartItem *area, QLineSeries *lineSeries,QGraphicsItem* item = 0) : LineChartItem(lineSeries, item), m_item(area) { + // We do not actually want to draw anything from LineChartItem. + // Drawing is done in AreaChartItem only. + setVisible(false); } ~AreaBoundItem() {} @@ -97,6 +102,9 @@ public: // Turn off points drawing from component line chart item, as that // messes up the fill for area series. suppressPoints(); + // Component lineseries are not necessarily themselves on the chart, + // so get the chart type for them from area chart. + forceChartType(m_item->series()->chart()->chartType()); LineChartItem::updateGeometry(); m_item->updatePath(); } diff --git a/src/axis/axis.pri b/src/axis/axis.pri index 264ed896..6f706aac 100644 --- a/src/axis/axis.pri +++ b/src/axis/axis.pri @@ -13,7 +13,8 @@ DEPENDPATH += $$PWD \ $$PWD/logvalueaxis SOURCES += \ - $$PWD/chartaxis.cpp \ + $$PWD/chartaxiselement.cpp \ + $$PWD/cartesianchartaxis.cpp \ $$PWD/qabstractaxis.cpp \ $$PWD/verticalaxis.cpp \ $$PWD/horizontalaxis.cpp \ @@ -31,10 +32,12 @@ SOURCES += \ $$PWD/logvalueaxis/qlogvalueaxis.cpp PRIVATE_HEADERS += \ - $$PWD/chartaxis_p.h \ + $$PWD/chartaxiselement_p.h \ + $$PWD/cartesianchartaxis_p.h \ $$PWD/qabstractaxis_p.h \ $$PWD/verticalaxis_p.h \ $$PWD/horizontalaxis_p.h \ + $$PWD/linearrowitem_p.h \ $$PWD/valueaxis/chartvalueaxisx_p.h \ $$PWD/valueaxis/chartvalueaxisy_p.h \ $$PWD/valueaxis/qvalueaxis_p.h \ @@ -55,6 +58,29 @@ PUBLIC_HEADERS += \ $$PWD/categoryaxis/qcategoryaxis.h \ $$PWD/logvalueaxis/qlogvalueaxis.h \ +# polar +SOURCES += \ + $$PWD/polarchartaxis.cpp \ + $$PWD/polarchartaxisangular.cpp \ + $$PWD/polarchartaxisradial.cpp \ + $$PWD/valueaxis/polarchartvalueaxisangular.cpp \ + $$PWD/valueaxis/polarchartvalueaxisradial.cpp \ + $$PWD/logvalueaxis/polarchartlogvalueaxisangular.cpp \ + $$PWD/logvalueaxis/polarchartlogvalueaxisradial.cpp \ + $$PWD/categoryaxis/polarchartcategoryaxisangular.cpp \ + $$PWD/categoryaxis/polarchartcategoryaxisradial.cpp + +PRIVATE_HEADERS += \ + $$PWD/polarchartaxis_p.h \ + $$PWD/polarchartaxisangular_p.h \ + $$PWD/polarchartaxisradial_p.h \ + $$PWD/valueaxis/polarchartvalueaxisangular_p.h \ + $$PWD/valueaxis/polarchartvalueaxisradial_p.h \ + $$PWD/logvalueaxis/polarchartlogvalueaxisangular_p.h \ + $$PWD/logvalueaxis/polarchartlogvalueaxisradial_p.h \ + $$PWD/categoryaxis/polarchartcategoryaxisangular_p.h \ + $$PWD/categoryaxis/polarchartcategoryaxisradial_p.h + !linux-arm*: { INCLUDEPATH += \ $$PWD/datetimeaxis @@ -65,12 +91,16 @@ DEPENDPATH += \ SOURCES += \ $$PWD/datetimeaxis/chartdatetimeaxisx.cpp \ $$PWD/datetimeaxis/chartdatetimeaxisy.cpp \ - $$PWD/datetimeaxis/qdatetimeaxis.cpp + $$PWD/datetimeaxis/qdatetimeaxis.cpp \ + $$PWD/datetimeaxis/polarchartdatetimeaxisangular.cpp \ + $$PWD/datetimeaxis/polarchartdatetimeaxisradial.cpp PRIVATE_HEADERS += \ $$PWD/datetimeaxis/chartdatetimeaxisx_p.h \ $$PWD/datetimeaxis/chartdatetimeaxisy_p.h \ - $$PWD/datetimeaxis/qdatetimeaxis_p.h + $$PWD/datetimeaxis/qdatetimeaxis_p.h \ + $$PWD/datetimeaxis/polarchartdatetimeaxisangular_p.h \ + $$PWD/datetimeaxis/polarchartdatetimeaxisradial_p.h PUBLIC_HEADERS += \ $$PWD/datetimeaxis/qdatetimeaxis.h diff --git a/src/axis/barcategoryaxis/chartbarcategoryaxisx.cpp b/src/axis/barcategoryaxis/chartbarcategoryaxisx.cpp index 8d86b120..9d09aebf 100644 --- a/src/axis/barcategoryaxis/chartbarcategoryaxisx.cpp +++ b/src/axis/barcategoryaxis/chartbarcategoryaxisx.cpp @@ -21,7 +21,7 @@ #include "chartbarcategoryaxisx_p.h" #include "chartpresenter_p.h" #include "qbarcategoryaxis_p.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QFontMetrics> #include <QDebug> #include <qmath.h> @@ -87,7 +87,7 @@ QStringList ChartBarCategoryAxisX::createCategoryLabels(const QVector<qreal>& la void ChartBarCategoryAxisX::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal>& layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; setLabels(createCategoryLabels(layout)); @@ -104,7 +104,7 @@ QSizeF ChartBarCategoryAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constra { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = HorizontalAxis::sizeHint(which, constraint); QStringList ticksList = m_categoriesAxis->categories(); diff --git a/src/axis/barcategoryaxis/chartbarcategoryaxisy.cpp b/src/axis/barcategoryaxis/chartbarcategoryaxisy.cpp index 0ca8116c..20194bbc 100644 --- a/src/axis/barcategoryaxis/chartbarcategoryaxisy.cpp +++ b/src/axis/barcategoryaxis/chartbarcategoryaxisy.cpp @@ -21,7 +21,7 @@ #include "chartbarcategoryaxisy_p.h" #include "chartpresenter_p.h" #include "qbarcategoryaxis_p.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <qmath.h> #include <QFontMetrics> #include <QDebug> @@ -86,7 +86,7 @@ QStringList ChartBarCategoryAxisY::createCategoryLabels(const QVector<qreal>& la void ChartBarCategoryAxisY::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal>& layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; setLabels(createCategoryLabels(layout)); @@ -103,7 +103,7 @@ QSizeF ChartBarCategoryAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constra { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = VerticalAxis::sizeHint(which, constraint); QStringList ticksList = m_categoriesAxis->categories(); diff --git a/src/axis/barcategoryaxis/qbarcategoryaxis.cpp b/src/axis/barcategoryaxis/qbarcategoryaxis.cpp index 0dffff0d..0dfbea1f 100644 --- a/src/axis/barcategoryaxis/qbarcategoryaxis.cpp +++ b/src/axis/barcategoryaxis/qbarcategoryaxis.cpp @@ -111,7 +111,7 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE /*! \fn void QBarCategoryAxis::categoriesChanged() - Axis emits signal when the categories of the axis has changed. + Axis emits signal when the categories of the axis have changed. */ /*! @@ -550,7 +550,7 @@ void QBarCategoryAxisPrivate::setRange(const QString &minCategory, const QStrin void QBarCategoryAxisPrivate::initializeGraphics(QGraphicsItem* parent) { Q_Q(QBarCategoryAxis); - ChartAxis* axis(0); + ChartAxisElement* axis(0); if (orientation() == Qt::Vertical) axis = new ChartBarCategoryAxisY(q,parent); if (orientation() == Qt::Horizontal) diff --git a/src/axis/cartesianchartaxis.cpp b/src/axis/cartesianchartaxis.cpp new file mode 100644 index 00000000..85978202 --- /dev/null +++ b/src/axis/cartesianchartaxis.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "cartesianchartaxis_p.h" +#include "qabstractaxis.h" +#include "qabstractaxis_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "abstractdomain_p.h" +#include "linearrowitem_p.h" +#include <QValueAxis> +#include <QLogValueAxis> +#include <QGraphicsLayout> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +CartesianChartAxis::CartesianChartAxis(QAbstractAxis *axis, QGraphicsItem *item , bool intervalAxis) + : ChartAxisElement(axis, item, intervalAxis) +{ + Q_ASSERT(item); +} + + +CartesianChartAxis::~CartesianChartAxis() +{ +} + +void CartesianChartAxis::createItems(int count) +{ + if (arrowItems().size() == 0) { + QGraphicsLineItem *arrow = new LineArrowItem(this, this); + arrow->setPen(axis()->linePen()); + arrowGroup()->addToGroup(arrow); + } + + if (intervalAxis() && gridItems().size() == 0) { + for (int i = 0 ; i < 2 ; i ++){ + QGraphicsLineItem *item = new QGraphicsLineItem(this); + item->setPen(axis()->gridLinePen()); + gridGroup()->addToGroup(item); + } + } + + for (int i = 0; i < count; ++i) { + QGraphicsLineItem *arrow = new QGraphicsLineItem(this); + QGraphicsLineItem *grid = new QGraphicsLineItem(this); + QGraphicsSimpleTextItem *label = new QGraphicsSimpleTextItem(this); + QGraphicsSimpleTextItem *title = titleItem(); + arrow->setPen(axis()->linePen()); + grid->setPen(axis()->gridLinePen()); + label->setFont(axis()->labelsFont()); + label->setPen(axis()->labelsPen()); + label->setBrush(axis()->labelsBrush()); + label->setRotation(axis()->labelsAngle()); + title->setFont(axis()->titleFont()); + title->setPen(axis()->titlePen()); + title->setBrush(axis()->titleBrush()); + title->setText(axis()->titleText()); + arrowGroup()->addToGroup(arrow); + gridGroup()->addToGroup(grid); + labelGroup()->addToGroup(label); + + if ((gridItems().size()) % 2 && gridItems().size() > 2) { + QGraphicsRectItem* shades = new QGraphicsRectItem(this); + shades->setPen(axis()->shadesPen()); + shades->setBrush(axis()->shadesBrush()); + shadeGroup()->addToGroup(shades); + } + } + +} + +void CartesianChartAxis::deleteItems(int count) +{ + QList<QGraphicsItem *> lines = gridItems(); + QList<QGraphicsItem *> labels = labelItems(); + QList<QGraphicsItem *> shades = shadeItems(); + QList<QGraphicsItem *> axis = arrowItems(); + + for (int i = 0; i < count; ++i) { + if (lines.size() % 2 && lines.size() > 1) + delete(shades.takeLast()); + delete(lines.takeLast()); + delete(labels.takeLast()); + delete(axis.takeLast()); + } +} + +void CartesianChartAxis::updateLayout(QVector<qreal> &layout) +{ + int diff = ChartAxisElement::layout().size() - layout.size(); + + if (diff > 0) + deleteItems(diff); + else if (diff < 0) + createItems(-diff); + + if (animation()) { + switch (presenter()->state()) { + case ChartPresenter::ZoomInState: + animation()->setAnimationType(AxisAnimation::ZoomInAnimation); + animation()->setAnimationPoint(presenter()->statePoint()); + break; + case ChartPresenter::ZoomOutState: + animation()->setAnimationType(AxisAnimation::ZoomOutAnimation); + animation()->setAnimationPoint(presenter()->statePoint()); + break; + case ChartPresenter::ScrollUpState: + case ChartPresenter::ScrollLeftState: + animation()->setAnimationType(AxisAnimation::MoveBackwordAnimation); + break; + case ChartPresenter::ScrollDownState: + case ChartPresenter::ScrollRightState: + animation()->setAnimationType(AxisAnimation::MoveForwardAnimation); + break; + case ChartPresenter::ShowState: + animation()->setAnimationType(AxisAnimation::DefaultAnimation); + break; + } + animation()->setValues(ChartAxisElement::layout(), layout); + presenter()->startAnimation(animation()); + } else { + setLayout(layout); + updateGeometry(); + } +} + +bool CartesianChartAxis::isEmpty() +{ + return axisGeometry().isEmpty() + || gridGeometry().isEmpty() + || qFuzzyCompare(min(), max()); +} + +void CartesianChartAxis::setGeometry(const QRectF &axis, const QRectF &grid) +{ + m_gridRect = grid; + setAxisGeometry(axis); + + if (isEmpty()) + return; + + QVector<qreal> layout = calculateLayout(); + updateLayout(layout); +} + +QSizeF CartesianChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(); +} + +void CartesianChartAxis::handleArrowPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, arrowItems()) + static_cast<QGraphicsLineItem *>(item)->setPen(pen); +} + +void CartesianChartAxis::handleGridPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, gridItems()) + static_cast<QGraphicsLineItem *>(item)->setPen(pen); +} + +void CartesianChartAxis::handleShadesBrushChanged(const QBrush &brush) +{ + foreach (QGraphicsItem *item, shadeItems()) + static_cast<QGraphicsRectItem *>(item)->setBrush(brush); +} + +void CartesianChartAxis::handleShadesPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, shadeItems()) + static_cast<QGraphicsRectItem *>(item)->setPen(pen); +} + +#include "moc_cartesianchartaxis_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/cartesianchartaxis_p.h b/src/axis/cartesianchartaxis_p.h new file mode 100644 index 00000000..51ed1326 --- /dev/null +++ b/src/axis/cartesianchartaxis_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CARTESIANCHARTAXIS_H +#define CARTESIANCHARTAXIS_H + +#include "qchartglobal.h" +#include "chartaxiselement_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QAbstractAxis; + +class CartesianChartAxis : public ChartAxisElement +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) +public: + + CartesianChartAxis(QAbstractAxis *axis, QGraphicsItem *item = 0, bool intervalAxis = false); + ~CartesianChartAxis(); + + void setGeometry(const QRectF &axis, const QRectF &grid); + QRectF gridGeometry() const { return m_gridRect; } + bool isEmpty(); + + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +protected: + void setGeometry(const QRectF &size) { Q_UNUSED(size);} + virtual void updateGeometry() = 0; + void updateLayout(QVector<qreal> &layout); + +public Q_SLOTS: + virtual void handleArrowPenChanged(const QPen &pen); + virtual void handleGridPenChanged(const QPen &pen); + virtual void handleShadesBrushChanged(const QBrush &brush); + virtual void handleShadesPenChanged(const QPen &pen); + +private: + void createItems(int count); + void deleteItems(int count); + +private: + QRectF m_gridRect; + + friend class AxisAnimation; + friend class LineArrowItem; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif /* CARTESIANCHARTAXIS_H */ diff --git a/src/axis/categoryaxis/chartcategoryaxisx.cpp b/src/axis/categoryaxis/chartcategoryaxisx.cpp index 9c9813c4..cb1ede68 100644 --- a/src/axis/categoryaxis/chartcategoryaxisx.cpp +++ b/src/axis/categoryaxis/chartcategoryaxisx.cpp @@ -22,6 +22,7 @@ #include "qcategoryaxis.h" #include "qabstractaxis.h" #include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -32,6 +33,7 @@ ChartCategoryAxisX::ChartCategoryAxisX(QCategoryAxis *axis, QGraphicsItem* item) : HorizontalAxis(axis, item, true), m_axis(axis) { + QObject::connect(axis, SIGNAL(categoriesChanged()), this, SLOT(handleCategoriesChanged())); } ChartCategoryAxisX::~ChartCategoryAxisX() @@ -72,16 +74,11 @@ void ChartCategoryAxisX::updateGeometry() HorizontalAxis::updateGeometry(); } -void ChartCategoryAxisX::handleAxisUpdated() -{ - updateGeometry(); -} - QSizeF ChartCategoryAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = HorizontalAxis::sizeHint(which, constraint); QStringList ticksList = m_axis->categoriesLabels(); @@ -114,4 +111,12 @@ QSizeF ChartCategoryAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint return sh; } +void ChartCategoryAxisX::handleCategoriesChanged() +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +#include "moc_chartcategoryaxisx_p.cpp" + QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/categoryaxis/chartcategoryaxisx_p.h b/src/axis/categoryaxis/chartcategoryaxisx_p.h index df338cc5..2fced9cf 100644 --- a/src/axis/categoryaxis/chartcategoryaxisx_p.h +++ b/src/axis/categoryaxis/chartcategoryaxisx_p.h @@ -35,23 +35,23 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QCategoryAxis; -class ChartPresenter; class ChartCategoryAxisX : public HorizontalAxis { + Q_OBJECT public: ChartCategoryAxisX(QCategoryAxis *axis, QGraphicsItem* item = 0); ~ChartCategoryAxisX(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; +public Q_SLOTS: + void handleCategoriesChanged(); + protected: QVector<qreal> calculateLayout() const; void updateGeometry(); -public Q_SLOTS: - void handleAxisUpdated(); - private: QCategoryAxis *m_axis; }; diff --git a/src/axis/categoryaxis/chartcategoryaxisy.cpp b/src/axis/categoryaxis/chartcategoryaxisy.cpp index ea86b561..96f5a2d8 100644 --- a/src/axis/categoryaxis/chartcategoryaxisy.cpp +++ b/src/axis/categoryaxis/chartcategoryaxisy.cpp @@ -22,6 +22,7 @@ #include "qcategoryaxis.h" #include "qabstractaxis.h" #include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -33,6 +34,7 @@ ChartCategoryAxisY::ChartCategoryAxisY(QCategoryAxis *axis, QGraphicsItem* item) : VerticalAxis(axis, item, true), m_axis(axis) { + QObject::connect(axis, SIGNAL(categoriesChanged()), this, SLOT(handleCategoriesChanged())); } ChartCategoryAxisY::~ChartCategoryAxisY() @@ -72,16 +74,11 @@ void ChartCategoryAxisY::updateGeometry() VerticalAxis::updateGeometry(); } -void ChartCategoryAxisY::handleAxisUpdated() -{ - updateGeometry(); -} - QSizeF ChartCategoryAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = VerticalAxis::sizeHint(which, constraint); QStringList ticksList = m_axis->categoriesLabels(); @@ -113,4 +110,12 @@ QSizeF ChartCategoryAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint return sh; } +void ChartCategoryAxisY::handleCategoriesChanged() +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +#include "moc_chartcategoryaxisy_p.cpp" + QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/categoryaxis/chartcategoryaxisy_p.h b/src/axis/categoryaxis/chartcategoryaxisy_p.h index a9dcbd81..20760b82 100644 --- a/src/axis/categoryaxis/chartcategoryaxisy_p.h +++ b/src/axis/categoryaxis/chartcategoryaxisy_p.h @@ -35,23 +35,23 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QCategoryAxis; -class ChartPresenter; class ChartCategoryAxisY : public VerticalAxis { + Q_OBJECT public: ChartCategoryAxisY(QCategoryAxis *axis, QGraphicsItem* item = 0); ~ChartCategoryAxisY(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; +public Q_SLOTS: + void handleCategoriesChanged(); + protected: QVector<qreal> calculateLayout() const; void updateGeometry(); -public Q_SLOTS: - void handleAxisUpdated(); - private: QCategoryAxis *m_axis; }; diff --git a/src/axis/categoryaxis/polarchartcategoryaxisangular.cpp b/src/axis/categoryaxis/polarchartcategoryaxisangular.cpp new file mode 100644 index 00000000..ead087f8 --- /dev/null +++ b/src/axis/categoryaxis/polarchartcategoryaxisangular.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartcategoryaxisangular_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qcategoryaxis.h" +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartCategoryAxisAngular::PolarChartCategoryAxisAngular(QCategoryAxis *axis, QGraphicsItem *item) + : PolarChartAxisAngular(axis, item, true) +{ + QObject::connect(axis, SIGNAL(categoriesChanged()), this, SLOT(handleCategoriesChanged())); +} + +PolarChartCategoryAxisAngular::~PolarChartCategoryAxisAngular() +{ +} + +QVector<qreal> PolarChartCategoryAxisAngular::calculateLayout() const +{ + QCategoryAxis *catAxis = static_cast<QCategoryAxis *>(axis()); + int tickCount = catAxis->categoriesLabels().count() + 1; + QVector<qreal> points; + + if (tickCount < 2) + return points; + + qreal range = max() - min(); + if (range > 0) { + points.resize(tickCount); + qreal scale = 360.0 / range; + qreal angle; + for (int i = 0; i < tickCount; ++i) { + if (i < tickCount - 1) + angle = (catAxis->startValue(catAxis->categoriesLabels().at(i)) - min()) * scale; + else + angle = (catAxis->endValue(catAxis->categoriesLabels().at(i - 1)) - min()) * scale; + points[i] = angle; + } + } + + return points; +} + +void PolarChartCategoryAxisAngular::createAxisLabels(const QVector<qreal> &layout) +{ + Q_UNUSED(layout); + setLabels(static_cast<QCategoryAxis *>(axis())->categoriesLabels() << ""); +} + +void PolarChartCategoryAxisAngular::handleCategoriesChanged() +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + + +#include "moc_polarchartcategoryaxisangular_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/categoryaxis/polarchartcategoryaxisangular_p.h b/src/axis/categoryaxis/polarchartcategoryaxisangular_p.h new file mode 100644 index 00000000..40d8d923 --- /dev/null +++ b/src/axis/categoryaxis/polarchartcategoryaxisangular_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTCATEGORYAXISANGULAR_P_H +#define POLARCHARTCATEGORYAXISANGULAR_P_H + +#include "polarchartaxisangular_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QCategoryAxis; + +class PolarChartCategoryAxisAngular : public PolarChartAxisAngular +{ + Q_OBJECT + +public: + PolarChartCategoryAxisAngular(QCategoryAxis *axis, QGraphicsItem *item); + ~PolarChartCategoryAxisAngular(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +public Q_SLOTS: + void handleCategoriesChanged(); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTCATEGORYAXISANGULAR_P_H diff --git a/src/axis/categoryaxis/polarchartcategoryaxisradial.cpp b/src/axis/categoryaxis/polarchartcategoryaxisradial.cpp new file mode 100644 index 00000000..f4fe3b7b --- /dev/null +++ b/src/axis/categoryaxis/polarchartcategoryaxisradial.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartcategoryaxisradial_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qcategoryaxis.h" +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartCategoryAxisRadial::PolarChartCategoryAxisRadial(QCategoryAxis *axis, QGraphicsItem *item) + : PolarChartAxisRadial(axis, item, true) +{ + QObject::connect(axis, SIGNAL(categoriesChanged()), this, SLOT(handleCategoriesChanged())); +} + +PolarChartCategoryAxisRadial::~PolarChartCategoryAxisRadial() +{ +} + +QVector<qreal> PolarChartCategoryAxisRadial::calculateLayout() const +{ + QCategoryAxis *catAxis = static_cast<QCategoryAxis *>(axis()); + int tickCount = catAxis->categoriesLabels().count() + 1; + QVector<qreal> points; + + if (tickCount < 2) + return points; + + qreal range = max() - min(); + if (range > 0) { + points.resize(tickCount); + qreal scale = (axisGeometry().width() / 2) / range; + qreal angle; + for (int i = 0; i < tickCount; ++i) { + if (i < tickCount - 1) + angle = (catAxis->startValue(catAxis->categoriesLabels().at(i)) - min()) * scale; + else + angle = (catAxis->endValue(catAxis->categoriesLabels().at(i - 1)) - min()) * scale; + points[i] = angle; + } + } + + return points; +} + +void PolarChartCategoryAxisRadial::createAxisLabels(const QVector<qreal> &layout) +{ + Q_UNUSED(layout); + setLabels(static_cast<QCategoryAxis *>(axis())->categoriesLabels() << ""); +} + +void PolarChartCategoryAxisRadial::handleCategoriesChanged() +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartcategoryaxisradial_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/categoryaxis/polarchartcategoryaxisradial_p.h b/src/axis/categoryaxis/polarchartcategoryaxisradial_p.h new file mode 100644 index 00000000..83be8d8a --- /dev/null +++ b/src/axis/categoryaxis/polarchartcategoryaxisradial_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTCATEGORYAXISRADIAL_P_H +#define POLARCHARTCATEGORYAXISRADIAL_P_H + +#include "polarchartaxisradial_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QCategoryAxis; + +class PolarChartCategoryAxisRadial : public PolarChartAxisRadial +{ + Q_OBJECT + +public: + PolarChartCategoryAxisRadial(QCategoryAxis *axis, QGraphicsItem *item); + ~PolarChartCategoryAxisRadial(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +public Q_SLOTS: + void handleCategoriesChanged(); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTCATEGORYAXISRADIAL_P_H diff --git a/src/axis/categoryaxis/qcategoryaxis.cpp b/src/axis/categoryaxis/qcategoryaxis.cpp index 40d8cc25..d187da84 100644 --- a/src/axis/categoryaxis/qcategoryaxis.cpp +++ b/src/axis/categoryaxis/qcategoryaxis.cpp @@ -22,6 +22,8 @@ #include "qcategoryaxis_p.h" #include "chartcategoryaxisx_p.h" #include "chartcategoryaxisy_p.h" +#include "polarchartcategoryaxisangular_p.h" +#include "polarchartcategoryaxisradial_p.h" #include "qchart.h" #include <qmath.h> #include <QDebug> @@ -103,6 +105,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE */ /*! + \fn void QCategoryAxis::categoriesChanged() + Axis emits signal when the categories of the axis have changed. +*/ + + +/*! Constructs an axis object which is a child of \a parent. */ QCategoryAxis::QCategoryAxis(QObject *parent): @@ -151,10 +159,12 @@ void QCategoryAxis::append(const QString &categoryLabel, qreal categoryEndValue) Range range(d->m_categoryMinimum, categoryEndValue); d->m_categoriesMap.insert(categoryLabel, range); d->m_categories.append(categoryLabel); + emit categoriesChanged(); } else if (categoryEndValue > endValue(d->m_categories.last())) { Range previousRange = d->m_categoriesMap.value(d->m_categories.last()); d->m_categoriesMap.insert(categoryLabel, Range(previousRange.second, categoryEndValue)); d->m_categories.append(categoryLabel); + emit categoriesChanged(); } } } @@ -169,10 +179,13 @@ void QCategoryAxis::setStartValue(qreal min) Q_D(QCategoryAxis); if (d->m_categories.isEmpty()) { d->m_categoryMinimum = min; + emit categoriesChanged(); } else { Range range = d->m_categoriesMap.value(d->m_categories.first()); - if (min < range.second) + if (min < range.second) { d->m_categoriesMap.insert(d->m_categories.first(), Range(min, range.second)); + emit categoriesChanged(); + } } } @@ -227,7 +240,7 @@ void QCategoryAxis::remove(const QString &categoryLabel) d->m_categoriesMap.insert(label, range); } } - //TODO:: d->emitUpdated(); + emit categoriesChanged(); } } @@ -251,7 +264,7 @@ void QCategoryAxis::replaceLabel(const QString &oldLabel, const QString &newLabe Range range = d->m_categoriesMap.value(oldLabel); d->m_categoriesMap.remove(oldLabel); d->m_categoriesMap.insert(newLabel, range); - //TODO:: d->emitUpdated(); + emit categoriesChanged(); } } @@ -300,14 +313,23 @@ int QCategoryAxisPrivate::ticksCount() const return m_categories.count() + 1; } -void QCategoryAxisPrivate::initializeGraphics(QGraphicsItem* parent) +void QCategoryAxisPrivate::initializeGraphics(QGraphicsItem *parent) { Q_Q(QCategoryAxis); - ChartAxis* axis(0); - if (orientation() == Qt::Vertical) - axis = new ChartCategoryAxisY(q,parent); - else if(orientation() == Qt::Horizontal) - axis = new ChartCategoryAxisX(q,parent); + ChartAxisElement *axis(0); + if (m_chart->chartType() == QChart::ChartTypeCartesian) { + if (orientation() == Qt::Vertical) + axis = new ChartCategoryAxisY(q,parent); + else if (orientation() == Qt::Horizontal) + axis = new ChartCategoryAxisX(q,parent); + } + + if (m_chart->chartType() == QChart::ChartTypePolar) { + if (orientation() == Qt::Vertical) + axis = new PolarChartCategoryAxisRadial(q, parent); + if (orientation() == Qt::Horizontal) + axis = new PolarChartCategoryAxisAngular(q, parent); + } m_item.reset(axis); QAbstractAxisPrivate::initializeGraphics(parent); diff --git a/src/axis/categoryaxis/qcategoryaxis.h b/src/axis/categoryaxis/qcategoryaxis.h index a7789275..73a33e21 100644 --- a/src/axis/categoryaxis/qcategoryaxis.h +++ b/src/axis/categoryaxis/qcategoryaxis.h @@ -57,6 +57,9 @@ public: QStringList categoriesLabels(); int count() const; +Q_SIGNALS: + void categoriesChanged(); + private: Q_DECLARE_PRIVATE(QCategoryAxis) Q_DISABLE_COPY(QCategoryAxis) diff --git a/src/axis/categoryaxis/qcategoryaxis_p.h b/src/axis/categoryaxis/qcategoryaxis_p.h index 09d80a57..466deaef 100644 --- a/src/axis/categoryaxis/qcategoryaxis_p.h +++ b/src/axis/categoryaxis/qcategoryaxis_p.h @@ -48,9 +48,6 @@ public: void initializeGraphics(QGraphicsItem* parent); int ticksCount() const; -Q_SIGNALS: - void changed(qreal min, qreal max, int tickCount, bool niceNumbers); - private: QMap<QString , Range> m_categoriesMap; QStringList m_categories; diff --git a/src/axis/chartaxis.cpp b/src/axis/chartaxis.cpp deleted file mode 100644 index 7e565acc..00000000 --- a/src/axis/chartaxis.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt Commercial Charts Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "chartaxis_p.h" -#include "qabstractaxis.h" -#include "qabstractaxis_p.h" -#include "chartpresenter_p.h" -#include "chartlayout_p.h" -#include "abstractdomain_p.h" -#include <qmath.h> -#include <QDateTime> -#include <QValueAxis> -#include <QLogValueAxis> -#include <QGraphicsLayout> -#include <QFontMetrics> - -QTCOMMERCIALCHART_BEGIN_NAMESPACE - -ChartAxis::ChartAxis(QAbstractAxis *axis, QGraphicsItem* item , bool intervalAxis) - : ChartElement(item), - m_axis(axis), - m_labelsAngle(axis->labelsAngle()), - m_grid(new QGraphicsItemGroup(item)), - m_arrow(new QGraphicsItemGroup(item)), - m_shades(new QGraphicsItemGroup(item)), - m_labels(new QGraphicsItemGroup(item)), - m_title(new QGraphicsSimpleTextItem(item)), - m_animation(0), - m_labelPadding(5), - m_intervalAxis(intervalAxis), - m_titlePadding(3) -{ - Q_ASSERT(item); - //initial initialization - m_arrow->setHandlesChildEvents(false); - m_arrow->setZValue(ChartPresenter::AxisZValue); - m_labels->setZValue(ChartPresenter::AxisZValue); - m_shades->setZValue(ChartPresenter::ShadesZValue); - m_grid->setZValue(ChartPresenter::GridZValue); - m_title->setZValue(ChartPresenter::GridZValue); - handleVisibleChanged(m_axis->isVisible()); - connectSlots(); - - setFlag(QGraphicsItem::ItemHasNoContents,true); -} - -void ChartAxis::connectSlots() -{ - QObject::connect(m_axis,SIGNAL(visibleChanged(bool)),this,SLOT(handleVisibleChanged(bool))); - QObject::connect(m_axis,SIGNAL(lineVisibleChanged(bool)),this,SLOT(handleArrowVisibleChanged(bool))); - QObject::connect(m_axis,SIGNAL(gridVisibleChanged(bool)),this,SLOT(handleGridVisibleChanged(bool))); - QObject::connect(m_axis,SIGNAL(labelsVisibleChanged(bool)),this,SLOT(handleLabelsVisibleChanged(bool))); - QObject::connect(m_axis,SIGNAL(shadesVisibleChanged(bool)),this,SLOT(handleShadesVisibleChanged(bool))); - QObject::connect(m_axis,SIGNAL(labelsAngleChanged(int)),this,SLOT(handleLabelsAngleChanged(int))); - QObject::connect(m_axis,SIGNAL(linePenChanged(const QPen&)),this,SLOT(handleArrowPenChanged(const QPen&))); - QObject::connect(m_axis,SIGNAL(labelsPenChanged(const QPen&)),this,SLOT(handleLabelsPenChanged(const QPen&))); - QObject::connect(m_axis,SIGNAL(labelsBrushChanged(const QBrush&)),this,SLOT(handleLabelsBrushChanged(const QBrush&))); - QObject::connect(m_axis,SIGNAL(labelsFontChanged(const QFont&)),this,SLOT(handleLabelsFontChanged(const QFont&))); - QObject::connect(m_axis,SIGNAL(gridLinePenChanged(const QPen&)),this,SLOT(handleGridPenChanged(const QPen&))); - QObject::connect(m_axis,SIGNAL(shadesPenChanged(const QPen&)),this,SLOT(handleShadesPenChanged(const QPen&))); - QObject::connect(m_axis,SIGNAL(shadesBrushChanged(const QBrush&)),this,SLOT(handleShadesBrushChanged(const QBrush&))); - QObject::connect(m_axis,SIGNAL(titleTextChanged(const QString&)),this,SLOT(handleTitleTextChanged(const QString&))); - QObject::connect(m_axis,SIGNAL(titleFontChanged(const QFont&)),this,SLOT(handleTitleFontChanged(const QFont&))); - QObject::connect(m_axis,SIGNAL(titlePenChanged(const QPen&)),this,SLOT(handleTitlePenChanged(const QPen&))); - QObject::connect(m_axis,SIGNAL(titleBrushChanged(const QBrush&)),this,SLOT(handleTitleBrushChanged(const QBrush&))); - QObject::connect(m_axis,SIGNAL(titleVisibleChanged(bool)),this,SLOT(handleTitleVisibleChanged(bool))); - QObject::connect(m_axis->d_ptr.data(),SIGNAL(rangeChanged(qreal,qreal)),this,SLOT(handleRangeChanged(qreal,qreal))); -} - -ChartAxis::~ChartAxis() -{ -} - -void ChartAxis::setAnimation(AxisAnimation *animation) -{ - m_animation = animation; -} - -void ChartAxis::setLayout(QVector<qreal> &layout) -{ - m_layoutVector = layout; -} - -void ChartAxis::createItems(int count) -{ - if (m_arrow->childItems().size() == 0){ - QGraphicsLineItem* arrow = new ArrowItem(this, this); - arrow->setPen(m_axis->linePen()); - m_arrow->addToGroup(arrow); - } - - if (m_intervalAxis && m_grid->childItems().size() == 0) { - for (int i = 0 ; i < 2 ; i ++){ - QGraphicsLineItem* item = new QGraphicsLineItem(this); - item->setPen(m_axis->gridLinePen()); - m_grid->addToGroup(item); - } - } - - for (int i = 0; i < count; ++i) { - QGraphicsLineItem* arrow = new QGraphicsLineItem(this); - arrow->setPen(m_axis->linePen()); - QGraphicsLineItem* grid = new QGraphicsLineItem(this); - grid->setPen(m_axis->gridLinePen()); - QGraphicsSimpleTextItem* label = new QGraphicsSimpleTextItem(this); - label->setFont(m_axis->labelsFont()); - label->setPen(m_axis->labelsPen()); - label->setBrush(m_axis->labelsBrush()); - label->setRotation(m_labelsAngle); - m_arrow->addToGroup(arrow); - m_grid->addToGroup(grid); - m_labels->addToGroup(label); - - if ((m_grid->childItems().size()) % 2 && m_grid->childItems().size() > 2){ - QGraphicsRectItem* shades = new QGraphicsRectItem(this); - shades->setPen(m_axis->shadesPen()); - shades->setBrush(m_axis->shadesBrush()); - m_shades->addToGroup(shades); - } - } - -} - -void ChartAxis::deleteItems(int count) -{ - QList<QGraphicsItem *> lines = m_grid->childItems(); - QList<QGraphicsItem *> labels = m_labels->childItems(); - QList<QGraphicsItem *> shades = m_shades->childItems(); - QList<QGraphicsItem *> axis = m_arrow->childItems(); - - for (int i = 0; i < count; ++i) { - if (lines.size() % 2 && lines.size() > 1) - delete(shades.takeLast()); - delete(lines.takeLast()); - delete(labels.takeLast()); - delete(axis.takeLast()); - } -} - -void ChartAxis::updateLayout(QVector<qreal> &layout) -{ - int diff = m_layoutVector.size() - layout.size(); - - if (diff > 0) - deleteItems(diff); - else if (diff < 0) - createItems(-diff); - - if (m_animation) { - switch (presenter()->state()) { - case ChartPresenter::ZoomInState: - m_animation->setAnimationType(AxisAnimation::ZoomInAnimation); - m_animation->setAnimationPoint(presenter()->statePoint()); - break; - case ChartPresenter::ZoomOutState: - m_animation->setAnimationType(AxisAnimation::ZoomOutAnimation); - m_animation->setAnimationPoint(presenter()->statePoint()); - break; - case ChartPresenter::ScrollUpState: - case ChartPresenter::ScrollLeftState: - m_animation->setAnimationType(AxisAnimation::MoveBackwordAnimation); - break; - case ChartPresenter::ScrollDownState: - case ChartPresenter::ScrollRightState: - m_animation->setAnimationType(AxisAnimation::MoveForwardAnimation); - break; - case ChartPresenter::ShowState: - m_animation->setAnimationType(AxisAnimation::DefaultAnimation); - break; - } - m_animation->setValues(m_layoutVector, layout); - presenter()->startAnimation(m_animation); - } else { - setLayout(layout); - updateGeometry(); - } -} - -void ChartAxis::setLabelPadding(int padding) -{ - m_labelPadding = padding; -} - -void ChartAxis::setTitlePadding(int padding) -{ - m_titlePadding = padding; -} - -bool ChartAxis::isEmpty() -{ - return m_axisRect.isEmpty() || m_gridRect.isEmpty() || qFuzzyCompare(min(),max()); -} - -void ChartAxis::setGeometry(const QRectF &axis, const QRectF &grid) -{ - m_gridRect = grid; - m_axisRect = axis; - - if (isEmpty()) - return; - - QVector<qreal> layout = calculateLayout(); - updateLayout(layout); -} - -qreal ChartAxis::min() const -{ - return m_axis->d_ptr->min(); -} - -qreal ChartAxis::max() const -{ - return m_axis->d_ptr->max(); -} - -QFont ChartAxis::font() const -{ - return m_axis->labelsFont(); -} - -QFont ChartAxis::titleFont() const -{ - return m_axis->titleFont(); -} - -QString ChartAxis::titleText() const -{ - return m_axis->titleText(); -} - -void ChartAxis::axisSelected() -{ - emit clicked(); -} - -Qt::Orientation ChartAxis::orientation() const -{ - return m_axis->orientation(); -} - -Qt::Alignment ChartAxis::alignment() const -{ - return m_axis->alignment(); -} - -void ChartAxis::setLabels(const QStringList &labels) -{ - m_labelsList = labels; -} - -QSizeF ChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const -{ - Q_UNUSED(which); - Q_UNUSED(constraint); - return QSizeF(); -} - -//handlers - -void ChartAxis::handleArrowVisibleChanged(bool visible) -{ - m_arrow->setVisible(visible); -} - -void ChartAxis::handleGridVisibleChanged(bool visible) -{ - m_grid->setVisible(visible); -} - -void ChartAxis::handleLabelsVisibleChanged(bool visible) -{ - m_labels->setVisible(visible); -} - -void ChartAxis::handleShadesVisibleChanged(bool visible) -{ - m_shades->setVisible(visible); -} - -void ChartAxis::handleTitleVisibleChanged(bool visible) -{ - QGraphicsLayoutItem::updateGeometry(); - presenter()->layout()->invalidate(); - m_title->setVisible(visible); -} - -void ChartAxis::handleLabelsAngleChanged(int angle) -{ - foreach (QGraphicsItem *item, m_labels->childItems()) - item->setRotation(angle); - - m_labelsAngle = angle; -} - -void ChartAxis::handleLabelsPenChanged(const QPen &pen) -{ - foreach (QGraphicsItem *item , m_labels->childItems()) - static_cast<QGraphicsSimpleTextItem *>(item)->setPen(pen); -} - -void ChartAxis::handleLabelsBrushChanged(const QBrush &brush) -{ - foreach (QGraphicsItem *item , m_labels->childItems()) - static_cast<QGraphicsSimpleTextItem *>(item)->setBrush(brush); -} - -void ChartAxis::handleLabelsFontChanged(const QFont &font) -{ - foreach (QGraphicsItem *item , m_labels->childItems()) - static_cast<QGraphicsSimpleTextItem *>(item)->setFont(font); - QGraphicsLayoutItem::updateGeometry(); - presenter()->layout()->invalidate(); -} - -void ChartAxis::handleShadesBrushChanged(const QBrush &brush) -{ - foreach (QGraphicsItem *item , m_shades->childItems()) - static_cast<QGraphicsRectItem *>(item)->setBrush(brush); -} - -void ChartAxis::handleShadesPenChanged(const QPen &pen) -{ - foreach (QGraphicsItem *item , m_shades->childItems()) - static_cast<QGraphicsRectItem *>(item)->setPen(pen); -} - -void ChartAxis::handleArrowPenChanged(const QPen &pen) -{ - foreach (QGraphicsItem *item , m_arrow->childItems()) - static_cast<QGraphicsLineItem *>(item)->setPen(pen); -} - -void ChartAxis::handleGridPenChanged(const QPen &pen) -{ - foreach (QGraphicsItem *item , m_grid->childItems()) - static_cast<QGraphicsLineItem *>(item)->setPen(pen); -} - -void ChartAxis::handleTitleTextChanged(const QString &title) -{ - QGraphicsLayoutItem::updateGeometry(); - presenter()->layout()->invalidate(); - m_title->setText(title); -} - - -void ChartAxis::handleTitlePenChanged(const QPen &pen) -{ - m_title->setPen(pen); -} - -void ChartAxis::handleTitleBrushChanged(const QBrush &brush) -{ - m_title->setBrush(brush); -} - -void ChartAxis::handleTitleFontChanged(const QFont &font) -{ - if(m_title->font() != font){ - m_title->setFont(font); - QGraphicsLayoutItem::updateGeometry(); - presenter()->layout()->invalidate(); - } -} - -void ChartAxis::handleVisibleChanged(bool visible) -{ - setVisible(visible); - if(!visible) { - m_grid->setVisible(visible); - m_arrow->setVisible(visible); - m_shades->setVisible(visible); - m_labels->setVisible(visible); - m_title->setVisible(visible); - }else { - m_grid->setVisible(m_axis->isGridLineVisible()); - m_arrow->setVisible(m_axis->isLineVisible()); - m_shades->setVisible(m_axis->shadesVisible()); - m_labels->setVisible(m_axis->labelsVisible()); - m_title->setVisible(m_axis->isTitleVisible()); - } - - if(presenter()) presenter()->layout()->invalidate(); -} - -void ChartAxis::handleRangeChanged(qreal min, qreal max) -{ - Q_UNUSED(min); - Q_UNUSED(max); - - if (!isEmpty()) { - - QVector<qreal> layout = calculateLayout(); - updateLayout(layout); - QSizeF before = effectiveSizeHint(Qt::PreferredSize); - QSizeF after = sizeHint(Qt::PreferredSize); - - if (before != after) { - QGraphicsLayoutItem::updateGeometry(); - //we don't want to call invalidate on layout, since it will change minimum size of component, - //which we would like to avoid since it causes nasty flips when scrolling or zooming, - //instead recalculate layout and use plotArea for extra space. - presenter()->layout()->setGeometry(presenter()->layout()->geometry()); - } - } - -} - -//helpers - -QStringList ChartAxis::createValueLabels(qreal min, qreal max, int ticks,const QString& format) -{ - QStringList labels; - - if (max <= min || ticks < 1) - return labels; - - int n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0); - n++; - - if (format.isNull()) { - for (int i = 0; i < ticks; i++) { - qreal value = min + (i * (max - min) / (ticks - 1)); - labels << QString::number(value, 'f', n); - } - } else { - QByteArray array = format.toLatin1(); - for (int i = 0; i < ticks; i++) { - qreal value = min + (i * (max - min) / (ticks - 1)); - if (format.contains("d") - || format.contains("i") - || format.contains("c")) - labels << QString().sprintf(array, (qint64)value); - else if (format.contains("u") - || format.contains("o") - || format.contains("x", Qt::CaseInsensitive)) - labels << QString().sprintf(array, (quint64)value); - else if (format.contains("f", Qt::CaseInsensitive) - || format.contains("e", Qt::CaseInsensitive) - || format.contains("g", Qt::CaseInsensitive)) - labels << QString().sprintf(array, value); - else - labels << QString(); - } - } - - return labels; -} - -QStringList ChartAxis::createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString& format) -{ -// Q_ASSERT(m_max > m_min); - // Q_ASSERT(ticks > 1); - - QStringList labels; - - int n = 0; - if (ticks > 1) - n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0); - n++; - - int firstTick; - if (base > 1) - firstTick = ceil(log10(min) / log10(base)); - else - firstTick = ceil(log10(max) / log10(base)); - - if (format.isNull()) { - for (int i = firstTick; i < ticks + firstTick; i++) { - qreal value = qPow(base, i); - labels << QString::number(value, 'f', n); - } - } else { - QByteArray array = format.toLatin1(); - for (int i = firstTick; i < ticks + firstTick; i++) { - qreal value = qPow(base, i); - if (format.contains("d") - || format.contains("i") - || format.contains("c")) - labels << QString().sprintf(array, (qint64)value); - else if (format.contains("u") - || format.contains("o") - || format.contains("x", Qt::CaseInsensitive)) - labels << QString().sprintf(array, (quint64)value); - else if (format.contains("f", Qt::CaseInsensitive) - || format.contains("e", Qt::CaseInsensitive) - || format.contains("g", Qt::CaseInsensitive)) - labels << QString().sprintf(array, value); - else - labels << QString(); - } - } - - return labels; -} - -QStringList ChartAxis::createDateTimeLabels(qreal min, qreal max,int ticks,const QString& format) -{ - QStringList labels; - - if (max <= min || ticks < 1) { - return labels; - } - - int n = qMax(int(-floor(log10((max - min) / (ticks - 1)))), 0); - n++; - for (int i = 0; i < ticks; i++) { - qreal value = min + (i * (max - min) / (ticks - 1)); - labels << QDateTime::fromMSecsSinceEpoch(value).toString(format); - } - return labels; -} - -QRect ChartAxis::labelBoundingRect(const QFontMetrics &fn, const QString &label) const -{ - QRect boundingRect = fn.boundingRect(label); - - // Take label rotation into account - if (m_labelsAngle) { - QTransform transform; - transform.rotate(m_labelsAngle); - boundingRect = transform.mapRect(boundingRect); - } - - return boundingRect; -} - -#include "moc_chartaxis_p.cpp" - -QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/chartaxis_p.h b/src/axis/chartaxis_p.h deleted file mode 100644 index 681613f8..00000000 --- a/src/axis/chartaxis_p.h +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt Commercial Charts Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// W A R N I N G -// ------------- -// -// This file is not part of the QtCommercial Chart API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. - -#ifndef CHARTAXIS_H -#define CHARTAXIS_H - -#include "qchartglobal.h" -#include "chartelement_p.h" -#include "axisanimation_p.h" -#include <QGraphicsItem> -#include <QGraphicsLayoutItem> -#include <QFont> - -QTCOMMERCIALCHART_BEGIN_NAMESPACE - -class QAbstractAxis; -class ChartPresenter; - -class ChartAxis : public ChartElement, public QGraphicsLayoutItem -{ - Q_OBJECT - Q_INTERFACES(QGraphicsLayoutItem) -public: - - ChartAxis(QAbstractAxis *axis, QGraphicsItem* item = 0, bool intervalAxis = false); - ~ChartAxis(); - - QAbstractAxis* axis() const { return m_axis; } - - void setLabelPadding(int padding); - int labelPadding() const { return m_labelPadding;}; - - void setTitlePadding(int padding); - int titlePadding() const { return m_titlePadding;}; - - QFont titleFont() const; - QString titleText() const; - - void setLayout(QVector<qreal> &layout); - QVector<qreal> layout() const { return m_layoutVector; } - - void setAnimation(AxisAnimation *animation); - ChartAnimation *animation() const { return m_animation; }; - - Qt::Orientation orientation() const; - Qt::Alignment alignment() const; - - void setGeometry(const QRectF &axis, const QRectF &grid); - QRectF axisGeometry() const { return m_axisRect; } - QRectF gridGeometry() const { return m_gridRect; } - - void setLabels(const QStringList &labels); - QStringList labels() const { return m_labelsList; } - - //this flag indicates that axis is used to show intervals it means labels are in between ticks - bool intervalAxis() const { return m_intervalAxis; } - - virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; - - - QRectF boundingRect() const{ - return QRectF(); - } - - void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) - { - - } - -//helpers - static QStringList createValueLabels(qreal max, qreal min, int ticks, const QString &format); - static QStringList createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString& format); - static QStringList createDateTimeLabels(qreal max, qreal min, int ticks, const QString &format); - -protected: - void setGeometry(const QRectF &size) { Q_UNUSED(size);}; - virtual void updateGeometry() = 0; - virtual QVector<qreal> calculateLayout() const = 0; - - QList<QGraphicsItem *> lineItems() { return m_grid->childItems(); }; - QList<QGraphicsItem *> labelItems() { return m_labels->childItems();}; - QList<QGraphicsItem *> shadeItems() { return m_shades->childItems();}; - QList<QGraphicsItem *> arrowItems() { return m_arrow->childItems();}; - QGraphicsSimpleTextItem* titleItem() const { return m_title.data();} - - QFont font() const; - qreal min() const; - qreal max() const; - QRect labelBoundingRect(const QFontMetrics &fn, const QString &label) const; - -//handlers -public Q_SLOTS: - void handleVisibleChanged(bool visible); - void handleArrowVisibleChanged(bool visible); - void handleGridVisibleChanged(bool visible); - void handleLabelsVisibleChanged(bool visible); - void handleShadesVisibleChanged(bool visible); - void handleLabelsAngleChanged(int angle); - void handleShadesBrushChanged(const QBrush &brush); - void handleShadesPenChanged(const QPen &pen); - void handleArrowPenChanged(const QPen &pen); - void handleGridPenChanged(const QPen &pen); - void handleLabelsPenChanged(const QPen &pen); - void handleLabelsBrushChanged(const QBrush &brush); - void handleLabelsFontChanged(const QFont &font); - void handleTitlePenChanged(const QPen &pen); - void handleTitleBrushChanged(const QBrush &brush); - void handleTitleFontChanged(const QFont &font); - void handleTitleTextChanged(const QString &title); - void handleTitleVisibleChanged(bool visible); - void handleRangeChanged(qreal min , qreal max); - -Q_SIGNALS: - void clicked(); - -private: - inline bool isEmpty(); - void createItems(int count); - void deleteItems(int count); - void updateLayout(QVector<qreal> &layout); - void axisSelected(); - void connectSlots(); - -private: - QAbstractAxis *m_axis; - int m_labelsAngle; - QRectF m_axisRect; - QRectF m_gridRect; - QScopedPointer<QGraphicsItemGroup> m_grid; - QScopedPointer<QGraphicsItemGroup> m_arrow; - QScopedPointer<QGraphicsItemGroup> m_shades; - QScopedPointer<QGraphicsItemGroup> m_labels; - QScopedPointer<QGraphicsSimpleTextItem> m_title; - QVector<qreal> m_layoutVector; - AxisAnimation *m_animation; - int m_labelPadding; - QStringList m_labelsList; - bool m_intervalAxis; - int m_titlePadding; - - friend class AxisAnimation; - friend class ArrowItem; - -}; - -class ArrowItem: public QGraphicsLineItem -{ - -public: - explicit ArrowItem(ChartAxis *axis, QGraphicsItem *parent = 0) : QGraphicsLineItem(parent), m_axis(axis) {} - -protected: - void mousePressEvent(QGraphicsSceneMouseEvent *event) { - Q_UNUSED(event) - m_axis->axisSelected(); - } - - QRectF boundingRect() const { - return shape().boundingRect(); - } - - QPainterPath shape() const { - QPainterPath path = QGraphicsLineItem::shape(); - QRectF rect = path.boundingRect(); - path.addRect(rect.adjusted(0, 0, m_axis->orientation() != Qt::Horizontal ? 8 : 0, m_axis->orientation() != Qt::Vertical ? 8 : 0)); - return path; - } - -private: - ChartAxis *m_axis; -}; - -QTCOMMERCIALCHART_END_NAMESPACE - -#endif /* CHARTAXI_H */ diff --git a/src/axis/chartaxiselement.cpp b/src/axis/chartaxiselement.cpp new file mode 100644 index 00000000..19fa8e17 --- /dev/null +++ b/src/axis/chartaxiselement.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chartaxiselement_p.h" +#include "qabstractaxis_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include <qmath.h> +#include <QDateTime> +#include <QFontMetrics> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : ChartElement(item), + m_axis(axis), + m_animation(0), + m_grid(new QGraphicsItemGroup(item)), + m_arrow(new QGraphicsItemGroup(item)), + m_shades(new QGraphicsItemGroup(item)), + m_labels(new QGraphicsItemGroup(item)), + m_title(new QGraphicsSimpleTextItem(item)), + m_intervalAxis(intervalAxis) + +{ + //initial initialization + m_arrow->setHandlesChildEvents(false); + m_arrow->setZValue(ChartPresenter::AxisZValue); + m_labels->setZValue(ChartPresenter::AxisZValue); + m_shades->setZValue(ChartPresenter::ShadesZValue); + m_grid->setZValue(ChartPresenter::GridZValue); + m_title->setZValue(ChartPresenter::GridZValue); + handleVisibleChanged(axis->isVisible()); + connectSlots(); + + setFlag(QGraphicsItem::ItemHasNoContents, true); +} + +ChartAxisElement::~ChartAxisElement() +{ +} + +void ChartAxisElement::connectSlots() +{ + QObject::connect(axis(), SIGNAL(visibleChanged(bool)), this, SLOT(handleVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), this, SLOT(handleArrowVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(gridVisibleChanged(bool)), this, SLOT(handleGridVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(shadesVisibleChanged(bool)), this, SLOT(handleShadesVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(labelsAngleChanged(int)), this, SLOT(handleLabelsAngleChanged(int))); + QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, SLOT(handleArrowPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(labelsPenChanged(const QPen&)), this, SLOT(handleLabelsPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(labelsBrushChanged(const QBrush&)), this, SLOT(handleLabelsBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(labelsFontChanged(const QFont&)), this, SLOT(handleLabelsFontChanged(const QFont&))); + QObject::connect(axis(), SIGNAL(gridLinePenChanged(const QPen&)), this, SLOT(handleGridPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(shadesPenChanged(const QPen&)), this, SLOT(handleShadesPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(shadesBrushChanged(const QBrush&)), this, SLOT(handleShadesBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(titleTextChanged(const QString&)), this, SLOT(handleTitleTextChanged(const QString&))); + QObject::connect(axis(), SIGNAL(titleFontChanged(const QFont&)), this, SLOT(handleTitleFontChanged(const QFont&))); + QObject::connect(axis(), SIGNAL(titlePenChanged(const QPen&)), this, SLOT(handleTitlePenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(titleBrushChanged(const QBrush&)), this, SLOT(handleTitleBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(titleVisibleChanged(bool)), this, SLOT(handleTitleVisibleChanged(bool))); + QObject::connect(axis()->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(handleRangeChanged(qreal, qreal))); +} + +void ChartAxisElement::handleArrowVisibleChanged(bool visible) +{ + m_arrow->setVisible(visible); +} + +void ChartAxisElement::handleGridVisibleChanged(bool visible) +{ + m_grid->setVisible(visible); +} + +void ChartAxisElement::handleLabelsVisibleChanged(bool visible) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + m_labels->setVisible(visible); +} + +void ChartAxisElement::handleShadesVisibleChanged(bool visible) +{ + m_shades->setVisible(visible); +} + +void ChartAxisElement::handleTitleVisibleChanged(bool visible) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + m_title->setVisible(visible); +} + +void ChartAxisElement::handleLabelsAngleChanged(int angle) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + item->setRotation(angle); + + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +void ChartAxisElement::handleLabelsPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + static_cast<QGraphicsSimpleTextItem *>(item)->setPen(pen); +} + +void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + static_cast<QGraphicsSimpleTextItem *>(item)->setBrush(brush); +} + +void ChartAxisElement::handleLabelsFontChanged(const QFont &font) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + static_cast<QGraphicsSimpleTextItem *>(item)->setFont(font); + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +void ChartAxisElement::handleTitleTextChanged(const QString &title) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + m_title->setText(title); +} + +void ChartAxisElement::handleTitlePenChanged(const QPen &pen) +{ + m_title->setPen(pen); +} + +void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush) +{ + m_title->setBrush(brush); +} + +void ChartAxisElement::handleTitleFontChanged(const QFont &font) +{ + if (m_title->font() != font) { + m_title->setFont(font); + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + } +} + +void ChartAxisElement::handleVisibleChanged(bool visible) +{ + setVisible(visible); + if (!visible) { + m_grid->setVisible(visible); + m_arrow->setVisible(visible); + m_shades->setVisible(visible); + m_labels->setVisible(visible); + m_title->setVisible(visible); + } else { + m_grid->setVisible(axis()->isGridLineVisible()); + m_arrow->setVisible(axis()->isLineVisible()); + m_shades->setVisible(axis()->shadesVisible()); + m_labels->setVisible(axis()->labelsVisible()); + m_title->setVisible(axis()->isTitleVisible()); + } + + if (presenter()) presenter()->layout()->invalidate(); +} + +QRect ChartAxisElement::labelBoundingRect(const QFontMetrics &fn, const QString &label) const +{ + QRect boundingRect = fn.boundingRect(label); + // Take label rotation into account + if (axis()->labelsAngle()) { + QTransform transform; + transform.rotate(axis()->labelsAngle()); + boundingRect = transform.mapRect(boundingRect); + } + + return boundingRect; +} + +void ChartAxisElement::handleRangeChanged(qreal min, qreal max) +{ + Q_UNUSED(min); + Q_UNUSED(max); + + if (!isEmpty()) { + QVector<qreal> layout = calculateLayout(); + updateLayout(layout); + QSizeF before = effectiveSizeHint(Qt::PreferredSize); + QSizeF after = sizeHint(Qt::PreferredSize); + + if (before != after) { + QGraphicsLayoutItem::updateGeometry(); + // We don't want to call invalidate on layout, since it will change minimum size of + // component, which we would like to avoid since it causes nasty flips when scrolling + // or zooming, instead recalculate layout and use plotArea for extra space. + presenter()->layout()->setGeometry(presenter()->layout()->geometry()); + } + } +} + +bool ChartAxisElement::isEmpty() +{ + return axisGeometry().isEmpty() + || gridGeometry().isEmpty() + || qFuzzyCompare(min(), max()); +} + +qreal ChartAxisElement::min() const +{ + return m_axis->d_ptr->min(); +} + +qreal ChartAxisElement::max() const +{ + return m_axis->d_ptr->max(); +} + +QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks, const QString &format) +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + int n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0); + n++; + + if (format.isNull()) { + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + labels << QString::number(value, 'f', n); + } + } else { + QByteArray array = format.toLatin1(); + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + if (format.contains("d") + || format.contains("i") + || format.contains("c")) { + labels << QString().sprintf(array, (qint64)value); + } else if (format.contains("u") + || format.contains("o") + || format.contains("x", Qt::CaseInsensitive)) { + labels << QString().sprintf(array, (quint64)value); + } else if (format.contains("f", Qt::CaseInsensitive) + || format.contains("e", Qt::CaseInsensitive) + || format.contains("g", Qt::CaseInsensitive)) { + labels << QString().sprintf(array, value); + } else { + labels << QString(); + } + } + } + + return labels; +} + +QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString &format) +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + int n = 0; + if (ticks > 1) + n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0); + n++; + + int firstTick; + if (base > 1) + firstTick = ceil(log10(min) / log10(base)); + else + firstTick = ceil(log10(max) / log10(base)); + + if (format.isNull()) { + for (int i = firstTick; i < ticks + firstTick; i++) { + qreal value = qPow(base, i); + labels << QString::number(value, 'f', n); + } + } else { + QByteArray array = format.toLatin1(); + for (int i = firstTick; i < ticks + firstTick; i++) { + qreal value = qPow(base, i); + if (format.contains("d") + || format.contains("i") + || format.contains("c")) { + labels << QString().sprintf(array, (qint64)value); + } else if (format.contains("u") + || format.contains("o") + || format.contains("x", Qt::CaseInsensitive)) { + labels << QString().sprintf(array, (quint64)value); + } else if (format.contains("f", Qt::CaseInsensitive) + || format.contains("e", Qt::CaseInsensitive) + || format.contains("g", Qt::CaseInsensitive)) { + labels << QString().sprintf(array, value); + } else { + labels << QString(); + } + } + } + + return labels; +} + +QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,const QString &format) +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + int n = qMax(int(-floor(log10((max - min) / (ticks - 1)))), 0); + n++; + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + labels << QDateTime::fromMSecsSinceEpoch(value).toString(format); + } + return labels; +} + +void ChartAxisElement::axisSelected() +{ + emit clicked(); +} + +#include "moc_chartaxiselement_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/chartaxiselement_p.h b/src/axis/chartaxiselement_p.h new file mode 100644 index 00000000..0fdd207d --- /dev/null +++ b/src/axis/chartaxiselement_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CHARTAXISELEMENT_H +#define CHARTAXISELEMENT_H + +#include "qchartglobal.h" +#include "chartelement_p.h" +#include "axisanimation_p.h" +#include <QGraphicsItem> +#include <QGraphicsLayoutItem> +#include <QFont> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class ChartPresenter; +class QAbstractAxis; + +class ChartAxisElement : public ChartElement, public QGraphicsLayoutItem +{ + Q_OBJECT +public: + ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false); + ~ChartAxisElement(); + + virtual QRectF gridGeometry() const = 0; + virtual void setGeometry(const QRectF &axis, const QRectF &grid) = 0; + virtual bool isEmpty() = 0; + + void setAnimation(AxisAnimation *animation) { m_animation = animation; } + AxisAnimation *animation() const { return m_animation; } + + QAbstractAxis *axis() const { return m_axis; } + void setLayout(QVector<qreal> &layout) { m_layout = layout; } + QVector<qreal> &layout() { return m_layout; } // Modifiable reference + int labelPadding() const { return 5; } + int titlePadding() const { return 3; } + void setLabels(const QStringList &labels) { m_labelsList = labels; } + QStringList labels() const { return m_labelsList; } + + qreal min() const; + qreal max() const; + + QRectF axisGeometry() const { return m_axisRect; } + void setAxisGeometry(const QRectF &axisGeometry) { m_axisRect = axisGeometry; } + + QRect labelBoundingRect(const QFontMetrics &fn, const QString &label) const; + + void axisSelected(); + + //this flag indicates that axis is used to show intervals it means labels are in between ticks + bool intervalAxis() const { return m_intervalAxis; } + + static QStringList createValueLabels(qreal max, qreal min, int ticks, const QString &format); + static QStringList createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString &format); + static QStringList createDateTimeLabels(qreal max, qreal min, int ticks, const QString &format); + + // from QGraphicsLayoutItem + QRectF boundingRect() const + { + return QRectF(); + } + + // from QGraphicsLayoutItem + void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) + { + } + +protected: + virtual QVector<qreal> calculateLayout() const = 0; + virtual void updateLayout(QVector<qreal> &layout) = 0; + + QList<QGraphicsItem *> gridItems() { return m_grid->childItems(); } + QList<QGraphicsItem *> labelItems() { return m_labels->childItems(); } + QList<QGraphicsItem *> shadeItems() { return m_shades->childItems(); } + QList<QGraphicsItem *> arrowItems() { return m_arrow->childItems(); } + QGraphicsSimpleTextItem *titleItem() const { return m_title.data(); } + QGraphicsItemGroup *gridGroup() { return m_grid.data(); } + QGraphicsItemGroup *labelGroup() { return m_labels.data(); } + QGraphicsItemGroup *shadeGroup() { return m_shades.data(); } + QGraphicsItemGroup *arrowGroup() { return m_arrow.data(); } + +public Q_SLOTS: + void handleVisibleChanged(bool visible); + void handleArrowVisibleChanged(bool visible); + void handleGridVisibleChanged(bool visible); + void handleLabelsVisibleChanged(bool visible); + void handleShadesVisibleChanged(bool visible); + void handleLabelsAngleChanged(int angle); + virtual void handleShadesBrushChanged(const QBrush &brush) = 0; + virtual void handleShadesPenChanged(const QPen &pen) = 0; + virtual void handleArrowPenChanged(const QPen &pen) = 0; + virtual void handleGridPenChanged(const QPen &pen) = 0; + void handleLabelsPenChanged(const QPen &pen); + void handleLabelsBrushChanged(const QBrush &brush); + void handleLabelsFontChanged(const QFont &font); + void handleTitlePenChanged(const QPen &pen); + void handleTitleBrushChanged(const QBrush &brush); + void handleTitleFontChanged(const QFont &font); + void handleTitleTextChanged(const QString &title); + void handleTitleVisibleChanged(bool visible); + void handleRangeChanged(qreal min, qreal max); + +Q_SIGNALS: + void clicked(); + +private: + void connectSlots(); + + QAbstractAxis *m_axis; + AxisAnimation *m_animation; + QVector<qreal> m_layout; + QStringList m_labelsList; + QRectF m_axisRect; + QScopedPointer<QGraphicsItemGroup> m_grid; + QScopedPointer<QGraphicsItemGroup> m_arrow; + QScopedPointer<QGraphicsItemGroup> m_shades; + QScopedPointer<QGraphicsItemGroup> m_labels; + QScopedPointer<QGraphicsSimpleTextItem> m_title; + bool m_intervalAxis; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif /* CHARTAXISELEMENT_H */ diff --git a/src/axis/datetimeaxis/chartdatetimeaxisx.cpp b/src/axis/datetimeaxis/chartdatetimeaxisx.cpp index 39fc3ba7..6ff32c5e 100644 --- a/src/axis/datetimeaxis/chartdatetimeaxisx.cpp +++ b/src/axis/datetimeaxis/chartdatetimeaxisx.cpp @@ -21,7 +21,7 @@ #include "chartdatetimeaxisx_p.h" #include "chartpresenter_p.h" #include "qdatetimeaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QDateTime> #include <QFontMetrics> @@ -29,12 +29,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartDateTimeAxisX::ChartDateTimeAxisX(QDateTimeAxis *axis, QGraphicsItem* item) +ChartDateTimeAxisX::ChartDateTimeAxisX(QDateTimeAxis *axis, QGraphicsItem *item) : HorizontalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis,SIGNAL(tickCountChanged(int)),this, SLOT(handleTickCountChanged(int))); - QObject::connect(m_axis,SIGNAL(formatChanged(QString)),this, SLOT(handleFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(m_axis, SIGNAL(formatChanged(QString)), this, SLOT(handleFormatChanged(QString))); } ChartDateTimeAxisX::~ChartDateTimeAxisX() @@ -58,10 +58,10 @@ QVector<qreal> ChartDateTimeAxisX::calculateLayout() const void ChartDateTimeAxisX::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal>& layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; - setLabels(createDateTimeLabels(min(),max(), layout.size(),m_axis->format())); + setLabels(createDateTimeLabels(min(), max(), layout.size(), m_axis->format())); HorizontalAxis::updateGeometry(); } @@ -69,25 +69,27 @@ void ChartDateTimeAxisX::handleTickCountChanged(int tick) { Q_UNUSED(tick) QGraphicsLayoutItem::updateGeometry(); - if(presenter()) presenter()->layout()->invalidate(); + if (presenter()) + presenter()->layout()->invalidate(); } void ChartDateTimeAxisX::handleFormatChanged(const QString &format) { Q_UNUSED(format); QGraphicsLayoutItem::updateGeometry(); - if(presenter()) presenter()->layout()->invalidate(); + if (presenter()) + presenter()->layout()->invalidate(); } QSizeF ChartDateTimeAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = HorizontalAxis::sizeHint(which, constraint); - QStringList ticksList = createDateTimeLabels(min(),max(),m_axis->tickCount(),m_axis->format()); + QStringList ticksList = createDateTimeLabels(min(), max(), m_axis->tickCount(), m_axis->format()); // Width of horizontal axis sizeHint indicates the maximum distance labels can extend past // first and last ticks. Base width is irrelevant. qreal width = 0; @@ -97,7 +99,7 @@ QSizeF ChartDateTimeAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint return sh; switch (which) { - case Qt::MinimumSize:{ + case Qt::MinimumSize: { QRectF boundingRect = labelBoundingRect(fn, "..."); width = boundingRect.width() / 2.0; height = boundingRect.height() + labelPadding(); diff --git a/src/axis/datetimeaxis/chartdatetimeaxisx_p.h b/src/axis/datetimeaxis/chartdatetimeaxisx_p.h index 841ae1ea..03fe98bb 100644 --- a/src/axis/datetimeaxis/chartdatetimeaxisx_p.h +++ b/src/axis/datetimeaxis/chartdatetimeaxisx_p.h @@ -35,7 +35,6 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QDateTimeAxis; -class ChartPresenter; class ChartDateTimeAxisX : public HorizontalAxis { diff --git a/src/axis/datetimeaxis/chartdatetimeaxisy.cpp b/src/axis/datetimeaxis/chartdatetimeaxisy.cpp index a534617f..a8fe58a2 100644 --- a/src/axis/datetimeaxis/chartdatetimeaxisy.cpp +++ b/src/axis/datetimeaxis/chartdatetimeaxisy.cpp @@ -21,7 +21,7 @@ #include "chartdatetimeaxisy_p.h" #include "chartpresenter_p.h" #include "qdatetimeaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <QDateTime> @@ -29,12 +29,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartDateTimeAxisY::ChartDateTimeAxisY(QDateTimeAxis *axis, QGraphicsItem* item) +ChartDateTimeAxisY::ChartDateTimeAxisY(QDateTimeAxis *axis, QGraphicsItem *item) : VerticalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis,SIGNAL(tickCountChanged(int)),this, SLOT(handleTickCountChanged(int))); - QObject::connect(m_axis,SIGNAL(formatChanged(QString)),this, SLOT(handleFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(m_axis, SIGNAL(formatChanged(QString)), this, SLOT(handleFormatChanged(QString))); } ChartDateTimeAxisY::~ChartDateTimeAxisY() @@ -59,10 +59,10 @@ QVector<qreal> ChartDateTimeAxisY::calculateLayout() const void ChartDateTimeAxisY::updateGeometry() { - const QVector<qreal> &layout = ChartAxis::layout(); + const QVector<qreal> &layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; - setLabels(createDateTimeLabels(min(),max(), layout.size(),m_axis->format())); + setLabels(createDateTimeLabels(min(), max(), layout.size(), m_axis->format())); VerticalAxis::updateGeometry(); } @@ -70,25 +70,27 @@ void ChartDateTimeAxisY::handleTickCountChanged(int tick) { Q_UNUSED(tick) QGraphicsLayoutItem::updateGeometry(); - if(presenter()) presenter()->layout()->invalidate(); + if (presenter()) + presenter()->layout()->invalidate(); } void ChartDateTimeAxisY::handleFormatChanged(const QString &format) { Q_UNUSED(format); QGraphicsLayoutItem::updateGeometry(); - if(presenter()) presenter()->layout()->invalidate(); + if (presenter()) + presenter()->layout()->invalidate(); } QSizeF ChartDateTimeAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = VerticalAxis::sizeHint(which, constraint); - QStringList ticksList = createDateTimeLabels(min(),max(),m_axis->tickCount(),m_axis->format()); + QStringList ticksList = createDateTimeLabels(min(), max(), m_axis->tickCount(), m_axis->format()); qreal width = 0; // Height of vertical axis sizeHint indicates the maximum distance labels can extend past // first and last ticks. Base height is irrelevant. diff --git a/src/axis/datetimeaxis/chartdatetimeaxisy_p.h b/src/axis/datetimeaxis/chartdatetimeaxisy_p.h index 0bbe0d5a..be3adc54 100644 --- a/src/axis/datetimeaxis/chartdatetimeaxisy_p.h +++ b/src/axis/datetimeaxis/chartdatetimeaxisy_p.h @@ -35,7 +35,6 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QDateTimeAxis; -class ChartPresenter; class ChartDateTimeAxisY : public VerticalAxis { diff --git a/src/axis/datetimeaxis/polarchartdatetimeaxisangular.cpp b/src/axis/datetimeaxis/polarchartdatetimeaxisangular.cpp new file mode 100644 index 00000000..6b1d86af --- /dev/null +++ b/src/axis/datetimeaxis/polarchartdatetimeaxisangular.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartdatetimeaxisangular_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qdatetimeaxis.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartDateTimeAxisAngular::PolarChartDateTimeAxisAngular(QDateTimeAxis *axis, QGraphicsItem *item) + : PolarChartAxisAngular(axis, item) +{ + QObject::connect(axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(axis, SIGNAL(formatChanged(QString)), this, SLOT(handleFormatChanged(QString))); +} + +PolarChartDateTimeAxisAngular::~PolarChartDateTimeAxisAngular() +{ +} + +QVector<qreal> PolarChartDateTimeAxisAngular::calculateLayout() const +{ + int tickCount = static_cast<QDateTimeAxis *>(axis())->tickCount(); + Q_ASSERT(tickCount >= 2); + + QVector<qreal> points; + points.resize(tickCount); + + const qreal d = 360.0 / qreal(tickCount - 1); + + for (int i = 0; i < tickCount; ++i) { + qreal angularCoordinate = qreal(i) * d; + points[i] = angularCoordinate; + } + + return points; +} +void PolarChartDateTimeAxisAngular::createAxisLabels(const QVector<qreal> &layout) +{ + QStringList labelList = createDateTimeLabels(min(), max(), layout.size(), static_cast<QDateTimeAxis *>(axis())->format()); + setLabels(labelList); +} + +void PolarChartDateTimeAxisAngular::handleTickCountChanged(int tick) +{ + Q_UNUSED(tick); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartDateTimeAxisAngular::handleFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartdatetimeaxisangular_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/datetimeaxis/polarchartdatetimeaxisangular_p.h b/src/axis/datetimeaxis/polarchartdatetimeaxisangular_p.h new file mode 100644 index 00000000..9a7ecffe --- /dev/null +++ b/src/axis/datetimeaxis/polarchartdatetimeaxisangular_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTDATETIMEAXISANGULAR_P_H +#define POLARCHARTDATETIMEAXISANGULAR_P_H + +#include "polarchartaxisangular_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QDateTimeAxis; + +class PolarChartDateTimeAxisAngular : public PolarChartAxisAngular +{ + Q_OBJECT +public: + PolarChartDateTimeAxisAngular(QDateTimeAxis *axis, QGraphicsItem *item); + ~PolarChartDateTimeAxisAngular(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleTickCountChanged(int tick); + void handleFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTDATETIMEAXISANGULAR_P_H diff --git a/src/axis/datetimeaxis/polarchartdatetimeaxisradial.cpp b/src/axis/datetimeaxis/polarchartdatetimeaxisradial.cpp new file mode 100644 index 00000000..bcd04283 --- /dev/null +++ b/src/axis/datetimeaxis/polarchartdatetimeaxisradial.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartdatetimeaxisradial_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qdatetimeaxis.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartDateTimeAxisRadial::PolarChartDateTimeAxisRadial(QDateTimeAxis *axis, QGraphicsItem *item) + : PolarChartAxisRadial(axis, item) +{ + QObject::connect(axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(axis, SIGNAL(formatChanged(QString)), this, SLOT(handleFormatChanged(QString))); +} + +PolarChartDateTimeAxisRadial::~PolarChartDateTimeAxisRadial() +{ +} + +QVector<qreal> PolarChartDateTimeAxisRadial::calculateLayout() const +{ + int tickCount = static_cast<QDateTimeAxis *>(axis())->tickCount(); + Q_ASSERT(tickCount >= 2); + + QVector<qreal> points; + points.resize(tickCount); + + const qreal d = (axisGeometry().width() / 2) / qreal(tickCount - 1); + + for (int i = 0; i < tickCount; ++i) { + qreal radialCoordinate = qreal(i) * d; + points[i] = radialCoordinate; + } + + return points; +} +void PolarChartDateTimeAxisRadial::createAxisLabels(const QVector<qreal> &layout) +{ + setLabels(createDateTimeLabels(min(), max(), layout.size(), static_cast<QDateTimeAxis *>(axis())->format())); +} + +void PolarChartDateTimeAxisRadial::handleTickCountChanged(int tick) +{ + Q_UNUSED(tick); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartDateTimeAxisRadial::handleFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartdatetimeaxisradial_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/datetimeaxis/polarchartdatetimeaxisradial_p.h b/src/axis/datetimeaxis/polarchartdatetimeaxisradial_p.h new file mode 100644 index 00000000..e10e9194 --- /dev/null +++ b/src/axis/datetimeaxis/polarchartdatetimeaxisradial_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTDATETIMEAXISRADIAL_P_H +#define POLARCHARTDATETIMEAXISRADIAL_P_H + +#include "polarchartaxisradial_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QDateTimeAxis; + +class PolarChartDateTimeAxisRadial : public PolarChartAxisRadial +{ + Q_OBJECT +public: + PolarChartDateTimeAxisRadial(QDateTimeAxis *axis, QGraphicsItem *item); + ~PolarChartDateTimeAxisRadial(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleTickCountChanged(int tick); + void handleFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTDATETIMEAXISRADIAL_P_H diff --git a/src/axis/datetimeaxis/qdatetimeaxis.cpp b/src/axis/datetimeaxis/qdatetimeaxis.cpp index 3e00ee90..ed1b4a1c 100644 --- a/src/axis/datetimeaxis/qdatetimeaxis.cpp +++ b/src/axis/datetimeaxis/qdatetimeaxis.cpp @@ -22,6 +22,8 @@ #include "qdatetimeaxis_p.h" #include "chartdatetimeaxisx_p.h" #include "chartdatetimeaxisy_p.h" +#include "polarchartdatetimeaxisangular_p.h" +#include "polarchartdatetimeaxisradial_p.h" #include "abstractdomain_p.h" #include "qchart.h" #include <float.h> @@ -342,11 +344,20 @@ void QDateTimeAxisPrivate::setRange(const QVariant &min, const QVariant &max) void QDateTimeAxisPrivate::initializeGraphics(QGraphicsItem* parent) { Q_Q(QDateTimeAxis); - ChartAxis* axis(0); - if (orientation() == Qt::Vertical) - axis = new ChartDateTimeAxisY(q,parent); - if (orientation() == Qt::Horizontal) - axis = new ChartDateTimeAxisX(q,parent); + ChartAxisElement *axis(0); + if (m_chart->chartType() == QChart::ChartTypeCartesian) { + if (orientation() == Qt::Vertical) + axis = new ChartDateTimeAxisY(q,parent); + if (orientation() == Qt::Horizontal) + axis = new ChartDateTimeAxisX(q,parent); + } + + if (m_chart->chartType() == QChart::ChartTypePolar) { + if (orientation() == Qt::Vertical) + axis = new PolarChartDateTimeAxisRadial(q, parent); + if (orientation() == Qt::Horizontal) + axis = new PolarChartDateTimeAxisAngular(q, parent); + } m_item.reset(axis); QAbstractAxisPrivate::initializeGraphics(parent); diff --git a/src/axis/horizontalaxis.cpp b/src/axis/horizontalaxis.cpp index 9dcd7750..7068d597 100644 --- a/src/axis/horizontalaxis.cpp +++ b/src/axis/horizontalaxis.cpp @@ -19,15 +19,15 @@ ****************************************************************************/ #include "horizontalaxis_p.h" -#include "qabstractaxis.h" +#include "qabstractaxis_p.h" #include <QFontMetrics> #include <qmath.h> #include <QDebug> QTCOMMERCIALCHART_BEGIN_NAMESPACE -HorizontalAxis::HorizontalAxis(QAbstractAxis *axis, QGraphicsItem* item , bool intervalAxis) - : ChartAxis(axis, item, intervalAxis) +HorizontalAxis::HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : CartesianChartAxis(axis, item, intervalAxis) { } @@ -37,18 +37,18 @@ HorizontalAxis::~HorizontalAxis() void HorizontalAxis::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal> &layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; QStringList labelList = labels(); - QList<QGraphicsItem *> lines = lineItems(); + QList<QGraphicsItem *> lines = gridItems(); QList<QGraphicsItem *> labels = labelItems(); QList<QGraphicsItem *> shades = shadeItems(); - QList<QGraphicsItem *> axis = arrowItems(); - QGraphicsSimpleTextItem* title = titleItem(); + QList<QGraphicsItem *> arrow = arrowItems(); + QGraphicsSimpleTextItem *title = titleItem(); Q_ASSERT(labels.size() == labelList.size()); Q_ASSERT(layout.size() == labelList.size()); @@ -57,24 +57,24 @@ void HorizontalAxis::updateGeometry() const QRectF &gridRect = gridGeometry(); //arrow - QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem *>(axis.at(0)); + QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem *>(arrow.at(0)); - if (alignment() == Qt::AlignTop) + if (axis()->alignment() == Qt::AlignTop) arrowItem->setLine(gridRect.left(), axisRect.bottom(), gridRect.right(), axisRect.bottom()); - else if (alignment() == Qt::AlignBottom) + else if (axis()->alignment() == Qt::AlignBottom) arrowItem->setLine(gridRect.left(), axisRect.top(), gridRect.right(), axisRect.top()); qreal width = 0; - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); //title int titlePad = 0; QRectF titleBoundingRect; - if (!titleText().isEmpty() && titleItem()->isVisible()) { + QString titleText = axis()->titleText(); + if (!titleText.isEmpty() && titleItem()->isVisible()) { QFontMetrics fn(title->font()); int size(0); size = gridRect.width(); - QString titleText = this->titleText(); if (fn.boundingRect(titleText).width() > size) { QString string = titleText + "..."; @@ -89,9 +89,9 @@ void HorizontalAxis::updateGeometry() titleBoundingRect = title->boundingRect(); QPointF center = gridRect.center() - titleBoundingRect.center(); - if (alignment() == Qt::AlignTop) { + if (axis()->alignment() == Qt::AlignTop) { title->setPos(center.x(), axisRect.top() + titlePad); - } else if (alignment() == Qt::AlignBottom) { + } else if (axis()->alignment() == Qt::AlignBottom) { title->setPos(center.x(), axisRect.bottom() - titleBoundingRect.height() - titlePad); } } @@ -100,7 +100,7 @@ void HorizontalAxis::updateGeometry() //items QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem*>(lines.at(i)); - QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem*>(axis.at(i + 1)); + QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem*>(arrow.at(i + 1)); QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem *>(labels.at(i)); //grid line @@ -128,10 +128,10 @@ void HorizontalAxis::updateGeometry() int heightDiff = rect.height() - boundingRect.height(); //ticks and label position - if (alignment() == Qt::AlignTop) { + if (axis()->alignment() == Qt::AlignTop) { labelItem->setPos(layout[i] - center.x(), axisRect.bottom() - rect.height() + (heightDiff / 2) - labelPadding()); tickItem->setLine(layout[i], axisRect.bottom(), layout[i], axisRect.bottom() - labelPadding()); - } else if (alignment() == Qt::AlignBottom) { + } else if (axis()->alignment() == Qt::AlignBottom) { labelItem->setPos(layout[i] - center.x(), axisRect.top() - (heightDiff / 2) + labelPadding()); tickItem->setLine(layout[i], axisRect.top(), layout[i], axisRect.top() + labelPadding()); } @@ -178,7 +178,7 @@ void HorizontalAxis::updateGeometry() if (x < gridRect.left() || x > gridRect.right()) { gridItem->setVisible(false); tickItem->setVisible(false); - }else{ + } else { gridItem->setVisible(true); tickItem->setVisible(true); } @@ -200,10 +200,10 @@ void HorizontalAxis::updateGeometry() QSizeF HorizontalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint); - QFontMetrics fn(titleFont()); + QFontMetrics fn(axis()->titleFont()); QSizeF sh(0,0); - if (titleText().isEmpty() || !titleItem()->isVisible()) + if (axis()->titleText().isEmpty() || !titleItem()->isVisible()) return sh; switch (which) { diff --git a/src/axis/horizontalaxis_p.h b/src/axis/horizontalaxis_p.h index 02aa2587..23116792 100644 --- a/src/axis/horizontalaxis_p.h +++ b/src/axis/horizontalaxis_p.h @@ -30,14 +30,14 @@ #ifndef HORIZONTALAXIS_P_H_ #define HORIZONTALAXIS_P_H_ -#include "chartaxis_p.h" +#include "cartesianchartaxis_p.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE -class HorizontalAxis : public ChartAxis +class HorizontalAxis : public CartesianChartAxis { public: - HorizontalAxis(QAbstractAxis *axis, QGraphicsItem* item = 0, bool intervalAxis = false); + HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item = 0, bool intervalAxis = false); ~HorizontalAxis(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; protected: diff --git a/src/axis/linearrowitem_p.h b/src/axis/linearrowitem_p.h new file mode 100644 index 00000000..1adfd3bb --- /dev/null +++ b/src/axis/linearrowitem_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef LINEARROWITEM_P_H +#define LINEARROWITEM_P_H + +#include "chartaxiselement_p.h" +#include "qabstractaxis_p.h" +#include <QGraphicsLineItem> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class LineArrowItem: public QGraphicsLineItem +{ +public: + explicit LineArrowItem(ChartAxisElement *axis, QGraphicsItem *parent = 0) + : QGraphicsLineItem(parent), + m_axis(axis), + m_axisOrientation(axis->axis()->orientation()) + { + } + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) + { + Q_UNUSED(event) + m_axis->axisSelected(); + } + + QRectF boundingRect() const + { + return shape().boundingRect(); + } + + QPainterPath shape() const + { + QPainterPath path = QGraphicsLineItem::shape(); + QRectF rect = path.boundingRect(); + path.addRect(rect.adjusted(0, 0, m_axisOrientation != Qt::Horizontal ? 8 : 0, m_axisOrientation != Qt::Vertical ? 8 : 0)); + return path; + } + +private: + ChartAxisElement *m_axis; + Qt::Orientation m_axisOrientation; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif /* LINEARROWITEM_P_H */ diff --git a/src/axis/logvalueaxis/chartlogvalueaxisx.cpp b/src/axis/logvalueaxis/chartlogvalueaxisx.cpp index 3edc6d95..ac8f1f08 100644 --- a/src/axis/logvalueaxis/chartlogvalueaxisx.cpp +++ b/src/axis/logvalueaxis/chartlogvalueaxisx.cpp @@ -21,7 +21,7 @@ #include "chartlogvalueaxisx_p.h" #include "chartpresenter_p.h" #include "qlogvalueaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -29,12 +29,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartLogValueAxisX::ChartLogValueAxisX(QLogValueAxis *axis, QGraphicsItem* item) +ChartLogValueAxisX::ChartLogValueAxisX(QLogValueAxis *axis, QGraphicsItem *item) : HorizontalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis,SIGNAL(baseChanged(qreal)),this, SLOT(handleBaseChanged(qreal))); - QObject::connect(m_axis,SIGNAL(labelFormatChanged(QString)),this, SLOT(handleLabelFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal))); + QObject::connect(m_axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); } ChartLogValueAxisX::~ChartLogValueAxisX() @@ -55,14 +55,14 @@ QVector<qreal> ChartLogValueAxisX::calculateLayout() const const QRectF &gridRect = gridGeometry(); const qreal deltaX = gridRect.width() / qAbs(logMax - logMin); for (int i = 0; i < tickCount; ++i) - points[i] = (ceilEdge + i) * deltaX - leftEdge * deltaX + gridRect.left(); + points[i] = (ceilEdge + qreal(i)) * deltaX - leftEdge * deltaX + gridRect.left(); return points; } void ChartLogValueAxisX::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal>& layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; setLabels(createLogValueLabels(m_axis->min(), m_axis->max(), m_axis->base(), layout.size(), m_axis->labelFormat())); @@ -87,7 +87,7 @@ QSizeF ChartLogValueAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = HorizontalAxis::sizeHint(which, constraint); diff --git a/src/axis/logvalueaxis/chartlogvalueaxisx_p.h b/src/axis/logvalueaxis/chartlogvalueaxisx_p.h index c0b24a58..3017e260 100644 --- a/src/axis/logvalueaxis/chartlogvalueaxisx_p.h +++ b/src/axis/logvalueaxis/chartlogvalueaxisx_p.h @@ -35,20 +35,18 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QLogValueAxis; -class ChartPresenter; class ChartLogValueAxisX : public HorizontalAxis { Q_OBJECT public: - ChartLogValueAxisX(QLogValueAxis *axis, QGraphicsItem* item); + ChartLogValueAxisX(QLogValueAxis *axis, QGraphicsItem *item); ~ChartLogValueAxisX(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; protected: - void handleAxisUpdated(); QVector<qreal> calculateLayout() const; void updateGeometry(); diff --git a/src/axis/logvalueaxis/chartlogvalueaxisy.cpp b/src/axis/logvalueaxis/chartlogvalueaxisy.cpp index ec474e2f..42ae875c 100644 --- a/src/axis/logvalueaxis/chartlogvalueaxisy.cpp +++ b/src/axis/logvalueaxis/chartlogvalueaxisy.cpp @@ -21,7 +21,7 @@ #include "chartlogvalueaxisy_p.h" #include "chartpresenter_p.h" #include "qlogvalueaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -29,12 +29,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartLogValueAxisY::ChartLogValueAxisY(QLogValueAxis *axis, QGraphicsItem* item) +ChartLogValueAxisY::ChartLogValueAxisY(QLogValueAxis *axis, QGraphicsItem *item) : VerticalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis, SIGNAL(baseChanged(qreal)),this, SLOT(handleBaseChanged(qreal))); - QObject::connect(m_axis,SIGNAL(labelFormatChanged(QString)),this, SLOT(handleLabelFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal))); + QObject::connect(m_axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); } ChartLogValueAxisY::~ChartLogValueAxisY() @@ -54,7 +54,7 @@ QVector<qreal> ChartLogValueAxisY::calculateLayout() const const QRectF &gridRect = gridGeometry(); const qreal deltaY = gridRect.height() / qAbs(logMax - logMin); for (int i = 0; i < tickCount; ++i) - points[i] = (ceilEdge + i) * -deltaY - leftEdge * -deltaY + gridRect.bottom(); + points[i] = (ceilEdge + qreal(i)) * -deltaY - leftEdge * -deltaY + gridRect.bottom(); return points; } @@ -62,7 +62,7 @@ QVector<qreal> ChartLogValueAxisY::calculateLayout() const void ChartLogValueAxisY::updateGeometry() { - const QVector<qreal> &layout = ChartAxis::layout(); + const QVector<qreal> &layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; setLabels(createLogValueLabels(m_axis->min(), m_axis->max(), m_axis->base(), layout.size(), m_axis->labelFormat())); @@ -87,7 +87,7 @@ QSizeF ChartLogValueAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = VerticalAxis::sizeHint(which, constraint); diff --git a/src/axis/logvalueaxis/chartlogvalueaxisy_p.h b/src/axis/logvalueaxis/chartlogvalueaxisy_p.h index 8506cc29..39750976 100644 --- a/src/axis/logvalueaxis/chartlogvalueaxisy_p.h +++ b/src/axis/logvalueaxis/chartlogvalueaxisy_p.h @@ -35,20 +35,18 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QLogValueAxis; -class ChartPresenter; class ChartLogValueAxisY : public VerticalAxis { Q_OBJECT public: - ChartLogValueAxisY(QLogValueAxis *axis, QGraphicsItem* item); + ChartLogValueAxisY(QLogValueAxis *axis, QGraphicsItem *item); ~ChartLogValueAxisY(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; protected: - void handleAxisUpdated(); QVector<qreal> calculateLayout() const; void updateGeometry(); diff --git a/src/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp b/src/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp new file mode 100644 index 00000000..ddbd9fe6 --- /dev/null +++ b/src/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartlogvalueaxisangular_p.h" +#include "abstractchartlayout_p.h" +#include "chartpresenter_p.h" +#include "qlogvalueaxis.h" +#include <qmath.h> +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartLogValueAxisAngular::PolarChartLogValueAxisAngular(QLogValueAxis *axis, QGraphicsItem *item) + : PolarChartAxisAngular(axis, item) +{ + QObject::connect(axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal))); + QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); +} + +PolarChartLogValueAxisAngular::~PolarChartLogValueAxisAngular() +{ +} + +QVector<qreal> PolarChartLogValueAxisAngular::calculateLayout() const +{ + QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis()); + const qreal logMax = log10(logValueAxis->max()) / log10(logValueAxis->base()); + const qreal logMin = log10(logValueAxis->min()) / log10(logValueAxis->base()); + const qreal startEdge = logMin < logMax ? logMin : logMax; + const qreal delta = 360.0 / qAbs(logMax - logMin); + const qreal initialSpan = (ceil(startEdge) - startEdge) * delta; + int tickCount = qAbs(ceil(logMax) - ceil(logMin)); + + QVector<qreal> points; + points.resize(tickCount); + + for (int i = 0; i < tickCount; ++i) { + qreal angularCoordinate = initialSpan + (delta * qreal(i)); + points[i] = angularCoordinate; + } + + return points; +} + +void PolarChartLogValueAxisAngular::createAxisLabels(const QVector<qreal> &layout) +{ + QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis()); + setLabels(createLogValueLabels(logValueAxis->min(), + logValueAxis->max(), + logValueAxis->base(), + layout.size(), + logValueAxis->labelFormat())); +} + +void PolarChartLogValueAxisAngular::handleBaseChanged(qreal base) +{ + Q_UNUSED(base); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartLogValueAxisAngular::handleLabelFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartlogvalueaxisangular_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/logvalueaxis/polarchartlogvalueaxisangular_p.h b/src/axis/logvalueaxis/polarchartlogvalueaxisangular_p.h new file mode 100644 index 00000000..231172aa --- /dev/null +++ b/src/axis/logvalueaxis/polarchartlogvalueaxisangular_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTLOGVALUEAXISANGULAR_P_H +#define POLARCHARTLOGVALUEAXISANGULAR_P_H + +#include "polarchartaxisangular_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QLogValueAxis; + +class PolarChartLogValueAxisAngular : public PolarChartAxisAngular +{ + Q_OBJECT +public: + PolarChartLogValueAxisAngular(QLogValueAxis *axis, QGraphicsItem *item); + ~PolarChartLogValueAxisAngular(); + +protected: + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleBaseChanged(qreal base); + void handleLabelFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTLOGVALUEAXISANGULAR_P_H diff --git a/src/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp b/src/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp new file mode 100644 index 00000000..d0719dbc --- /dev/null +++ b/src/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartlogvalueaxisradial_p.h" +#include "abstractchartlayout_p.h" +#include "chartpresenter_p.h" +#include "qlogvalueaxis.h" +#include <qmath.h> +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartLogValueAxisRadial::PolarChartLogValueAxisRadial(QLogValueAxis *axis, QGraphicsItem *item) + : PolarChartAxisRadial(axis, item) +{ + QObject::connect(axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal))); + QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); +} + +PolarChartLogValueAxisRadial::~PolarChartLogValueAxisRadial() +{ +} + +QVector<qreal> PolarChartLogValueAxisRadial::calculateLayout() const +{ + QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis()); + const qreal logMax = log10(logValueAxis->max()) / log10(logValueAxis->base()); + const qreal logMin = log10(logValueAxis->min()) / log10(logValueAxis->base()); + const qreal innerEdge = logMin < logMax ? logMin : logMax; + const qreal outerEdge = logMin > logMax ? logMin : logMax; + const qreal delta = (axisGeometry().width() / 2.0) / qAbs(logMax - logMin); + const qreal initialSpan = (ceil(innerEdge) - innerEdge) * delta; + int tickCount = qAbs(ceil(logMax) - ceil(logMin)); + + // Extra tick if outer edge is exactly at the tick + if (outerEdge == ceil(outerEdge)) + tickCount++; + + QVector<qreal> points; + points.resize(tickCount); + + for (int i = 0; i < tickCount; ++i) { + qreal radialCoordinate = initialSpan + (delta * qreal(i)); + points[i] = radialCoordinate; + } + + return points; +} + +void PolarChartLogValueAxisRadial::createAxisLabels(const QVector<qreal> &layout) +{ + QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis()); + setLabels(createLogValueLabels(logValueAxis->min(), + logValueAxis->max(), + logValueAxis->base(), + layout.size(), + logValueAxis->labelFormat())); +} + +void PolarChartLogValueAxisRadial::handleBaseChanged(qreal base) +{ + Q_UNUSED(base); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartLogValueAxisRadial::handleLabelFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartlogvalueaxisradial_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/logvalueaxis/polarchartlogvalueaxisradial_p.h b/src/axis/logvalueaxis/polarchartlogvalueaxisradial_p.h new file mode 100644 index 00000000..bc7e8982 --- /dev/null +++ b/src/axis/logvalueaxis/polarchartlogvalueaxisradial_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTLOGVALUEAXISRADIAL_P_H +#define POLARCHARTLOGVALUEAXISRADIAL_P_H + +#include "polarchartaxisradial_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QLogValueAxis; + +class PolarChartLogValueAxisRadial : public PolarChartAxisRadial +{ + Q_OBJECT +public: + PolarChartLogValueAxisRadial(QLogValueAxis *axis, QGraphicsItem *item); + ~PolarChartLogValueAxisRadial(); + +protected: + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleBaseChanged(qreal base); + void handleLabelFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTLOGVALUEAXISRADIAL_P_H diff --git a/src/axis/logvalueaxis/qlogvalueaxis.cpp b/src/axis/logvalueaxis/qlogvalueaxis.cpp index 12170b8b..27896b51 100644 --- a/src/axis/logvalueaxis/qlogvalueaxis.cpp +++ b/src/axis/logvalueaxis/qlogvalueaxis.cpp @@ -22,6 +22,8 @@ #include "qlogvalueaxis_p.h" #include "chartlogvalueaxisx_p.h" #include "chartlogvalueaxisy_p.h" +#include "polarchartlogvalueaxisangular_p.h" +#include "polarchartlogvalueaxisradial_p.h" #include "abstractdomain_p.h" #include <float.h> #include <cmath> @@ -287,14 +289,24 @@ void QLogValueAxisPrivate::setRange(qreal min, qreal max) } } -void QLogValueAxisPrivate::initializeGraphics(QGraphicsItem* parent) +void QLogValueAxisPrivate::initializeGraphics(QGraphicsItem *parent) { Q_Q(QLogValueAxis); - ChartAxis* axis(0); - if (orientation() == Qt::Vertical) - axis = new ChartLogValueAxisY(q,parent); - if (orientation() == Qt::Horizontal) - axis = new ChartLogValueAxisX(q,parent); + ChartAxisElement *axis(0); + + if (m_chart->chartType() == QChart::ChartTypeCartesian) { + if (orientation() == Qt::Vertical) + axis = new ChartLogValueAxisY(q,parent); + if (orientation() == Qt::Horizontal) + axis = new ChartLogValueAxisX(q,parent); + } + + if (m_chart->chartType() == QChart::ChartTypePolar) { + if (orientation() == Qt::Vertical) + axis = new PolarChartLogValueAxisRadial(q, parent); + if (orientation() == Qt::Horizontal) + axis = new PolarChartLogValueAxisAngular(q, parent); + } m_item.reset(axis); QAbstractAxisPrivate::initializeGraphics(parent); @@ -304,7 +316,7 @@ void QLogValueAxisPrivate::initializeGraphics(QGraphicsItem* parent) void QLogValueAxisPrivate::initializeDomain(AbstractDomain *domain) { if (orientation() == Qt::Vertical) { - if(!qFuzzyCompare(m_max, m_min)) { + if (!qFuzzyCompare(m_max, m_min)) { domain->setRangeY(m_min, m_max); } else if ( domain->minY() > 0) { setRange(domain->minY(), domain->maxY()); @@ -315,7 +327,7 @@ void QLogValueAxisPrivate::initializeDomain(AbstractDomain *domain) } } if (orientation() == Qt::Horizontal) { - if(!qFuzzyCompare(m_max, m_min)) { + if (!qFuzzyCompare(m_max, m_min)) { domain->setRangeX(m_min, m_max); } else if (domain->minX() > 0){ setRange(domain->minX(), domain->maxX()); diff --git a/src/axis/polarchartaxis.cpp b/src/axis/polarchartaxis.cpp new file mode 100644 index 00000000..8bb80420 --- /dev/null +++ b/src/axis/polarchartaxis.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartaxis_p.h" +#include "qabstractaxis_p.h" +#include "chartpresenter_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartAxis::PolarChartAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : ChartAxisElement(axis, item, intervalAxis) +{ +} + +PolarChartAxis::~PolarChartAxis() +{ + +} + +void PolarChartAxis::setGeometry(const QRectF &axis, const QRectF &grid) +{ + Q_UNUSED(grid); + setAxisGeometry(axis); + + if (isEmpty()) + return; + + QVector<qreal> layout = calculateLayout(); + updateLayout(layout); +} + +QRectF PolarChartAxis::gridGeometry() const +{ + return QRectF(); +} + +void PolarChartAxis::updateLayout(QVector<qreal> &layout) +{ + int diff = ChartAxisElement::layout().size() - layout.size(); + + if (animation()) { + switch (presenter()->state()) { + case ChartPresenter::ShowState: + animation()->setAnimationType(AxisAnimation::DefaultAnimation); + break; + } + // Update to "old" geometry before starting animation to avoid incorrectly sized + // axes lingering in wrong position compared to series plot before animation can kick in. + // Note that the position mismatch still exists even with this update, but it will be + // far less ugly. + updateGeometry(); + } + + if (diff > 0) + deleteItems(diff); + else if (diff < 0) + createItems(-diff); + + if (animation()) { + animation()->setValues(ChartAxisElement::layout(), layout); + presenter()->startAnimation(animation()); + } else { + setLayout(layout); + updateGeometry(); + } +} + +bool PolarChartAxis::isEmpty() +{ + return !axisGeometry().isValid() || qFuzzyIsNull(min() - max()); +} + +void PolarChartAxis::deleteItems(int count) +{ + QList<QGraphicsItem *> gridLines = gridItems(); + QList<QGraphicsItem *> labels = labelItems(); + QList<QGraphicsItem *> shades = shadeItems(); + QList<QGraphicsItem *> axis = arrowItems(); + + for (int i = 0; i < count; ++i) { + if (gridItems().size() == 1 || (((gridLines.size() + 1) % 2) && gridLines.size() > 0)) + delete(shades.takeLast()); + delete(gridLines.takeLast()); + delete(labels.takeLast()); + delete(axis.takeLast()); + } +} + +void PolarChartAxis::handleShadesBrushChanged(const QBrush &brush) +{ + foreach (QGraphicsItem *item, shadeItems()) + static_cast<QGraphicsPathItem *>(item)->setBrush(brush); +} + +void PolarChartAxis::handleShadesPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, shadeItems()) + static_cast<QGraphicsPathItem *>(item)->setPen(pen); +} + +#include "moc_polarchartaxis_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/polarchartaxis_p.h b/src/axis/polarchartaxis_p.h new file mode 100644 index 00000000..605b8fee --- /dev/null +++ b/src/axis/polarchartaxis_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTAXIS_P_H +#define POLARCHARTAXIS_P_H + +#include "chartaxiselement_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class PolarChartAxis : public ChartAxisElement +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) +public: + PolarChartAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false); + ~PolarChartAxis(); + + void setGeometry(const QRectF &axis, const QRectF &grid); + virtual qreal preferredAxisRadius(const QSizeF &maxSize) = 0; + int tickWidth() { return 3; } + +public: // from ChartAxisElement + QRectF gridGeometry() const; + bool isEmpty(); + +protected: + void updateLayout(QVector<qreal> &layout); + +protected: // virtual functions + virtual void createItems(int count) = 0; + virtual void createAxisLabels(const QVector<qreal> &layout) = 0; + +public Q_SLOTS: + virtual void handleShadesBrushChanged(const QBrush &brush); + virtual void handleShadesPenChanged(const QPen &pen); + +private: + void deleteItems(int count); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTAXIS_P_H diff --git a/src/axis/polarchartaxisangular.cpp b/src/axis/polarchartaxisangular.cpp new file mode 100644 index 00000000..8eed1f9d --- /dev/null +++ b/src/axis/polarchartaxisangular.cpp @@ -0,0 +1,428 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartaxisangular_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qabstractaxis.h" +#include "qabstractaxis_p.h" +#include <QFontMetrics> +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : PolarChartAxis(axis, item, intervalAxis) +{ +} + +PolarChartAxisAngular::~PolarChartAxisAngular() +{ +} + +void PolarChartAxisAngular::updateGeometry() +{ + QGraphicsLayoutItem::updateGeometry(); + + const QVector<qreal> &layout = this->layout(); + if (layout.isEmpty()) + return; + + createAxisLabels(layout); + QStringList labelList = labels(); + QPointF center = axisGeometry().center(); + QList<QGraphicsItem *> arrowItemList = arrowItems(); + QList<QGraphicsItem *> gridItemList = gridItems(); + QList<QGraphicsItem *> labelItemList = labelItems(); + QList<QGraphicsItem *> shadeItemList = shadeItems(); + QGraphicsSimpleTextItem *title = titleItem(); + + QGraphicsEllipseItem *axisLine = static_cast<QGraphicsEllipseItem *>(arrowItemList.at(0)); + axisLine->setRect(axisGeometry()); + + qreal radius = axisGeometry().height() / 2.0; + + QFontMetrics fn(axis()->labelsFont()); + QRectF previousLabelRect; + QRectF firstLabelRect; + + qreal labelHeight = 0; + + bool firstShade = true; + bool nextTickVisible = false; + if (layout.size()) + nextTickVisible = !(layout.at(0) < 0.0 || layout.at(0) > 360.0); + + for (int i = 0; i < layout.size(); ++i) { + qreal angularCoordinate = layout.at(i); + + QGraphicsLineItem *gridLineItem = static_cast<QGraphicsLineItem *>(gridItemList.at(i)); + QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i + 1)); + QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem *>(labelItemList.at(i)); + QGraphicsPathItem *shadeItem = 0; + if (i == 0) + shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0)); + else if (i % 2) + shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at((i / 2) + 1)); + + // Ignore ticks outside valid range + bool currentTickVisible = nextTickVisible; + if ((i == layout.size() - 1) + || layout.at(i + 1) < 0.0 + || layout.at(i + 1) > 360.0) { + nextTickVisible = false; + } else { + nextTickVisible = true; + } + + qreal labelCoordinate = angularCoordinate; + qreal labelVisible = currentTickVisible; + if (intervalAxis()) { + qreal farEdge; + if (i == (layout.size() - 1)) + farEdge = 360.0; + else + farEdge = qMin(360.0, layout.at(i + 1)); + + // Adjust the labelCoordinate to show it if next tick is visible + if (nextTickVisible) + labelCoordinate = qMax(0.0, labelCoordinate); + + labelCoordinate = (labelCoordinate + farEdge) / 2.0; + // Don't display label once the category gets too small near the axis + if (labelCoordinate < 5.0 || labelCoordinate > 355.0) + labelVisible = false; + else + labelVisible = true; + } + + // Need this also in label calculations, so determine it first + QLineF tickLine(QLineF::fromPolar(radius - tickWidth(), 90.0 - angularCoordinate).p2(), + QLineF::fromPolar(radius + tickWidth(), 90.0 - angularCoordinate).p2()); + tickLine.translate(center); + + // Angular axis label + if (axis()->labelsVisible() && labelVisible) { + labelItem->setText(labelList.at(i)); + const QRectF &rect = labelItem->boundingRect(); + QPointF labelCenter = rect.center(); + labelItem->setTransformOriginPoint(labelCenter.x(), labelCenter.y()); + QRectF boundingRect = labelBoundingRect(fn, labelList.at(i)); + boundingRect.moveCenter(labelCenter); + QPointF positionDiff(rect.topLeft() - boundingRect.topLeft()); + + QPointF labelPoint; + if (intervalAxis()) { + QLineF labelLine = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate); + labelLine.translate(center); + labelPoint = labelLine.p2(); + } else { + labelPoint = tickLine.p2(); + } + + QRectF labelRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect); + labelItem->setPos(labelRect.topLeft() + positionDiff); + + // Store height for title calculations + qreal labelClearance = axisGeometry().top() - labelRect.top(); + labelHeight = qMax(labelHeight, labelClearance); + + // Label overlap detection + if (i && (previousLabelRect.intersects(labelRect) || firstLabelRect.intersects(labelRect))) { + labelVisible = false; + } else { + // Store labelRect for future comparison. Some area is deducted to make things look + // little nicer, as usually intersection happens at label corner with angular labels. + labelRect.adjust(-2.0, -4.0, -2.0, -4.0); + if (firstLabelRect.isEmpty()) + firstLabelRect = labelRect; + + previousLabelRect = labelRect; + labelVisible = true; + } + } + + labelItem->setVisible(labelVisible); + if (!currentTickVisible) { + gridLineItem->setVisible(false); + tickItem->setVisible(false); + if (shadeItem) + shadeItem->setVisible(false); + continue; + } + + // Angular grid line + QLineF gridLine = QLineF::fromPolar(radius, 90.0 - angularCoordinate); + gridLine.translate(center); + gridLineItem->setLine(gridLine); + gridLineItem->setVisible(true); + + // Tick + tickItem->setLine(tickLine); + tickItem->setVisible(true); + + // Shades + if (i % 2 || (i == 0 && !nextTickVisible)) { + QPainterPath path; + path.moveTo(center); + if (i == 0) { + // If first tick is also the last, we need to custom fill the first partial arc + // or it won't get filled. + path.arcTo(axisGeometry(), 90.0 - layout.at(0), layout.at(0)); + path.closeSubpath(); + } else { + qreal nextCoordinate; + if (!nextTickVisible) // Last visible tick + nextCoordinate = 360.0; + else + nextCoordinate = layout.at(i + 1); + qreal arcSpan = angularCoordinate - nextCoordinate; + path.arcTo(axisGeometry(), 90.0 - angularCoordinate, arcSpan); + path.closeSubpath(); + + // Add additional arc for first shade item if there is a partial arc to be filled + if (firstShade) { + QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0)); + if (layout.at(i - 1) > 0.0) { + QPainterPath specialPath; + specialPath.moveTo(center); + specialPath.arcTo(axisGeometry(), 90.0 - layout.at(i - 1), layout.at(i - 1)); + specialPath.closeSubpath(); + specialShadeItem->setPath(specialPath); + specialShadeItem->setVisible(true); + } else { + specialShadeItem->setVisible(false); + } + } + } + shadeItem->setPath(path); + shadeItem->setVisible(true); + firstShade = false; + } + } + + // Title, centered above the chart + QString titleText = axis()->titleText(); + if (!titleText.isEmpty() && axis()->isTitleVisible()) { + int size(0); + size = axisGeometry().width(); + + QFontMetrics titleMetrics(axis()->titleFont()); + if (titleMetrics.boundingRect(titleText).width() > size) { + QString string = titleText + "..."; + while (titleMetrics.boundingRect(string).width() > size && string.length() > 3) + string.remove(string.length() - 4, 1); + title->setText(string); + } else { + title->setText(titleText); + } + + QRectF titleBoundingRect; + titleBoundingRect = title->boundingRect(); + QPointF titleCenter = center - titleBoundingRect.center(); + title->setPos(titleCenter.x(), axisGeometry().top() - qreal(titlePadding()) * 2.0 - titleBoundingRect.height() - labelHeight); + } +} + +Qt::Orientation PolarChartAxisAngular::orientation() const +{ + return Qt::Horizontal; +} + +void PolarChartAxisAngular::createItems(int count) +{ + if (arrowItems().count() == 0) { + // angular axis line + // TODO: need class similar to LineArrowItem for click handling? + QGraphicsEllipseItem *arrow = new QGraphicsEllipseItem(presenter()->rootItem()); + arrow->setPen(axis()->linePen()); + arrowGroup()->addToGroup(arrow); + } + + for (int i = 0; i < count; ++i) { + QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem()); + QGraphicsLineItem *grid = new QGraphicsLineItem(presenter()->rootItem()); + QGraphicsSimpleTextItem *label = new QGraphicsSimpleTextItem(presenter()->rootItem()); + QGraphicsSimpleTextItem *title = titleItem(); + arrow->setPen(axis()->linePen()); + grid->setPen(axis()->gridLinePen()); + label->setFont(axis()->labelsFont()); + label->setPen(axis()->labelsPen()); + label->setBrush(axis()->labelsBrush()); + label->setRotation(axis()->labelsAngle()); + title->setFont(axis()->titleFont()); + title->setPen(axis()->titlePen()); + title->setBrush(axis()->titleBrush()); + title->setText(axis()->titleText()); + arrowGroup()->addToGroup(arrow); + gridGroup()->addToGroup(grid); + labelGroup()->addToGroup(label); + if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) { + QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem()); + shade->setPen(axis()->shadesPen()); + shade->setBrush(axis()->shadesBrush()); + shadeGroup()->addToGroup(shade); + } + } +} + +void PolarChartAxisAngular::handleArrowPenChanged(const QPen &pen) +{ + bool first = true; + foreach (QGraphicsItem *item, arrowItems()) { + if (first) { + first = false; + // First arrow item is the outer circle of axis + static_cast<QGraphicsEllipseItem *>(item)->setPen(pen); + } else { + static_cast<QGraphicsLineItem *>(item)->setPen(pen); + } + } +} + +void PolarChartAxisAngular::handleGridPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, gridItems()) + static_cast<QGraphicsLineItem *>(item)->setPen(pen); +} + +QSizeF PolarChartAxisAngular::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(-1, -1); +} + +qreal PolarChartAxisAngular::preferredAxisRadius(const QSizeF &maxSize) +{ + qreal radius = maxSize.height() / 2.0; + if (maxSize.width() < maxSize.height()) + radius = maxSize.width() / 2.0; + + int labelHeight = 0; + + if (axis()->labelsVisible()) { + QVector<qreal> layout = calculateLayout(); + if (layout.isEmpty()) + return radius; + + createAxisLabels(layout); + QStringList labelList = labels(); + QFont font = axis()->labelsFont(); + + QRectF maxRect; + maxRect.setSize(maxSize); + maxRect.moveCenter(QPointF(0.0, 0.0)); + + // This is a horrible way to find out the maximum radius for angular axis and its labels. + // It just increments the radius down until everyhing fits the constraint size. + // Proper way would be to actually calculate it but this seems to work reasonably fast as it is. + QFontMetrics fm(font); + bool nextTickVisible = false; + for (int i = 0; i < layout.size(); ) { + if ((i == layout.size() - 1) + || layout.at(i + 1) < 0.0 + || layout.at(i + 1) > 360.0) { + nextTickVisible = false; + } else { + nextTickVisible = true; + } + + qreal labelCoordinate = layout.at(i); + qreal labelVisible; + + if (intervalAxis()) { + qreal farEdge; + if (i == (layout.size() - 1)) + farEdge = 360.0; + else + farEdge = qMin(360.0, layout.at(i + 1)); + + // Adjust the labelCoordinate to show it if next tick is visible + if (nextTickVisible) + labelCoordinate = qMax(0.0, labelCoordinate); + + labelCoordinate = (labelCoordinate + farEdge) / 2.0; + } + + if (labelCoordinate < 0.0 || labelCoordinate > 360.0) + labelVisible = false; + else + labelVisible = true; + + if (!labelVisible) { + i++; + continue; + } + + QRectF boundingRect = labelBoundingRect(fm, labelList.at(i)); + labelHeight = boundingRect.height(); + QPointF labelPoint = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate).p2(); + + boundingRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect); + if (boundingRect.isEmpty() || maxRect.intersected(boundingRect) == boundingRect) { + i++; + } else { + radius -= 1.0; + if (radius < 1.0) // safeguard + return 1.0; + } + } + } + + if (!axis()->titleText().isEmpty() && axis()->isTitleVisible()) { + QFontMetrics titleMetrics(axis()->titleFont()); + int titleHeight = titleMetrics.boundingRect(axis()->titleText()).height(); + radius -= titlePadding() + (titleHeight / 2); + if (radius < 1.0) // safeguard + return 1.0; + } + + return radius; +} + +QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const +{ + // TODO use fuzzy compare for "==" cases? + // TODO Adjust the rect position near 0, 90, 180, and 270 angles for smoother animation? + if (angularCoordinate == 0.0) + labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0)); + else if (angularCoordinate < 90.0) + labelRect.moveBottomLeft(labelPoint); + else if (angularCoordinate == 90.0) + labelRect.moveCenter(labelPoint + QPointF(labelRect.width() / 2.0 + 2.0, 0)); // +2 so that it does not hit the radial axis + else if (angularCoordinate < 180.0) + labelRect.moveTopLeft(labelPoint); + else if (angularCoordinate == 180.0) + labelRect.moveCenter(labelPoint + QPointF(0, labelRect.height() / 2.0)); + else if (angularCoordinate < 270.0) + labelRect.moveTopRight(labelPoint); + else if (angularCoordinate == 270.0) + labelRect.moveCenter(labelPoint + QPointF(-labelRect.width() / 2.0 - 2.0, 0)); // -2 so that it does not hit the radial axis + else if (angularCoordinate < 360.0) + labelRect.moveBottomRight(labelPoint); + else + labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0)); + return labelRect; +} + +#include "moc_polarchartaxisangular_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/polarchartaxisangular_p.h b/src/axis/polarchartaxisangular_p.h new file mode 100644 index 00000000..7e7b292d --- /dev/null +++ b/src/axis/polarchartaxisangular_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTAXISANGULAR_P_H +#define POLARCHARTAXISANGULAR_P_H + +#include "polarchartaxis_p.h" +#include "qvalueaxis.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class PolarChartAxisAngular : public PolarChartAxis +{ + Q_OBJECT +public: + PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false); + ~PolarChartAxisAngular(); + + Qt::Orientation orientation() const; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + virtual void updateGeometry(); + virtual void createItems(int count); + + qreal preferredAxisRadius(const QSizeF &maxSize); + +public Q_SLOTS: + virtual void handleArrowPenChanged(const QPen &pen); + virtual void handleGridPenChanged(const QPen &pen); + +private: + QRectF moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTAXISANGULAR_P_H diff --git a/src/axis/polarchartaxisradial.cpp b/src/axis/polarchartaxisradial.cpp new file mode 100644 index 00000000..55923985 --- /dev/null +++ b/src/axis/polarchartaxisradial.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartaxisradial_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include "qabstractaxis_p.h" +#include "linearrowitem_p.h" +#include <QFontMetrics> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartAxisRadial::PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : PolarChartAxis(axis, item, intervalAxis) +{ +} + +PolarChartAxisRadial::~PolarChartAxisRadial() +{ +} + +void PolarChartAxisRadial::updateGeometry() +{ + const QVector<qreal> &layout = this->layout(); + if (layout.isEmpty()) + return; + + createAxisLabels(layout); + QStringList labelList = labels(); + QPointF center = axisGeometry().center(); + QList<QGraphicsItem *> arrowItemList = arrowItems(); + QList<QGraphicsItem *> gridItemList = gridItems(); + QList<QGraphicsItem *> labelItemList = labelItems(); + QList<QGraphicsItem *> shadeItemList = shadeItems(); + QGraphicsSimpleTextItem* title = titleItem(); + qreal radius = axisGeometry().height() / 2.0; + + QLineF line(center, center + QPointF(0, -radius)); + QGraphicsLineItem *axisLine = static_cast<QGraphicsLineItem *>(arrowItemList.at(0)); + axisLine->setLine(line); + + QFontMetrics fn(axis()->labelsFont()); + QRectF previousLabelRect; + bool firstShade = true; + bool nextTickVisible = false; + if (layout.size()) + nextTickVisible = !(layout.at(0) < 0.0 || layout.at(0) > radius); + + for (int i = 0; i < layout.size(); ++i) { + qreal radialCoordinate = layout.at(i); + + QGraphicsEllipseItem *gridItem = static_cast<QGraphicsEllipseItem *>(gridItemList.at(i)); + QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i + 1)); + QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem *>(labelItemList.at(i)); + QGraphicsPathItem *shadeItem = 0; + if (i == 0) + shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0)); + else if (i % 2) + shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at((i / 2) + 1)); + + // Ignore ticks outside valid range + bool currentTickVisible = nextTickVisible; + if ((i == layout.size() - 1) + || layout.at(i + 1) < 0.0 + || layout.at(i + 1) > radius) { + nextTickVisible = false; + } else { + nextTickVisible = true; + } + + qreal labelCoordinate = radialCoordinate; + qreal labelVisible = currentTickVisible; + qreal labelPad = labelPadding() / 2.0; + if (intervalAxis()) { + qreal farEdge; + if (i == (layout.size() - 1)) + farEdge = radius; + else + farEdge = qMin(radius, layout.at(i + 1)); + + // Adjust the labelCoordinate to show it if next tick is visible + if (nextTickVisible) + labelCoordinate = qMax(0.0, labelCoordinate); + + labelCoordinate = (labelCoordinate + farEdge) / 2.0; + if (labelCoordinate > 0.0 && labelCoordinate < radius) + labelVisible = true; + else + labelVisible = false; + } + + // Radial axis label + if (axis()->labelsVisible() && labelVisible) { + labelItem->setText(labelList.at(i)); + QRectF labelRect = labelItem->boundingRect(); + QPointF labelCenter = labelRect.center(); + labelItem->setTransformOriginPoint(labelCenter.x(), labelCenter.y()); + QRectF boundingRect = labelBoundingRect(fn, labelList.at(i)); + boundingRect.moveCenter(labelCenter); + QPointF positionDiff(labelRect.topLeft() - boundingRect.topLeft()); + QPointF labelPoint = center; + if (intervalAxis()) + labelPoint += QPointF(labelPad, -labelCoordinate - (boundingRect.height() / 2.0)); + else + labelPoint += QPointF(labelPad, labelPad - labelCoordinate); + labelRect.moveTopLeft(labelPoint); + labelItem->setPos(labelRect.topLeft() + positionDiff); + + // Label overlap detection + labelRect.setSize(boundingRect.size()); + if ((i && previousLabelRect.intersects(labelRect)) + || !axisGeometry().contains(labelRect)) { + labelVisible = false; + } else { + previousLabelRect = labelRect; + labelVisible = true; + } + } + + labelItem->setVisible(labelVisible); + if (!currentTickVisible) { + gridItem->setVisible(false); + tickItem->setVisible(false); + if (shadeItem) + shadeItem->setVisible(false); + continue; + } + + // Radial grid line + QRectF gridRect; + gridRect.setWidth(radialCoordinate * 2.0); + gridRect.setHeight(radialCoordinate * 2.0); + gridRect.moveCenter(center); + + gridItem->setRect(gridRect); + gridItem->setVisible(true); + + // Tick + QLineF tickLine(-tickWidth(), 0.0, tickWidth(), 0.0); + tickLine.translate(center.rx(), gridRect.top()); + tickItem->setLine(tickLine); + tickItem->setVisible(true); + + // Shades + if (i % 2 || (i == 0 && !nextTickVisible)) { + QPainterPath path; + if (i == 0) { + // If first tick is also the last, we need to custom fill the inner circle + // or it won't get filled. + QRectF innerCircle(0.0, 0.0, layout.at(0) * 2.0, layout.at(0) * 2.0); + innerCircle.moveCenter(center); + path.addEllipse(innerCircle); + } else { + QRectF otherGridRect; + if (!nextTickVisible) { // Last visible tick + otherGridRect = axisGeometry(); + } else { + qreal otherGridRectDimension = layout.at(i + 1) * 2.0; + otherGridRect.setWidth(otherGridRectDimension); + otherGridRect.setHeight(otherGridRectDimension); + otherGridRect.moveCenter(center); + } + path.addEllipse(gridRect); + path.addEllipse(otherGridRect); + + // Add additional shading in first visible shade item if there is a partial tick + // to be filled at the center (log & category axes) + if (firstShade) { + QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0)); + if (layout.at(i - 1) > 0.0) { + QRectF innerCircle(0.0, 0.0, layout.at(i - 1) * 2.0, layout.at(i - 1) * 2.0); + innerCircle.moveCenter(center); + QPainterPath specialPath; + specialPath.addEllipse(innerCircle); + specialShadeItem->setPath(specialPath); + specialShadeItem->setVisible(true); + } else { + specialShadeItem->setVisible(false); + } + } + } + shadeItem->setPath(path); + shadeItem->setVisible(true); + firstShade = false; + } + } + + // Title, along the 0 axis + QString titleText = axis()->titleText(); + if (!titleText.isEmpty() && axis()->isTitleVisible()) { + QFontMetrics titleMetrics(axis()->titleFont()); + if (titleMetrics.boundingRect(titleText).width() > radius) { + QString string = titleText + "..."; + while (titleMetrics.boundingRect(string).width() > radius && string.length() > 3) + string.remove(string.length() - 4, 1); + title->setText(string); + } else { + title->setText(titleText); + } + + QRectF titleBoundingRect; + titleBoundingRect = title->boundingRect(); + QPointF titleCenter = titleBoundingRect.center(); + QPointF arrowCenter = axisLine->boundingRect().center(); + QPointF titleCenterDiff = arrowCenter - titleCenter; + title->setPos(titleCenterDiff.x() - qreal(titlePadding()) - (titleBoundingRect.height() / 2.0), titleCenterDiff.y()); + title->setTransformOriginPoint(titleCenter); + title->setRotation(270.0); + } + + QGraphicsLayoutItem::updateGeometry(); +} + +Qt::Orientation PolarChartAxisRadial::orientation() const +{ + return Qt::Vertical; +} + +void PolarChartAxisRadial::createItems(int count) +{ + if (arrowItems().count() == 0) { + // radial axis center line + QGraphicsLineItem *arrow = new LineArrowItem(this, presenter()->rootItem()); + arrow->setPen(axis()->linePen()); + arrowGroup()->addToGroup(arrow); + } + + for (int i = 0; i < count; ++i) { + QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem()); + QGraphicsEllipseItem *grid = new QGraphicsEllipseItem(presenter()->rootItem()); + QGraphicsSimpleTextItem *label = new QGraphicsSimpleTextItem(presenter()->rootItem()); + QGraphicsSimpleTextItem *title = titleItem(); + arrow->setPen(axis()->linePen()); + grid->setPen(axis()->gridLinePen()); + label->setFont(axis()->labelsFont()); + label->setPen(axis()->labelsPen()); + label->setBrush(axis()->labelsBrush()); + label->setRotation(axis()->labelsAngle()); + title->setFont(axis()->titleFont()); + title->setPen(axis()->titlePen()); + title->setBrush(axis()->titleBrush()); + title->setText(axis()->titleText()); + arrowGroup()->addToGroup(arrow); + gridGroup()->addToGroup(grid); + labelGroup()->addToGroup(label); + if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) { + QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem()); + shade->setPen(axis()->shadesPen()); + shade->setBrush(axis()->shadesBrush()); + shadeGroup()->addToGroup(shade); + } + } +} + +void PolarChartAxisRadial::handleArrowPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, arrowItems()) + static_cast<QGraphicsLineItem *>(item)->setPen(pen); +} + +void PolarChartAxisRadial::handleGridPenChanged(const QPen &pen) +{ + foreach (QGraphicsItem *item, gridItems()) + static_cast<QGraphicsEllipseItem *>(item)->setPen(pen); +} + +QSizeF PolarChartAxisRadial::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(-1.0, -1.0); +} + +qreal PolarChartAxisRadial::preferredAxisRadius(const QSizeF &maxSize) +{ + qreal radius = maxSize.height() / 2.0; + if (maxSize.width() < maxSize.height()) + radius = maxSize.width() / 2.0; + return radius; +} + +#include "moc_polarchartaxisradial_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/polarchartaxisradial_p.h b/src/axis/polarchartaxisradial_p.h new file mode 100644 index 00000000..9874f28a --- /dev/null +++ b/src/axis/polarchartaxisradial_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTAXISRADIAL_P_H +#define POLARCHARTAXISRADIAL_P_H + +#include "polarchartaxis_p.h" +#include "qvalueaxis.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class PolarChartAxisRadial : public PolarChartAxis +{ + Q_OBJECT +public: + PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false); + ~PolarChartAxisRadial(); + + Qt::Orientation orientation() const; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + virtual void updateGeometry(); + virtual void createItems(int count); + + qreal preferredAxisRadius(const QSizeF &maxSize); + +public Q_SLOTS: + virtual void handleArrowPenChanged(const QPen &pen); + virtual void handleGridPenChanged(const QPen &pen); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTAXISRADIAL_P_H diff --git a/src/axis/qabstractaxis.cpp b/src/axis/qabstractaxis.cpp index 60e071d6..77c89b53 100644 --- a/src/axis/qabstractaxis.cpp +++ b/src/axis/qabstractaxis.cpp @@ -254,11 +254,11 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE /*! \property QAbstractAxis::alignment - The alignment of the axis. Either Qt::AlignLeft or Qt::AlignBottom. + The alignment of the axis. Can be Qt::AlignLeft, Qt::AlignRight, Qt::AlignBottom, or Qt::AlignTop. */ /*! \qmlproperty alignment AbstractAxis::alignment - The alignment of the axis. Either Qt.AlignLeft or Qt.AlignBottom. + The alignment of the axis. Can be Qt.AlignLeft, Qt.AlignRight, Qt.AlignBottom, or Qt.AlignTop. */ /*! @@ -990,11 +990,11 @@ void QAbstractAxisPrivate::initializeGraphics(QGraphicsItem* parent) void QAbstractAxisPrivate::initializeAnimations(QChart::AnimationOptions options) { - ChartAxis* axis = m_item.data(); + ChartAxisElement *axis = m_item.data(); Q_ASSERT(axis); - if(options.testFlag(QChart::GridAxisAnimations)) { + if (options.testFlag(QChart::GridAxisAnimations)) { axis->setAnimation(new AxisAnimation(axis)); - }else{ + } else { axis->setAnimation(0); } } diff --git a/src/axis/qabstractaxis.h b/src/axis/qabstractaxis.h index 5ebd7974..e29325de 100644 --- a/src/axis/qabstractaxis.h +++ b/src/axis/qabstractaxis.h @@ -155,36 +155,37 @@ public: Q_SIGNALS: void visibleChanged(bool visible); - void linePenChanged(const QPen& pen); + void linePenChanged(const QPen &pen); void lineVisibleChanged(bool visible); void labelsVisibleChanged(bool visible); - void labelsPenChanged(const QPen& pen); - void labelsBrushChanged(const QBrush& brush); - void labelsFontChanged(const QFont& pen); + void labelsPenChanged(const QPen &pen); + void labelsBrushChanged(const QBrush &brush); + void labelsFontChanged(const QFont &pen); void labelsAngleChanged(int angle); - void gridLinePenChanged(const QPen& pen); + void gridLinePenChanged(const QPen &pen); void gridVisibleChanged(bool visible); void colorChanged(QColor color); void labelsColorChanged(QColor color); - void titleTextChanged(const QString& title); - void titlePenChanged(const QPen& pen); - void titleBrushChanged(const QBrush& brush); + void titleTextChanged(const QString &title); + void titlePenChanged(const QPen &pen); + void titleBrushChanged(const QBrush &brush); void titleVisibleChanged(bool visible); - void titleFontChanged(const QFont& font); + void titleFontChanged(const QFont &font); void shadesVisibleChanged(bool visible); void shadesColorChanged(QColor color); void shadesBorderColorChanged(QColor color); - void shadesPenChanged(const QPen& pen); - void shadesBrushChanged(const QBrush& brush); + void shadesPenChanged(const QPen &pen); + void shadesBrushChanged(const QBrush &brush); protected: QScopedPointer<QAbstractAxisPrivate> d_ptr; Q_DISABLE_COPY(QAbstractAxis) friend class ChartDataSet; - friend class ChartAxis; friend class ChartPresenter; friend class ChartThemeManager; friend class AbstractDomain; + friend class ChartAxisElement; + friend class XYChart; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/qabstractaxis_p.h b/src/axis/qabstractaxis_p.h index a284d46f..9ab993de 100644 --- a/src/axis/qabstractaxis_p.h +++ b/src/axis/qabstractaxis_p.h @@ -31,7 +31,7 @@ #define QABSTRACTAXIS_P_H #include "qabstractaxis.h" -#include "chartaxis_p.h" +#include "chartaxiselement_p.h" #include "qchart.h" #include <QDebug> @@ -59,7 +59,7 @@ public: void setAlignment( Qt::Alignment alignment); virtual void initializeDomain(AbstractDomain *domain) = 0; - virtual void initializeGraphics(QGraphicsItem* parent) = 0; + virtual void initializeGraphics(QGraphicsItem *parent) = 0; virtual void initializeTheme(ChartTheme* theme, bool forced = false); virtual void initializeAnimations(QChart::AnimationOptions options); @@ -73,7 +73,7 @@ public: virtual qreal min() = 0; virtual qreal max() = 0; - ChartAxis* axisItem() { return m_item.data(); } + ChartAxisElement *axisItem() { return m_item.data(); } public Q_SLOTS: void handleRangeChanged(qreal min, qreal max); @@ -84,7 +84,7 @@ Q_SIGNALS: protected: QAbstractAxis *q_ptr; QChart *m_chart; - QScopedPointer<ChartAxis> m_item; + QScopedPointer<ChartAxisElement> m_item; private: QList<QAbstractSeries*> m_series; diff --git a/src/axis/valueaxis/chartvalueaxisx.cpp b/src/axis/valueaxis/chartvalueaxisx.cpp index a084ac32..da6055f1 100644 --- a/src/axis/valueaxis/chartvalueaxisx.cpp +++ b/src/axis/valueaxis/chartvalueaxisx.cpp @@ -22,7 +22,7 @@ #include "qabstractaxis.h" #include "chartpresenter_p.h" #include "qvalueaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -31,12 +31,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartValueAxisX::ChartValueAxisX(QValueAxis *axis, QGraphicsItem* item ) +ChartValueAxisX::ChartValueAxisX(QValueAxis *axis, QGraphicsItem *item ) : HorizontalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis,SIGNAL(tickCountChanged(int)),this, SLOT(handleTickCountChanged(int))); - QObject::connect(m_axis,SIGNAL(labelFormatChanged(QString)),this, SLOT(handleLabelFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(m_axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); } ChartValueAxisX::~ChartValueAxisX() @@ -61,10 +61,10 @@ QVector<qreal> ChartValueAxisX::calculateLayout() const void ChartValueAxisX::updateGeometry() { - const QVector<qreal>& layout = ChartAxis::layout(); + const QVector<qreal>& layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; - setLabels(createValueLabels(min(),max(),layout.size(),m_axis->labelFormat())); + setLabels(createValueLabels(min(), max(), layout.size(), m_axis->labelFormat())); HorizontalAxis::updateGeometry(); } @@ -86,7 +86,7 @@ QSizeF ChartValueAxisX::sizeHint(Qt::SizeHint which, const QSizeF &constraint) c { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = HorizontalAxis::sizeHint(which, constraint); diff --git a/src/axis/valueaxis/chartvalueaxisx_p.h b/src/axis/valueaxis/chartvalueaxisx_p.h index e0556b60..bebfdb38 100644 --- a/src/axis/valueaxis/chartvalueaxisx_p.h +++ b/src/axis/valueaxis/chartvalueaxisx_p.h @@ -35,13 +35,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QValueAxis; -class ChartPresenter; class ChartValueAxisX : public HorizontalAxis { Q_OBJECT public: - ChartValueAxisX(QValueAxis *axis, QGraphicsItem* item = 0); + ChartValueAxisX(QValueAxis *axis, QGraphicsItem *item = 0); ~ChartValueAxisX(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; diff --git a/src/axis/valueaxis/chartvalueaxisy.cpp b/src/axis/valueaxis/chartvalueaxisy.cpp index 14b2d207..54b58242 100644 --- a/src/axis/valueaxis/chartvalueaxisy.cpp +++ b/src/axis/valueaxis/chartvalueaxisy.cpp @@ -22,7 +22,7 @@ #include "qabstractaxis.h" #include "chartpresenter_p.h" #include "qvalueaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include <QGraphicsLayout> #include <QFontMetrics> #include <qmath.h> @@ -30,12 +30,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartValueAxisY::ChartValueAxisY(QValueAxis *axis, QGraphicsItem* item) +ChartValueAxisY::ChartValueAxisY(QValueAxis *axis, QGraphicsItem *item) : VerticalAxis(axis, item), m_axis(axis) { - QObject::connect(m_axis,SIGNAL(tickCountChanged(int)),this, SLOT(handleTickCountChanged(int))); - QObject::connect(m_axis,SIGNAL(labelFormatChanged(QString)),this, SLOT(handleLabelFormatChanged(QString))); + QObject::connect(m_axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(m_axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); } ChartValueAxisY::~ChartValueAxisY() @@ -62,7 +62,7 @@ QVector<qreal> ChartValueAxisY::calculateLayout() const void ChartValueAxisY::updateGeometry() { - const QVector<qreal> &layout = ChartAxis::layout(); + const QVector<qreal> &layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; setLabels(createValueLabels(min(),max(),layout.size(),m_axis->labelFormat())); @@ -87,7 +87,7 @@ QSizeF ChartValueAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint) c { Q_UNUSED(constraint) - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); QSizeF sh; QSizeF base = VerticalAxis::sizeHint(which, constraint); QStringList ticksList = createValueLabels(min(),max(),m_axis->tickCount(),m_axis->labelFormat()); diff --git a/src/axis/valueaxis/chartvalueaxisy_p.h b/src/axis/valueaxis/chartvalueaxisy_p.h index 5a01b9b2..3b586698 100644 --- a/src/axis/valueaxis/chartvalueaxisy_p.h +++ b/src/axis/valueaxis/chartvalueaxisy_p.h @@ -35,13 +35,12 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE class QValueAxis; -class ChartPresenter; class ChartValueAxisY : public VerticalAxis { Q_OBJECT public: - ChartValueAxisY(QValueAxis *axis, QGraphicsItem* item = 0); + ChartValueAxisY(QValueAxis *axis, QGraphicsItem *item = 0); ~ChartValueAxisY(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; diff --git a/src/axis/valueaxis/polarchartvalueaxisangular.cpp b/src/axis/valueaxis/polarchartvalueaxisangular.cpp new file mode 100644 index 00000000..5fdbcb4b --- /dev/null +++ b/src/axis/valueaxis/polarchartvalueaxisangular.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartvalueaxisangular_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartValueAxisAngular::PolarChartValueAxisAngular(QValueAxis *axis, QGraphicsItem *item) + : PolarChartAxisAngular(axis, item) +{ + QObject::connect(axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); +} + +PolarChartValueAxisAngular::~PolarChartValueAxisAngular() +{ +} + +QVector<qreal> PolarChartValueAxisAngular::calculateLayout() const +{ + int tickCount = static_cast<QValueAxis *>(axis())->tickCount(); + Q_ASSERT(tickCount >= 2); + + QVector<qreal> points; + points.resize(tickCount); + + const qreal d = 360.0 / qreal(tickCount - 1); + + for (int i = 0; i < tickCount; ++i) { + qreal angularCoordinate = qreal(i) * d; + points[i] = angularCoordinate; + } + + return points; +} + +void PolarChartValueAxisAngular::createAxisLabels(const QVector<qreal> &layout) +{ + QStringList labelList = createValueLabels(min(), max(), layout.size(), static_cast<QValueAxis *>(axis())->labelFormat()); + setLabels(labelList); +} + +void PolarChartValueAxisAngular::handleTickCountChanged(int tick) +{ + Q_UNUSED(tick); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartValueAxisAngular::handleLabelFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartvalueaxisangular_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/valueaxis/polarchartvalueaxisangular_p.h b/src/axis/valueaxis/polarchartvalueaxisangular_p.h new file mode 100644 index 00000000..afe5192a --- /dev/null +++ b/src/axis/valueaxis/polarchartvalueaxisangular_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTVALUEAXISANGULAR_P_H +#define POLARCHARTVALUEAXISANGULAR_P_H + +#include "polarchartaxisangular_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QValueAxis; + +class PolarChartValueAxisAngular : public PolarChartAxisAngular +{ + Q_OBJECT +public: + PolarChartValueAxisAngular(QValueAxis *axis, QGraphicsItem *item); + ~PolarChartValueAxisAngular(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleTickCountChanged(int tick); + void handleLabelFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTVALUEAXISANGULAR_P_H diff --git a/src/axis/valueaxis/polarchartvalueaxisradial.cpp b/src/axis/valueaxis/polarchartvalueaxisradial.cpp new file mode 100644 index 00000000..f11e9265 --- /dev/null +++ b/src/axis/valueaxis/polarchartvalueaxisradial.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartvalueaxisradial_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarChartValueAxisRadial::PolarChartValueAxisRadial(QValueAxis *axis, QGraphicsItem *item) + : PolarChartAxisRadial(axis, item) +{ + QObject::connect(axis, SIGNAL(tickCountChanged(int)), this, SLOT(handleTickCountChanged(int))); + QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString))); +} + +PolarChartValueAxisRadial::~PolarChartValueAxisRadial() +{ +} + +QVector<qreal> PolarChartValueAxisRadial::calculateLayout() const +{ + int tickCount = static_cast<QValueAxis *>(axis())->tickCount(); + Q_ASSERT(tickCount >= 2); + + QVector<qreal> points; + points.resize(tickCount); + + const qreal d = (axisGeometry().width() / 2) / qreal(tickCount - 1); + + for (int i = 0; i < tickCount; ++i) { + qreal radialCoordinate = qreal(i) * d; + points[i] = radialCoordinate; + } + + return points; +} + +void PolarChartValueAxisRadial::createAxisLabels(const QVector<qreal> &layout) +{ + setLabels(createValueLabels(min(), max(), layout.size(), static_cast<QValueAxis *>(axis())->labelFormat())); +} + +void PolarChartValueAxisRadial::handleTickCountChanged(int tick) +{ + Q_UNUSED(tick); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +void PolarChartValueAxisRadial::handleLabelFormatChanged(const QString &format) +{ + Q_UNUSED(format); + QGraphicsLayoutItem::updateGeometry(); + if (presenter()) + presenter()->layout()->invalidate(); +} + +#include "moc_polarchartvalueaxisradial_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/axis/valueaxis/polarchartvalueaxisradial_p.h b/src/axis/valueaxis/polarchartvalueaxisradial_p.h new file mode 100644 index 00000000..f443d1dc --- /dev/null +++ b/src/axis/valueaxis/polarchartvalueaxisradial_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTVALUEAXISRADIAL_P_H +#define POLARCHARTVALUEAXISRADIAL_P_H + +#include "polarchartaxisradial_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QValueAxis; + +class PolarChartValueAxisRadial : public PolarChartAxisRadial +{ + Q_OBJECT +public: + PolarChartValueAxisRadial(QValueAxis *axis, QGraphicsItem *item); + ~PolarChartValueAxisRadial(); + + virtual QVector<qreal> calculateLayout() const; + virtual void createAxisLabels(const QVector<qreal> &layout); + +private Q_SLOTS: + void handleTickCountChanged(int tick); + void handleLabelFormatChanged(const QString &format); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTVALUEAXISRADIAL_P_H diff --git a/src/axis/valueaxis/qvalueaxis.cpp b/src/axis/valueaxis/qvalueaxis.cpp index 032fb4e1..80aa25ce 100644 --- a/src/axis/valueaxis/qvalueaxis.cpp +++ b/src/axis/valueaxis/qvalueaxis.cpp @@ -23,6 +23,8 @@ #include "chartvalueaxisx_p.h" #include "chartvalueaxisy_p.h" #include "abstractdomain_p.h" +#include "polarchartvalueaxisangular_p.h" +#include "polarchartvalueaxisradial_p.h" #include "chartdataset_p.h" #include "chartpresenter_p.h" #include "charttheme_p.h" @@ -387,14 +389,24 @@ void QValueAxisPrivate::setRange(qreal min, qreal max) } } -void QValueAxisPrivate::initializeGraphics(QGraphicsItem* parent) +void QValueAxisPrivate::initializeGraphics(QGraphicsItem *parent) { Q_Q(QValueAxis); - ChartAxis* axis(0); - if (orientation() == Qt::Vertical) - axis = new ChartValueAxisY(q,parent); - if (orientation() == Qt::Horizontal) - axis = new ChartValueAxisX(q,parent); + ChartAxisElement *axis(0); + + if (m_chart->chartType() == QChart::ChartTypeCartesian) { + if (orientation() == Qt::Vertical) + axis = new ChartValueAxisY(q,parent); + if (orientation() == Qt::Horizontal) + axis = new ChartValueAxisX(q,parent); + } + + if (m_chart->chartType() == QChart::ChartTypePolar) { + if (orientation() == Qt::Vertical) + axis = new PolarChartValueAxisRadial(q, parent); + if (orientation() == Qt::Horizontal) + axis = new PolarChartValueAxisAngular(q, parent); + } m_item.reset(axis); QAbstractAxisPrivate::initializeGraphics(parent); @@ -404,20 +416,16 @@ void QValueAxisPrivate::initializeGraphics(QGraphicsItem* parent) void QValueAxisPrivate::initializeDomain(AbstractDomain *domain) { if (orientation() == Qt::Vertical) { - if(!qFuzzyIsNull(m_max - m_min)) { + if (!qFuzzyIsNull(m_max - m_min)) domain->setRangeY(m_min, m_max); - } - else { + else setRange(domain->minY(), domain->maxY()); - } } if (orientation() == Qt::Horizontal) { - if(!qFuzzyIsNull(m_max - m_min)) { + if (!qFuzzyIsNull(m_max - m_min)) domain->setRangeX(m_min, m_max); - } - else { + else setRange(domain->minX(), domain->maxX()); - } } } diff --git a/src/axis/verticalaxis.cpp b/src/axis/verticalaxis.cpp index d3c5efa6..3726359b 100644 --- a/src/axis/verticalaxis.cpp +++ b/src/axis/verticalaxis.cpp @@ -25,31 +25,29 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -VerticalAxis::VerticalAxis(QAbstractAxis *axis, QGraphicsItem* item, bool intervalAxis) - : ChartAxis(axis, item, intervalAxis) +VerticalAxis::VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : CartesianChartAxis(axis, item, intervalAxis) { - } VerticalAxis::~VerticalAxis() { - } void VerticalAxis::updateGeometry() { - const QVector<qreal> &layout = ChartAxis::layout(); + const QVector<qreal> &layout = ChartAxisElement::layout(); if (layout.isEmpty()) return; QStringList labelList = labels(); - QList<QGraphicsItem *> lines = lineItems(); + QList<QGraphicsItem *> lines = gridItems(); QList<QGraphicsItem *> labels = labelItems(); QList<QGraphicsItem *> shades = shadeItems(); - QList<QGraphicsItem *> axis = arrowItems(); - QGraphicsSimpleTextItem* title = titleItem(); + QList<QGraphicsItem *> arrow = arrowItems(); + QGraphicsSimpleTextItem *title = titleItem(); Q_ASSERT(labels.size() == labelList.size()); Q_ASSERT(layout.size() == labelList.size()); @@ -61,32 +59,31 @@ void VerticalAxis::updateGeometry() //arrow - QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem*>(axis.at(0)); + QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem*>(arrow.at(0)); //arrow position - if (alignment()==Qt::AlignLeft) - arrowItem->setLine( axisRect.right() , gridRect.top(), axisRect.right(), gridRect.bottom()); - else if(alignment()==Qt::AlignRight) - arrowItem->setLine( axisRect.left() , gridRect.top(), axisRect.left(), gridRect.bottom()); + if (axis()->alignment() == Qt::AlignLeft) + arrowItem->setLine(axisRect.right(), gridRect.top(), axisRect.right(), gridRect.bottom()); + else if (axis()->alignment() == Qt::AlignRight) + arrowItem->setLine(axisRect.left(), gridRect.top(), axisRect.left(), gridRect.bottom()); - QFontMetrics fn(font()); + QFontMetrics fn(axis()->labelsFont()); //title int titlePad = 0; QRectF titleBoundingRect; - if (!titleText().isEmpty() && titleItem()->isVisible()) { + QString titleText = axis()->titleText(); + if (!titleText.isEmpty() && titleItem()->isVisible()) { QFontMetrics fn(title->font()); int size(0); size = gridRect.height(); - QString titleText = this->titleText(); if (fn.boundingRect(titleText).width() > size) { QString string = titleText + "..."; while (fn.boundingRect(string).width() > size && string.length() > 3) - string.remove(string.length() - 4, 1); + string.remove(string.length() - 4, 1); title->setText(string); - } - else { + } else { title->setText(titleText); } @@ -94,10 +91,10 @@ void VerticalAxis::updateGeometry() titleBoundingRect = title->boundingRect(); QPointF center = gridRect.center() - titleBoundingRect.center(); - if (alignment() == Qt::AlignLeft) { + if (axis()->alignment() == Qt::AlignLeft) { title->setPos(axisRect.left() - titleBoundingRect.width() / 2 + titleBoundingRect.height() / 2 + titlePad, center.y()); } - else if (alignment() == Qt::AlignRight) { + else if (axis()->alignment() == Qt::AlignRight) { title->setPos(axisRect.right() - titleBoundingRect.width() / 2 - titleBoundingRect.height() / 2 - titlePad, center.y()); } title->setTransformOriginPoint(titleBoundingRect.center()); @@ -105,14 +102,13 @@ void VerticalAxis::updateGeometry() } for (int i = 0; i < layout.size(); ++i) { - //items QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem *>(lines.at(i)); - QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(axis.at(i + 1)); + QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrow.at(i + 1)); QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem *>(labels.at(i)); //grid line - gridItem->setLine(gridRect.left() , layout[i], gridRect.right(), layout[i]); + gridItem->setLine(gridRect.left(), layout[i], gridRect.right(), layout[i]); //label text wrapping QString text = labelList.at(i); @@ -138,10 +134,10 @@ void VerticalAxis::updateGeometry() int widthDiff = rect.width() - boundingRect.width(); //ticks and label position - if (alignment() == Qt::AlignLeft) { + if (axis()->alignment() == Qt::AlignLeft) { labelItem->setPos(axisRect.right() - rect.width() + (widthDiff / 2) - labelPadding(), layout[i] - center.y()); tickItem->setLine(axisRect.right() - labelPadding(), layout[i], axisRect.right(), layout[i]); - } else if (alignment() == Qt::AlignRight) { + } else if (axis()->alignment() == Qt::AlignRight) { labelItem->setPos(axisRect.left() + labelPadding() - (widthDiff / 2), layout[i] - center.y()); tickItem->setLine(axisRect.left(), layout[i], axisRect.left() + labelPadding(), layout[i]); } @@ -202,7 +198,7 @@ void VerticalAxis::updateGeometry() gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size())); gridLine->setLine(gridRect.left(), gridRect.top(), gridRect.right(), gridRect.top()); gridLine->setVisible(true); - gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size()+1)); + gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size() + 1)); gridLine->setLine(gridRect.left(), gridRect.bottom(), gridRect.right(), gridRect.bottom()); gridLine->setVisible(true); } @@ -212,10 +208,10 @@ QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) cons { Q_UNUSED(constraint); - QFontMetrics fn(titleFont()); - QSizeF sh(0,0); + QFontMetrics fn(axis()->titleFont()); + QSizeF sh(0, 0); - if (titleText().isEmpty() || !titleItem()->isVisible()) + if (axis()->titleText().isEmpty() || !titleItem()->isVisible()) return sh; switch (which) { diff --git a/src/axis/verticalaxis_p.h b/src/axis/verticalaxis_p.h index 4d186c44..159bba63 100644 --- a/src/axis/verticalaxis_p.h +++ b/src/axis/verticalaxis_p.h @@ -30,14 +30,14 @@ #ifndef VERTICALAXIS_P_H_ #define VERTICALAXIS_P_H_ -#include "chartaxis_p.h" +#include "cartesianchartaxis_p.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE -class VerticalAxis : public ChartAxis +class VerticalAxis : public CartesianChartAxis { public: - VerticalAxis(QAbstractAxis *axis, QGraphicsItem* item = 0, bool intervalAxis = false); + VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item = 0, bool intervalAxis = false); ~VerticalAxis(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; protected: diff --git a/src/chartdataset.cpp b/src/chartdataset.cpp index d5401663..4d7e1a09 100644 --- a/src/chartdataset.cpp +++ b/src/chartdataset.cpp @@ -33,9 +33,13 @@ #include "qpieseries.h" #include "chartitem_p.h" #include "xydomain_p.h" +#include "xypolardomain_p.h" #include "xlogydomain_p.h" #include "logxydomain_p.h" #include "logxlogydomain_p.h" +#include "xlogypolardomain_p.h" +#include "logxypolardomain_p.h" +#include "logxlogypolardomain_p.h" #ifndef QT_ON_ARM #include "qdatetimeaxis.h" @@ -66,6 +70,20 @@ void ChartDataSet::addSeries(QAbstractSeries *series) return; } + // Ignore unsupported series added to polar chart + if (m_chart && m_chart->chartType() == QChart::ChartTypePolar) { + if (!(series->type() == QAbstractSeries::SeriesTypeArea + || series->type() == QAbstractSeries::SeriesTypeLine + || series->type() == QAbstractSeries::SeriesTypeScatter + || series->type() == QAbstractSeries::SeriesTypeSpline)) { + qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart."); + return; + } + series->d_ptr->setDomain(new XYPolarDomain()); + } else { + series->d_ptr->setDomain(new XYDomain()); + } + series->d_ptr->initializeDomain(); m_seriesList.append(series); @@ -78,7 +96,7 @@ void ChartDataSet::addSeries(QAbstractSeries *series) /* * This method adds axis to chartdataset, axis ownership is taken from caller. */ -void ChartDataSet::addAxis(QAbstractAxis *axis,Qt::Alignment aligment) +void ChartDataSet::addAxis(QAbstractAxis *axis, Qt::Alignment aligment) { if (m_axisList.contains(axis)) { qWarning() << QObject::tr("Can not add axis. Axis already on the chart."); @@ -87,12 +105,25 @@ void ChartDataSet::addAxis(QAbstractAxis *axis,Qt::Alignment aligment) axis->d_ptr->setAlignment(aligment); - if(!axis->alignment()) { - qWarning()<< QObject::tr("No alignment specified !"); + if (!axis->alignment()) { + qWarning() << QObject::tr("No alignment specified !"); return; }; - QSharedPointer<AbstractDomain> domain(new XYDomain()); + AbstractDomain *newDomain; + if (m_chart && m_chart->chartType() == QChart::ChartTypePolar) { + foreach (QAbstractAxis *existingAxis, axes()) { + if (existingAxis->orientation() == axis->orientation()) { + qWarning() << QObject::tr("Cannot add multiple axes of same orientation to a polar chart!"); + return; + } + } + newDomain = new XYPolarDomain(); + } else { + newDomain = new XYDomain(); + } + + QSharedPointer<AbstractDomain> domain(newDomain); axis->d_ptr->initializeDomain(domain.data()); axis->setParent(this); @@ -122,6 +153,8 @@ void ChartDataSet::removeSeries(QAbstractSeries *series) emit seriesRemoved(series); m_seriesList.removeAll(series); + // Reset domain to default + series->d_ptr->setDomain(new XYDomain()); series->setParent(0); series->d_ptr->m_chart = 0; } @@ -152,13 +185,13 @@ void ChartDataSet::removeAxis(QAbstractAxis *axis) /* * This method attaches axis to series, return true if success. */ -bool ChartDataSet::attachAxis(QAbstractSeries* series,QAbstractAxis *axis) +bool ChartDataSet::attachAxis(QAbstractSeries *series,QAbstractAxis *axis) { Q_ASSERT(series); Q_ASSERT(axis); - QList<QAbstractSeries* > attachedSeriesList = axis->d_ptr->m_series; - QList<QAbstractAxis* > attachedAxisList = series->d_ptr->m_axes; + QList<QAbstractSeries *> attachedSeriesList = axis->d_ptr->m_series; + QList<QAbstractAxis *> attachedAxisList = series->d_ptr->m_axes; if (!m_seriesList.contains(series)) { qWarning() << QObject::tr("Can not find series on the chart."); @@ -180,25 +213,42 @@ bool ChartDataSet::attachAxis(QAbstractSeries* series,QAbstractAxis *axis) return false; } - AbstractDomain* domain = series->d_ptr->domain(); + AbstractDomain *domain = series->d_ptr->domain(); AbstractDomain::DomainType type = selectDomain(attachedAxisList<<axis); - if(type == AbstractDomain::UndefinedDomain) return false; + if (type == AbstractDomain::UndefinedDomain) return false; - if(domain->type()!=type){ + if (domain->type() != type) { AbstractDomain *old = domain; - domain = createDomain(type); + domain = createDomain(type); domain->setRange(old->minX(), old->maxX(), old->minY(), old->maxY()); + // Initialize domain size to old domain size, as it won't get updated + // unless geometry changes. + domain->setSize(old->size()); } - if(!domain) return false; + if (!domain) + return false; + + if (!domain->attachAxis(axis)) + return false; - if(!domain->attachAxis(axis)) return false; + QList<AbstractDomain *> blockedDomains; + domain->blockRangeSignals(true); + blockedDomains << domain; - if(domain!=series->d_ptr->domain()){ - foreach(QAbstractAxis* axis,series->d_ptr->m_axes){ + if (domain != series->d_ptr->domain()) { + foreach (QAbstractAxis *axis, series->d_ptr->m_axes) { series->d_ptr->domain()->detachAxis(axis); domain->attachAxis(axis); + foreach (QAbstractSeries *otherSeries, axis->d_ptr->m_series) { + if (otherSeries != series && otherSeries->d_ptr->domain()) { + if (!otherSeries->d_ptr->domain()->rangeSignalsBlocked()) { + otherSeries->d_ptr->domain()->blockRangeSignals(true); + blockedDomains << otherSeries->d_ptr->domain(); + } + } + } } series->d_ptr->setDomain(domain); series->d_ptr->initializeDomain(); @@ -210,6 +260,9 @@ bool ChartDataSet::attachAxis(QAbstractSeries* series,QAbstractAxis *axis) series->d_ptr->initializeAxes(); axis->d_ptr->initializeDomain(domain); + foreach (AbstractDomain *blockedDomain, blockedDomains) + blockedDomain->blockRangeSignals(false); + return true; } @@ -327,12 +380,12 @@ void ChartDataSet::findMinMaxForSeries(QList<QAbstractSeries *> series,Qt::Orien { Q_ASSERT(!series.isEmpty()); - AbstractDomain* domain = series.first()->d_ptr->domain(); + AbstractDomain *domain = series.first()->d_ptr->domain(); min = (orientation == Qt::Vertical) ? domain->minY() : domain->minX(); max = (orientation == Qt::Vertical) ? domain->maxY() : domain->maxX(); - for(int i = 1; i< series.size(); i++) { - AbstractDomain* domain = series[i]->d_ptr->domain(); + for (int i = 1; i< series.size(); i++) { + AbstractDomain *domain = series[i]->d_ptr->domain(); min = qMin((orientation == Qt::Vertical) ? domain->minY() : domain->minX(), min); max = qMax((orientation == Qt::Vertical) ? domain->maxY() : domain->maxX(), max); } @@ -437,7 +490,7 @@ QPointF ChartDataSet::mapToPosition(const QPointF &value, QAbstractSeries *serie return point; } -QList<QAbstractAxis*> ChartDataSet::axes() const +QList<QAbstractAxis *> ChartDataSet::axes() const { return m_axisList; } @@ -447,7 +500,7 @@ QList<QAbstractSeries *> ChartDataSet::series() const return m_seriesList; } -AbstractDomain::DomainType ChartDataSet::selectDomain(QList<QAbstractAxis*> axes) +AbstractDomain::DomainType ChartDataSet::selectDomain(QList<QAbstractAxis *> axes) { enum Type { Undefined = 0, @@ -458,75 +511,95 @@ AbstractDomain::DomainType ChartDataSet::selectDomain(QList<QAbstractAxis*> axes int horizontal(Undefined); int vertical(Undefined); - foreach(QAbstractAxis* axis, axes) - { - switch(axis->type()) { - case QAbstractAxis::AxisTypeLogValue: - - if(axis->orientation()==Qt::Horizontal) { - horizontal|=LogType; - } - if(axis->orientation()==Qt::Vertical) { - vertical|=LogType; - } + // Assume cartesian chart type, unless chart is set + QChart::ChartType chartType(QChart::ChartTypeCartesian); + if (m_chart) + chartType = m_chart->chartType(); + foreach (QAbstractAxis *axis, axes) + { + switch (axis->type()) { + case QAbstractAxis::AxisTypeLogValue: + if (axis->orientation() == Qt::Horizontal) + horizontal |= LogType; + if (axis->orientation() == Qt::Vertical) + vertical |= LogType; break; - case QAbstractAxis::AxisTypeValue: - case QAbstractAxis::AxisTypeBarCategory: - case QAbstractAxis::AxisTypeCategory: - case QAbstractAxis::AxisTypeDateTime: - if(axis->orientation()==Qt::Horizontal) { - horizontal|=ValueType; - } - if(axis->orientation()==Qt::Vertical) { - vertical|=ValueType; - } + case QAbstractAxis::AxisTypeValue: + case QAbstractAxis::AxisTypeBarCategory: + case QAbstractAxis::AxisTypeCategory: + case QAbstractAxis::AxisTypeDateTime: + if (axis->orientation() == Qt::Horizontal) + horizontal |= ValueType; + if (axis->orientation() == Qt::Vertical) + vertical |= ValueType; break; - default: - qWarning()<<"Undefined type"; + default: + qWarning() << "Undefined type"; break; } } - if(vertical==Undefined) vertical=ValueType; - if(horizontal==Undefined) horizontal=ValueType; + if (vertical == Undefined) + vertical = ValueType; + if (horizontal == Undefined) + horizontal = ValueType; - if(vertical==ValueType && horizontal== ValueType) { - return AbstractDomain::XYDomain; + if (vertical == ValueType && horizontal == ValueType) { + if (chartType == QChart::ChartTypeCartesian) + return AbstractDomain::XYDomain; + else if (chartType == QChart::ChartTypePolar) + return AbstractDomain::XYPolarDomain; } - if(vertical==LogType && horizontal== ValueType) { - return AbstractDomain::XLogYDomain; + if (vertical == LogType && horizontal == ValueType) { + if (chartType == QChart::ChartTypeCartesian) + return AbstractDomain::XLogYDomain; + if (chartType == QChart::ChartTypePolar) + return AbstractDomain::XLogYPolarDomain; } - if(vertical==ValueType && horizontal== LogType) { - return AbstractDomain::LogXYDomain; + if (vertical == ValueType && horizontal == LogType) { + if (chartType == QChart::ChartTypeCartesian) + return AbstractDomain::LogXYDomain; + else if (chartType == QChart::ChartTypePolar) + return AbstractDomain::LogXYPolarDomain; } - if(vertical==LogType && horizontal== LogType) { - return AbstractDomain::LogXLogYDomain; + if (vertical == LogType && horizontal == LogType) { + if (chartType == QChart::ChartTypeCartesian) + return AbstractDomain::LogXLogYDomain; + else if (chartType == QChart::ChartTypePolar) + return AbstractDomain::LogXLogYPolarDomain; } return AbstractDomain::UndefinedDomain; } - //refactor create factory AbstractDomain* ChartDataSet::createDomain(AbstractDomain::DomainType type) { - switch(type) - { - case AbstractDomain::LogXLogYDomain: - return new LogXLogYDomain(); - case AbstractDomain::XYDomain: - return new XYDomain(); - case AbstractDomain::XLogYDomain: - return new XLogYDomain(); - case AbstractDomain::LogXYDomain: - return new LogXYDomain(); - default: - return 0; - } + switch (type) + { + case AbstractDomain::LogXLogYDomain: + return new LogXLogYDomain(); + case AbstractDomain::XYDomain: + return new XYDomain(); + case AbstractDomain::XLogYDomain: + return new XLogYDomain(); + case AbstractDomain::LogXYDomain: + return new LogXYDomain(); + case AbstractDomain::XYPolarDomain: + return new XYPolarDomain(); + case AbstractDomain::XLogYPolarDomain: + return new XLogYPolarDomain(); + case AbstractDomain::LogXYPolarDomain: + return new LogXYPolarDomain(); + case AbstractDomain::LogXLogYPolarDomain: + return new LogXLogYPolarDomain(); + default: + return 0; + } } #include "moc_chartdataset_p.cpp" diff --git a/src/chartpresenter.cpp b/src/chartpresenter.cpp index 8194f084..f8242aac 100644 --- a/src/chartpresenter.cpp +++ b/src/chartpresenter.cpp @@ -27,24 +27,28 @@ #include "chartanimation_p.h" #include "qabstractseries_p.h" #include "qareaseries.h" -#include "chartaxis_p.h" +#include "chartaxiselement_p.h" #include "chartbackground_p.h" -#include "chartlayout_p.h" +#include "cartesianchartlayout_p.h" +#include "polarchartlayout_p.h" #include "charttitle_p.h" #include <QTimer> QTCOMMERCIALCHART_BEGIN_NAMESPACE -ChartPresenter::ChartPresenter(QChart *chart) +ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) : QObject(chart), m_chart(chart), m_options(QChart::NoAnimation), m_state(ShowState), - m_layout(new ChartLayout(this)), m_background(0), m_title(0) { - + if (type == QChart::ChartTypeCartesian) + m_layout = new CartesianChartLayout(this); + else if (type == QChart::ChartTypePolar) + m_layout = new PolarChartLayout(this); + Q_ASSERT(m_layout); } ChartPresenter::~ChartPresenter() @@ -54,25 +58,25 @@ ChartPresenter::~ChartPresenter() void ChartPresenter::setGeometry(const QRectF rect) { - if(m_rect != rect) { - m_rect=rect; - foreach (ChartItem *chart, m_chartItems){ - chart->domain()->setSize(rect.size()); - chart->setPos(rect.topLeft()); - } - } + if (m_rect != rect) { + m_rect = rect; + foreach (ChartItem *chart, m_chartItems) { + chart->domain()->setSize(rect.size()); + chart->setPos(rect.topLeft()); + } + } } QRectF ChartPresenter::geometry() const { - return m_rect; + return m_rect; } void ChartPresenter::handleAxisAdded(QAbstractAxis *axis) { axis->d_ptr->initializeGraphics(rootItem()); axis->d_ptr->initializeAnimations(m_options); - ChartAxis *item = axis->d_ptr->axisItem(); + ChartAxisElement *item = axis->d_ptr->axisItem(); item->setPresenter(this); item->setThemeManager(m_chart->d_ptr->m_themeManager); m_axisItems<<item; @@ -82,7 +86,7 @@ void ChartPresenter::handleAxisAdded(QAbstractAxis *axis) void ChartPresenter::handleAxisRemoved(QAbstractAxis *axis) { - ChartAxis *item = axis->d_ptr->m_item.take(); + ChartAxisElement *item = axis->d_ptr->m_item.take(); item->hide(); item->disconnect(); item->deleteLater(); @@ -275,7 +279,7 @@ bool ChartPresenter::isBackgroundDropShadowEnabled() const } -ChartLayout *ChartPresenter::layout() +AbstractChartLayout *ChartPresenter::layout() { return m_layout; } @@ -295,7 +299,7 @@ ChartBackground *ChartPresenter::backgroundElement() return m_background; } -QList<ChartAxis *> ChartPresenter::axisItems() const +QList<ChartAxisElement *> ChartPresenter::axisItems() const { return m_axisItems; } diff --git a/src/chartpresenter_p.h b/src/chartpresenter_p.h index 9f1c8b89..19bc5c42 100644 --- a/src/chartpresenter_p.h +++ b/src/chartpresenter_p.h @@ -42,12 +42,12 @@ class AxisItem; class QAbstractSeries; class ChartDataSet; class AbstractDomain; -class ChartAxis; +class ChartAxisElement; class ChartAnimator; class ChartBackground; class ChartTitle; class ChartAnimation; -class ChartLayout; +class AbstractChartLayout; class ChartPresenter: public QObject { @@ -78,7 +78,7 @@ public: ZoomOutState }; - ChartPresenter(QChart *chart); + ChartPresenter(QChart *chart, QChart::ChartType type); virtual ~ChartPresenter(); @@ -88,12 +88,9 @@ public: QGraphicsItem *rootItem(){ return m_chart; } ChartBackground *backgroundElement(); ChartTitle *titleElement(); - QList<ChartAxis *> axisItems() const; + QList<ChartAxisElement *> axisItems() const; QList<ChartItem *> chartItems() const; - ChartItem* chartElement(QAbstractSeries* series) const; - ChartAxis* chartElement(QAbstractAxis* axis) const; - QLegend *legend(); void setBackgroundBrush(const QBrush &brush); @@ -128,7 +125,9 @@ public: void setState(State state,QPointF point); State state() const { return m_state; } QPointF statePoint() const { return m_statePoint; } - ChartLayout *layout(); + AbstractChartLayout *layout(); + + QChart::ChartType chartType() const { return m_chart->chartType(); } private: void createBackgroundItem(); @@ -149,14 +148,14 @@ Q_SIGNALS: private: QChart *m_chart; QList<ChartItem *> m_chartItems; - QList<ChartAxis *> m_axisItems; + QList<ChartAxisElement *> m_axisItems; QList<QAbstractSeries *> m_series; QList<QAbstractAxis *> m_axes; QChart::AnimationOptions m_options; State m_state; QPointF m_statePoint; QList<ChartAnimation *> m_animations; - ChartLayout *m_layout; + AbstractChartLayout *m_layout; ChartBackground *m_background; ChartTitle *m_title; QRectF m_rect; diff --git a/src/domain/abstractdomain.cpp b/src/domain/abstractdomain.cpp index bcaa5164..277472d1 100644 --- a/src/domain/abstractdomain.cpp +++ b/src/domain/abstractdomain.cpp @@ -38,7 +38,7 @@ AbstractDomain::~AbstractDomain() { } -void AbstractDomain::setSize(const QSizeF& size) +void AbstractDomain::setSize(const QSizeF &size) { if(m_size!=size) { @@ -122,9 +122,9 @@ void AbstractDomain::handleHorizontalAxisRangeChanged(qreal min, qreal max) void AbstractDomain::blockRangeSignals(bool block) { - if(m_signalsBlocked!=block){ + if (m_signalsBlocked!=block) { m_signalsBlocked=block; - if(!block) { + if (!block) { emit rangeHorizontalChanged(m_minX,m_maxX); emit rangeVerticalChanged(m_minY,m_maxY); } @@ -165,14 +165,14 @@ qreal AbstractDomain::niceNumber(qreal x, bool ceiling) return q * z; } -bool AbstractDomain::attachAxis(QAbstractAxis* axis) +bool AbstractDomain::attachAxis(QAbstractAxis *axis) { - if(axis->orientation()==Qt::Vertical) { + if (axis->orientation() == Qt::Vertical) { QObject::connect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); QObject::connect(this, SIGNAL(rangeVerticalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); } - if(axis->orientation()==Qt::Horizontal) { + if (axis->orientation() == Qt::Horizontal) { QObject::connect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); QObject::connect(this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); } @@ -180,14 +180,14 @@ bool AbstractDomain::attachAxis(QAbstractAxis* axis) return true; } -bool AbstractDomain::detachAxis(QAbstractAxis* axis) +bool AbstractDomain::detachAxis(QAbstractAxis *axis) { - if(axis->orientation()==Qt::Vertical) { + if (axis->orientation() == Qt::Vertical) { QObject::disconnect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); QObject::disconnect(this, SIGNAL(rangeVerticalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); } - if(axis->orientation()==Qt::Horizontal) { + if (axis->orientation() == Qt::Horizontal) { QObject::disconnect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); QObject::disconnect(this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); } @@ -199,10 +199,10 @@ bool AbstractDomain::detachAxis(QAbstractAxis* axis) bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const AbstractDomain &domain1, const AbstractDomain &domain2) { - return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) && - qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) && - qFuzzyIsNull(domain1.m_minX - domain2.m_minX) && - qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); } @@ -218,6 +218,17 @@ QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const AbstractDo return dbg.maybeSpace(); } +// This function adjusts min/max ranges to failsafe values if negative/zero values are attempted. +void AbstractDomain::adjustLogDomainRanges(qreal &min, qreal &max) +{ + if (min <= 0) { + min = 1.0; + if (max <= min) + max = min + 1.0; + } +} + + #include "moc_abstractdomain_p.cpp" QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/abstractdomain_p.h b/src/domain/abstractdomain_p.h index 907cccff..cf7c2bac 100644 --- a/src/domain/abstractdomain_p.h +++ b/src/domain/abstractdomain_p.h @@ -42,12 +42,20 @@ class QTCOMMERCIALCHART_AUTOTEST_EXPORT AbstractDomain: public QObject { Q_OBJECT public: - enum DomainType { UndefinedDomain, XYDomain, XLogYDomain, LogXYDomain, LogXLogYDomain }; + enum DomainType { UndefinedDomain, + XYDomain, + XLogYDomain, + LogXYDomain, + LogXLogYDomain, + XYPolarDomain, + XLogYPolarDomain, + LogXYPolarDomain, + LogXLogYPolarDomain }; public: explicit AbstractDomain(QObject *object = 0); virtual ~AbstractDomain(); - void setSize(const QSizeF& size); + virtual void setSize(const QSizeF &size); QSizeF size() const; virtual DomainType type() = 0; @@ -82,10 +90,10 @@ public: virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0; virtual QPointF calculateDomainPoint(const QPointF &point) const = 0; - virtual QVector<QPointF> calculateGeometryPoints(const QList<QPointF>& vector) const = 0; + virtual QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const = 0; - virtual bool attachAxis(QAbstractAxis* axis); - virtual bool detachAxis(QAbstractAxis* axis); + virtual bool attachAxis(QAbstractAxis *axis); + virtual bool detachAxis(QAbstractAxis *axis); static void looseNiceNumbers(qreal &min, qreal &max, int &ticksCount); static qreal niceNumber(qreal x, bool ceiling); @@ -100,6 +108,8 @@ public Q_SLOTS: void handleHorizontalAxisRangeChanged(qreal min,qreal max); protected: + void adjustLogDomainRanges(qreal &min, qreal &max); + qreal m_minX; qreal m_maxX; qreal m_minY; diff --git a/src/domain/domain.pri b/src/domain/domain.pri index 1d5823d1..ab4fcfce 100644 --- a/src/domain/domain.pri +++ b/src/domain/domain.pri @@ -5,14 +5,24 @@ DEPENDPATH += $$PWD SOURCES += \ $$PWD/abstractdomain.cpp \ + $$PWD/polardomain.cpp \ $$PWD/xydomain.cpp \ + $$PWD/xypolardomain.cpp \ $$PWD/xlogydomain.cpp \ + $$PWD/xlogypolardomain.cpp \ $$PWD/logxydomain.cpp \ - $$PWD/logxlogydomain.cpp + $$PWD/logxypolardomain.cpp \ + $$PWD/logxlogydomain.cpp \ + $$PWD/logxlogypolardomain.cpp PRIVATE_HEADERS += \ $$PWD/abstractdomain_p.h \ + $$PWD/polardomain_p.h \ $$PWD/xydomain_p.h \ + $$PWD/xypolardomain_p.h \ $$PWD/xlogydomain_p.h \ + $$PWD/xlogypolardomain_p.h \ $$PWD/logxydomain_p.h \ - $$PWD/logxlogydomain_p.h + $$PWD/logxypolardomain_p.h \ + $$PWD/logxlogydomain_p.h \ + $$PWD/logxlogypolardomain_p.h diff --git a/src/domain/logxlogydomain.cpp b/src/domain/logxlogydomain.cpp index 7032d137..adb9bc1d 100644 --- a/src/domain/logxlogydomain.cpp +++ b/src/domain/logxlogydomain.cpp @@ -45,6 +45,9 @@ void LogXLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) bool axisXChanged = false; bool axisYChanged = false; + adjustLogDomainRanges(minX, maxX); + adjustLogDomainRanges(minY, maxY); + if (!qFuzzyIsNull(m_minX - minX) || !qFuzzyIsNull(m_maxX - maxX)) { m_minX = minX; m_maxX = maxX; @@ -65,7 +68,7 @@ void LogXLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); m_logLeftY = logMinY < logMaxY ? logMinY : logMaxY; m_logRightY = logMinY > logMaxY ? logMinY : logMaxY; - if(!m_signalsBlocked) + if (!m_signalsBlocked) emit rangeVerticalChanged(m_minY, m_maxY); } @@ -141,13 +144,13 @@ QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c ok = true; return QPointF(x, y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; ok = false; return QPointF(); } } -QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF>& vector) const +QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const { const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX); const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); @@ -162,7 +165,7 @@ QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF>& v result[i].setX(x); result[i].setY(y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; return QVector<QPointF>(); } } @@ -178,17 +181,17 @@ QPointF LogXLogYDomain::calculateDomainPoint(const QPointF &point) const return QPointF(x, y); } -bool LogXLogYDomain::attachAxis(QAbstractAxis* axis) +bool LogXLogYDomain::attachAxis(QAbstractAxis *axis) { AbstractDomain::attachAxis(axis); QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Vertical) { + if (logAxis && logAxis->orientation() == Qt::Vertical) { QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); handleVerticalAxisBaseChanged(logAxis->base()); } - if(logAxis && logAxis->orientation()==Qt::Horizontal) { + if (logAxis && logAxis->orientation() == Qt::Horizontal) { QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); handleHorizontalAxisBaseChanged(logAxis->base()); } @@ -196,15 +199,15 @@ bool LogXLogYDomain::attachAxis(QAbstractAxis* axis) return true; } -bool LogXLogYDomain::detachAxis(QAbstractAxis* axis) +bool LogXLogYDomain::detachAxis(QAbstractAxis *axis) { AbstractDomain::detachAxis(axis); QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Vertical) + if (logAxis && logAxis->orientation() == Qt::Vertical) QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); - if(logAxis && logAxis->orientation()==Qt::Horizontal) + if (logAxis && logAxis->orientation() == Qt::Horizontal) QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); return true; @@ -234,10 +237,10 @@ void LogXLogYDomain::handleHorizontalAxisBaseChanged(qreal baseX) bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXLogYDomain &domain1, const LogXLogYDomain &domain2) { - return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) && - qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) && - qFuzzyIsNull(domain1.m_minX - domain2.m_minX) && - qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); } diff --git a/src/domain/logxlogydomain_p.h b/src/domain/logxlogydomain_p.h index 2d604163..014bf803 100644 --- a/src/domain/logxlogydomain_p.h +++ b/src/domain/logxlogydomain_p.h @@ -56,10 +56,10 @@ public: QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; QPointF calculateDomainPoint(const QPointF &point) const; - QVector<QPointF> calculateGeometryPoints(const QList<QPointF>& vector) const; + QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const; - bool attachAxis(QAbstractAxis* axis); - bool detachAxis(QAbstractAxis* axis); + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); public Q_SLOTS: void handleVerticalAxisBaseChanged(qreal baseY); diff --git a/src/domain/logxlogypolardomain.cpp b/src/domain/logxlogypolardomain.cpp new file mode 100644 index 00000000..a193ce11 --- /dev/null +++ b/src/domain/logxlogypolardomain.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logxlogypolardomain_p.h" +#include "qabstractaxis_p.h" +#include "qlogvalueaxis.h" +#include <qmath.h> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +LogXLogYPolarDomain::LogXLogYPolarDomain(QObject *parent) + : PolarDomain(parent), + m_logLeftX(0), + m_logRightX(1), + m_logBaseX(10), + m_logInnerY(0), + m_logOuterY(1), + m_logBaseY(10) +{ +} + +LogXLogYPolarDomain::~LogXLogYPolarDomain() +{ +} + +void LogXLogYPolarDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) +{ + bool axisXChanged = false; + bool axisYChanged = false; + + adjustLogDomainRanges(minX, maxX); + adjustLogDomainRanges(minY, maxY); + + if (!qFuzzyCompare(m_minX, minX) || !qFuzzyCompare(m_maxX, maxX)) { + m_minX = minX; + m_maxX = maxX; + axisXChanged = true; + qreal logMinX = log10(m_minX) / log10(m_logBaseX); + qreal logMaxX = log10(m_maxX) / log10(m_logBaseX); + m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; + m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; + if (!m_signalsBlocked) + emit rangeHorizontalChanged(m_minX, m_maxX); + } + + if (!qFuzzyIsNull(m_minY - minY) || !qFuzzyIsNull(m_maxY - maxY)) { + m_minY = minY; + m_maxY = maxY; + axisYChanged = true; + qreal logMinY = log10(m_minY) / log10(m_logBaseY); + qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); + m_logInnerY = logMinY < logMaxY ? logMinY : logMaxY; + m_logOuterY = logMinY > logMaxY ? logMinY : logMaxY; + if (!m_signalsBlocked) + emit rangeVerticalChanged(m_minY, m_maxY); + } + + if (axisXChanged || axisYChanged) + emit updated(); +} + +void LogXLogYPolarDomain::zoomIn(const QRectF &rect) +{ + qreal logLeftX = rect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal logRightX = rect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal leftX = qPow(m_logBaseX, logLeftX); + qreal rightX = qPow(m_logBaseX, logRightX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + qreal logLeftY = m_logOuterY - rect.bottom() * (m_logOuterY - m_logInnerY) / m_size.height(); + qreal logRightY = m_logOuterY - rect.top() * (m_logOuterY - m_logInnerY) / m_size.height(); + qreal leftY = qPow(m_logBaseY, logLeftY); + qreal rightY = qPow(m_logBaseY, logRightY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +void LogXLogYPolarDomain::zoomOut(const QRectF &rect) +{ + const qreal factorX = m_size.width() / rect.width(); + + qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 - factorX); + qreal logRIghtX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 + factorX); + qreal leftX = qPow(m_logBaseX, logLeftX); + qreal rightX = qPow(m_logBaseX, logRIghtX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + const qreal factorY = m_size.height() / rect.height(); + qreal newLogMinY = m_logInnerY + (m_logOuterY - m_logInnerY) / 2.0 * (1.0 - factorY); + qreal newLogMaxY = m_logInnerY + (m_logOuterY - m_logInnerY) / 2.0 * (1.0 + factorY); + qreal leftY = qPow(m_logBaseY, newLogMinY); + qreal rightY = qPow(m_logBaseY, newLogMaxY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +void LogXLogYPolarDomain::move(qreal dx, qreal dy) +{ + qreal stepX = dx * (m_logRightX - m_logLeftX) / m_size.width(); + qreal leftX = qPow(m_logBaseX, m_logLeftX + stepX); + qreal rightX = qPow(m_logBaseX, m_logRightX + stepX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + qreal stepY = dy * (m_logOuterY - m_logInnerY) / m_radius; + qreal leftY = qPow(m_logBaseY, m_logInnerY + stepY); + qreal rightY = qPow(m_logBaseY, m_logOuterY + stepY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +qreal LogXLogYPolarDomain::toAngularCoordinate(qreal value, bool &ok) const +{ + qreal retVal; + if (value <= 0) { + ok = false; + retVal = 0.0; + } else { + ok = true; + const qreal tickSpan = 360.0 / qAbs(m_logRightX - m_logLeftX); + const qreal logValue = log10(value) / log10(m_logBaseX); + const qreal valueDelta = logValue - m_logLeftX; + + retVal = valueDelta * tickSpan; + } + return retVal; +} + +qreal LogXLogYPolarDomain::toRadialCoordinate(qreal value, bool &ok) const +{ + qreal retVal; + if (value <= 0) { + ok = false; + retVal = 0.0; + } else { + ok = true; + const qreal tickSpan = m_radius / qAbs(m_logOuterY - m_logInnerY); + const qreal logValue = log10(value) / log10(m_logBaseY); + const qreal valueDelta = logValue - m_logInnerY; + + retVal = valueDelta * tickSpan; + + if (retVal < 0.0) + retVal = 0.0; + } + return retVal; +} + +QPointF LogXLogYPolarDomain::calculateDomainPoint(const QPointF &point) const +{ + if (point == m_center) + return QPointF(0.0, m_minY); + + QLineF line(m_center, point); + qreal a = 90.0 - line.angle(); + if (a < 0.0) + a += 360.0; + + const qreal deltaX = 360.0 / qAbs(m_logRightX - m_logLeftX); + a = qPow(m_logBaseX, m_logLeftX + (a / deltaX)); + + const qreal deltaY = m_radius / qAbs(m_logOuterY - m_logInnerY); + qreal r = qPow(m_logBaseY, m_logInnerY + (line.length() / deltaY)); + + return QPointF(a, r); +} + +bool LogXLogYPolarDomain::attachAxis(QAbstractAxis *axis) +{ + AbstractDomain::attachAxis(axis); + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Horizontal) { + QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); + handleHorizontalAxisBaseChanged(logAxis->base()); + } else if (logAxis && logAxis->orientation() == Qt::Vertical){ + QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); + handleVerticalAxisBaseChanged(logAxis->base()); + } + + return true; +} + +bool LogXLogYPolarDomain::detachAxis(QAbstractAxis *axis) +{ + AbstractDomain::detachAxis(axis); + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Horizontal) + QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); + else if (logAxis && logAxis->orientation() == Qt::Vertical) + QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); + + return true; +} + +void LogXLogYPolarDomain::handleHorizontalAxisBaseChanged(qreal baseX) +{ + m_logBaseX = baseX; + qreal logMinX = log10(m_minX) / log10(m_logBaseX); + qreal logMaxX = log10(m_maxX) / log10(m_logBaseX); + m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; + m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; + emit updated(); +} + +void LogXLogYPolarDomain::handleVerticalAxisBaseChanged(qreal baseY) +{ + m_logBaseY = baseY; + qreal logMinY = log10(m_minY) / log10(m_logBaseY); + qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); + m_logInnerY = logMinY < logMaxY ? logMinY : logMaxY; + m_logOuterY = logMinY > logMaxY ? logMinY : logMaxY; + emit updated(); +} + +// operators + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXLogYPolarDomain &domain1, const LogXLogYPolarDomain &domain2) +{ + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); +} + + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const LogXLogYPolarDomain &domain1, const LogXLogYPolarDomain &domain2) +{ + return !(domain1 == domain2); +} + + +QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXLogYPolarDomain &domain) +{ + dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; + return dbg.maybeSpace(); +} + +#include "moc_logxlogypolardomain_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/logxlogypolardomain_p.h b/src/domain/logxlogypolardomain_p.h new file mode 100644 index 00000000..106f58a6 --- /dev/null +++ b/src/domain/logxlogypolardomain_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef LOGXLOGYPOLARDOMAIN_H +#define LOGXLOGYPOLARDOMAIN_H +#include "polardomain_p.h" +#include <QRectF> +#include <QSizeF> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_AUTOTEST_EXPORT LogXLogYPolarDomain: public PolarDomain +{ + Q_OBJECT +public: + explicit LogXLogYPolarDomain(QObject *object = 0); + virtual ~LogXLogYPolarDomain(); + + DomainType type() { return AbstractDomain::LogXLogYPolarDomain; } + + void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY); + + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXLogYPolarDomain &domain1, const LogXLogYPolarDomain &domain2); + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const LogXLogYPolarDomain &domain1, const LogXLogYPolarDomain &domain2); + friend QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXLogYPolarDomain &domain); + + void zoomIn(const QRectF &rect); + void zoomOut(const QRectF &rect); + void move(qreal dx, qreal dy); + + QPointF calculateDomainPoint(const QPointF &point) const; + + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); + +public Q_SLOTS: + void handleVerticalAxisBaseChanged(qreal baseY); + void handleHorizontalAxisBaseChanged(qreal baseX); + +protected: + qreal toAngularCoordinate(qreal value, bool &ok) const; + qreal toRadialCoordinate(qreal value, bool &ok) const; + +private: + qreal m_logLeftX; + qreal m_logRightX; + qreal m_logBaseX; + qreal m_logInnerY; + qreal m_logOuterY; + qreal m_logBaseY; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // LOGXLOGYPOLARDOMAIN_H diff --git a/src/domain/logxydomain.cpp b/src/domain/logxydomain.cpp index ffa739bf..da6e210a 100644 --- a/src/domain/logxydomain.cpp +++ b/src/domain/logxydomain.cpp @@ -42,6 +42,8 @@ void LogXYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) bool axisXChanged = false; bool axisYChanged = false; + adjustLogDomainRanges(minX, maxX); + if (!qFuzzyCompare(m_minX, minX) || !qFuzzyCompare(m_maxX, maxX)) { m_minX = minX; m_maxX = maxX; @@ -58,7 +60,7 @@ void LogXYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) m_minY = minY; m_maxY = maxY; axisYChanged = true; - if(!m_signalsBlocked) + if (!m_signalsBlocked) emit rangeVerticalChanged(m_minY, m_maxY); } @@ -137,13 +139,13 @@ QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons ok = true; return QPointF(x, y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; ok = false; return QPointF(); } } -QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QList<QPointF>& vector) const +QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const { const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX); const qreal deltaY = m_size.height() / (m_maxY - m_minY); @@ -158,7 +160,7 @@ QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QList<QPointF>& vect result[i].setX(x); result[i].setY(y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; return QVector<QPointF>(); } } @@ -174,12 +176,12 @@ QPointF LogXYDomain::calculateDomainPoint(const QPointF &point) const return QPointF(x, y); } -bool LogXYDomain::attachAxis(QAbstractAxis* axis) +bool LogXYDomain::attachAxis(QAbstractAxis *axis) { AbstractDomain::attachAxis(axis); QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Horizontal) { + if (logAxis && logAxis->orientation() == Qt::Horizontal) { QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); handleHorizontalAxisBaseChanged(logAxis->base()); } @@ -187,12 +189,12 @@ bool LogXYDomain::attachAxis(QAbstractAxis* axis) return true; } -bool LogXYDomain::detachAxis(QAbstractAxis* axis) +bool LogXYDomain::detachAxis(QAbstractAxis *axis) { AbstractDomain::detachAxis(axis); QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Horizontal) + if (logAxis && logAxis->orientation() == Qt::Horizontal) QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); return true; @@ -212,10 +214,10 @@ void LogXYDomain::handleHorizontalAxisBaseChanged(qreal baseX) bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXYDomain &domain1, const LogXYDomain &domain2) { - return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) && - qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) && - qFuzzyIsNull(domain1.m_minX - domain2.m_minX) && - qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); } diff --git a/src/domain/logxydomain_p.h b/src/domain/logxydomain_p.h index cbc59e18..2f0e49ea 100644 --- a/src/domain/logxydomain_p.h +++ b/src/domain/logxydomain_p.h @@ -56,10 +56,10 @@ public: QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; QPointF calculateDomainPoint(const QPointF &point) const; - QVector<QPointF> calculateGeometryPoints(const QList<QPointF>& vector) const; + QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const; - bool attachAxis(QAbstractAxis* axis); - bool detachAxis(QAbstractAxis* axis); + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); public Q_SLOTS: void handleHorizontalAxisBaseChanged(qreal baseX); diff --git a/src/domain/logxypolardomain.cpp b/src/domain/logxypolardomain.cpp new file mode 100644 index 00000000..b08f6a04 --- /dev/null +++ b/src/domain/logxypolardomain.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logxypolardomain_p.h" +#include "qabstractaxis_p.h" +#include "qlogvalueaxis.h" +#include <qmath.h> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +LogXYPolarDomain::LogXYPolarDomain(QObject *parent) + : PolarDomain(parent), + m_logLeftX(0), + m_logRightX(1), + m_logBaseX(10) +{ +} + +LogXYPolarDomain::~LogXYPolarDomain() +{ +} + +void LogXYPolarDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) +{ + bool axisXChanged = false; + bool axisYChanged = false; + + adjustLogDomainRanges(minX, maxX); + + if (!qFuzzyCompare(m_minX, minX) || !qFuzzyCompare(m_maxX, maxX)) { + m_minX = minX; + m_maxX = maxX; + axisXChanged = true; + qreal logMinX = log10(m_minX) / log10(m_logBaseX); + qreal logMaxX = log10(m_maxX) / log10(m_logBaseX); + m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; + m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; + if (!m_signalsBlocked) + emit rangeHorizontalChanged(m_minX, m_maxX); + } + + if (!qFuzzyIsNull(m_minY - minY) || !qFuzzyIsNull(m_maxY - maxY)) { + m_minY = minY; + m_maxY = maxY; + axisYChanged = true; + if (!m_signalsBlocked) + emit rangeVerticalChanged(m_minY, m_maxY); + } + + if (axisXChanged || axisYChanged) + emit updated(); +} + +void LogXYPolarDomain::zoomIn(const QRectF &rect) +{ + qreal logLeftX = rect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal logRightX = rect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal leftX = qPow(m_logBaseX, logLeftX); + qreal rightX = qPow(m_logBaseX, logRightX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + qreal dy = spanY() / m_size.height(); + qreal minY = m_minY; + qreal maxY = m_maxY; + + minY = maxY - dy * rect.bottom(); + maxY = maxY - dy * rect.top(); + + setRange(minX, maxX, minY, maxY); +} + +void LogXYPolarDomain::zoomOut(const QRectF &rect) +{ + const qreal factorX = m_size.width() / rect.width(); + + qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 - factorX); + qreal logRIghtX = m_logLeftX + (m_logRightX - m_logLeftX) / 2.0 * (1.0 + factorX); + qreal leftX = qPow(m_logBaseX, logLeftX); + qreal rightX = qPow(m_logBaseX, logRIghtX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + qreal dy = spanY() / rect.height(); + qreal minY = m_minY; + qreal maxY = m_maxY; + + maxY = minY + dy * rect.bottom(); + minY = maxY - dy * m_size.height(); + + setRange(minX, maxX, minY, maxY); +} + +void LogXYPolarDomain::move(qreal dx, qreal dy) +{ + qreal stepX = dx * (m_logRightX - m_logLeftX) / m_size.width(); + qreal leftX = qPow(m_logBaseX, m_logLeftX + stepX); + qreal rightX = qPow(m_logBaseX, m_logRightX + stepX); + qreal minX = leftX < rightX ? leftX : rightX; + qreal maxX = leftX > rightX ? leftX : rightX; + + qreal y = spanY() / m_radius; + qreal minY = m_minY; + qreal maxY = m_maxY; + + if (dy != 0) { + minY = minY + y * dy; + maxY = maxY + y * dy; + } + setRange(minX, maxX, minY, maxY); +} + +qreal LogXYPolarDomain::toAngularCoordinate(qreal value, bool &ok) const +{ + qreal retVal; + if (value <= 0) { + ok = false; + retVal = 0.0; + } else { + ok = true; + const qreal tickSpan = 360.0 / qAbs(m_logRightX - m_logLeftX); + const qreal logValue = log10(value) / log10(m_logBaseX); + const qreal valueDelta = logValue - m_logLeftX; + + retVal = valueDelta * tickSpan; + } + return retVal; +} + +qreal LogXYPolarDomain::toRadialCoordinate(qreal value, bool &ok) const +{ + ok = true; + if (value < m_minY) + value = m_minY; + + // Dont limit the max. The drawing should clip the stuff that goes out of the grid + qreal f = (value - m_minY) / (m_maxY - m_minY); + + return f * m_radius; +} + +QPointF LogXYPolarDomain::calculateDomainPoint(const QPointF &point) const +{ + if (point == m_center) + return QPointF(0.0, m_minY); + + QLineF line(m_center, point); + qreal a = 90.0 - line.angle(); + if (a < 0.0) + a += 360.0; + + const qreal deltaX = 360.0 / qAbs(m_logRightX - m_logLeftX); + a = qPow(m_logBaseX, m_logLeftX + (a / deltaX)); + + qreal r = m_minY + ((m_maxY - m_minY) * (line.length() / m_radius)); + + return QPointF(a, r); +} + +bool LogXYPolarDomain::attachAxis(QAbstractAxis *axis) +{ + AbstractDomain::attachAxis(axis); + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Horizontal) { + QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); + handleHorizontalAxisBaseChanged(logAxis->base()); + } + + return true; +} + +bool LogXYPolarDomain::detachAxis(QAbstractAxis *axis) +{ + AbstractDomain::detachAxis(axis); + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Horizontal) + QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleHorizontalAxisBaseChanged(qreal))); + + return true; +} + +void LogXYPolarDomain::handleHorizontalAxisBaseChanged(qreal baseX) +{ + m_logBaseX = baseX; + qreal logMinX = log10(m_minX) / log10(m_logBaseX); + qreal logMaxX = log10(m_maxX) / log10(m_logBaseX); + m_logLeftX = logMinX < logMaxX ? logMinX : logMaxX; + m_logRightX = logMinX > logMaxX ? logMinX : logMaxX; + emit updated(); +} + +// operators + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2) +{ + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); +} + + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2) +{ + return !(domain1 == domain2); +} + + +QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXYPolarDomain &domain) +{ + dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; + return dbg.maybeSpace(); +} + +#include "moc_logxypolardomain_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/logxypolardomain_p.h b/src/domain/logxypolardomain_p.h new file mode 100644 index 00000000..c7468ab9 --- /dev/null +++ b/src/domain/logxypolardomain_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef LOGXYPOLARDOMAIN_H +#define LOGXYPOLARDOMAIN_H +#include "polardomain_p.h" +#include <QRectF> +#include <QSizeF> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_AUTOTEST_EXPORT LogXYPolarDomain: public PolarDomain +{ + Q_OBJECT +public: + explicit LogXYPolarDomain(QObject *object = 0); + virtual ~LogXYPolarDomain(); + + DomainType type() { return AbstractDomain::LogXYPolarDomain; } + + void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY); + + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2); + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const LogXYPolarDomain &domain1, const LogXYPolarDomain &domain2); + friend QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const LogXYPolarDomain &domain); + + void zoomIn(const QRectF &rect); + void zoomOut(const QRectF &rect); + void move(qreal dx, qreal dy); + + QPointF calculateDomainPoint(const QPointF &point) const; + + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); + +public Q_SLOTS: + void handleHorizontalAxisBaseChanged(qreal baseX); + +protected: + qreal toAngularCoordinate(qreal value, bool &ok) const; + qreal toRadialCoordinate(qreal value, bool &ok) const; + +private: + qreal m_logLeftX; + qreal m_logRightX; + qreal m_logBaseX; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // LOGXYPOLARDOMAIN_H diff --git a/src/domain/polardomain.cpp b/src/domain/polardomain.cpp new file mode 100644 index 00000000..d9a9d48f --- /dev/null +++ b/src/domain/polardomain.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polardomain_p.h" +#include "qabstractaxis_p.h" +#include <qmath.h> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +PolarDomain::PolarDomain(QObject *parent) + : AbstractDomain(parent) +{ +} + +PolarDomain::~PolarDomain() +{ +} + +void PolarDomain::setSize(const QSizeF &size) +{ + Q_ASSERT(size.width() == size.height()); + m_radius = size.height() / 2; + m_center = QPointF(m_radius, m_radius); + AbstractDomain::setSize(size); +} + +QPointF PolarDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const +{ + qreal r; + qreal a = toAngularCoordinate(point.x(), ok); + if (ok) + r = toRadialCoordinate(point.y(), ok); + if (ok) { + return m_center + polarCoordinateToPoint(a, r); + } else { + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; + return QPointF(); + } +} + +QVector<QPointF> PolarDomain::calculateGeometryPoints(const QList<QPointF> &vector) const +{ + QVector<QPointF> result; + result.resize(vector.count()); + bool ok; + qreal r; + qreal a; + + for (int i = 0; i < vector.count(); ++i) { + a = toAngularCoordinate(vector[i].x(), ok); + if (ok) + r = toRadialCoordinate(vector[i].y(), ok); + if (ok) { + result[i] = m_center + polarCoordinateToPoint(a, r); + } else { + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; + return QVector<QPointF>(); + } + } + + return result; +} + +QPointF PolarDomain::polarCoordinateToPoint(qreal angularCoordinate, qreal radialCoordinate) const +{ + qreal dx = qSin(angularCoordinate * (M_PI / 180)) * radialCoordinate; + qreal dy = qCos(angularCoordinate * (M_PI / 180)) * radialCoordinate; + + return QPointF(dx, -dy); +} + +#include "moc_polardomain_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/polardomain_p.h b/src/domain/polardomain_p.h new file mode 100644 index 00000000..81e921d5 --- /dev/null +++ b/src/domain/polardomain_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARDOMAIN_H +#define POLARDOMAIN_H +#include "abstractdomain_p.h" +#include <QRectF> +#include <QSizeF> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_AUTOTEST_EXPORT PolarDomain: public AbstractDomain +{ + Q_OBJECT +public: + explicit PolarDomain(QObject *object = 0); + virtual ~PolarDomain(); + + void setSize(const QSizeF &size); + + QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; + QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const; + + virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0; + virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0; + +protected: + QPointF polarCoordinateToPoint(qreal angularCoordinate, qreal radialCoordinate) const; + + QPointF m_center; + qreal m_radius; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARDOMAIN_H diff --git a/src/domain/xlogydomain.cpp b/src/domain/xlogydomain.cpp index 42e7edae..850eb99b 100644 --- a/src/domain/xlogydomain.cpp +++ b/src/domain/xlogydomain.cpp @@ -42,6 +42,8 @@ void XLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) bool axisXChanged = false; bool axisYChanged = false; + adjustLogDomainRanges(minY, maxY); + if (!qFuzzyIsNull(m_minX - minX) || !qFuzzyIsNull(m_maxX - maxX)) { m_minX = minX; m_maxX = maxX; @@ -58,7 +60,7 @@ void XLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); m_logLeftY = logMinY < logMaxY ? logMinY : logMaxY; m_logRightY = logMinY > logMaxY ? logMinY : logMaxY; - if(!m_signalsBlocked) + if (!m_signalsBlocked) emit rangeVerticalChanged(m_minY, m_maxY); } @@ -136,13 +138,13 @@ QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons ok = true; return QPointF(x, y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; ok = false; return QPointF(); } } -QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QList<QPointF>& vector) const +QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const { const qreal deltaX = m_size.width() / (m_maxX - m_minX); const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); @@ -157,7 +159,7 @@ QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QList<QPointF>& vect result[i].setX(x); result[i].setY(y); } else { - qWarning() << "Logarithm of negative value is undefined. Empty layout returned"; + qWarning() << "Logarithm of negative value is undefined. Empty layout returned."; return QVector<QPointF>(); } } @@ -173,22 +175,22 @@ QPointF XLogYDomain::calculateDomainPoint(const QPointF &point) const return QPointF(x, y); } -bool XLogYDomain::attachAxis(QAbstractAxis* axis) +bool XLogYDomain::attachAxis(QAbstractAxis *axis) { QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Vertical){ + if (logAxis && logAxis->orientation() == Qt::Vertical) { QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); handleVerticalAxisBaseChanged(logAxis->base()); } return AbstractDomain::attachAxis(axis); } -bool XLogYDomain::detachAxis(QAbstractAxis* axis) +bool XLogYDomain::detachAxis(QAbstractAxis *axis) { QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); - if(logAxis && logAxis->orientation()==Qt::Vertical) + if (logAxis && logAxis->orientation() == Qt::Vertical) QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); return AbstractDomain::detachAxis(axis); @@ -208,10 +210,10 @@ void XLogYDomain::handleVerticalAxisBaseChanged(qreal baseY) bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XLogYDomain &domain1, const XLogYDomain &domain2) { - return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) && - qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) && - qFuzzyIsNull(domain1.m_minX - domain2.m_minX) && - qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); } diff --git a/src/domain/xlogydomain_p.h b/src/domain/xlogydomain_p.h index 88dd989f..68ac6f81 100644 --- a/src/domain/xlogydomain_p.h +++ b/src/domain/xlogydomain_p.h @@ -56,10 +56,10 @@ public: QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; QPointF calculateDomainPoint(const QPointF &point) const; - QVector<QPointF> calculateGeometryPoints(const QList<QPointF>& vector) const; + QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const; - bool attachAxis(QAbstractAxis* axis); - bool detachAxis(QAbstractAxis* axis); + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); public Q_SLOTS: void handleVerticalAxisBaseChanged(qreal baseY); diff --git a/src/domain/xlogypolardomain.cpp b/src/domain/xlogypolardomain.cpp new file mode 100644 index 00000000..730a9dcd --- /dev/null +++ b/src/domain/xlogypolardomain.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xlogypolardomain_p.h" +#include "qabstractaxis_p.h" +#include "qlogvalueaxis.h" +#include <qmath.h> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +XLogYPolarDomain::XLogYPolarDomain(QObject *parent) + : PolarDomain(parent), + m_logInnerY(0), + m_logOuterY(1), + m_logBaseY(10) +{ +} + +XLogYPolarDomain::~XLogYPolarDomain() +{ +} + +void XLogYPolarDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) +{ + bool axisXChanged = false; + bool axisYChanged = false; + + adjustLogDomainRanges(minY, maxY); + + if (!qFuzzyIsNull(m_minX - minX) || !qFuzzyIsNull(m_maxX - maxX)) { + m_minX = minX; + m_maxX = maxX; + axisXChanged = true; + if (!m_signalsBlocked) + emit rangeHorizontalChanged(m_minX, m_maxX); + } + + if (!qFuzzyIsNull(m_minY - minY) || !qFuzzyIsNull(m_maxY - maxY)) { + m_minY = minY; + m_maxY = maxY; + axisYChanged = true; + qreal logMinY = log10(m_minY) / log10(m_logBaseY); + qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); + m_logInnerY = logMinY < logMaxY ? logMinY : logMaxY; + m_logOuterY = logMinY > logMaxY ? logMinY : logMaxY; + if (!m_signalsBlocked) + emit rangeVerticalChanged(m_minY, m_maxY); + } + + if (axisXChanged || axisYChanged) + emit updated(); +} + +void XLogYPolarDomain::zoomIn(const QRectF &rect) +{ + qreal dx = spanX() / m_size.width(); + qreal maxX = m_maxX; + qreal minX = m_minX; + + maxX = minX + dx * rect.right(); + minX = minX + dx * rect.left(); + + qreal logLeftY = m_logOuterY - rect.bottom() * (m_logOuterY - m_logInnerY) / m_size.height(); + qreal logRightY = m_logOuterY - rect.top() * (m_logOuterY - m_logInnerY) / m_size.height(); + qreal leftY = qPow(m_logBaseY, logLeftY); + qreal rightY = qPow(m_logBaseY, logRightY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +void XLogYPolarDomain::zoomOut(const QRectF &rect) +{ + qreal dx = spanX() / rect.width(); + qreal maxX = m_maxX; + qreal minX = m_minX; + + minX = maxX - dx * rect.right(); + maxX = minX + dx * m_size.width(); + + const qreal factorY = m_size.height() / rect.height(); + qreal newLogMinY = m_logInnerY + (m_logOuterY - m_logInnerY) / 2.0 * (1.0 - factorY); + qreal newLogMaxY = m_logInnerY + (m_logOuterY - m_logInnerY) / 2.0 * (1.0 + factorY); + qreal leftY = qPow(m_logBaseY, newLogMinY); + qreal rightY = qPow(m_logBaseY, newLogMaxY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +void XLogYPolarDomain::move(qreal dx, qreal dy) +{ + qreal x = spanX() / 360.0; + + qreal maxX = m_maxX; + qreal minX = m_minX; + + if (dx != 0) { + minX = minX + x * dx; + maxX = maxX + x * dx; + } + + qreal stepY = dy * (m_logOuterY - m_logInnerY) / m_radius; + qreal leftY = qPow(m_logBaseY, m_logInnerY + stepY); + qreal rightY = qPow(m_logBaseY, m_logOuterY + stepY); + qreal minY = leftY < rightY ? leftY : rightY; + qreal maxY = leftY > rightY ? leftY : rightY; + + setRange(minX, maxX, minY, maxY); +} + +qreal XLogYPolarDomain::toAngularCoordinate(qreal value, bool &ok) const +{ + ok = true; + qreal f = (value - m_minX) / (m_maxX - m_minX); + return f * 360.0; +} + +qreal XLogYPolarDomain::toRadialCoordinate(qreal value, bool &ok) const +{ + qreal retVal; + if (value <= 0) { + ok = false; + retVal = 0.0; + } else { + ok = true; + const qreal tickSpan = m_radius / qAbs(m_logOuterY - m_logInnerY); + const qreal logValue = log10(value) / log10(m_logBaseY); + const qreal valueDelta = logValue - m_logInnerY; + + retVal = valueDelta * tickSpan; + + if (retVal < 0.0) + retVal = 0.0; + } + return retVal; +} + +QPointF XLogYPolarDomain::calculateDomainPoint(const QPointF &point) const +{ + if (point == m_center) + return QPointF(0.0, m_minY); + + QLineF line(m_center, point); + qreal a = 90.0 - line.angle(); + if (a < 0.0) + a += 360.0; + a = ((a / 360.0) * (m_maxX - m_minX)) + m_minX; + + const qreal deltaY = m_radius / qAbs(m_logOuterY - m_logInnerY); + qreal r = qPow(m_logBaseY, m_logInnerY + (line.length() / deltaY)); + + return QPointF(a, r); +} + +bool XLogYPolarDomain::attachAxis(QAbstractAxis *axis) +{ + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Vertical) { + QObject::connect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); + handleVerticalAxisBaseChanged(logAxis->base()); + } + return AbstractDomain::attachAxis(axis); +} + +bool XLogYPolarDomain::detachAxis(QAbstractAxis *axis) +{ + QLogValueAxis *logAxis = qobject_cast<QLogValueAxis *>(axis); + + if (logAxis && logAxis->orientation() == Qt::Vertical) + QObject::disconnect(logAxis, SIGNAL(baseChanged(qreal)), this, SLOT(handleVerticalAxisBaseChanged(qreal))); + + return AbstractDomain::detachAxis(axis); +} + +void XLogYPolarDomain::handleVerticalAxisBaseChanged(qreal baseY) +{ + m_logBaseY = baseY; + qreal logMinY = log10(m_minY) / log10(m_logBaseY); + qreal logMaxY = log10(m_maxY) / log10(m_logBaseY); + m_logInnerY = logMinY < logMaxY ? logMinY : logMaxY; + m_logOuterY = logMinY > logMaxY ? logMinY : logMaxY; + emit updated(); +} + +// operators + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XLogYPolarDomain &domain1, const XLogYPolarDomain &domain2) +{ + return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) + && qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) + && qFuzzyIsNull(domain1.m_minX - domain2.m_minX) + && qFuzzyIsNull(domain1.m_minY - domain2.m_minY)); +} + + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const XLogYPolarDomain &domain1, const XLogYPolarDomain &domain2) +{ + return !(domain1 == domain2); +} + + +QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const XLogYPolarDomain &domain) +{ + dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; + return dbg.maybeSpace(); +} + +#include "moc_xlogypolardomain_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/xlogypolardomain_p.h b/src/domain/xlogypolardomain_p.h new file mode 100644 index 00000000..3c3a45b7 --- /dev/null +++ b/src/domain/xlogypolardomain_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef XLOGYPOLARDOMAIN_H +#define XLOGYPOLARDOMAIN_H +#include "polardomain_p.h" +#include <QRectF> +#include <QSizeF> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_AUTOTEST_EXPORT XLogYPolarDomain: public PolarDomain +{ + Q_OBJECT +public: + explicit XLogYPolarDomain(QObject *object = 0); + virtual ~XLogYPolarDomain(); + + DomainType type() { return AbstractDomain::XLogYPolarDomain; } + + void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY); + + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XLogYPolarDomain &domain1, const XLogYPolarDomain &domain2); + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const XLogYPolarDomain &domain1, const XLogYPolarDomain &domain2); + friend QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const XLogYPolarDomain &domain); + + void zoomIn(const QRectF &rect); + void zoomOut(const QRectF &rect); + void move(qreal dx, qreal dy); + + QPointF calculateDomainPoint(const QPointF &point) const; + + bool attachAxis(QAbstractAxis *axis); + bool detachAxis(QAbstractAxis *axis); + +public Q_SLOTS: + void handleVerticalAxisBaseChanged(qreal baseY); + +protected: + qreal toAngularCoordinate(qreal value, bool &ok) const; + qreal toRadialCoordinate(qreal value, bool &ok) const; + +private: + qreal m_logInnerY; + qreal m_logOuterY; + qreal m_logBaseY; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // XLOGYPOLARDOMAIN_H diff --git a/src/domain/xydomain.cpp b/src/domain/xydomain.cpp index 26f82a15..8ac1f0a0 100644 --- a/src/domain/xydomain.cpp +++ b/src/domain/xydomain.cpp @@ -126,7 +126,7 @@ QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const return QPointF(x, y); } -QVector<QPointF> XYDomain::calculateGeometryPoints(const QList<QPointF>& vector) const +QVector<QPointF> XYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const { const qreal deltaX = m_size.width() / (m_maxX - m_minX); const qreal deltaY = m_size.height() / (m_maxY - m_minY); @@ -156,10 +156,10 @@ QPointF XYDomain::calculateDomainPoint(const QPointF &point) const bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XYDomain &domain1, const XYDomain &domain2) { - return (qFuzzyCompare(domain1.m_maxX, domain2.m_maxX) && - qFuzzyCompare(domain1.m_maxY, domain2.m_maxY) && - qFuzzyCompare(domain1.m_minX, domain2.m_minX) && - qFuzzyCompare(domain1.m_minY, domain2.m_minY)); + return (qFuzzyCompare(domain1.m_maxX, domain2.m_maxX) + && qFuzzyCompare(domain1.m_maxY, domain2.m_maxY) + && qFuzzyCompare(domain1.m_minX, domain2.m_minX) + && qFuzzyCompare(domain1.m_minY, domain2.m_minY)); } diff --git a/src/domain/xydomain_p.h b/src/domain/xydomain_p.h index 0a990388..657377b2 100644 --- a/src/domain/xydomain_p.h +++ b/src/domain/xydomain_p.h @@ -56,7 +56,7 @@ public: QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const; QPointF calculateDomainPoint(const QPointF &point) const; - QVector<QPointF> calculateGeometryPoints(const QList<QPointF>& vector) const; + QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/xypolardomain.cpp b/src/domain/xypolardomain.cpp new file mode 100644 index 00000000..e9471293 --- /dev/null +++ b/src/domain/xypolardomain.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xypolardomain_p.h" +#include "qabstractaxis_p.h" +#include <qmath.h> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +XYPolarDomain::XYPolarDomain(QObject *parent) + : PolarDomain(parent) +{ +} + +XYPolarDomain::~XYPolarDomain() +{ +} + +void XYPolarDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) +{ + bool axisXChanged = false; + bool axisYChanged = false; + + if (!qFuzzyCompare(m_minX, minX) || !qFuzzyCompare(m_maxX, maxX)) { + m_minX = minX; + m_maxX = maxX; + axisXChanged = true; + if (!m_signalsBlocked) + emit rangeHorizontalChanged(m_minX, m_maxX); + } + + if (!qFuzzyCompare(m_minY, minY) || !qFuzzyCompare(m_maxY, maxY)) { + m_minY = minY; + m_maxY = maxY; + axisYChanged = true; + if (!m_signalsBlocked) + emit rangeVerticalChanged(m_minY, m_maxY); + } + + if (axisXChanged || axisYChanged) + emit updated(); +} + + +void XYPolarDomain::zoomIn(const QRectF &rect) +{ + qreal dx = spanX() / m_size.width(); + qreal dy = spanY() / m_size.height(); + + qreal maxX = m_maxX; + qreal minX = m_minX; + qreal minY = m_minY; + qreal maxY = m_maxY; + + maxX = minX + dx * rect.right(); + minX = minX + dx * rect.left(); + minY = maxY - dy * rect.bottom(); + maxY = maxY - dy * rect.top(); + + setRange(minX, maxX, minY, maxY); +} + +void XYPolarDomain::zoomOut(const QRectF &rect) +{ + qreal dx = spanX() / rect.width(); + qreal dy = spanY() / rect.height(); + + qreal maxX = m_maxX; + qreal minX = m_minX; + qreal minY = m_minY; + qreal maxY = m_maxY; + + minX = maxX - dx * rect.right(); + maxX = minX + dx * m_size.width(); + maxY = minY + dy * rect.bottom(); + minY = maxY - dy * m_size.height(); + + setRange(minX, maxX, minY, maxY); +} + +void XYPolarDomain::move(qreal dx, qreal dy) +{ + // One unit scrolls one degree angular and one pixel radial + qreal x = spanX() / 360.0; + qreal y = spanY() / m_radius; + + qreal maxX = m_maxX; + qreal minX = m_minX; + qreal minY = m_minY; + qreal maxY = m_maxY; + + if (dx != 0) { + minX = minX + x * dx; + maxX = maxX + x * dx; + } + if (dy != 0) { + minY = minY + y * dy; + maxY = maxY + y * dy; + } + setRange(minX, maxX, minY, maxY); +} + +QPointF XYPolarDomain::calculateDomainPoint(const QPointF &point) const +{ + if (point == m_center) + return QPointF(0.0, m_minX); + + QLineF line(m_center, point); + qreal a = 90.0 - line.angle(); + if (a < 0.0) + a += 360.0; + a = ((a / 360.0) * (m_maxX - m_minX)) + m_minX; + qreal r = m_minY + ((m_maxY - m_minY) * (line.length() / m_radius)); + return QPointF(a, r); +} + +qreal XYPolarDomain::toAngularCoordinate(qreal value, bool &ok) const +{ + ok = true; + qreal f = (value - m_minX) / (m_maxX - m_minX); + return f * 360.0; +} + +qreal XYPolarDomain::toRadialCoordinate(qreal value, bool &ok) const +{ + ok = true; + if (value < m_minY) + value = m_minY; + + // Dont limit the max. The drawing should clip the stuff that goes out of the grid + qreal f = (value - m_minY) / (m_maxY - m_minY); + + return f * m_radius; +} + +// operators + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XYPolarDomain &domain1, const XYPolarDomain &domain2) +{ + return (qFuzzyCompare(domain1.m_maxX, domain2.m_maxX) + && qFuzzyCompare(domain1.m_maxY, domain2.m_maxY) + && qFuzzyCompare(domain1.m_minX, domain2.m_minX) + && qFuzzyCompare(domain1.m_minY, domain2.m_minY)); +} + + +bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const XYPolarDomain &domain1, const XYPolarDomain &domain2) +{ + return !(domain1 == domain2); +} + + +QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const XYPolarDomain &domain) +{ + dbg.nospace() << "AbstractDomain(" << domain.m_minX << ',' << domain.m_maxX << ',' << domain.m_minY << ',' << domain.m_maxY << ')' << domain.m_size; + return dbg.maybeSpace(); +} + +#include "moc_xypolardomain_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/domain/xypolardomain_p.h b/src/domain/xypolardomain_p.h new file mode 100644 index 00000000..662cc5ee --- /dev/null +++ b/src/domain/xypolardomain_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef XYPOLARDOMAIN_H +#define XYPOLARDOMAIN_H +#include "polardomain_p.h" +#include <QRectF> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_AUTOTEST_EXPORT XYPolarDomain: public PolarDomain +{ + Q_OBJECT +public: + explicit XYPolarDomain(QObject *object = 0); + virtual ~XYPolarDomain(); + + DomainType type(){ return AbstractDomain::XYPolarDomain;} + + void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY); + + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator== (const XYPolarDomain &Domain1, const XYPolarDomain &Domain2); + friend bool QTCOMMERCIALCHART_AUTOTEST_EXPORT operator!= (const XYPolarDomain &Domain1, const XYPolarDomain &Domain2); + friend QDebug QTCOMMERCIALCHART_AUTOTEST_EXPORT operator<<(QDebug dbg, const XYPolarDomain &AbstractDomain); + + void zoomIn(const QRectF &rect); + void zoomOut(const QRectF &rect); + void move(qreal dx, qreal dy); + + QPointF calculateDomainPoint(const QPointF &point) const; + +protected: + qreal toAngularCoordinate(qreal value, bool &ok) const; + qreal toRadialCoordinate(qreal value, bool &ok) const; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // XYPOLARDOMAIN_H diff --git a/src/layout/abstractchartlayout.cpp b/src/layout/abstractchartlayout.cpp new file mode 100644 index 00000000..092e8c0c --- /dev/null +++ b/src/layout/abstractchartlayout.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractchartlayout_p.h" +#include "chartpresenter_p.h" +#include "qlegend_p.h" +#include "chartaxiselement_p.h" +#include "charttitle_p.h" +#include "chartbackground_p.h" +#include <QDebug> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +static const qreal golden_ratio = 0.4; + +AbstractChartLayout::AbstractChartLayout(ChartPresenter *presenter) + : m_presenter(presenter), + m_margins(20, 20, 20, 20), + m_minChartRect(0, 0, 200, 200) +{ +} + +AbstractChartLayout::~AbstractChartLayout() +{ +} + +void AbstractChartLayout::setGeometry(const QRectF &rect) +{ + if (!rect.isValid()) + return; + + QList<ChartAxisElement *> axes = m_presenter->axisItems(); + ChartTitle *title = m_presenter->titleElement(); + QLegend *legend = m_presenter->legend(); + ChartBackground *background = m_presenter->backgroundElement(); + + QRectF contentGeometry = calculateBackgroundGeometry(rect, background); + + contentGeometry = calculateContentGeometry(contentGeometry); + + if (title && title->isVisible()) + contentGeometry = calculateTitleGeometry(contentGeometry, title); + + if (legend->isAttachedToChart() && legend->isVisible()) + contentGeometry = calculateLegendGeometry(contentGeometry, legend); + + contentGeometry = calculateAxisGeometry(contentGeometry, axes); + + m_presenter->setGeometry(contentGeometry); + + QGraphicsLayout::setGeometry(rect); +} + +QRectF AbstractChartLayout::calculateContentGeometry(const QRectF &geometry) const +{ + return geometry.adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom()); +} + +QRectF AbstractChartLayout::calculateContentMinimum(const QRectF &minimum) const +{ + return minimum.adjusted(0, 0, m_margins.left() + m_margins.right(), m_margins.top() + m_margins.bottom()); +} + + +QRectF AbstractChartLayout::calculateBackgroundGeometry(const QRectF &geometry, ChartBackground *background) const +{ + qreal left; + qreal top; + qreal right; + qreal bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRectF backgroundGeometry = geometry.adjusted(left, top, -right, -bottom); + if (background) + background->setRect(backgroundGeometry); + return backgroundGeometry; +} + +QRectF AbstractChartLayout::calculateBackgroundMinimum(const QRectF &minimum) const +{ + qreal left; + qreal top; + qreal right; + qreal bottom; + getContentsMargins(&left, &top, &right, &bottom); + return minimum.adjusted(0, 0, left + right, top + bottom); +} + +QRectF AbstractChartLayout::calculateLegendGeometry(const QRectF &geometry, QLegend *legend) const +{ + QSizeF size = legend->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1)); + QRectF legendRect; + QRectF result; + + switch (legend->alignment()) { + case Qt::AlignTop: { + legendRect = QRectF(geometry.topLeft(), QSizeF(geometry.width(), size.height())); + result = geometry.adjusted(0, legendRect.height(), 0, 0); + break; + } + case Qt::AlignBottom: { + legendRect = QRectF(QPointF(geometry.left(), geometry.bottom() - size.height()), QSizeF(geometry.width(), size.height())); + result = geometry.adjusted(0, 0, 0, -legendRect.height()); + break; + } + case Qt::AlignLeft: { + qreal width = qMin(size.width(), geometry.width() * golden_ratio); + legendRect = QRectF(geometry.topLeft(), QSizeF(width, geometry.height())); + result = geometry.adjusted(width, 0, 0, 0); + break; + } + case Qt::AlignRight: { + qreal width = qMin(size.width(), geometry.width() * golden_ratio); + legendRect = QRectF(QPointF(geometry.right() - width, geometry.top()), QSizeF(width, geometry.height())); + result = geometry.adjusted(0, 0, -width, 0); + break; + } + default: { + legendRect = QRectF(0, 0, 0, 0); + result = geometry; + break; + } + } + + legend->setGeometry(legendRect); + + return result; +} + +QRectF AbstractChartLayout::calculateLegendMinimum(const QRectF &geometry, QLegend *legend) const +{ + QSizeF minSize = legend->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1)); + return geometry.adjusted(0, 0, minSize.width(), minSize.height()); +} + +QRectF AbstractChartLayout::calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const +{ + title->setGeometry(geometry); + QPointF center = geometry.center() - title->boundingRect().center(); + title->setPos(center.x(), title->pos().y()); + return geometry.adjusted(0, title->boundingRect().height()+1, 0, 0); +} + +QRectF AbstractChartLayout::calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const +{ + QSizeF min = title->sizeHint(Qt::MinimumSize); + return minimum.adjusted(0, 0, min.width(), min.height()); +} + +QSizeF AbstractChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(constraint); + if (which == Qt::MinimumSize) { + QList<ChartAxisElement *> axes = m_presenter->axisItems(); + ChartTitle *title = m_presenter->titleElement(); + QLegend *legend = m_presenter->legend(); + QRectF minimumRect(0, 0, 0, 0); + minimumRect = calculateBackgroundMinimum(minimumRect); + minimumRect = calculateContentMinimum(minimumRect); + minimumRect = calculateTitleMinimum(minimumRect, title); + minimumRect = calculateLegendMinimum(minimumRect, legend); + minimumRect = calculateAxisMinimum(minimumRect, axes); + return minimumRect.united(m_minChartRect).size().toSize(); + } + return QSize(-1, -1); +} + +void AbstractChartLayout::setMargins(const QMargins &margins) +{ + if (m_margins != margins) { + m_margins = margins; + updateGeometry(); + } +} + +QMargins AbstractChartLayout::margins() const +{ + return m_margins; +} + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/chartlayout_p.h b/src/layout/abstractchartlayout_p.h index 5b8d38a8..d28af3d2 100644 --- a/src/chartlayout_p.h +++ b/src/layout/abstractchartlayout_p.h @@ -27,51 +27,50 @@ // // We mean it. -#ifndef CHARTLAYOUT_H -#define CHARTLAYOUT_H +#ifndef ABSTRACTCHARTLAYOUT_H +#define ABSTRACTCHARTLAYOUT_H + #include <QGraphicsLayout> #include <QMargins> #include "qchartglobal.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE -class ChartPresenter; class ChartTitle; +class ChartAxisElement; +class ChartPresenter; class QLegend; -class ChartAxis; class ChartBackground; -class ChartLayout : public QGraphicsLayout +class AbstractChartLayout : public QGraphicsLayout { public: + AbstractChartLayout(ChartPresenter *presenter); + virtual ~AbstractChartLayout(); - ChartLayout(ChartPresenter *presenter); - virtual ~ChartLayout(); + virtual void setMargins(const QMargins &margins); + virtual QMargins margins() const; + virtual void setGeometry(const QRectF &rect); - void setMargins(const QMargins &margins); - QMargins margins() const; +protected: + virtual QRectF calculateBackgroundGeometry(const QRectF &geometry, ChartBackground *background) const; + virtual QRectF calculateBackgroundMinimum(const QRectF &minimum) const; + virtual QRectF calculateContentGeometry(const QRectF &geometry) const; + virtual QRectF calculateContentMinimum(const QRectF &minimum) const; + virtual QRectF calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const; + virtual QRectF calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const; + virtual QRectF calculateLegendGeometry(const QRectF &geometry, QLegend *legend) const; + virtual QRectF calculateLegendMinimum(const QRectF &minimum, QLegend *legend) const; - void setGeometry(const QRectF &rect); + virtual QRectF calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxisElement *>& axes) const = 0; + virtual QRectF calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *>& axes) const = 0; -protected: + // from QGraphicsLayout QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; int count() const { return 0; } QGraphicsLayoutItem *itemAt(int) const { return 0; }; void removeAt(int) {}; -private: - QRectF calculateBackgroundGeometry(const QRectF &geometry, ChartBackground *background) const; - QRectF calculateContentGeometry(const QRectF &geometry) const; - QRectF calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const; - QRectF calculateLegendGeometry(const QRectF &geometry, QLegend *legend) const; - QRectF calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxis *>& axes) const; - QRectF calculateBackgroundMinimum(const QRectF &minimum) const; - QRectF calculateContentMinimum(const QRectF &minimum) const; - QRectF calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const; - QRectF calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxis *>& axes) const; - QRectF calculateLegendMinimum(const QRectF &minimum, QLegend *legend) const; - -private: ChartPresenter *m_presenter; QMargins m_margins; QRectF m_minChartRect; @@ -80,4 +79,4 @@ private: QTCOMMERCIALCHART_END_NAMESPACE -#endif +#endif // ABSTRACTCHARTLAYOUT_H diff --git a/src/chartlayout.cpp b/src/layout/cartesianchartlayout.cpp index bd885ba9..f5317752 100644 --- a/src/chartlayout.cpp +++ b/src/layout/cartesianchartlayout.cpp @@ -18,88 +18,25 @@ ** ****************************************************************************/ -#include "chartlayout_p.h" +#include "cartesianchartlayout_p.h" #include "chartpresenter_p.h" -#include "qlegend_p.h" -#include "chartaxis_p.h" -#include "charttitle_p.h" -#include "chartbackground_p.h" +#include "chartaxiselement_p.h" #include <QDebug> QTCOMMERCIALCHART_BEGIN_NAMESPACE static const qreal maxAxisPortion = 0.4; -ChartLayout::ChartLayout(ChartPresenter *presenter) - : m_presenter(presenter), - m_margins(20, 20, 20, 20), - m_minChartRect(0, 0, 200, 200) +CartesianChartLayout::CartesianChartLayout(ChartPresenter *presenter) + : AbstractChartLayout(presenter) { - -} - -ChartLayout::~ChartLayout() -{ - -} - -void ChartLayout::setGeometry(const QRectF &rect) -{ - if (!rect.isValid()) - return; - - QList<ChartAxis *> axes = m_presenter->axisItems(); - ChartTitle *title = m_presenter->titleElement(); - QLegend *legend = m_presenter->legend(); - ChartBackground *background = m_presenter->backgroundElement(); - - QRectF contentGeometry = calculateBackgroundGeometry(rect, background); - - contentGeometry = calculateContentGeometry(contentGeometry); - - if (title && title->isVisible()) - contentGeometry = calculateTitleGeometry(contentGeometry, title); - - if (legend->isAttachedToChart() && legend->isVisible()) - contentGeometry = calculateLegendGeometry(contentGeometry, legend); - - contentGeometry = calculateAxisGeometry(contentGeometry, axes); - - m_presenter->setGeometry(contentGeometry); - - QGraphicsLayout::setGeometry(rect); } -QRectF ChartLayout::calculateContentGeometry(const QRectF &geometry) const +CartesianChartLayout::~CartesianChartLayout() { - return geometry.adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom()); } -QRectF ChartLayout::calculateContentMinimum(const QRectF &minimum) const -{ - return minimum.adjusted(0, 0, m_margins.left() + m_margins.right(), m_margins.top() + m_margins.bottom()); -} - - -QRectF ChartLayout::calculateBackgroundGeometry(const QRectF &geometry, ChartBackground *background) const -{ - qreal left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - QRectF backgroundGeometry = geometry.adjusted(left, top, -right, -bottom); - if (background) - background->setRect(backgroundGeometry); - return backgroundGeometry; -} - -QRectF ChartLayout::calculateBackgroundMinimum(const QRectF &minimum) const -{ - qreal left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - return minimum.adjusted(0, 0, left + right, top + bottom); -} - - -QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxis *>& axes) const +QRectF CartesianChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxisElement *> &axes) const { QSizeF left(0,0); QSizeF minLeft(0,0); @@ -115,16 +52,17 @@ QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<Ch int topCount = 0; int bottomCount = 0; - foreach (ChartAxis *axis , axes) { + foreach (ChartAxisElement *axis , axes) { if (!axis->isVisible()) continue; + QSizeF size = axis->effectiveSizeHint(Qt::PreferredSize); //this is used to get single thick font size QSizeF minSize = axis->effectiveSizeHint(Qt::MinimumSize); - switch (axis->alignment()) { + switch (axis->axis()->alignment()) { case Qt::AlignLeft: left.setWidth(left.width()+size.width()); left.setHeight(qMax(left.height(),size.height())); @@ -163,7 +101,7 @@ QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<Ch } } - int totalVerticalAxes = leftCount + rightCount; + qreal totalVerticalAxes = leftCount + rightCount; qreal leftSqueezeRatio = 1.0; qreal rightSqueezeRatio = 1.0; qreal vratio = 0; @@ -186,7 +124,7 @@ QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<Ch } } - int totalHorizontalAxes = topCount + bottomCount; + qreal totalHorizontalAxes = topCount + bottomCount; qreal topSqueezeRatio = 1.0; qreal bottomSqueezeRatio = 1.0; qreal hratio = 0; @@ -225,15 +163,14 @@ QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<Ch qreal topOffset = 0; qreal bottomOffset = 0; - foreach(ChartElement *axisElement , axes) { - ChartAxis* axis = qobject_cast<ChartAxis*>(axisElement); + foreach (ChartAxisElement *axis , axes) { if (!axis->isVisible()) continue; QSizeF size = axis->effectiveSizeHint(Qt::PreferredSize); - switch(axis->alignment()){ + switch (axis->axis()->alignment()){ case Qt::AlignLeft:{ qreal width = size.width(); if (leftSqueezeRatio < 1.0) @@ -271,21 +208,20 @@ QRectF ChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<Ch return chartRect; } -QRectF ChartLayout::calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxis *>& axes) const +QRectF CartesianChartLayout::calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *> &axes) const { QSizeF left; QSizeF right; QSizeF bottom; QSizeF top; - foreach (ChartAxis *axis, axes) { - + foreach (ChartAxisElement *axis, axes) { QSizeF size = axis->effectiveSizeHint(Qt::MinimumSize); if (!axis->isVisible()) continue; - switch (axis->alignment()) { + switch (axis->axis()->alignment()) { case Qt::AlignLeft: left.setWidth(left.width() + size.width()); left.setHeight(qMax(left.height() * 2, size.height())); @@ -307,96 +243,4 @@ QRectF ChartLayout::calculateAxisMinimum(const QRectF &minimum, const QList<Char return minimum.adjusted(0, 0, left.width() + right.width() + qMax(top.width(), bottom.width()), top.height() + bottom.height() + qMax(left.height(), right.height())); } -QRectF ChartLayout::calculateLegendGeometry(const QRectF &geometry, QLegend *legend) const -{ - QSizeF size = legend->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1)); - QRectF legendRect; - QRectF result; - - switch (legend->alignment()) { - case Qt::AlignTop: { - legendRect = QRectF(geometry.topLeft(), QSizeF(geometry.width(), size.height())); - result = geometry.adjusted(0, legendRect.height(), 0, 0); - break; - } - case Qt::AlignBottom: { - legendRect = QRectF(QPointF(geometry.left(), geometry.bottom() - size.height()), QSizeF(geometry.width(), size.height())); - result = geometry.adjusted(0, 0, 0, -legendRect.height()); - break; - } - case Qt::AlignLeft: { - qreal width = qMin(size.width(), geometry.width() * maxAxisPortion); - legendRect = QRectF(geometry.topLeft(), QSizeF(width, geometry.height())); - result = geometry.adjusted(width, 0, 0, 0); - break; - } - case Qt::AlignRight: { - qreal width = qMin(size.width(), geometry.width() * maxAxisPortion); - legendRect = QRectF(QPointF(geometry.right() - width, geometry.top()), QSizeF(width, geometry.height())); - result = geometry.adjusted(0, 0, -width, 0); - break; - } - default: { - legendRect = QRectF(0, 0, 0, 0); - result = geometry; - break; - } - } - - legend->setGeometry(legendRect); - - return result; -} - -QRectF ChartLayout::calculateLegendMinimum(const QRectF &geometry, QLegend *legend) const -{ - QSizeF minSize = legend->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1)); - return geometry.adjusted(0, 0, minSize.width(), minSize.height()); -} - -QRectF ChartLayout::calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const -{ - title->setGeometry(geometry); - QPointF center = geometry.center() - title->boundingRect().center(); - title->setPos(center.x(),title->pos().y()); - return geometry.adjusted(0,title->boundingRect().height()+1,0,0); -} - -QRectF ChartLayout::calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const -{ - QSizeF min = title->sizeHint(Qt::MinimumSize); - return minimum.adjusted(0, 0, min.width(), min.height()); -} - -QSizeF ChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const -{ - Q_UNUSED(constraint); - if (which == Qt::MinimumSize) { - QList<ChartAxis *> axes = m_presenter->axisItems(); - ChartTitle *title = m_presenter->titleElement(); - QLegend *legend = m_presenter->legend(); - QRectF minimumRect(0, 0, 0, 0); - minimumRect = calculateBackgroundMinimum(minimumRect); - minimumRect = calculateContentMinimum(minimumRect); - minimumRect = calculateTitleMinimum(minimumRect, title); - minimumRect = calculateLegendMinimum(minimumRect, legend); - minimumRect = calculateAxisMinimum(minimumRect, axes); - return minimumRect.united(m_minChartRect).size().toSize(); - } - return QSize(-1, -1); -} - -void ChartLayout::setMargins(const QMargins &margins) -{ - if (m_margins != margins) { - m_margins = margins; - updateGeometry(); - } -} - -QMargins ChartLayout::margins() const -{ - return m_margins; -} - QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/layout/cartesianchartlayout_p.h b/src/layout/cartesianchartlayout_p.h new file mode 100644 index 00000000..88d540b0 --- /dev/null +++ b/src/layout/cartesianchartlayout_p.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CARTESIANCHARTLAYOUT_H +#define CARTESIANCHARTLAYOUT_H + +#include "abstractchartlayout_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class CartesianChartLayout : public AbstractChartLayout +{ +public: + CartesianChartLayout(ChartPresenter *presenter); + virtual ~CartesianChartLayout(); + + // from AbstractChartLayout + QRectF calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *> &axes) const; + QRectF calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxisElement *> &axes) const; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // CARTESIANCHARTLAYOUT_H diff --git a/src/layout/layout.pri b/src/layout/layout.pri new file mode 100644 index 00000000..159eab13 --- /dev/null +++ b/src/layout/layout.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +SOURCES += \ + $$PWD/abstractchartlayout.cpp \ + $$PWD/cartesianchartlayout.cpp \ + $$PWD/polarchartlayout.cpp + +PRIVATE_HEADERS += \ + $$PWD/abstractchartlayout_p.h \ + $$PWD/cartesianchartlayout_p.h \ + $$PWD/polarchartlayout_p.h diff --git a/src/layout/polarchartlayout.cpp b/src/layout/polarchartlayout.cpp new file mode 100644 index 00000000..ad48244a --- /dev/null +++ b/src/layout/polarchartlayout.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "polarchartlayout_p.h" +#include "chartpresenter_p.h" +#include "polarchartaxis_p.h" +#include <QDebug> +#include <QFontMetrics> + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +static const qreal golden_ratio = 0.4; + +PolarChartLayout::PolarChartLayout(ChartPresenter *presenter) + : AbstractChartLayout(presenter) +{ +} + +PolarChartLayout::~PolarChartLayout() +{ +} + +QRectF PolarChartLayout::calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxisElement *> &axes) const +{ + // How to handle multiple angular/radial axes? + qreal axisRadius = geometry.height() / 2.0; + if (geometry.width() < geometry.height()) + axisRadius = geometry.width() / 2.0; + + int titleHeight = 0; + foreach (ChartAxisElement *chartAxis, axes) { + if (!chartAxis->isVisible()) + continue; + + PolarChartAxis *polarChartAxis = static_cast<PolarChartAxis *>(chartAxis); + qreal radius = polarChartAxis->preferredAxisRadius(geometry.size()); + if (radius < axisRadius) + axisRadius = radius; + + if (chartAxis->axis()->orientation() == Qt::Horizontal + && chartAxis->axis()->isTitleVisible() + && !chartAxis->axis()->titleText().isEmpty()) { + // If axis has angular title, adjust geometry down by the space title takes + QFontMetrics titleMetrics(chartAxis->axis()->titleFont()); + titleHeight = (titleMetrics.boundingRect(chartAxis->axis()->titleText()).height() / 2) + chartAxis->titlePadding(); + } + } + + QRectF axisRect; + axisRect.setSize(QSizeF(axisRadius * 2.0, axisRadius * 2.0)); + axisRect.moveCenter(geometry.center()); + axisRect.adjust(0, titleHeight, 0, titleHeight); + + foreach (ChartAxisElement *chartAxis, axes) + chartAxis->setGeometry(axisRect, QRectF()); + + return axisRect; +} + +QRectF PolarChartLayout::calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *> &axes) const +{ + Q_UNUSED(axes); + return minimum; +} + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/layout/polarchartlayout_p.h b/src/layout/polarchartlayout_p.h new file mode 100644 index 00000000..e8d5db44 --- /dev/null +++ b/src/layout/polarchartlayout_p.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef POLARCHARTLAYOUT_H +#define POLARCHARTLAYOUT_H + +#include "abstractchartlayout_p.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class PolarChartLayout : public AbstractChartLayout +{ +public: + PolarChartLayout(ChartPresenter *presenter); + virtual ~PolarChartLayout(); + + // from AbstractChartLayout + QRectF calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *> &axes) const; + QRectF calculateAxisGeometry(const QRectF &geometry, const QList<ChartAxisElement *> &axes) const; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // POLARCHARTLAYOUT_H diff --git a/src/legend/legendlayout.cpp b/src/legend/legendlayout.cpp index e405c793..6d5f8328 100644 --- a/src/legend/legendlayout.cpp +++ b/src/legend/legendlayout.cpp @@ -21,7 +21,7 @@ #include "legendlayout_p.h" #include "chartpresenter_p.h" #include "qlegend_p.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include "qlegendmarker_p.h" #include "legendmarkeritem_p.h" diff --git a/src/legend/qlegend.cpp b/src/legend/qlegend.cpp index 8a34afe3..1920d258 100644 --- a/src/legend/qlegend.cpp +++ b/src/legend/qlegend.cpp @@ -25,7 +25,7 @@ #include "qchart_p.h" #include "legendlayout_p.h" #include "chartpresenter_p.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include "qlegendmarker.h" #include "qlegendmarker_p.h" #include "legendmarkeritem_p.h" diff --git a/src/linechart/linechartitem.cpp b/src/linechart/linechartitem.cpp index 1f10d53e..dd5f540d 100644 --- a/src/linechart/linechartitem.cpp +++ b/src/linechart/linechartitem.cpp @@ -22,7 +22,7 @@ #include "qlineseries.h" #include "qlineseries_p.h" #include "chartpresenter_p.h" -#include "abstractdomain_p.h" +#include "polardomain_p.h" #include <QPainter> #include <QGraphicsSceneMouseEvent> @@ -30,10 +30,11 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE const qreal mouseEventMinWidth(12); -LineChartItem::LineChartItem(QLineSeries *series,QGraphicsItem* item) +LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item) : XYChart(series,item), m_series(series), - m_pointsVisible(false) + m_pointsVisible(false), + m_chartType(QChart::ChartTypeUndefined) { setAcceptHoverEvents(true); setZValue(ChartPresenter::LineChartZValue); @@ -50,54 +51,235 @@ QRectF LineChartItem::boundingRect() const QPainterPath LineChartItem::shape() const { - return m_path; + return m_shapePath; } void LineChartItem::updateGeometry() { m_points = geometryPoints(); + const QVector<QPointF> &points = m_points; - if (m_points.size() == 0) { + if (points.size() == 0) { prepareGeometryChange(); - m_path = QPainterPath(); + m_fullPath = QPainterPath(); m_linePath = QPainterPath(); m_rect = QRect(); return; } - QPainterPath linePath(m_points.at(0)); + QPainterPath linePath; + QPainterPath fullPath; + // Use worst case scenario to determine required margin. + qreal margin = m_linePen.width() * 1.42; - if (m_pointsVisible) { + // Area series use component line series that aren't necessarily added to the chart themselves, + // so check if chart type is forced before trying to obtain it from the chart. + QChart::ChartType chartType = m_chartType; + if (chartType == QChart::ChartTypeUndefined) + chartType = m_series->chart()->chartType(); + // For polar charts, we need special handling for angular (horizontal) + // points that are off-grid. + if (chartType == QChart::ChartTypePolar) { + QPainterPath linePathLeft; + QPainterPath linePathRight; + QPainterPath *currentSegmentPath = 0; + QPainterPath *previousSegmentPath = 0; + qreal minX = domain()->minX(); + qreal maxX = domain()->maxX(); + qreal minY = domain()->minY(); + QPointF currentSeriesPoint = m_series->pointAt(0); + QPointF currentGeometryPoint = points.at(0); + QPointF previousGeometryPoint = points.at(0); int size = m_linePen.width(); - linePath.addEllipse(m_points.at(0), size, size); - linePath.moveTo(m_points.at(0)); - for (int i = 1; i < m_points.size(); i++) { - linePath.lineTo(m_points.at(i)); - linePath.addEllipse(m_points.at(i), size, size); - linePath.moveTo(m_points.at(i)); + bool pointOffGrid = false; + bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX); + + qreal domainRadius = domain()->size().height() / 2.0; + const QPointF centerPoint(domainRadius, domainRadius); + + if (!previousPointWasOffGrid) { + fullPath.moveTo(points.at(0)); + if (m_pointsVisible && currentSeriesPoint.y() >= minY) { + // Do not draw ellipses for points below minimum Y. + linePath.addEllipse(points.at(0), size, size); + fullPath.addEllipse(points.at(0), size, size); + linePath.moveTo(points.at(0)); + fullPath.moveTo(points.at(0)); + } } - } else { - for (int i = 1; i < m_points.size(); i++) - linePath.lineTo(m_points.at(i)); - } + qreal leftMarginLine = centerPoint.x() - margin; + qreal rightMarginLine = centerPoint.x() + margin; + qreal horizontal = centerPoint.y(); - m_linePath = linePath; + for (int i = 1; i < points.size(); i++) { + // Interpolating line fragments would be ugly when thick pen is used, + // so we work around it by utilizing three separate + // paths for line segments and clip those with custom regions at paint time. + // "Right" path contains segments that cross the axis line with visible point on the + // right side of the axis line, as well as segments that have one point within the margin + // on the right side of the axis line and another point on the right side of the chart. + // "Left" path contains points with similarly on the left side. + // "Full" path contains rest of the points. + // This doesn't yield perfect results always. E.g. when segment covers more than 90 + // degrees and both of the points are within the margin, one in the top half and one in the + // bottom half of the chart, the bottom one gets clipped incorrectly. + // However, this should be rare occurrence in any sensible chart. + currentSeriesPoint = m_series->pointAt(i); + currentGeometryPoint = points.at(i); + pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX); + + // Draw something unless both off-grid + if (!pointOffGrid || !previousPointWasOffGrid) { + QPointF intersectionPoint; + qreal y; + if (pointOffGrid != previousPointWasOffGrid) { + if (currentGeometryPoint.x() == previousGeometryPoint.x()) { + y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0; + } else { + qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x()); + y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio; + } + intersectionPoint = QPointF(centerPoint.x(), y); + } + + bool dummyOk; // We know points are ok, but this is needed + qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk); + qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->pointAt(i - 1).x(), dummyOk); + + if ((qAbs(currentAngle - previousAngle) > 180.0)) { + // If the angle between two points is over 180 degrees (half X range), + // any direct segment between them becomes meaningless. + // In this case two line segments are drawn instead, from previous + // point to the center and from center to current point. + if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine)) + && previousGeometryPoint.y() < horizontal) { + currentSegmentPath = &linePathRight; + } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine)) + && previousGeometryPoint.y() < horizontal) { + currentSegmentPath = &linePathLeft; + } else if (previousAngle > 0.0 && previousAngle < 360.0) { + currentSegmentPath = &linePath; + } else { + currentSegmentPath = 0; + } + + if (currentSegmentPath) { + if (previousSegmentPath != currentSegmentPath) + currentSegmentPath->moveTo(previousGeometryPoint); + if (previousPointWasOffGrid) + fullPath.moveTo(intersectionPoint); + + currentSegmentPath->lineTo(centerPoint); + fullPath.lineTo(centerPoint); + } + + previousSegmentPath = currentSegmentPath; + + if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine)) + && currentGeometryPoint.y() < horizontal) { + currentSegmentPath = &linePathRight; + } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &¤tGeometryPoint.x() > leftMarginLine)) + && currentGeometryPoint.y() < horizontal) { + currentSegmentPath = &linePathLeft; + } else if (currentAngle > 0.0 && currentAngle < 360.0) { + currentSegmentPath = &linePath; + } else { + currentSegmentPath = 0; + } + + if (currentSegmentPath) { + if (previousSegmentPath != currentSegmentPath) + currentSegmentPath->moveTo(centerPoint); + if (!previousSegmentPath) + fullPath.moveTo(centerPoint); + + currentSegmentPath->lineTo(currentGeometryPoint); + if (pointOffGrid) + fullPath.lineTo(intersectionPoint); + else + fullPath.lineTo(currentGeometryPoint); + } + } else { + if (previousAngle < 0.0 || currentAngle < 0.0 + || ((previousAngle <= 180.0 && currentAngle <= 180.0) + && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal) + || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) { + currentSegmentPath = &linePathRight; + } else if (previousAngle > 360.0 || currentAngle > 360.0 + || ((previousAngle > 180.0 && currentAngle > 180.0) + && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal) + || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) { + currentSegmentPath = &linePathLeft; + } else { + currentSegmentPath = &linePath; + } + + if (currentSegmentPath != previousSegmentPath) + currentSegmentPath->moveTo(previousGeometryPoint); + if (previousPointWasOffGrid) + fullPath.moveTo(intersectionPoint); + + if (pointOffGrid) + fullPath.lineTo(intersectionPoint); + else + fullPath.lineTo(currentGeometryPoint); + currentSegmentPath->lineTo(currentGeometryPoint); + } + } else { + currentSegmentPath = 0; + } + + previousPointWasOffGrid = pointOffGrid; + if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) { + linePath.addEllipse(points.at(i), size, size); + fullPath.addEllipse(points.at(i), size, size); + linePath.moveTo(points.at(i)); + fullPath.moveTo(points.at(i)); + } + previousSegmentPath = currentSegmentPath; + previousGeometryPoint = currentGeometryPoint; + } + m_linePathPolarRight = linePathRight; + m_linePathPolarLeft = linePathLeft; + // Note: This construction of m_fullpath is not perfect. The partial segments that are + // outside left/right clip regions at axis boundary still generate hover/click events, + // because shape doesn't get clipped. It doesn't seem possible to do sensibly. + } else { // not polar + linePath.moveTo(points.at(0)); + if (m_pointsVisible) { + int size = m_linePen.width(); + linePath.addEllipse(points.at(0), size, size); + linePath.moveTo(points.at(0)); + for (int i = 1; i < points.size(); i++) { + linePath.lineTo(points.at(i)); + linePath.addEllipse(points.at(i), size, size); + linePath.moveTo(points.at(i)); + } + } else { + for (int i = 1; i < points.size(); i++) + linePath.lineTo(points.at(i)); + } + fullPath = linePath; + } QPainterPathStroker stroker; // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin. // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and // multiply line width with square root of two when defining shape and bounding rectangle. - stroker.setWidth(m_linePen.width() * 1.42); + stroker.setWidth(margin); stroker.setJoinStyle(Qt::MiterJoin); stroker.setCapStyle(Qt::SquareCap); stroker.setMiterLimit(m_linePen.miterLimit()); prepareGeometryChange(); - m_path = stroker.createStroke(linePath); - m_rect = m_path.boundingRect(); + m_linePath = linePath; + m_fullPath = fullPath; + m_shapePath = stroker.createStroke(fullPath); + + m_rect = m_shapePath.boundingRect(); } void LineChartItem::handleUpdated() @@ -121,16 +303,35 @@ void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt Q_UNUSED(widget) Q_UNUSED(option) + QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); + painter->save(); painter->setPen(m_linePen); - painter->setClipRect(QRectF(QPointF(0,0),domain()->size())); + bool alwaysUsePath = false; + + if (m_series->chart()->chartType() == QChart::ChartTypePolar) { + qreal halfWidth = domain()->size().width() / 2.0; + QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height()); + QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height()); + QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse); + QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect())); + QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect())); + painter->setClipRegion(clipRegionLeft); + painter->drawPath(m_linePathPolarLeft); + painter->setClipRegion(clipRegionRight); + painter->drawPath(m_linePathPolarRight); + painter->setClipRegion(fullPolarClipRegion); + alwaysUsePath = true; // required for proper clipping + } else { + painter->setClipRect(clipRect); + } if (m_pointsVisible) { painter->setBrush(m_linePen.color()); painter->drawPath(m_linePath); } else { painter->setBrush(QBrush(Qt::NoBrush)); - if (m_linePen.style() != Qt::SolidLine) { + if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) { // If pen style is not solid line, always fall back to path painting // to ensure proper continuity of the pattern painter->drawPath(m_linePath); diff --git a/src/linechart/linechartitem_p.h b/src/linechart/linechartitem_p.h index 6db4ccfb..2a728f3b 100644 --- a/src/linechart/linechartitem_p.h +++ b/src/linechart/linechartitem_p.h @@ -32,6 +32,7 @@ #include "qchartglobal.h" #include "xychart_p.h" +#include "qchart.h" #include <QPen> QTCOMMERCIALCHART_BEGIN_NAMESPACE @@ -44,7 +45,7 @@ class LineChartItem : public XYChart Q_OBJECT Q_INTERFACES(QGraphicsItem) public: - explicit LineChartItem(QLineSeries *series, QGraphicsItem* item = 0); + explicit LineChartItem(QLineSeries *series, QGraphicsItem *item = 0); ~LineChartItem() {} //from QGraphicsItem @@ -52,7 +53,7 @@ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QPainterPath shape() const; - QPainterPath path() const { return m_linePath; } + QPainterPath path() const { return m_fullPath; } public Q_SLOTS: void handleUpdated(); @@ -63,15 +64,21 @@ protected: void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); void suppressPoints() { m_pointsVisible = false; } + void forceChartType(QChart::ChartType chartType) { m_chartType = chartType; } private: QLineSeries *m_series; - QPainterPath m_path; QPainterPath m_linePath; + QPainterPath m_linePathPolarRight; + QPainterPath m_linePathPolarLeft; + QPainterPath m_fullPath; + QPainterPath m_shapePath; + QVector<QPointF> m_points; QRectF m_rect; QPen m_linePen; bool m_pointsVisible; + QChart::ChartType m_chartType; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/qchart.cpp b/src/qchart.cpp index 8ab736ec..f2fd1b67 100644 --- a/src/qchart.cpp +++ b/src/qchart.cpp @@ -24,7 +24,7 @@ #include "qlegend_p.h" #include "chartbackground_p.h" #include "qabstractaxis.h" -#include "chartlayout_p.h" +#include "abstractchartlayout_p.h" #include "charttheme_p.h" #include "chartpresenter_p.h" #include "chartdataset_p.h" @@ -59,6 +59,16 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE */ /*! + \enum QChart::ChartType + + This enum describes the chart type. + + \value ChartTypeUndefined + \value ChartTypeCartesian + \value ChartTypePolar + */ + +/*! \class QChart \brief QtCommercial chart API. @@ -110,15 +120,34 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE */ /*! - Constructs a chart object which is a child of a\a parent. Parameter \a wFlags is passed to the QGraphicsWidget constructor. + \property QChart::chartType + Chart type indicates if the chart is a cartesian chart or a polar chart. + This property is set internally and is read only. + \sa QPolarChart + */ + +/*! + \internal + Constructs a chart object of \a type which is a child of a \a parent. + Parameter \a wFlags is passed to the QGraphicsWidget constructor. + This constructor is called only by subclasses. +*/ +QChart::QChart(QChart::ChartType type, QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(parent, wFlags), + d_ptr(new QChartPrivate(this, type)) +{ + d_ptr->init(); +} + +/*! + Constructs a chart object which is a child of a \a parent. + Parameter \a wFlags is passed to the QGraphicsWidget constructor. */ QChart::QChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) : QGraphicsWidget(parent, wFlags), - d_ptr(new QChartPrivate(this)) + d_ptr(new QChartPrivate(this, ChartTypeCartesian)) { - d_ptr->m_legend = new LegendScroller(this); - setTheme(QChart::ChartThemeLight); - setLayout(d_ptr->m_presenter->layout()); + d_ptr->init(); } /*! @@ -269,9 +298,12 @@ void QChart::zoomIn() /*! Zooms in the view to a maximum level at which \a rect is still fully visible. + \note This is not supported for polar charts. */ void QChart::zoomIn(const QRectF &rect) { + if (d_ptr->m_type == QChart::ChartTypePolar) + return; d_ptr->zoomIn(rect); } @@ -306,8 +338,8 @@ void QChart::zoom(qreal factor) } /*! - Returns the pointer to the x axis object of the chart asociated with the specified \a series - If no series is provided then pointer to currently visible axis is provided + Returns the pointer to the x axis object of the chart associated with the specified \a series. + If no series is provided then pointer to currently visible axis is provided. */ QAbstractAxis *QChart::axisX(QAbstractSeries *series) const { @@ -318,8 +350,8 @@ QAbstractAxis *QChart::axisX(QAbstractSeries *series) const } /*! - Returns the pointer to the y axis object of the chart asociated with the specified \a series - If no series is provided then pointer to currently visible axis is provided + Returns the pointer to the y axis object of the chart associated with the specified \a series. + If no series is provided then pointer to currently visible axis is provided. */ QAbstractAxis *QChart::axisY(QAbstractSeries *series) const { @@ -438,6 +470,11 @@ QMargins QChart::margins() const return d_ptr->m_presenter->layout()->margins(); } +QChart::ChartType QChart::chartType() const +{ + return d_ptr->m_type; +} + /*! Returns the the rect within which the drawing of the chart is done. It does not include the area defines by margins. @@ -471,6 +508,8 @@ QChart::AnimationOptions QChart::animationOptions() const /*! Scrolls the visible area of the chart by the distance defined in the \a dx and \a dy. + + For polar charts, \a dx indicates the angle along angular axis instead of distance. */ void QChart::scroll(qreal dx, qreal dy) { @@ -581,11 +620,12 @@ QPointF QChart::mapToPosition(const QPointF &value, QAbstractSeries *series) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -QChartPrivate::QChartPrivate(QChart *q): +QChartPrivate::QChartPrivate(QChart *q, QChart::ChartType type): q_ptr(q), + m_type(type), m_legend(0), m_dataset(new ChartDataSet(q)), - m_presenter(new ChartPresenter(q)), + m_presenter(new ChartPresenter(q, type)), m_themeManager(new ChartThemeManager(q)) { QObject::connect(m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), m_presenter, SLOT(handleSeriesAdded(QAbstractSeries*))); @@ -603,6 +643,13 @@ QChartPrivate::~QChartPrivate() } +void QChartPrivate::init() +{ + m_legend = new LegendScroller(q_ptr); + q_ptr->setTheme(QChart::ChartThemeLight); + q_ptr->setLayout(m_presenter->layout()); +} + void QChartPrivate::zoomIn(qreal factor) { QRectF rect = m_presenter->geometry(); diff --git a/src/qchart.h b/src/qchart.h index e18b127f..3f026732 100644 --- a/src/qchart.h +++ b/src/qchart.h @@ -45,10 +45,18 @@ class QTCOMMERCIALCHART_EXPORT QChart : public QGraphicsWidget Q_PROPERTY(QChart::AnimationOptions animationOptions READ animationOptions WRITE setAnimationOptions) Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins) Q_PROPERTY(QMargins margins READ margins WRITE setMargins) + Q_PROPERTY(QChart::ChartType chartType READ chartType) Q_ENUMS(ChartTheme) Q_ENUMS(AnimationOption) + Q_ENUMS(ChartType) public: + enum ChartType { + ChartTypeUndefined = 0, + ChartTypeCartesian, + ChartTypePolar + }; + enum ChartTheme { ChartThemeLight = 0, ChartThemeBlueCerulean, @@ -84,7 +92,7 @@ public: QAbstractAxis *axisY(QAbstractSeries *series = 0) const; // ****************** - void addAxis(QAbstractAxis *axis,Qt::Alignment alignment); + void addAxis(QAbstractAxis *axis, Qt::Alignment alignment); void removeAxis(QAbstractAxis *axis); QList<QAbstractAxis*> axes(Qt::Orientations orientation = Qt::Horizontal|Qt::Vertical, QAbstractSeries *series = 0) const; @@ -133,7 +141,10 @@ public: QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0); QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0); + ChartType chartType() const; + protected: + explicit QChart(QChart::ChartType type, QGraphicsItem *parent, Qt::WindowFlags wFlags); QScopedPointer<QChartPrivate> d_ptr; friend class QLegend; friend class DeclarativeChart; diff --git a/src/qchart_p.h b/src/qchart_p.h index f26a3600..e715bc63 100644 --- a/src/qchart_p.h +++ b/src/qchart_p.h @@ -31,10 +31,10 @@ #define QCHART_P_H #include "qchartglobal.h" +#include "qchart.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE -class QChart; class ChartThemeManager; class ChartPresenter; class QLegend; @@ -44,14 +44,16 @@ class QChartPrivate { public: - QChartPrivate(QChart *q); + QChartPrivate(QChart *q, QChart::ChartType type); ~QChartPrivate(); QChart *q_ptr; QLegend *m_legend; ChartDataSet *m_dataset; ChartPresenter *m_presenter; ChartThemeManager *m_themeManager; + QChart::ChartType m_type; + void init(); void zoomIn(qreal factor); void zoomOut(qreal factor); void zoomIn(const QRectF &rect); diff --git a/src/qpolarchart.cpp b/src/qpolarchart.cpp new file mode 100644 index 00000000..1d93d35e --- /dev/null +++ b/src/qpolarchart.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpolarchart.h" +#include "qabstractaxis.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +/*! + \enum QPolarChart::PolarOrientation + + This type is used to signify the polar orientation of an axis. + + \value PolarOrientationRadial + \value PolarOrientationAngular +*/ + +/*! + \class QPolarChart + \brief QtCommercial chart API. + + QPolarChart is a specialization of QChart to show a polar chart. + + Polar charts support line, spline, area, and scatter series, and all axis types + supported by those series. + + \note When setting ticks to an angular QValueAxis, keep in mind that the first and last tick + are co-located at 0/360 degree angle. + + \note If the angular distance between two consecutive points in a series is more than 180 degrees, + any line connecting the two points becomes meaningless, so choose the axis ranges accordingly + when displaying line, spline, or area series. + + \note Polar charts do not support multiple axes of same orientation. + + \sa QChart + */ + +/*! + Constructs a polar chart as a child of a \a parent. + Parameter \a wFlags is passed to the QChart constructor. + */ +QPolarChart::QPolarChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QChart(QChart::ChartTypePolar, parent, wFlags) +{ +} + +/*! + Destroys the object and it's children, like series and axis objects added to it. + */ +QPolarChart::~QPolarChart() +{ +} + +/*! + Returns the axes added for the \a series with \a polarOrientation. If no series is provided, then any axis with the + specified polar orientation is returned. + \sa addAxis() + */ +QList<QAbstractAxis *> QPolarChart::axes(PolarOrientations polarOrientation, QAbstractSeries *series) const +{ + Qt::Orientations orientation(0); + if (polarOrientation.testFlag(PolarOrientationAngular)) + orientation |= Qt::Horizontal; + if (polarOrientation.testFlag(PolarOrientationRadial)) + orientation |= Qt::Vertical; + + return QChart::axes(orientation, series); +} + +/*! + This convenience method adds \a axis to the polar chart with \a polarOrientation. + The chart takes the ownership of the axis. + + \note Axes can be added to a polar chart also with QChart::addAxis() instead of this method. + The specified alignment determines the polar orientation: horizontal alignments indicate angular + axis and vertical alignments indicate radial axis. + \sa QChart::removeAxis(), QChart::createDefaultAxes(), QAbstractSeries::attachAxis(), QChart::addAxis() +*/ +void QPolarChart::addAxis(QAbstractAxis *axis, PolarOrientation polarOrientation) +{ + if (!axis || axis->type() == QAbstractAxis::AxisTypeBarCategory) { + qWarning("QAbstractAxis::AxisTypeBarCategory is not a supported axis type for polar charts."); + } else { + Qt::Alignment alignment = Qt::AlignLeft; + if (polarOrientation == PolarOrientationAngular) + alignment = Qt::AlignBottom; + QChart::addAxis(axis, alignment); + } +} + +/*! + Angular axes of a polar chart report horizontal orientation and radial axes report + vertical orientation. + This function is a convenience function for converting the orientation of an \a axis to + corresponding polar orientation. If the \a axis is NULL or not added to a polar chart, + the return value is meaningless. +*/ +QPolarChart::PolarOrientation QPolarChart::axisPolarOrientation(QAbstractAxis *axis) +{ + if (axis && axis->orientation() == Qt::Horizontal) + return PolarOrientationAngular; + else + return PolarOrientationRadial; +} + +#include "moc_qpolarchart.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/qpolarchart.h b/src/qpolarchart.h new file mode 100644 index 00000000..123eb578 --- /dev/null +++ b/src/qpolarchart.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPOLARCHART_H +#define QPOLARCHART_H + +#include "qchart.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QAbstractSeries; +class QAbstractAxis; + +class QTCOMMERCIALCHART_EXPORT QPolarChart : public QChart +{ + Q_OBJECT + Q_ENUMS(PolarOrientation) + Q_FLAGS(PolarOrientations) + +public: + enum PolarOrientation { + PolarOrientationRadial = 0x1, + PolarOrientationAngular = 0x2 + }; + Q_DECLARE_FLAGS(PolarOrientations, PolarOrientation) + +public: + explicit QPolarChart(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + ~QPolarChart(); + + void addAxis(QAbstractAxis *axis, PolarOrientation polarOrientation); + + QList<QAbstractAxis*> axes(PolarOrientations polarOrientation = PolarOrientations(PolarOrientationRadial | PolarOrientationAngular), QAbstractSeries *series = 0) const; + + static PolarOrientation axisPolarOrientation(QAbstractAxis *axis); + +protected: + Q_DISABLE_COPY(QPolarChart) +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // QCHART_H diff --git a/src/scatterchart/scatterchartitem.cpp b/src/scatterchart/scatterchartitem.cpp index 93761ed1..fecbeb1b 100644 --- a/src/scatterchart/scatterchartitem.cpp +++ b/src/scatterchart/scatterchartitem.cpp @@ -23,6 +23,7 @@ #include "qscatterseries_p.h" #include "chartpresenter_p.h" #include "abstractdomain_p.h" +#include "qchart.h" #include <QPainter> #include <QGraphicsScene> #include <QDebug> @@ -30,7 +31,7 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE -ScatterChartItem::ScatterChartItem(QScatterSeries *series, QGraphicsItem* item) +ScatterChartItem::ScatterChartItem(QScatterSeries *series, QGraphicsItem *item) : XYChart(series,item), m_series(series), m_items(this), @@ -93,12 +94,12 @@ void ScatterChartItem::deletePoints(int count) void ScatterChartItem::markerSelected(QGraphicsItem *marker) { - emit XYChart::clicked(domain()->calculateDomainPoint(m_markerMap[marker])); + emit XYChart::clicked(m_markerMap[marker]); } void ScatterChartItem::markerHovered(QGraphicsItem *marker, bool state) { - emit XYChart::hovered(domain()->calculateDomainPoint(m_markerMap[marker]), state); + emit XYChart::hovered(m_markerMap[marker], state); } void ScatterChartItem::updateGeometry() @@ -125,13 +126,16 @@ void ScatterChartItem::updateGeometry() QRectF clipRect(QPointF(0,0),domain()->size()); + QVector<bool> offGridStatus = offGridStatusVector(); + for (int i = 0; i < points.size(); i++) { QGraphicsItem *item = items.at(i); const QPointF &point = points.at(i); const QRectF &rect = item->boundingRect(); - m_markerMap[item] = point; + m_markerMap[item] = m_series->pointAt(i); item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2); - if (!m_visible || !clipRect.contains(point)) + + if (!m_visible || offGridStatus.at(i)) item->setVisible(false); else item->setVisible(true); diff --git a/src/scatterchart/scatterchartitem_p.h b/src/scatterchart/scatterchartitem_p.h index 6e8010bc..e00404a4 100644 --- a/src/scatterchart/scatterchartitem_p.h +++ b/src/scatterchart/scatterchartitem_p.h @@ -44,7 +44,7 @@ class ScatterChartItem : public XYChart Q_OBJECT Q_INTERFACES(QGraphicsItem) public: - explicit ScatterChartItem(QScatterSeries *series, QGraphicsItem* item = 0); + explicit ScatterChartItem(QScatterSeries *series, QGraphicsItem *item = 0); public: //from QGraphicsItem diff --git a/src/splinechart/splinechartitem.cpp b/src/splinechart/splinechartitem.cpp index 261a753f..4a46a724 100644 --- a/src/splinechart/splinechartitem.cpp +++ b/src/splinechart/splinechartitem.cpp @@ -22,13 +22,13 @@ #include "qsplineseries_p.h" #include "chartpresenter_p.h" #include "splineanimation_p.h" -#include "abstractdomain_p.h" +#include "polardomain_p.h" #include <QPainter> #include <QGraphicsSceneMouseEvent> QTCOMMERCIALCHART_BEGIN_NAMESPACE -SplineChartItem::SplineChartItem(QSplineSeries *series, QGraphicsItem* item) +SplineChartItem::SplineChartItem(QSplineSeries *series, QGraphicsItem *item) : XYChart(series,item), m_series(series), m_pointsVisible(false), @@ -49,8 +49,7 @@ QRectF SplineChartItem::boundingRect() const QPainterPath SplineChartItem::shape() const { - QPainterPathStroker stroker; - return stroker.createStroke(m_path); + return m_fullPath; } void SplineChartItem::setAnimation(SplineAnimation *animation) @@ -107,20 +106,179 @@ void SplineChartItem::updateGeometry() Q_ASSERT(points.count() * 2 - 2 == controlPoints.count()); - QPainterPath splinePath(points.at(0)); + QPainterPath splinePath; + QPainterPath fullPath; + // Use worst case scenario to determine required margin. + qreal margin = m_linePen.width() * 1.42; + + if (m_series->chart()->chartType() == QChart::ChartTypePolar) { + QPainterPath splinePathLeft; + QPainterPath splinePathRight; + QPainterPath *currentSegmentPath = 0; + QPainterPath *previousSegmentPath = 0; + qreal minX = domain()->minX(); + qreal maxX = domain()->maxX(); + qreal minY = domain()->minY(); + QPointF currentSeriesPoint = m_series->pointAt(0); + QPointF currentGeometryPoint = points.at(0); + QPointF previousGeometryPoint = points.at(0); + bool pointOffGrid = false; + bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX); + m_visiblePoints.clear(); + m_visiblePoints.reserve(points.size()); + + qreal domainRadius = domain()->size().height() / 2.0; + const QPointF centerPoint(domainRadius, domainRadius); + + if (!previousPointWasOffGrid) { + fullPath.moveTo(points.at(0)); + // Do not draw points for points below minimum Y. + if (m_pointsVisible && currentSeriesPoint.y() >= minY) + m_visiblePoints.append(currentGeometryPoint); + } + + qreal leftMarginLine = centerPoint.x() - margin; + qreal rightMarginLine = centerPoint.x() + margin; + qreal horizontal = centerPoint.y(); + + for (int i = 1; i < points.size(); i++) { + // Interpolating spline fragments accurately is not trivial, and would anyway be ugly + // when thick pen is used, so we work around it by utilizing three separate + // paths for spline segments and clip those with custom regions at paint time. + // "Right" path contains segments that cross the axis line with visible point on the + // right side of the axis line, as well as segments that have one point within the margin + // on the right side of the axis line and another point on the right side of the chart. + // "Left" path contains points with similarly on the left side. + // "Full" path contains rest of the points. + // This doesn't yield perfect results always. E.g. when segment covers more than 90 + // degrees and both of the points are within the margin, one in the top half and one in the + // bottom half of the chart, the bottom one gets clipped incorrectly. + // However, this should be rare occurrence in any sensible chart. + currentSeriesPoint = m_series->pointAt(i); + currentGeometryPoint = points.at(i); + pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX); + + // Draw something unless both off-grid + if (!pointOffGrid || !previousPointWasOffGrid) { + bool dummyOk; // We know points are ok, but this is needed + qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk); + qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->pointAt(i - 1).x(), dummyOk); + + if ((qAbs(currentAngle - previousAngle) > 180.0)) { + // If the angle between two points is over 180 degrees (half X range), + // any direct segment between them becomes meaningless. + // In this case two line segments are drawn instead, from previous + // point to the center and from center to current point. + if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine)) + && previousGeometryPoint.y() < horizontal) { + currentSegmentPath = &splinePathRight; + } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine)) + && previousGeometryPoint.y() < horizontal) { + currentSegmentPath = &splinePathLeft; + } else if (previousAngle > 0.0 && previousAngle < 360.0) { + currentSegmentPath = &splinePath; + } else { + currentSegmentPath = 0; + } + + if (currentSegmentPath) { + if (previousSegmentPath != currentSegmentPath) + currentSegmentPath->moveTo(previousGeometryPoint); + if (!previousSegmentPath) + fullPath.moveTo(previousGeometryPoint); + + currentSegmentPath->lineTo(centerPoint); + fullPath.lineTo(centerPoint); + } + + previousSegmentPath = currentSegmentPath; + + if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine)) + && currentGeometryPoint.y() < horizontal) { + currentSegmentPath = &splinePathRight; + } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &¤tGeometryPoint.x() > leftMarginLine)) + && currentGeometryPoint.y() < horizontal) { + currentSegmentPath = &splinePathLeft; + } else if (currentAngle > 0.0 && currentAngle < 360.0) { + currentSegmentPath = &splinePath; + } else { + currentSegmentPath = 0; + } + + if (currentSegmentPath) { + if (previousSegmentPath != currentSegmentPath) + currentSegmentPath->moveTo(centerPoint); + if (!previousSegmentPath) + fullPath.moveTo(centerPoint); + + currentSegmentPath->lineTo(currentGeometryPoint); + fullPath.lineTo(currentGeometryPoint); + } + } else { + QPointF cp1 = controlPoints[2 * (i - 1)]; + QPointF cp2 = controlPoints[(2 * i) - 1]; + + if (previousAngle < 0.0 || currentAngle < 0.0 + || ((previousAngle <= 180.0 && currentAngle <= 180.0) + && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal) + || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) { + currentSegmentPath = &splinePathRight; + } else if (previousAngle > 360.0 || currentAngle > 360.0 + || ((previousAngle > 180.0 && currentAngle > 180.0) + && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal) + || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) { + currentSegmentPath = &splinePathLeft; + } else { + currentSegmentPath = &splinePath; + } + + if (currentSegmentPath != previousSegmentPath) + currentSegmentPath->moveTo(previousGeometryPoint); + if (!previousSegmentPath) + fullPath.moveTo(previousGeometryPoint); + + fullPath.cubicTo(cp1, cp2, currentGeometryPoint); + currentSegmentPath->cubicTo(cp1, cp2, currentGeometryPoint); + } + } else { + currentSegmentPath = 0; + } + + previousPointWasOffGrid = pointOffGrid; + if (!pointOffGrid && m_pointsVisible && currentSeriesPoint.y() >= minY) + m_visiblePoints.append(currentGeometryPoint); + previousSegmentPath = currentSegmentPath; + previousGeometryPoint = currentGeometryPoint; + } - for (int i = 0; i < points.size() - 1; i++) { - const QPointF &point = points.at(i + 1); - splinePath.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point); + m_pathPolarRight = splinePathRight; + m_pathPolarLeft = splinePathLeft; + // Note: This construction of m_fullpath is not perfect. The partial segments that are + // outside left/right clip regions at axis boundary still generate hover/click events, + // because shape doesn't get clipped. It doesn't seem possible to do sensibly. + } else { // not polar + splinePath.moveTo(points.at(0)); + for (int i = 0; i < points.size() - 1; i++) { + const QPointF &point = points.at(i + 1); + splinePath.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point); + } + fullPath = splinePath; } + m_path = splinePath; + + QPainterPathStroker stroker; + // The full path is comprised of three separate paths. + // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and + // multiply line width with square root of two when defining shape and bounding rectangle. + stroker.setWidth(margin); + stroker.setJoinStyle(Qt::MiterJoin); + stroker.setCapStyle(Qt::SquareCap); + stroker.setMiterLimit(m_linePen.miterLimit()); prepareGeometryChange(); - // QPainterPathStroker stroker; - // stroker.setWidth(m_linePen.width() / 2.0); - // m_path = stroker.createStroke(splinePath); - m_path = splinePath; - m_rect = splinePath.boundingRect(); + m_fullPath = stroker.createStroke(fullPath); + m_rect = m_fullPath.boundingRect(); } /*! @@ -240,16 +398,38 @@ void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o Q_UNUSED(widget) Q_UNUSED(option) + QRectF clipRect = QRectF(QPointF(0, 0), domain()->size()); + painter->save(); - painter->setClipRect(QRectF(QPointF(0,0),domain()->size())); painter->setPen(m_linePen); - // painter->setBrush(m_linePen.color()); + painter->setBrush(Qt::NoBrush); + + if (m_series->chart()->chartType() == QChart::ChartTypePolar) { + qreal halfWidth = domain()->size().width() / 2.0; + QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height()); + QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height()); + QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse); + QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect())); + QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect())); + painter->setClipRegion(clipRegionLeft); + painter->drawPath(m_pathPolarLeft); + painter->setClipRegion(clipRegionRight); + painter->drawPath(m_pathPolarRight); + painter->setClipRegion(fullPolarClipRegion); + } else { + painter->setClipRect(clipRect); + } painter->drawPath(m_path); + if (m_pointsVisible) { painter->setPen(m_pointPen); - painter->drawPoints(geometryPoints()); + if (m_series->chart()->chartType() == QChart::ChartTypePolar) + painter->drawPoints(m_visiblePoints); + else + painter->drawPoints(geometryPoints()); } + painter->restore(); } diff --git a/src/splinechart/splinechartitem_p.h b/src/splinechart/splinechartitem_p.h index a15b6c6a..37e1b52d 100644 --- a/src/splinechart/splinechartitem_p.h +++ b/src/splinechart/splinechartitem_p.h @@ -42,7 +42,7 @@ class SplineChartItem : public XYChart Q_OBJECT Q_INTERFACES(QGraphicsItem) public: - SplineChartItem(QSplineSeries *series, QGraphicsItem* item = 0); + SplineChartItem(QSplineSeries *series, QGraphicsItem *item = 0); //from QGraphicsItem QRectF boundingRect() const; @@ -70,11 +70,15 @@ protected: private: QSplineSeries *m_series; QPainterPath m_path; + QPainterPath m_pathPolarRight; + QPainterPath m_pathPolarLeft; + QPainterPath m_fullPath; QRectF m_rect; QPen m_linePen; QPen m_pointPen; bool m_pointsVisible; QVector<QPointF> m_controlPoints; + QVector<QPointF> m_visiblePoints; SplineAnimation *m_animation; friend class SplineAnimation; diff --git a/src/src.pro b/src/src.pro index bfb90ba0..a60a6cf2 100644 --- a/src/src.pro +++ b/src/src.pro @@ -39,8 +39,8 @@ SOURCES += \ $$PWD/chartelement.cpp \ $$PWD/chartitem.cpp \ $$PWD/scroller.cpp \ - $$PWD/chartlayout.cpp \ - $$PWD/charttitle.cpp + $$PWD/charttitle.cpp \ + $$PWD/qpolarchart.cpp PRIVATE_HEADERS += \ $$PWD/chartdataset_p.h \ $$PWD/chartitem_p.h \ @@ -53,7 +53,6 @@ PRIVATE_HEADERS += \ $$PWD/qchartview_p.h \ $$PWD/scroller_p.h \ $$PWD/qabstractseries_p.h \ - $$PWD/chartlayout_p.h \ $$PWD/charttitle_p.h \ $$PWD/charthelpers_p.h PUBLIC_HEADERS += \ @@ -61,7 +60,8 @@ PUBLIC_HEADERS += \ $$PWD/qchartglobal.h \ $$PWD/qabstractseries.h \ $$PWD/qchartview.h \ - $$PWD/chartsnamespace.h + $$PWD/chartsnamespace.h \ + $$PWD/qpolarchart.h include(animations/animations.pri) include(areachart/areachart.pri) @@ -75,6 +75,7 @@ include(scatterchart/scatter.pri) include(splinechart/splinechart.pri) include(themes/themes.pri) include(xychart/xychart.pri) +include(layout/layout.pri) HEADERS += $$PUBLIC_HEADERS HEADERS += $$PRIVATE_HEADERS diff --git a/src/xychart/qxyseries.cpp b/src/xychart/qxyseries.cpp index 3d5f7eec..285c078d 100644 --- a/src/xychart/qxyseries.cpp +++ b/src/xychart/qxyseries.cpp @@ -69,6 +69,18 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE */ /*! + \qmlproperty AbstractAxis XYSeries::axisAngular + The angular axis used for the series, drawn around the polar chart view. + \sa axisX +*/ + +/*! + \qmlproperty AbstractAxis XYSeries::axisRadial + The radial axis used for the series, drawn inside the polar chart view. + \sa axisY +*/ + +/*! \property QXYSeries::pointsVisible Controls if the data points are visible and should be drawn. */ @@ -354,6 +366,15 @@ QList<QPointF> QXYSeries::points() const } /*! + Returns point at \a index in internal points vector. +*/ +const QPointF &QXYSeries::pointAt(int index) const +{ + Q_D(const QXYSeries); + return d->m_points.at(index); +} + +/*! Returns number of data points within series. */ int QXYSeries::count() const diff --git a/src/xychart/qxyseries.h b/src/xychart/qxyseries.h index d0e6bb48..8eff380e 100644 --- a/src/xychart/qxyseries.h +++ b/src/xychart/qxyseries.h @@ -56,6 +56,7 @@ public: int count() const; QList<QPointF> points() const; + const QPointF &pointAt(int index) const; QXYSeries &operator << (const QPointF &point); QXYSeries &operator << (const QList<QPointF> &points); diff --git a/src/xychart/xychart.cpp b/src/xychart/xychart.cpp index ecadf676..3d442c5a 100644 --- a/src/xychart/xychart.cpp +++ b/src/xychart/xychart.cpp @@ -24,6 +24,7 @@ #include "chartpresenter_p.h" #include "abstractdomain_p.h" #include "qxymodelmapper.h" +#include "qabstractaxis_p.h" #include <QPainter> #include <QAbstractItemModel> @@ -32,7 +33,7 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE //TODO: optimize : remove points which are not visible -XYChart::XYChart(QXYSeries *series,QGraphicsItem* item): +XYChart::XYChart(QXYSeries *series, QGraphicsItem *item): ChartItem(series->d_func(),item), m_series(series), m_animation(0), @@ -46,7 +47,7 @@ XYChart::XYChart(QXYSeries *series,QGraphicsItem* item): QObject::connect(this, SIGNAL(hovered(QPointF,bool)), series, SIGNAL(hovered(QPointF,bool))); } -void XYChart::setGeometryPoints(const QVector<QPointF>& points) +void XYChart::setGeometryPoints(const QVector<QPointF> &points) { m_points = points; } @@ -61,6 +62,32 @@ 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()); + + for (int i = 0; i < m_points.size(); i++) { + const QPointF &seriesPoint = m_series->pointAt(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) { diff --git a/src/xychart/xychart_p.h b/src/xychart/xychart_p.h index cfdb296b..49c57bc3 100644 --- a/src/xychart/xychart_p.h +++ b/src/xychart/xychart_p.h @@ -45,10 +45,10 @@ class XYChart : public ChartItem { Q_OBJECT public: - explicit XYChart(QXYSeries *series,QGraphicsItem* item = 0); + explicit XYChart(QXYSeries *series,QGraphicsItem *item = 0); ~XYChart() {} - void setGeometryPoints(const QVector<QPointF>& points); + void setGeometryPoints(const QVector<QPointF> &points); QVector<QPointF> geometryPoints() const { return m_points; } void setAnimation(XYAnimation *animation); @@ -58,6 +58,9 @@ public: bool isDirty() const { return m_dirty; } void setDirty(bool dirty); + void getSeriesRanges(qreal &minX, qreal &maxX, qreal &minY, qreal &maxY); + QVector<bool> offGridStatusVector(); + public Q_SLOTS: void handlePointAdded(int index); void handlePointRemoved(int index); diff --git a/tests/auto/README b/tests/auto/README new file mode 100644 index 00000000..6ecc179e --- /dev/null +++ b/tests/auto/README @@ -0,0 +1,9 @@ +Testing polar chart: + +Since the tests typically initialize the chart once for the whole test case, +it is difficult to test the components against polar chart unless all tests +are duplicated or massively refactored. Neither option is desirable, so instead +we check environment variable TEST_POLAR_CHART. If it is not empty, we run +the tests that are relevant for polar chart against polar chart. Unfortunately +This means two runs of the tests with different environment are required for +full coverage. diff --git a/tests/auto/inc/tst_definitions.h b/tests/auto/inc/tst_definitions.h index d5802235..32ddf103 100644 --- a/tests/auto/inc/tst_definitions.h +++ b/tests/auto/inc/tst_definitions.h @@ -21,6 +21,7 @@ #ifndef TST_DEFINITIONS_H #define TST_DEFINITIONS_H +#include "qpolarchart.h" #include <QtTest/QtTest> #include <QPushButton> @@ -63,6 +64,16 @@ namespace QTest QSKIP("Cannot test mouse events in this environment"); \ } while (0); \ } + + #define SKIP_ON_POLAR() { \ + if (isPolarTest()) \ + QSKIP("Test not supported by polar chart"); \ + } + + #define SKIP_ON_CARTESIAN() { \ + if (!isPolarTest()) \ + QSKIP("Test not supported by cartesian chart"); \ + } #else #define SKIP_IF_CANNOT_TEST_MOUSE_EVENTS() { \ do { \ @@ -76,6 +87,39 @@ namespace QTest QSKIP("Cannot test mouse events in this environment", SkipAll); \ } while (0); \ } + + #define SKIP_ON_POLAR() { \ + if (isPolarTest()) \ + QSKIP("Test not supported by polar chart", SkipAll); \ + } + + #define SKIP_ON_CARTESIAN() { \ + if (!isPolarTest()) \ + QSKIP("Test not supported by cartesian chart", SkipAll); \ + } #endif +static inline bool isPolarTest() +{ + static bool isPolar = false; + static bool polarEnvChecked = false; + if (!polarEnvChecked) { + isPolar = !(qgetenv("TEST_POLAR_CHART").isEmpty()); + polarEnvChecked = true; + if (isPolar) + qDebug() << "TEST_POLAR_CHART found -> Testing polar chart!"; + } + return isPolar; +} + +static inline QtCommercialChart::QChart *newQChartOrQPolarChart() +{ + if (isPolarTest()) + return new QtCommercialChart::QPolarChart(); + else + return new QtCommercialChart::QChart(); +} + + + #endif // TST_DEFINITIONS_H diff --git a/tests/auto/qabstractaxis/tst_qabstractaxis.cpp b/tests/auto/qabstractaxis/tst_qabstractaxis.cpp index a66c21d7..99f39d88 100644 --- a/tests/auto/qabstractaxis/tst_qabstractaxis.cpp +++ b/tests/auto/qabstractaxis/tst_qabstractaxis.cpp @@ -35,7 +35,7 @@ void tst_QAbstractAxis::init(QAbstractAxis* axis, QAbstractSeries* series) { m_axis = axis; m_series = series; - m_view = new QChartView(new QChart()); + m_view = new QChartView(newQChartOrQPolarChart()); m_chart = m_view->chart(); } diff --git a/tests/auto/qchart/tst_qchart.cpp b/tests/auto/qchart/tst_qchart.cpp index 0558ad68..f92be649 100644 --- a/tests/auto/qchart/tst_qchart.cpp +++ b/tests/auto/qchart/tst_qchart.cpp @@ -101,6 +101,7 @@ private slots: void zoomOut(); void createDefaultAxesForLineSeries_data(); void createDefaultAxesForLineSeries(); + void axisPolarOrientation(); private: void createTestData(); @@ -121,7 +122,7 @@ void tst_QChart::cleanupTestCase() void tst_QChart::init() { - m_view = new QChartView(new QChart()); + m_view = new QChartView(newQChartOrQPolarChart()); m_chart = m_view->chart(); } @@ -191,20 +192,22 @@ void tst_QChart::addSeries_data() QAbstractSeries* area = new QAreaSeries(static_cast<QLineSeries*>(line)); QAbstractSeries* scatter = new QScatterSeries(this); QAbstractSeries* spline = new QSplineSeries(this); - QAbstractSeries* pie = new QPieSeries(this); - QAbstractSeries* bar = new QBarSeries(this); - QAbstractSeries* percent = new QPercentBarSeries(this); - QAbstractSeries* stacked = new QStackedBarSeries(this); QTest::newRow("lineSeries") << line; QTest::newRow("areaSeries") << area; QTest::newRow("scatterSeries") << scatter; QTest::newRow("splineSeries") << spline; - QTest::newRow("pieSeries") << pie; - QTest::newRow("barSeries") << bar; - QTest::newRow("percentBarSeries") << percent; - QTest::newRow("stackedBarSeries") << stacked; + if (!isPolarTest()) { + QAbstractSeries* pie = new QPieSeries(this); + QAbstractSeries* bar = new QBarSeries(this); + QAbstractSeries* percent = new QPercentBarSeries(this); + QAbstractSeries* stacked = new QStackedBarSeries(this); + QTest::newRow("pieSeries") << pie; + QTest::newRow("barSeries") << bar; + QTest::newRow("percentBarSeries") << percent; + QTest::newRow("stackedBarSeries") << stacked; + } } void tst_QChart::addSeries() @@ -258,20 +261,23 @@ void tst_QChart::axisX_data() QTest::newRow("categories,areaSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QAreaSeries(new QLineSeries(this)); QTest::newRow("categories,scatterSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QScatterSeries(this); QTest::newRow("categories,splineSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QSplineSeries(this); - QTest::newRow("categories,pieSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QPieSeries(this); - QTest::newRow("categories,barSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QBarSeries(this); - QTest::newRow("categories,percentBarSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QPercentBarSeries(this); - QTest::newRow("categories,stackedBarSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QStackedBarSeries(this); + if (!isPolarTest()) { + QTest::newRow("categories,pieSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QPieSeries(this); + QTest::newRow("categories,barSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QBarSeries(this); + QTest::newRow("categories,percentBarSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QPercentBarSeries(this); + QTest::newRow("categories,stackedBarSeries") << (QAbstractAxis*) new QBarCategoryAxis() << (QAbstractSeries*) new QStackedBarSeries(this); + } QTest::newRow("value,lineSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QLineSeries(this); QTest::newRow("value,areaSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QAreaSeries(new QLineSeries(this)); QTest::newRow("value,scatterSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QScatterSeries(this); QTest::newRow("value,splineSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QSplineSeries(this); - QTest::newRow("value,pieSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QPieSeries(this); - QTest::newRow("value,barSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QBarSeries(this); - QTest::newRow("value,percentBarSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QPercentBarSeries(this); - QTest::newRow("value,stackedBarSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QStackedBarSeries(this); - + if (!isPolarTest()) { + QTest::newRow("value,pieSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QPieSeries(this); + QTest::newRow("value,barSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QBarSeries(this); + QTest::newRow("value,percentBarSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QPercentBarSeries(this); + QTest::newRow("value,stackedBarSeries") << (QAbstractAxis*) new QValueAxis() << (QAbstractSeries*) new QStackedBarSeries(this); + } } void tst_QChart::axisX() @@ -850,14 +856,14 @@ void tst_QChart::createDefaultAxesForLineSeries() series2->append(series2minX, series2minY); series2->append(series2midX, series2midY); series2->append(series2maxX, series2maxY); - QChart chart; - chart.addSeries(series1); - chart.addSeries(series2); - chart.createDefaultAxes(); - QValueAxis *xAxis = (QValueAxis *)chart.axisX(); + QChart *chart = newQChartOrQPolarChart(); + chart->addSeries(series1); + chart->addSeries(series2); + chart->createDefaultAxes(); + QValueAxis *xAxis = (QValueAxis *)chart->axisX(); QCOMPARE(xAxis->min(), overallminX); QCOMPARE(xAxis->max(), overallmaxX); - QValueAxis *yAxis = (QValueAxis *)chart.axisY(); + QValueAxis *yAxis = (QValueAxis *)chart->axisY(); QCOMPARE(yAxis->min(), overallminY); QCOMPARE(yAxis->max(), overallmaxY); QLineSeries *series3 = new QLineSeries(this); @@ -865,12 +871,37 @@ void tst_QChart::createDefaultAxesForLineSeries() series3->append(0, 0); series3->append(100, 100); // Adding a new series should not change the axes as they have not been told to update - chart.addSeries(series3); + chart->addSeries(series3); QCOMPARE(xAxis->min(), overallminX); QCOMPARE(xAxis->max(), overallmaxX); QCOMPARE(yAxis->min(), overallminY); QCOMPARE(yAxis->max(), overallmaxY); +} + +void tst_QChart::axisPolarOrientation() +{ + QLineSeries* series1 = new QLineSeries(this); + series1->append(1, 2); + series1->append(2, 4); + series1->append(3, 8); + QPolarChart chart; + chart.addSeries(series1); + + QValueAxis *xAxis = new QValueAxis(); + QValueAxis *yAxis = new QValueAxis(); + chart.addAxis(xAxis, QPolarChart::PolarOrientationAngular); + chart.addAxis(yAxis, QPolarChart::PolarOrientationRadial); + + QList<QAbstractAxis *> xAxes = chart.axes(QPolarChart::PolarOrientationAngular); + QList<QAbstractAxis *> yAxes = chart.axes(QPolarChart::PolarOrientationRadial); + + QCOMPARE(xAxes.size(), 1); + QCOMPARE(yAxes.size(), 1); + QCOMPARE(xAxes[0], xAxis); + QCOMPARE(yAxes[0], yAxis); + QCOMPARE(chart.axisPolarOrientation(xAxes[0]), QPolarChart::PolarOrientationAngular); + QCOMPARE(chart.axisPolarOrientation(yAxes[0]), QPolarChart::PolarOrientationRadial); } QTEST_MAIN(tst_QChart) diff --git a/tests/auto/qchartview/tst_qchartview.cpp b/tests/auto/qchartview/tst_qchartview.cpp index d8019717..878b2757 100644 --- a/tests/auto/qchartview/tst_qchartview.cpp +++ b/tests/auto/qchartview/tst_qchartview.cpp @@ -67,7 +67,7 @@ void tst_QChartView::cleanupTestCase() void tst_QChartView::init() { - m_view = new QChartView(new QChart()); + m_view = new QChartView(newQChartOrQPolarChart()); m_view->chart()->legend()->setVisible(false); } @@ -120,9 +120,15 @@ void tst_QChartView::rubberBand_data() QTest::addColumn<QPoint>("min"); QTest::addColumn<QPoint>("max"); - QTest::newRow("HorizonalRubberBand") << QChartView::RubberBands(QChartView::HorizonalRubberBand) << 0 << 1 << QPoint(5,5) << QPoint(5,5); - QTest::newRow("VerticalRubberBand") << QChartView::RubberBands(QChartView::VerticalRubberBand) << 1 << 0 << QPoint(5,5) << QPoint(5,5); - QTest::newRow("RectangleRubberBand") << QChartView::RubberBands(QChartView::RectangleRubberBand) << 1 << 1 << QPoint(5,5) << QPoint(5,5); + if (isPolarTest()) { + QTest::newRow("HorizonalRubberBand") << QChartView::RubberBands(QChartView::HorizonalRubberBand) << 0 << 0 << QPoint(5,5) << QPoint(5,5); + QTest::newRow("VerticalRubberBand") << QChartView::RubberBands(QChartView::VerticalRubberBand) << 0 << 0 << QPoint(5,5) << QPoint(5,5); + QTest::newRow("RectangleRubberBand") << QChartView::RubberBands(QChartView::RectangleRubberBand) << 0 << 0 << QPoint(5,5) << QPoint(5,5); + } else { + QTest::newRow("HorizonalRubberBand") << QChartView::RubberBands(QChartView::HorizonalRubberBand) << 0 << 1 << QPoint(5,5) << QPoint(5,5); + QTest::newRow("VerticalRubberBand") << QChartView::RubberBands(QChartView::VerticalRubberBand) << 1 << 0 << QPoint(5,5) << QPoint(5,5); + QTest::newRow("RectangleRubberBand") << QChartView::RubberBands(QChartView::RectangleRubberBand) << 1 << 1 << QPoint(5,5) << QPoint(5,5); + } } void tst_QChartView::rubberBand() @@ -194,7 +200,8 @@ void tst_QChartView::setChart() series1->append(1,1); oldChart->addSeries(series1); - QPointer<QChart> newChart = new QChart(); + QPointer<QChart> newChart = newQChartOrQPolarChart(); + QLineSeries *series2 = new QLineSeries(); series2->append(0,1); series2->append(1,0); diff --git a/tests/auto/qlegend/tst_qlegend.cpp b/tests/auto/qlegend/tst_qlegend.cpp index 5eb6e245..de493565 100644 --- a/tests/auto/qlegend/tst_qlegend.cpp +++ b/tests/auto/qlegend/tst_qlegend.cpp @@ -63,7 +63,6 @@ private slots: private: QChart *m_chart; - }; void tst_QLegend::initTestCase() @@ -76,7 +75,7 @@ void tst_QLegend::cleanupTestCase() void tst_QLegend::init() { - m_chart = new QChart(); + m_chart = newQChartOrQPolarChart(); } void tst_QLegend::cleanup() @@ -98,6 +97,8 @@ void tst_QLegend::qlegend() void tst_QLegend::qpieLegendMarker() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -190,6 +191,8 @@ void tst_QLegend::qxyLegendMarker() void tst_QLegend::qbarLegendMarker() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -230,6 +233,8 @@ void tst_QLegend::qbarLegendMarker() void tst_QLegend::markers() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -270,6 +275,8 @@ void tst_QLegend::markers() void tst_QLegend::addAndRemoveSeries() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -291,6 +298,8 @@ void tst_QLegend::addAndRemoveSeries() void tst_QLegend::pieMarkerProperties() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -327,6 +336,8 @@ void tst_QLegend::pieMarkerProperties() void tst_QLegend::barMarkerProperties() { + SKIP_ON_POLAR(); + QVERIFY(m_chart); QLegend *legend = m_chart->legend(); @@ -482,8 +493,10 @@ void tst_QLegend::xyMarkerPropertiesScatter() void tst_QLegend::markerSignals() { + SKIP_ON_POLAR(); + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); - QChart *chart = new QChart(); + QChart *chart = newQChartOrQPolarChart(); QLegend *legend = chart->legend(); QBarSeries *bar = new QBarSeries(); diff --git a/tests/auto/qml/tst_qml.cpp b/tests/auto/qml/tst_qml.cpp index 2c57036f..98880afd 100644 --- a/tests/auto/qml/tst_qml.cpp +++ b/tests/auto/qml/tst_qml.cpp @@ -36,7 +36,8 @@ private slots: void checkPlugin(); private: QString componentErrors(const QDeclarativeComponent* component) const; - QString imports(); + QString imports_1_1(); + QString imports_1_3(); }; @@ -53,12 +54,18 @@ QString tst_QML::componentErrors(const QDeclarativeComponent* component) const return errors.join("\n"); } -QString tst_QML::imports() +QString tst_QML::imports_1_1() { return "import QtQuick 1.0 \n" "import QtCommercial.Chart 1.1 \n"; } +QString tst_QML::imports_1_3() +{ + return "import QtQuick 1.0 \n" + "import QtCommercial.Chart 1.3 \n"; +} + void tst_QML::initTestCase() { @@ -82,34 +89,37 @@ void tst_QML::checkPlugin_data() { QTest::addColumn<QString>("source"); - QTest::newRow("createChartView") << imports() + "ChartView{}"; - QTest::newRow("XYPoint") << imports() + "XYPoint{}"; - QTest::newRow("scatterSeries") << imports() + "ScatterSeries{}"; - QTest::newRow("lineSeries") << imports() + "LineSeries{}"; - QTest::newRow("splineSeries") << imports() + "SplineSeries{}"; - QTest::newRow("areaSeries") << imports() + "AreaSeries{}"; - QTest::newRow("barSeries") << imports() + "BarSeries{}"; - QTest::newRow("stackedBarSeries") << imports() + "StackedBarSeries{}"; - QTest::newRow("precentBarSeries") << imports() + "PercentBarSeries{}"; - QTest::newRow("horizonatlBarSeries") << imports() + "HorizontalBarSeries{}"; - QTest::newRow("horizonatlStackedBarSeries") << imports() + "HorizontalStackedBarSeries{}"; - QTest::newRow("horizonatlstackedBarSeries") << imports() + "HorizontalPercentBarSeries{}"; - QTest::newRow("pieSeries") << imports() + "PieSeries{}"; - QTest::newRow("PieSlice") << imports() + "PieSlice{}"; - QTest::newRow("BarSet") << imports() + "BarSet{}"; - QTest::newRow("HXYModelMapper") << imports() + "HXYModelMapper{}"; - QTest::newRow("VXYModelMapper") << imports() + "VXYModelMapper{}"; - QTest::newRow("HPieModelMapper") << imports() + "HPieModelMapper{}"; - QTest::newRow("HPieModelMapper") << imports() + "HPieModelMapper{}"; - QTest::newRow("HBarModelMapper") << imports() + "HBarModelMapper{}"; - QTest::newRow("VBarModelMapper") << imports() + "VBarModelMapper{}"; - QTest::newRow("ValueAxis") << imports() + "ValueAxis{}"; + QTest::newRow("createChartView") << imports_1_1() + "ChartView{}"; + QTest::newRow("XYPoint") << imports_1_1() + "XYPoint{}"; + QTest::newRow("scatterSeries") << imports_1_1() + "ScatterSeries{}"; + QTest::newRow("lineSeries") << imports_1_1() + "LineSeries{}"; + QTest::newRow("splineSeries") << imports_1_1() + "SplineSeries{}"; + QTest::newRow("areaSeries") << imports_1_1() + "AreaSeries{}"; + QTest::newRow("barSeries") << imports_1_1() + "BarSeries{}"; + QTest::newRow("stackedBarSeries") << imports_1_1() + "StackedBarSeries{}"; + QTest::newRow("precentBarSeries") << imports_1_1() + "PercentBarSeries{}"; + QTest::newRow("horizonatlBarSeries") << imports_1_1() + "HorizontalBarSeries{}"; + QTest::newRow("horizonatlStackedBarSeries") << imports_1_1() + "HorizontalStackedBarSeries{}"; + QTest::newRow("horizonatlstackedBarSeries") << imports_1_1() + "HorizontalPercentBarSeries{}"; + QTest::newRow("pieSeries") << imports_1_1() + "PieSeries{}"; + QTest::newRow("PieSlice") << imports_1_1() + "PieSlice{}"; + QTest::newRow("BarSet") << imports_1_1() + "BarSet{}"; + QTest::newRow("HXYModelMapper") << imports_1_1() + "HXYModelMapper{}"; + QTest::newRow("VXYModelMapper") << imports_1_1() + "VXYModelMapper{}"; + QTest::newRow("HPieModelMapper") << imports_1_1() + "HPieModelMapper{}"; + QTest::newRow("HPieModelMapper") << imports_1_1() + "HPieModelMapper{}"; + QTest::newRow("HBarModelMapper") << imports_1_1() + "HBarModelMapper{}"; + QTest::newRow("VBarModelMapper") << imports_1_1() + "VBarModelMapper{}"; + QTest::newRow("ValueAxis") << imports_1_1() + "ValueAxis{}"; #ifndef QT_ON_ARM - QTest::newRow("DateTimeAxis") << imports() + "DateTimeAxis{}"; + QTest::newRow("DateTimeAxis") << imports_1_1() + "DateTimeAxis{}"; #endif - QTest::newRow("CategoryAxis") << imports() + "CategoryAxis{}"; - QTest::newRow("CategoryRange") << imports() + "CategoryRange{}"; - QTest::newRow("BarCategoryAxis") << imports() + "BarCategoryAxis{}"; + QTest::newRow("CategoryAxis") << imports_1_1() + "CategoryAxis{}"; + QTest::newRow("CategoryRange") << imports_1_1() + "CategoryRange{}"; + QTest::newRow("BarCategoryAxis") << imports_1_1() + "BarCategoryAxis{}"; + + QTest::newRow("createPolarChartView") << imports_1_3() + "PolarChartView{}"; + //QTest::newRow("LogValueAxis") << imports_1_3() + "LogValueAxis{}"; } void tst_QML::checkPlugin() diff --git a/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp b/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp index e553f5e4..f4c36200 100644 --- a/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp +++ b/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp @@ -28,6 +28,7 @@ #include <qvxymodelmapper.h> #include <qhxymodelmapper.h> #include <QStandardItemModel> +#include "tst_definitions.h" QTCOMMERCIALCHART_USE_NAMESPACE @@ -145,7 +146,7 @@ void tst_qxymodelmapper::cleanup() void tst_qxymodelmapper::initTestCase() { - m_chart = new QChart; + m_chart = newQChartOrQPolarChart(); QChartView *chartView = new QChartView(m_chart); chartView->show(); } diff --git a/tests/auto/qxyseries/tst_qxyseries.cpp b/tests/auto/qxyseries/tst_qxyseries.cpp index 9a357b76..7988e9b7 100644 --- a/tests/auto/qxyseries/tst_qxyseries.cpp +++ b/tests/auto/qxyseries/tst_qxyseries.cpp @@ -32,7 +32,7 @@ void tst_QXYSeries::cleanupTestCase() void tst_QXYSeries::init() { - m_view = new QChartView(new QChart()); + m_view = new QChartView(newQChartOrQPolarChart()); m_chart = m_view->chart(); } diff --git a/tests/polarcharttest/chartview.cpp b/tests/polarcharttest/chartview.cpp new file mode 100644 index 00000000..4c8c5779 --- /dev/null +++ b/tests/polarcharttest/chartview.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chartview.h" +#include <QMouseEvent> +#include <QDebug> +#include <QAbstractAxis> + +QTCOMMERCIALCHART_USE_NAMESPACE + +ChartView::ChartView(QWidget *parent) : + QChartView(parent) +{ +} + +void ChartView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + chart()->zoomIn(); + break; + case Qt::Key_Minus: + chart()->zoomOut(); + break; + case Qt::Key_Left: + chart()->scroll(-1.0, 0); + break; + case Qt::Key_Right: + chart()->scroll(1.0, 0); + break; + case Qt::Key_Up: + chart()->scroll(0, 1.0); + break; + case Qt::Key_Down: + chart()->scroll(0, -1.0); + break; + default: + QGraphicsView::keyPressEvent(event); + break; + } +} diff --git a/tests/polarcharttest/chartview.h b/tests/polarcharttest/chartview.h new file mode 100644 index 00000000..82528c29 --- /dev/null +++ b/tests/polarcharttest/chartview.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CHARTVIEW_H +#define CHARTVIEW_H + +#include <QChartView> +#include <QPolarChart> + +QTCOMMERCIALCHART_USE_NAMESPACE + +class ChartView : public QChartView +{ +public: + ChartView(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *event); +}; + +#endif diff --git a/tests/polarcharttest/main.cpp b/tests/polarcharttest/main.cpp new file mode 100644 index 00000000..30db2e25 --- /dev/null +++ b/tests/polarcharttest/main.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/tests/polarcharttest/mainwindow.cpp b/tests/polarcharttest/mainwindow.cpp new file mode 100644 index 00000000..7e6cef04 --- /dev/null +++ b/tests/polarcharttest/mainwindow.cpp @@ -0,0 +1,1136 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "chartview.h" +#include <QScatterSeries> +#include <QLineSeries> +#include <QSplineSeries> +#include <QAreaSeries> +#include <QBarSeries> +#include <QBarSet> +#include <QValueAxis> +#include <QLogValueAxis> +#include <QDateTimeAxis> +#include <QCategoryAxis> +#include <QPolarChart> +#include <QDebug> +#include <qmath.h> +#include <QDateTime> + +QTCOMMERCIALCHART_USE_NAMESPACE +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + m_angularTickCount(9), + m_radialTickCount(11), + m_labelsAngle(0), + m_angularMin(0.0), + m_angularMax(40000.0), + m_radialMin(0.0), + m_radialMax(30000.0), + m_angularShadesVisible(false), + m_radialShadesVisible(false), + m_labelsVisible(true), + m_titleVisible(true), + m_gridVisible(true), + m_arrowVisible(true), + m_angularShadesBrush(new QBrush(Qt::NoBrush)), + m_radialShadesBrush(new QBrush(Qt::NoBrush)), + m_labelBrush(new QBrush(Qt::black)), + m_titleBrush(new QBrush(Qt::black)), + m_angularShadesPen(new QPen(Qt::NoPen)), + m_radialShadesPen(new QPen(Qt::NoPen)), + m_labelPen(new QPen(Qt::NoPen)), + m_titlePen(new QPen(Qt::NoPen)), + m_gridPen(new QPen(QRgb(0x010101))), // Note: Pure black is default color, so it gets overridden by + m_arrowPen(new QPen(QRgb(0x010101))), // default theme if set to that initially. This is an example of workaround. + m_labelFormat(QString("%.2f")), + m_animationOptions(QChart::NoAnimation), + m_angularTitle(QString("Angular Title")), + m_radialTitle(QString("Radial Title")), + m_base(2.0), + m_chart(0), + m_angularAxis(0), + m_radialAxis(0), + m_angularAxisMode(AxisModeNone), + m_radialAxisMode(AxisModeNone), + m_series1(0), + m_series2(0), + m_series3(0), + m_series4(0), + m_series5(0), + m_series6(0), + m_series7(0), + m_dateFormat(QString("mm-ss-zzz")), + m_moreCategories(false) +{ + ui->setupUi(this); + + ui->angularTicksSpin->setValue(m_angularTickCount); + ui->radialTicksSpin->setValue(m_radialTickCount); + ui->anglesSpin->setValue(m_labelsAngle); + ui->radialMinSpin->setValue(m_radialMin); + ui->radialMaxSpin->setValue(m_radialMax); + ui->angularMinSpin->setValue(m_angularMin); + ui->angularMaxSpin->setValue(m_angularMax); + ui->angularShadesComboBox->setCurrentIndex(0); + ui->radialShadesComboBox->setCurrentIndex(0); + ui->labelFormatEdit->setText(m_labelFormat); + ui->dateFormatEdit->setText(m_dateFormat); + ui->moreCategoriesCheckBox->setChecked(m_moreCategories); + + ui->series1checkBox->setChecked(true); + ui->series2checkBox->setChecked(true); + ui->series3checkBox->setChecked(true); + ui->series4checkBox->setChecked(true); + ui->series5checkBox->setChecked(true); + ui->series6checkBox->setChecked(true); + ui->series7checkBox->setChecked(true); + + m_currentLabelFont.setFamily(ui->labelFontComboBox->currentFont().family()); + m_currentLabelFont.setPixelSize(15); + m_currentTitleFont.setFamily(ui->titleFontComboBox->currentFont().family()); + m_currentTitleFont.setPixelSize(30); + + ui->labelFontSizeSpin->setValue(m_currentLabelFont.pixelSize()); + ui->titleFontSizeSpin->setValue(m_currentTitleFont.pixelSize()); + + ui->logBaseSpin->setValue(m_base); + + initXYValueChart(); + setAngularAxis(AxisModeValue); + setRadialAxis(AxisModeValue); + + ui->angularAxisComboBox->setCurrentIndex(int(m_angularAxisMode)); + ui->radialAxisComboBox->setCurrentIndex(int(m_radialAxisMode)); + + connect(ui->angularTicksSpin, SIGNAL(valueChanged(int)), this, SLOT(angularTicksChanged(int))); + connect(ui->radialTicksSpin, SIGNAL(valueChanged(int)), this, SLOT(radialTicksChanged(int))); + connect(ui->anglesSpin, SIGNAL(valueChanged(int)), this, SLOT(anglesChanged(int))); + connect(ui->radialMinSpin, SIGNAL(valueChanged(double)), this, SLOT(radialMinChanged(double))); + connect(ui->radialMaxSpin, SIGNAL(valueChanged(double)), this, SLOT(radialMaxChanged(double))); + connect(ui->angularMinSpin, SIGNAL(valueChanged(double)), this, SLOT(angularMinChanged(double))); + connect(ui->angularMaxSpin, SIGNAL(valueChanged(double)), this, SLOT(angularMaxChanged(double))); + connect(ui->angularShadesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(angularShadesIndexChanged(int))); + connect(ui->radialShadesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(radialShadesIndexChanged(int))); + connect(ui->animationsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(animationIndexChanged(int))); + connect(ui->labelFormatEdit, SIGNAL(textEdited(QString)), this, SLOT(labelFormatEdited(QString))); + connect(ui->labelFontComboBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(labelFontChanged(QFont))); + connect(ui->labelFontSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(labelFontSizeChanged(int))); + connect(ui->labelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(labelsIndexChanged(int))); + connect(ui->titleFontComboBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(titleFontChanged(QFont))); + connect(ui->titleFontSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(titleFontSizeChanged(int))); + connect(ui->titleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(titleIndexChanged(int))); + connect(ui->gridComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(gridIndexChanged(int))); + connect(ui->arrowComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(arrowIndexChanged(int))); + connect(ui->logBaseSpin, SIGNAL(valueChanged(double)), this, SLOT(logBaseChanged(double))); + connect(ui->angularAxisComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(angularAxisIndexChanged(int))); + connect(ui->radialAxisComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(radialAxisIndexChanged(int))); + connect(ui->niceNumbersCheckBox, SIGNAL(clicked()), this, SLOT(niceNumbersChecked())); + connect(ui->dateFormatEdit, SIGNAL(textEdited(QString)), this, SLOT(dateFormatEdited(QString))); + connect(ui->moreCategoriesCheckBox, SIGNAL(clicked()), this, SLOT(moreCategoriesChecked())); + connect(ui->series1checkBox, SIGNAL(clicked()), this, SLOT(series1CheckBoxChecked())); + connect(ui->series2checkBox, SIGNAL(clicked()), this, SLOT(series2CheckBoxChecked())); + connect(ui->series3checkBox, SIGNAL(clicked()), this, SLOT(series3CheckBoxChecked())); + connect(ui->series4checkBox, SIGNAL(clicked()), this, SLOT(series4CheckBoxChecked())); + connect(ui->series5checkBox, SIGNAL(clicked()), this, SLOT(series5CheckBoxChecked())); + connect(ui->series6checkBox, SIGNAL(clicked()), this, SLOT(series6CheckBoxChecked())); + connect(ui->series7checkBox, SIGNAL(clicked()), this, SLOT(series7CheckBoxChecked())); + connect(ui->themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(themeIndexChanged(int))); + + ui->chartView->setChart(m_chart); + ui->chartView->setRenderHint(QPainter::Antialiasing); +} + +MainWindow::~MainWindow() +{ + delete ui; + delete m_angularShadesBrush; + delete m_radialShadesBrush; + delete m_angularShadesPen; + delete m_radialShadesPen; +} + +void MainWindow::initXYValueChart() +{ + qreal seriesAngularMin = 1; + qreal seriesAngularMax = 46000; + qreal seriesRadialMin = 1; + qreal seriesRadialMax = 23500; + qreal radialDimension = seriesRadialMax - seriesRadialMin; + qreal angularDimension = seriesAngularMax - seriesAngularMin; + + // Scatter series, points outside min-max ranges should not be drawn + m_series1 = new QScatterSeries(); + m_series1->setName("scatter"); + qreal scatterCount = 10; + qreal scatterAngularStep = angularDimension / scatterCount; + qreal scatterRadialStep = radialDimension / scatterCount; + for (qreal i = 0.0; i < scatterCount; i++) { + m_series1->append((i * scatterAngularStep) + seriesAngularMin, (i * scatterRadialStep) + seriesRadialMin); + //qDebug() << m_series1->points().last(); + } + m_series1->setMarkerSize(10); + *m_series1 << QPointF(50, 50) << QPointF(150, 150) << QPointF(250, 250) << QPointF(350, 350) << QPointF(450, 450); + *m_series1 << QPointF(1050, 0.50) << QPointF(1150, 0.25) << QPointF(1250, 0.12) << QPointF(1350, 0.075) << QPointF(1450, 0.036); + *m_series1 << QPointF(0.50, 2000) << QPointF(0.25, 3500) << QPointF(0.12, 5000) << QPointF(0.075, 6500) << QPointF(0.036, 8000); + + // Line series, points outside min-max ranges should not be drawn, + // but lines should be properly interpolated at chart edges + m_series2 = new QLineSeries(); + m_series2->setName("line 1"); + qreal lineCount = 100; + qreal lineAngularStep = angularDimension / lineCount; + qreal lineRadialStep = radialDimension / lineCount; + for (qreal i = 0.0; i < lineCount; i++) { + m_series2->append((i * lineAngularStep) + seriesAngularMin, (i * lineRadialStep) + seriesRadialMin); + //qDebug() << m_series2->points().last(); + } + QPen series2Pen = QPen(Qt::blue, 10); + //series2Pen.setStyle(Qt::DashDotDotLine); + m_series2->setPen(series2Pen); + + m_series3 = new QLineSeries(); + m_series3->setName("Area upper"); + lineCount = 87; + lineAngularStep = angularDimension / lineCount; + lineRadialStep = radialDimension / lineCount; + for (qreal i = 1.0; i <= lineCount; i++) { + m_series3->append((i * lineAngularStep) + seriesAngularMin, (i * lineRadialStep) + seriesRadialMin + 200.0); + //qDebug() << m_series3->points().last(); + } + + m_series4 = new QLineSeries(); + m_series4->setName("Area lower"); + lineCount = 89; + lineAngularStep = angularDimension / lineCount; + lineRadialStep = radialDimension / lineCount; + for (qreal i = 1.0; i <= lineCount; i++) { + m_series4->append((i * lineAngularStep) + seriesAngularMin + 100.0, (i * lineRadialStep) + seriesRadialMin + i * 300.0); + //qDebug() << m_series4->points().last(); + } + + m_series5 = new QAreaSeries(); + m_series5->setName("area"); + m_series5->setUpperSeries(m_series3); + m_series5->setLowerSeries(m_series4); + m_series5->setOpacity(0.5); + + m_series6 = new QSplineSeries(); + m_series6->setName("spline"); + qreal ad = angularDimension / 20; + qreal rd = radialDimension / 10; + m_series6->append(seriesAngularMin, seriesRadialMin + rd * 2); + m_series6->append(seriesAngularMin + ad, seriesRadialMin + rd * 5); + m_series6->append(seriesAngularMin + ad * 2, seriesRadialMin + rd * 4); + m_series6->append(seriesAngularMin + ad * 3, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 4, seriesRadialMin + rd * 11); + m_series6->append(seriesAngularMin + ad * 5, seriesRadialMin + rd * 12); + m_series6->append(seriesAngularMin + ad * 6, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 7, seriesRadialMin + rd * 11); + m_series6->append(seriesAngularMin + ad * 8, seriesRadialMin + rd * 12); + m_series6->append(seriesAngularMin + ad * 9, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 10, seriesRadialMin + rd * 4); + m_series6->append(seriesAngularMin + ad * 10, seriesRadialMin + rd * 8); + m_series6->append(seriesAngularMin + ad * 11, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 12, seriesRadialMin + rd * 11); + m_series6->append(seriesAngularMin + ad * 13, seriesRadialMin + rd * 12); + m_series6->append(seriesAngularMin + ad * 14, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 15, seriesRadialMin + rd * 3); + m_series6->append(seriesAngularMin + ad * 16, seriesRadialMin + rd * 2); + m_series6->append(seriesAngularMin + ad * 17, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 18, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 19, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 20, seriesRadialMin + rd * 6); + m_series6->append(seriesAngularMin + ad * 19, seriesRadialMin + rd * 2); + m_series6->append(seriesAngularMin + ad * 18, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 17, seriesRadialMin + rd * 7); + m_series6->append(seriesAngularMin + ad * 16, seriesRadialMin + rd * 3); + m_series6->append(seriesAngularMin + ad * 15, seriesRadialMin + rd * 1); + m_series6->append(seriesAngularMin + ad * 14, seriesRadialMin + rd * 7); + m_series6->append(seriesAngularMin + ad * 13, seriesRadialMin + rd * 5); + m_series6->append(seriesAngularMin + ad * 12, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 11, seriesRadialMin + rd * 1); + m_series6->append(seriesAngularMin + ad * 10, seriesRadialMin + rd * 4); + m_series6->append(seriesAngularMin + ad * 9, seriesRadialMin + rd * 1); + m_series6->append(seriesAngularMin + ad * 8, seriesRadialMin + rd * 2); + m_series6->append(seriesAngularMin + ad * 7, seriesRadialMin + rd * 4); + m_series6->append(seriesAngularMin + ad * 6, seriesRadialMin + rd * 8); + m_series6->append(seriesAngularMin + ad * 5, seriesRadialMin + rd * 12); + m_series6->append(seriesAngularMin + ad * 4, seriesRadialMin + rd * 9); + m_series6->append(seriesAngularMin + ad * 3, seriesRadialMin + rd * 8); + m_series6->append(seriesAngularMin + ad * 2, seriesRadialMin + rd * 7); + m_series6->append(seriesAngularMin + ad, seriesRadialMin + rd * 4); + m_series6->append(seriesAngularMin, seriesRadialMin + rd * 10); + + m_series6->setPointsVisible(true); + QPen series6Pen = QPen(Qt::red, 10); + //series6Pen.setStyle(Qt::DashDotDotLine); + m_series6->setPen(series6Pen); + + // m_series7 shows points at category intersections + m_series7 = new QScatterSeries(); + m_series7->setName("Category check"); + m_series7->setMarkerSize(7); + m_series7->setBrush(QColor(Qt::red)); + m_series7->setMarkerShape(QScatterSeries::MarkerShapeRectangle); + *m_series7 << QPointF(1000, 1000) + << QPointF(1000, 2000) + << QPointF(1000, 4000) + << QPointF(1000, 9000) + << QPointF(1000, 14000) + << QPointF(1000, 16500) + << QPointF(1000, 19000) + + << QPointF(4000, 1000) + << QPointF(4000, 2000) + << QPointF(4000, 4000) + << QPointF(4000, 9000) + << QPointF(4000, 14000) + << QPointF(4000, 16500) + << QPointF(4000, 19000) + + << QPointF(7000, 1000) + << QPointF(7000, 2000) + << QPointF(7000, 4000) + << QPointF(7000, 9000) + << QPointF(7000, 14000) + << QPointF(7000, 16500) + << QPointF(7000, 19000) + + << QPointF(12000, 1000) + << QPointF(12000, 2000) + << QPointF(12000, 4000) + << QPointF(12000, 9000) + << QPointF(12000, 14000) + << QPointF(12000, 16500) + << QPointF(12000, 19000) + + << QPointF(17000, 1000) + << QPointF(17000, 2000) + << QPointF(17000, 4000) + << QPointF(17000, 9000) + << QPointF(17000, 14000) + << QPointF(17000, 16500) + << QPointF(17000, 19000) + + << QPointF(22000, 1000) + << QPointF(22000, 2000) + << QPointF(22000, 4000) + << QPointF(22000, 9000) + << QPointF(22000, 14000) + << QPointF(22000, 16500) + << QPointF(22000, 19000) + + << QPointF(28000, 1000) + << QPointF(28000, 2000) + << QPointF(28000, 4000) + << QPointF(28000, 9000) + << QPointF(28000, 14000) + << QPointF(28000, 16500) + << QPointF(28000, 19000); + + m_chart = new QPolarChart(); + + m_chart->addSeries(m_series1); + m_chart->addSeries(m_series2); + m_chart->addSeries(m_series3); + m_chart->addSeries(m_series4); + m_chart->addSeries(m_series5); + m_chart->addSeries(m_series6); + m_chart->addSeries(m_series7); + + connect(m_series1, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series2, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series3, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series4, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series5, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series6, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series7, SIGNAL(clicked(QPointF)), this, SLOT(seriesClicked(QPointF))); + connect(m_series1, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series2, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series3, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series4, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series5, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series6, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + connect(m_series7, SIGNAL(hovered(QPointF, bool)), this, SLOT(seriesHovered(QPointF, bool))); + + m_chart->setTitle("Use arrow keys to scroll and +/- to zoom"); + m_chart->setAnimationOptions(m_animationOptions); + //m_chart->legend()->setVisible(false); + m_chart->setAcceptHoverEvents(true); +} + +void MainWindow::setAngularAxis(MainWindow::AxisMode mode) +{ + if (m_angularAxis) { + m_chart->removeAxis(m_angularAxis); + delete m_angularAxis; + m_angularAxis = 0; + } + + m_angularAxisMode = mode; + + switch (m_angularAxisMode) { + case AxisModeNone: + return; + case AxisModeValue: + m_angularAxis = new QValueAxis(); + static_cast<QValueAxis *>(m_angularAxis)->setTickCount(m_angularTickCount); + static_cast<QValueAxis *>(m_angularAxis)->setLabelFormat(m_labelFormat); + break; + case AxisModeLogValue: + m_angularAxis = new QLogValueAxis(); + static_cast<QLogValueAxis *>(m_angularAxis)->setBase(m_base); + static_cast<QLogValueAxis *>(m_angularAxis)->setLabelFormat(m_labelFormat); + break; + case AxisModeDateTime: + m_angularAxis = new QDateTimeAxis(); + static_cast<QDateTimeAxis *>(m_angularAxis)->setTickCount(m_angularTickCount); + static_cast<QDateTimeAxis *>(m_angularAxis)->setFormat(m_dateFormat); + break; + case AxisModeCategory: + m_angularAxis = new QCategoryAxis(); + applyCategories(); + break; + default: + qWarning() << "Unsupported AxisMode"; + break; + } + + m_angularAxis->setLabelsAngle(m_labelsAngle); + m_angularAxis->setLabelsFont(m_currentLabelFont); + m_angularAxis->setLabelsBrush(*m_labelBrush); + m_angularAxis->setLabelsPen(*m_labelPen); + m_angularAxis->setLabelsVisible(m_labelsVisible); + m_angularAxis->setShadesBrush(*m_angularShadesBrush); + m_angularAxis->setShadesPen(*m_angularShadesPen); + m_angularAxis->setShadesVisible(m_angularShadesVisible); + m_angularAxis->setTitleFont(m_currentTitleFont); + m_angularAxis->setTitleBrush(*m_titleBrush); + m_angularAxis->setTitlePen(*m_titlePen); + m_angularAxis->setTitleVisible(m_titleVisible); + m_angularAxis->setTitleText(m_angularTitle); + m_angularAxis->setGridLinePen(*m_gridPen); + m_angularAxis->setGridLineVisible(m_gridVisible); + m_angularAxis->setLinePen(*m_arrowPen); + m_angularAxis->setLineVisible(m_arrowVisible); + + m_chart->addAxis(m_angularAxis, QPolarChart::PolarOrientationAngular); + + m_series1->attachAxis(m_angularAxis); + m_series2->attachAxis(m_angularAxis); + m_series3->attachAxis(m_angularAxis); + m_series4->attachAxis(m_angularAxis); + m_series5->attachAxis(m_angularAxis); + m_series6->attachAxis(m_angularAxis); + m_series7->attachAxis(m_angularAxis); + + applyRanges(); + + //connect(m_angularAxis, SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(angularRangeChanged(qreal, qreal))); +} + +void MainWindow::setRadialAxis(MainWindow::AxisMode mode) +{ + if (m_radialAxis) { + m_chart->removeAxis(m_radialAxis); + delete m_radialAxis; + m_radialAxis = 0; + } + + m_radialAxisMode = mode; + + switch (m_radialAxisMode) { + case AxisModeNone: + return; + case AxisModeValue: + m_radialAxis = new QValueAxis(); + static_cast<QValueAxis *>(m_radialAxis)->setTickCount(m_radialTickCount); + static_cast<QValueAxis *>(m_radialAxis)->setLabelFormat(m_labelFormat); + break; + case AxisModeLogValue: + m_radialAxis = new QLogValueAxis(); + static_cast<QLogValueAxis *>(m_radialAxis)->setBase(m_base); + static_cast<QLogValueAxis *>(m_radialAxis)->setLabelFormat(m_labelFormat); + break; + case AxisModeDateTime: + m_radialAxis = new QDateTimeAxis(); + static_cast<QDateTimeAxis *>(m_radialAxis)->setTickCount(m_radialTickCount); + static_cast<QDateTimeAxis *>(m_radialAxis)->setFormat(m_dateFormat); + break; + case AxisModeCategory: + m_radialAxis = new QCategoryAxis(); + applyCategories(); + break; + default: + qWarning() << "Unsupported AxisMode"; + break; + } + + m_radialAxis->setLabelsAngle(m_labelsAngle); + m_radialAxis->setLabelsFont(m_currentLabelFont); + m_radialAxis->setLabelsBrush(*m_labelBrush); + m_radialAxis->setLabelsPen(*m_labelPen); + m_radialAxis->setLabelsVisible(m_labelsVisible); + m_radialAxis->setShadesBrush(*m_radialShadesBrush); + m_radialAxis->setShadesPen(*m_radialShadesPen); + m_radialAxis->setShadesVisible(m_radialShadesVisible); + m_radialAxis->setTitleFont(m_currentTitleFont); + m_radialAxis->setTitleBrush(*m_titleBrush); + m_radialAxis->setTitlePen(*m_titlePen); + m_radialAxis->setTitleVisible(m_titleVisible); + m_radialAxis->setTitleText(m_radialTitle); + m_radialAxis->setGridLinePen(*m_gridPen); + m_radialAxis->setGridLineVisible(m_gridVisible); + m_radialAxis->setLinePen(*m_arrowPen); + m_radialAxis->setLineVisible(m_arrowVisible); + + m_chart->addAxis(m_radialAxis, QPolarChart::PolarOrientationRadial); + + m_series1->attachAxis(m_radialAxis); + m_series2->attachAxis(m_radialAxis); + m_series3->attachAxis(m_radialAxis); + m_series4->attachAxis(m_radialAxis); + m_series5->attachAxis(m_radialAxis); + m_series6->attachAxis(m_radialAxis); + m_series7->attachAxis(m_radialAxis); + + applyRanges(); + + series1CheckBoxChecked(); + series2CheckBoxChecked(); + series3CheckBoxChecked(); + series4CheckBoxChecked(); + series5CheckBoxChecked(); + series6CheckBoxChecked(); + series7CheckBoxChecked(); + + //connect(m_radialAxis, SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(radialRangeChanged(qreal, qreal))); +} + +void MainWindow::applyRanges() +{ + if (ui->niceNumbersCheckBox->isChecked()) { + if (m_angularAxisMode == AxisModeValue) { + static_cast<QValueAxis *>(m_angularAxis)->applyNiceNumbers(); + m_angularMin = static_cast<QValueAxis *>(m_angularAxis)->min(); + m_angularMax = static_cast<QValueAxis *>(m_angularAxis)->max(); + m_angularTickCount = static_cast<QValueAxis *>(m_angularAxis)->tickCount(); + } + if (m_radialAxisMode == AxisModeValue) { + static_cast<QValueAxis *>(m_radialAxis)->applyNiceNumbers(); + m_radialMin = static_cast<QValueAxis *>(m_radialAxis)->min(); + m_radialMax = static_cast<QValueAxis *>(m_radialAxis)->max(); + m_radialTickCount = static_cast<QValueAxis *>(m_radialAxis)->tickCount(); + } + } + + if (m_angularAxis) + m_angularAxis->setRange(m_angularMin, m_angularMax); + if (m_radialAxis) + m_radialAxis->setRange(m_radialMin, m_radialMax); +} + +void MainWindow::angularTicksChanged(int value) +{ + m_angularTickCount = value; + if (m_angularAxisMode == AxisModeValue) + static_cast<QValueAxis *>(m_angularAxis)->setTickCount(m_angularTickCount); + else if (m_angularAxisMode == AxisModeDateTime) + static_cast<QDateTimeAxis *>(m_angularAxis)->setTickCount(m_angularTickCount); +} + +void MainWindow::radialTicksChanged(int value) +{ + m_radialTickCount = value; + if (m_radialAxisMode == AxisModeValue) + static_cast<QValueAxis *>(m_radialAxis)->setTickCount(m_radialTickCount); + else if (m_radialAxisMode == AxisModeDateTime) + static_cast<QDateTimeAxis *>(m_radialAxis)->setTickCount(m_radialTickCount); +} + +void MainWindow::anglesChanged(int value) +{ + m_labelsAngle = value; + m_radialAxis->setLabelsAngle(m_labelsAngle); + m_angularAxis->setLabelsAngle(m_labelsAngle); +} + +void MainWindow::angularMinChanged(double value) +{ + m_angularMin = value; + if (m_angularAxisMode != AxisModeDateTime) { + m_angularAxis->setMin(m_angularMin); + } else { + QDateTime dateTime; + dateTime.setMSecsSinceEpoch(qint64(m_angularMin)); + m_angularAxis->setMin(dateTime); + } +} + +void MainWindow::angularMaxChanged(double value) +{ + m_angularMax = value; + if (m_angularAxisMode != AxisModeDateTime) { + m_angularAxis->setMax(m_angularMax); + } else { + QDateTime dateTime; + dateTime.setMSecsSinceEpoch(qint64(m_angularMax)); + m_angularAxis->setMax(dateTime); + } +} + +void MainWindow::radialMinChanged(double value) +{ + m_radialMin = value; + if (m_radialAxisMode != AxisModeDateTime) { + m_radialAxis->setMin(m_radialMin); + } else { + QDateTime dateTime; + dateTime.setMSecsSinceEpoch(qint64(m_radialMin)); + m_radialAxis->setMin(dateTime); + } +} + +void MainWindow::radialMaxChanged(double value) +{ + m_radialMax = value; + if (m_radialAxisMode != AxisModeDateTime) { + m_radialAxis->setMax(m_radialMax); + } else { + QDateTime dateTime; + dateTime.setMSecsSinceEpoch(qint64(m_radialMax)); + m_radialAxis->setMax(dateTime); + } +} + +void MainWindow::angularShadesIndexChanged(int index) +{ + delete m_angularShadesBrush; + delete m_angularShadesPen; + + switch (index) { + case 0: + m_angularShadesBrush = new QBrush(Qt::NoBrush); + m_angularShadesPen = new QPen(Qt::NoPen); + m_angularShadesVisible = false; + break; + case 1: + m_angularShadesBrush = new QBrush(Qt::lightGray); + m_angularShadesPen = new QPen(Qt::NoPen); + m_angularShadesVisible = true; + break; + case 2: + m_angularShadesBrush = new QBrush(Qt::yellow); + m_angularShadesPen = new QPen(Qt::DotLine); + m_angularShadesPen->setWidth(2); + m_angularShadesVisible = true; + break; + default: + break; + } + + m_angularAxis->setShadesBrush(*m_angularShadesBrush); + m_angularAxis->setShadesPen(*m_angularShadesPen); + m_angularAxis->setShadesVisible(m_angularShadesVisible); +} + +void MainWindow::radialShadesIndexChanged(int index) +{ + delete m_radialShadesBrush; + delete m_radialShadesPen; + + switch (index) { + case 0: + m_radialShadesBrush = new QBrush(Qt::NoBrush); + m_radialShadesPen = new QPen(Qt::NoPen); + m_radialShadesVisible = false; + break; + case 1: + m_radialShadesBrush = new QBrush(Qt::green); + m_radialShadesPen = new QPen(Qt::NoPen); + m_radialShadesVisible = true; + break; + case 2: + m_radialShadesBrush = new QBrush(Qt::blue); + m_radialShadesPen = new QPen(Qt::DotLine); + m_radialShadesPen->setWidth(2); + m_radialShadesVisible = true; + break; + default: + break; + } + + m_radialAxis->setShadesBrush(*m_radialShadesBrush); + m_radialAxis->setShadesPen(*m_radialShadesPen); + m_radialAxis->setShadesVisible(m_radialShadesVisible); +} + +void MainWindow::labelFormatEdited(const QString &text) +{ + m_labelFormat = text; + if (m_angularAxisMode == AxisModeValue) + static_cast<QValueAxis *>(m_angularAxis)->setLabelFormat(m_labelFormat); + else if (m_angularAxisMode == AxisModeLogValue) + static_cast<QLogValueAxis *>(m_angularAxis)->setLabelFormat(m_labelFormat); + + if (m_radialAxisMode == AxisModeValue) + static_cast<QValueAxis *>(m_radialAxis)->setLabelFormat(m_labelFormat); + else if (m_radialAxisMode == AxisModeLogValue) + static_cast<QLogValueAxis *>(m_radialAxis)->setLabelFormat(m_labelFormat); +} + +void MainWindow::labelFontChanged(const QFont &font) +{ + m_currentLabelFont = font; + m_currentLabelFont.setPixelSize(ui->labelFontSizeSpin->value()); + m_angularAxis->setLabelsFont(m_currentLabelFont); + m_radialAxis->setLabelsFont(m_currentLabelFont); +} + +void MainWindow::labelFontSizeChanged(int value) +{ + m_currentLabelFont = ui->labelFontComboBox->currentFont(); + m_currentLabelFont.setPixelSize(value); + m_angularAxis->setLabelsFont(m_currentLabelFont); + m_radialAxis->setLabelsFont(m_currentLabelFont); +} + +void MainWindow::animationIndexChanged(int index) +{ + switch (index) { + case 0: + m_animationOptions = QChart::NoAnimation; + break; + case 1: + m_animationOptions = QChart::SeriesAnimations; + break; + case 2: + m_animationOptions = QChart::GridAxisAnimations; + break; + case 3: + m_animationOptions = QChart::AllAnimations; + break; + default: + break; + } + + m_chart->setAnimationOptions(m_animationOptions); +} + +void MainWindow::labelsIndexChanged(int index) +{ + delete m_labelBrush; + delete m_labelPen; + + switch (index) { + case 0: + m_labelBrush = new QBrush(Qt::NoBrush); + m_labelPen = new QPen(Qt::NoPen); + m_labelsVisible = false; + break; + case 1: + m_labelBrush = new QBrush(Qt::black); + m_labelPen = new QPen(Qt::NoPen); + m_labelsVisible = true; + break; + case 2: + m_labelBrush = new QBrush(Qt::white); + m_labelPen = new QPen(Qt::blue); + m_labelsVisible = true; + break; + default: + break; + } + + m_radialAxis->setLabelsBrush(*m_labelBrush); + m_radialAxis->setLabelsPen(*m_labelPen); + m_radialAxis->setLabelsVisible(m_labelsVisible); + m_angularAxis->setLabelsBrush(*m_labelBrush); + m_angularAxis->setLabelsPen(*m_labelPen); + m_angularAxis->setLabelsVisible(m_labelsVisible); +} + +void MainWindow::titleIndexChanged(int index) +{ + delete m_titleBrush; + delete m_titlePen; + + switch (index) { + case 0: + m_titleBrush = new QBrush(Qt::NoBrush); + m_titlePen = new QPen(Qt::NoPen); + m_titleVisible = false; + m_angularTitle = QString(); + m_radialTitle = QString(); + break; + case 1: + m_titleBrush = new QBrush(Qt::NoBrush); + m_titlePen = new QPen(Qt::NoPen); + m_titleVisible = true; + m_angularTitle = QString(); + m_radialTitle = QString(); + break; + case 2: + m_titleBrush = new QBrush(Qt::NoBrush); + m_titlePen = new QPen(Qt::NoPen); + m_titleVisible = false; + m_angularTitle = QString("Invisible Ang. Title!"); + m_radialTitle = QString("Invisible Rad. Title!"); + break; + case 3: + m_titleBrush = new QBrush(Qt::black); + m_titlePen = new QPen(Qt::NoPen); + m_titleVisible = true; + m_angularTitle = QString("Angular Title"); + m_radialTitle = QString("Radial Title"); + break; + case 4: + m_titleBrush = new QBrush(Qt::white); + m_titlePen = new QPen(Qt::blue); + m_titleVisible = true; + m_angularTitle = QString("Angular Blue Title"); + m_radialTitle = QString("Radial Blue Title"); + break; + default: + break; + } + + m_radialAxis->setTitleBrush(*m_titleBrush); + m_radialAxis->setTitlePen(*m_titlePen); + m_radialAxis->setTitleVisible(m_titleVisible); + m_radialAxis->setTitleText(m_radialTitle); + m_angularAxis->setTitleBrush(*m_titleBrush); + m_angularAxis->setTitlePen(*m_titlePen); + m_angularAxis->setTitleVisible(m_titleVisible); + m_angularAxis->setTitleText(m_angularTitle); +} + +void MainWindow::titleFontChanged(const QFont &font) +{ + m_currentTitleFont = font; + m_currentTitleFont.setPixelSize(ui->titleFontSizeSpin->value()); + m_angularAxis->setTitleFont(m_currentTitleFont); + m_radialAxis->setTitleFont(m_currentTitleFont); +} + +void MainWindow::titleFontSizeChanged(int value) +{ + m_currentTitleFont = ui->titleFontComboBox->currentFont(); + m_currentTitleFont.setPixelSize(value); + m_angularAxis->setTitleFont(m_currentTitleFont); + m_radialAxis->setTitleFont(m_currentTitleFont); +} + +void MainWindow::gridIndexChanged(int index) +{ + delete m_gridPen; + + switch (index) { + case 0: + m_gridPen = new QPen(Qt::NoPen); + m_gridVisible = false; + break; + case 1: + m_gridPen = new QPen(Qt::black); + m_gridVisible = true; + break; + case 2: + m_gridPen = new QPen(Qt::red); + m_gridPen->setStyle(Qt::DashDotLine); + m_gridPen->setWidth(3); + m_gridVisible = true; + break; + default: + break; + } + + m_angularAxis->setGridLinePen(*m_gridPen); + m_angularAxis->setGridLineVisible(m_gridVisible); + m_radialAxis->setGridLinePen(*m_gridPen); + m_radialAxis->setGridLineVisible(m_gridVisible); +} + +void MainWindow::arrowIndexChanged(int index) +{ + delete m_arrowPen; + + switch (index) { + case 0: + m_arrowPen = new QPen(Qt::NoPen); + m_arrowVisible = false; + break; + case 1: + m_arrowPen = new QPen(Qt::black); + m_arrowVisible = true; + break; + case 2: + m_arrowPen = new QPen(Qt::red); + m_arrowPen->setStyle(Qt::DashDotLine); + m_arrowPen->setWidth(3); + m_arrowVisible = true; + break; + default: + break; + } + + m_angularAxis->setLinePen(*m_arrowPen); + m_angularAxis->setLineVisible(m_arrowVisible); + m_radialAxis->setLinePen(*m_arrowPen); + m_radialAxis->setLineVisible(m_arrowVisible); +} + +void MainWindow::angularRangeChanged(qreal min, qreal max) +{ + if (!qFuzzyCompare(ui->angularMinSpin->value(), min)) + ui->angularMinSpin->setValue(min); + if (!qFuzzyCompare(ui->angularMaxSpin->value(), max)) + ui->angularMaxSpin->setValue(max); +} + +void MainWindow::radialRangeChanged(qreal min, qreal max) +{ + if (!qFuzzyCompare(ui->radialMinSpin->value(), min)) + ui->radialMinSpin->setValue(min); + if (!qFuzzyCompare(ui->radialMaxSpin->value(), max)) + ui->radialMaxSpin->setValue(max); +} + +void MainWindow::angularAxisIndexChanged(int index) +{ + switch (index) { + case 0: + setAngularAxis(AxisModeNone); + break; + case 1: + setAngularAxis(AxisModeValue); + break; + case 2: + setAngularAxis(AxisModeLogValue); + break; + case 3: + setAngularAxis(AxisModeDateTime); + break; + case 4: + setAngularAxis(AxisModeCategory); + break; + default: + qWarning("Invalid Index!"); + } +} + +void MainWindow::radialAxisIndexChanged(int index) +{ + switch (index) { + case 0: + setRadialAxis(AxisModeNone); + break; + case 1: + setRadialAxis(AxisModeValue); + break; + case 2: + setRadialAxis(AxisModeLogValue); + break; + case 3: + setRadialAxis(AxisModeDateTime); + break; + case 4: + setRadialAxis(AxisModeCategory); + break; + default: + qWarning("Invalid Index!"); + } +} + +void MainWindow::logBaseChanged(double value) +{ + m_base = value; + if (m_angularAxisMode == AxisModeLogValue) + static_cast<QLogValueAxis *>(m_angularAxis)->setBase(m_base); + if (m_radialAxisMode == AxisModeLogValue) + static_cast<QLogValueAxis *>(m_radialAxis)->setBase(m_base); +} + +void MainWindow::niceNumbersChecked() +{ + if (ui->niceNumbersCheckBox->isChecked()) + applyRanges(); +} + +void MainWindow::dateFormatEdited(const QString &text) +{ + m_dateFormat = text; + if (m_angularAxisMode == AxisModeDateTime) + static_cast<QDateTimeAxis *>(m_angularAxis)->setFormat(m_dateFormat); + if (m_radialAxisMode == AxisModeDateTime) + static_cast<QDateTimeAxis *>(m_radialAxis)->setFormat(m_dateFormat); +} + +void MainWindow::moreCategoriesChecked() +{ + applyCategories(); + m_moreCategories = ui->moreCategoriesCheckBox->isChecked(); +} + +void MainWindow::series1CheckBoxChecked() +{ + if (ui->series1checkBox->isChecked()) + m_series1->setVisible(true); + else + m_series1->setVisible(false); +} + +void MainWindow::series2CheckBoxChecked() +{ + if (ui->series2checkBox->isChecked()) + m_series2->setVisible(true); + else + m_series2->setVisible(false); +} + +void MainWindow::series3CheckBoxChecked() +{ + if (ui->series3checkBox->isChecked()) + m_series3->setVisible(true); + else + m_series3->setVisible(false); +} + +void MainWindow::series4CheckBoxChecked() +{ + if (ui->series4checkBox->isChecked()) + m_series4->setVisible(true); + else + m_series4->setVisible(false); +} + +void MainWindow::series5CheckBoxChecked() +{ + if (ui->series5checkBox->isChecked()) + m_series5->setVisible(true); + else + m_series5->setVisible(false); +} + +void MainWindow::series6CheckBoxChecked() +{ + if (ui->series6checkBox->isChecked()) + m_series6->setVisible(true); + else + m_series6->setVisible(false); +} + +void MainWindow::series7CheckBoxChecked() +{ + if (ui->series7checkBox->isChecked()) + m_series7->setVisible(true); + else + m_series7->setVisible(false); +} + +void MainWindow::themeIndexChanged(int index) +{ + m_chart->setTheme(QChart::ChartTheme(index)); +} + +void MainWindow::seriesHovered(QPointF point, bool state) +{ + QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender()); + if (series) { + if (state) { + QString str("'%3' - %1 x %2"); + ui->hoverLabel->setText(str.arg(point.x()).arg(point.y()).arg(series->name())); + } else { + ui->hoverLabel->setText("No hover"); + } + } else { + qDebug() << "seriesHovered - invalid sender!"; + } +} + +void MainWindow::seriesClicked(const QPointF &point) +{ + QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender()); + if (series) { + QString str("'%3' clicked at: %1 x %2"); + m_angularTitle = str.arg(point.x()).arg(point.y()).arg(series->name()); + m_angularAxis->setTitleText(m_angularTitle); + } else { + qDebug() << "seriesClicked - invalid sender!"; + } +} + +void MainWindow::applyCategories() +{ + // Basic layout is three categories, extended has five + if (m_angularAxisMode == AxisModeCategory) { + QCategoryAxis *angCatAxis = static_cast<QCategoryAxis *>(m_angularAxis); + if (angCatAxis->count() == 0) { + angCatAxis->setStartValue(4000); + angCatAxis->append("Category A", 7000); + angCatAxis->append("Category B", 12000); + angCatAxis->append("Category C", 17000); + } + if (angCatAxis->count() == 3 && ui->moreCategoriesCheckBox->isChecked()) { + angCatAxis->setStartValue(1000); + angCatAxis->replaceLabel("Category A", "Cat A"); + angCatAxis->replaceLabel("Category B", "Cat B"); + angCatAxis->replaceLabel("Category C", "Cat C"); + angCatAxis->append("Cat D", 22000); + angCatAxis->append("Cat E", 28000); + } else if (angCatAxis->count() == 5 && !ui->moreCategoriesCheckBox->isChecked()) { + angCatAxis->setStartValue(4000); + angCatAxis->replaceLabel("Cat A", "Category A"); + angCatAxis->replaceLabel("Cat B", "Category B"); + angCatAxis->replaceLabel("Cat C", "Category C"); + angCatAxis->remove("Cat D"); + angCatAxis->remove("Cat E"); + } + } + + if (m_radialAxisMode == AxisModeCategory) { + QCategoryAxis *radCatAxis = static_cast<QCategoryAxis *>(m_radialAxis); + if (radCatAxis->count() == 0) { + radCatAxis->setStartValue(2000); + radCatAxis->append("Category 1", 4000); + radCatAxis->append("Category 2", 9000); + radCatAxis->append("Category 3", 14000); + } + if (radCatAxis->count() == 3 && ui->moreCategoriesCheckBox->isChecked()) { + radCatAxis->setStartValue(1000); + radCatAxis->replaceLabel("Category 1", "Cat 1"); + radCatAxis->replaceLabel("Category 2", "Cat 2"); + radCatAxis->replaceLabel("Category 3", "Cat 3"); + radCatAxis->append("Cat 4", 16500); + radCatAxis->append("Cat 5", 19000); + } else if (radCatAxis->count() == 5 && !ui->moreCategoriesCheckBox->isChecked()) { + radCatAxis->setStartValue(2000); + radCatAxis->replaceLabel("Cat 1", "Category 1"); + radCatAxis->replaceLabel("Cat 2", "Category 2"); + radCatAxis->replaceLabel("Cat 3", "Category 3"); + radCatAxis->remove("Cat 4"); + radCatAxis->remove("Cat 5"); + } + } +} diff --git a/tests/polarcharttest/mainwindow.h b/tests/polarcharttest/mainwindow.h new file mode 100644 index 00000000..94aa7d84 --- /dev/null +++ b/tests/polarcharttest/mainwindow.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QPolarChart> +#include <QMainWindow> +#include <QFont> +#include <QChart> +#include <QScatterSeries> +#include <QLineSeries> +#include <QSplineSeries> +#include <QAreaSeries> + +namespace Ui { +class MainWindow; +} + +QTCOMMERCIALCHART_USE_NAMESPACE + +class QBrush; +class QPen; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void angularTicksChanged(int value); + void radialTicksChanged(int value); + void anglesChanged(int value); + void angularMinChanged(double value); + void angularMaxChanged(double value); + void radialMinChanged(double value); + void radialMaxChanged(double value); + void angularShadesIndexChanged(int index); + void radialShadesIndexChanged(int index); + void labelFormatEdited(const QString &text); + void labelFontChanged(const QFont &font); + void labelFontSizeChanged(int value); + void animationIndexChanged(int index); + void labelsIndexChanged(int index); + void titleIndexChanged(int index); + void titleFontChanged(const QFont &font); + void titleFontSizeChanged(int value); + void gridIndexChanged(int index); + void arrowIndexChanged(int index); + void angularRangeChanged(qreal min, qreal max); + void radialRangeChanged(qreal min, qreal max); + void angularAxisIndexChanged(int index); + void radialAxisIndexChanged(int index); + void logBaseChanged(double value); + void niceNumbersChecked(); + void dateFormatEdited(const QString &text); + void moreCategoriesChecked(); + void series1CheckBoxChecked(); + void series2CheckBoxChecked(); + void series3CheckBoxChecked(); + void series4CheckBoxChecked(); + void series5CheckBoxChecked(); + void series6CheckBoxChecked(); + void series7CheckBoxChecked(); + void themeIndexChanged(int index); + void seriesHovered(QPointF point, bool state); + void seriesClicked(const QPointF &point); + +private: + enum AxisMode { + AxisModeNone, + AxisModeValue, + AxisModeLogValue, + AxisModeDateTime, + AxisModeCategory + }; + + void initXYValueChart(); + void setAngularAxis(AxisMode mode); + void setRadialAxis(AxisMode mode); + + void applyRanges(); + void applyCategories(); + + Ui::MainWindow *ui; + + int m_angularTickCount; + int m_radialTickCount; + qreal m_labelsAngle; + qreal m_angularMin; + qreal m_angularMax; + qreal m_radialMin; + qreal m_radialMax; + bool m_angularShadesVisible; + bool m_radialShadesVisible; + bool m_labelsVisible; + bool m_titleVisible; + bool m_gridVisible; + bool m_arrowVisible; + QBrush *m_angularShadesBrush; + QBrush *m_radialShadesBrush; + QBrush *m_labelBrush; + QBrush *m_titleBrush; + QPen *m_angularShadesPen; + QPen *m_radialShadesPen; + QPen *m_labelPen; + QPen *m_titlePen; + QPen *m_gridPen; + QPen *m_arrowPen; + QString m_labelFormat; + QFont m_currentLabelFont; + QFont m_currentTitleFont; + QChart::AnimationOptions m_animationOptions; + QString m_angularTitle; + QString m_radialTitle; + qreal m_base; + QString m_dateFormat; + + QPolarChart *m_chart; + QAbstractAxis *m_angularAxis; + QAbstractAxis *m_radialAxis; + AxisMode m_angularAxisMode; + AxisMode m_radialAxisMode; + bool m_moreCategories; + + QScatterSeries *m_series1; + QLineSeries *m_series2; + QLineSeries *m_series3; + QLineSeries *m_series4; + QAreaSeries *m_series5; + QSplineSeries *m_series6; + QScatterSeries *m_series7; +}; + +#endif // MAINWINDOW_H diff --git a/tests/polarcharttest/mainwindow.ui b/tests/polarcharttest/mainwindow.ui new file mode 100644 index 00000000..79cf3793 --- /dev/null +++ b/tests/polarcharttest/mainwindow.ui @@ -0,0 +1,929 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1207</width> + <height>905</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="ChartView" name="chartView"/> + </item> + <item> + <widget class="QGroupBox" name="settingsBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>0</height> + </size> + </property> + <property name="title"> + <string>Settings</string> + </property> + <widget class="QSpinBox" name="radialTicksSpin"> + <property name="geometry"> + <rect> + <x>110</x> + <y>90</y> + <width>71</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label"> + <property name="geometry"> + <rect> + <x>10</x> + <y>90</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Radial Tick count</string> + </property> + </widget> + <widget class="QLabel" name="label_2"> + <property name="geometry"> + <rect> + <x>10</x> + <y>120</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Angular Tick count</string> + </property> + </widget> + <widget class="QSpinBox" name="angularTicksSpin"> + <property name="geometry"> + <rect> + <x>110</x> + <y>120</y> + <width>71</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QSpinBox" name="anglesSpin"> + <property name="geometry"> + <rect> + <x>110</x> + <y>150</y> + <width>71</width> + <height>22</height> + </rect> + </property> + <property name="minimum"> + <number>-9999</number> + </property> + <property name="maximum"> + <number>9999</number> + </property> + <property name="singleStep"> + <number>5</number> + </property> + </widget> + <widget class="QLabel" name="label_3"> + <property name="geometry"> + <rect> + <x>10</x> + <y>150</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Label angles</string> + </property> + </widget> + <widget class="QLabel" name="label_4"> + <property name="geometry"> + <rect> + <x>10</x> + <y>180</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Angular min</string> + </property> + </widget> + <widget class="QLabel" name="label_5"> + <property name="geometry"> + <rect> + <x>10</x> + <y>210</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Angular max</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="angularMinSpin"> + <property name="geometry"> + <rect> + <x>90</x> + <y>180</y> + <width>91</width> + <height>22</height> + </rect> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-999999999.000000000000000</double> + </property> + <property name="maximum"> + <double>999999999.000000000000000</double> + </property> + <property name="singleStep"> + <double>10.000000000000000</double> + </property> + </widget> + <widget class="QDoubleSpinBox" name="angularMaxSpin"> + <property name="geometry"> + <rect> + <x>90</x> + <y>210</y> + <width>91</width> + <height>22</height> + </rect> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-999999999.000000000000000</double> + </property> + <property name="maximum"> + <double>999999999.000000000000000</double> + </property> + <property name="singleStep"> + <double>10.000000000000000</double> + </property> + </widget> + <widget class="QDoubleSpinBox" name="radialMaxSpin"> + <property name="geometry"> + <rect> + <x>90</x> + <y>270</y> + <width>91</width> + <height>22</height> + </rect> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-999999999.000000000000000</double> + </property> + <property name="maximum"> + <double>999999999.000000000000000</double> + </property> + <property name="singleStep"> + <double>10.000000000000000</double> + </property> + </widget> + <widget class="QDoubleSpinBox" name="radialMinSpin"> + <property name="geometry"> + <rect> + <x>90</x> + <y>240</y> + <width>91</width> + <height>22</height> + </rect> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-999999999.000000000000000</double> + </property> + <property name="maximum"> + <double>999999999.000000000000000</double> + </property> + <property name="singleStep"> + <double>10.000000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label_11"> + <property name="geometry"> + <rect> + <x>10</x> + <y>270</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Radial max</string> + </property> + </widget> + <widget class="QLabel" name="label_12"> + <property name="geometry"> + <rect> + <x>10</x> + <y>240</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Radial min</string> + </property> + </widget> + <widget class="QComboBox" name="angularShadesComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>300</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <item> + <property name="text"> + <string>No angular shades</string> + </property> + </item> + <item> + <property name="text"> + <string>Gray angular shades</string> + </property> + </item> + <item> + <property name="text"> + <string>Yellow ang. shades + custom pen</string> + </property> + </item> + </widget> + <widget class="QComboBox" name="radialShadesComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>330</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <item> + <property name="text"> + <string>No radial shades</string> + </property> + </item> + <item> + <property name="text"> + <string>Green radial shades</string> + </property> + </item> + <item> + <property name="text"> + <string>Blue rad. shades + custom pen</string> + </property> + </item> + </widget> + <widget class="QLabel" name="label_13"> + <property name="geometry"> + <rect> + <x>10</x> + <y>360</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Label format</string> + </property> + </widget> + <widget class="QLineEdit" name="labelFormatEdit"> + <property name="geometry"> + <rect> + <x>100</x> + <y>360</y> + <width>81</width> + <height>20</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label_14"> + <property name="geometry"> + <rect> + <x>10</x> + <y>390</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Label font size</string> + </property> + </widget> + <widget class="QFontComboBox" name="labelFontComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>420</y> + <width>171</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QSpinBox" name="labelFontSizeSpin"> + <property name="geometry"> + <rect> + <x>100</x> + <y>390</y> + <width>81</width> + <height>22</height> + </rect> + </property> + <property name="minimum"> + <number>-100000</number> + </property> + <property name="maximum"> + <number>100000</number> + </property> + </widget> + <widget class="QComboBox" name="animationsComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>480</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <item> + <property name="text"> + <string>No animations</string> + </property> + </item> + <item> + <property name="text"> + <string>Series animation</string> + </property> + </item> + <item> + <property name="text"> + <string>Grid animation</string> + </property> + </item> + <item> + <property name="text"> + <string>All animations</string> + </property> + </item> + </widget> + <widget class="QComboBox" name="labelComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>450</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="currentIndex"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>No labels</string> + </property> + </item> + <item> + <property name="text"> + <string>Black label</string> + </property> + </item> + <item> + <property name="text"> + <string>White label + blue pen</string> + </property> + </item> + </widget> + <widget class="QSpinBox" name="titleFontSizeSpin"> + <property name="geometry"> + <rect> + <x>100</x> + <y>510</y> + <width>81</width> + <height>22</height> + </rect> + </property> + <property name="minimum"> + <number>-100000</number> + </property> + <property name="maximum"> + <number>100000</number> + </property> + </widget> + <widget class="QComboBox" name="titleComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>570</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>3</number> + </property> + <item> + <property name="text"> + <string>Invisible empty title</string> + </property> + </item> + <item> + <property name="text"> + <string>Visible empty title</string> + </property> + </item> + <item> + <property name="text"> + <string>Invisible title</string> + </property> + </item> + <item> + <property name="text"> + <string>Black title</string> + </property> + </item> + <item> + <property name="text"> + <string>White title + blue pen</string> + </property> + </item> + </widget> + <widget class="QFontComboBox" name="titleFontComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>540</y> + <width>171</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label_15"> + <property name="geometry"> + <rect> + <x>10</x> + <y>510</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Title font size</string> + </property> + </widget> + <widget class="QComboBox" name="gridComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>600</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>Invisible grid</string> + </property> + </item> + <item> + <property name="text"> + <string>Black grid</string> + </property> + </item> + <item> + <property name="text"> + <string>Custom grid pen</string> + </property> + </item> + </widget> + <widget class="QComboBox" name="arrowComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>630</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>Invisible arrow</string> + </property> + </item> + <item> + <property name="text"> + <string>Black arrow</string> + </property> + </item> + <item> + <property name="text"> + <string>Custom arrow pen</string> + </property> + </item> + </widget> + <widget class="QComboBox" name="angularAxisComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>20</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="currentIndex"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>No Angular Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Angular Value Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Angular Log Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Angular DateTime Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Angular Category Axis</string> + </property> + </item> + </widget> + <widget class="QComboBox" name="radialAxisComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>50</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="currentIndex"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>No Radial Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Radial Value Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Radial Log Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Radial DateTime Axis</string> + </property> + </item> + <item> + <property name="text"> + <string>Radial Category Axis</string> + </property> + </item> + </widget> + <widget class="QLabel" name="label_16"> + <property name="geometry"> + <rect> + <x>10</x> + <y>660</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Log Base</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="logBaseSpin"> + <property name="geometry"> + <rect> + <x>90</x> + <y>660</y> + <width>91</width> + <height>22</height> + </rect> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-999999999.000000000000000</double> + </property> + <property name="maximum"> + <double>999999999.000000000000000</double> + </property> + <property name="value"> + <double>8.000000000000000</double> + </property> + </widget> + <widget class="QCheckBox" name="niceNumbersCheckBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>690</y> + <width>91</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Nice Numbers</string> + </property> + </widget> + <widget class="QLineEdit" name="dateFormatEdit"> + <property name="geometry"> + <rect> + <x>100</x> + <y>710</y> + <width>81</width> + <height>20</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label_17"> + <property name="geometry"> + <rect> + <x>10</x> + <y>710</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>DateTime format</string> + </property> + </widget> + <widget class="QCheckBox" name="moreCategoriesCheckBox"> + <property name="geometry"> + <rect> + <x>100</x> + <y>690</y> + <width>141</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>More Categories</string> + </property> + </widget> + <widget class="QCheckBox" name="series1checkBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>730</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>1</string> + </property> + </widget> + <widget class="QCheckBox" name="series2checkBox"> + <property name="geometry"> + <rect> + <x>40</x> + <y>730</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>2</string> + </property> + </widget> + <widget class="QCheckBox" name="series3checkBox"> + <property name="geometry"> + <rect> + <x>70</x> + <y>730</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>3</string> + </property> + </widget> + <widget class="QCheckBox" name="series4checkBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>750</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>4</string> + </property> + </widget> + <widget class="QCheckBox" name="series5checkBox"> + <property name="geometry"> + <rect> + <x>40</x> + <y>750</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>5</string> + </property> + </widget> + <widget class="QCheckBox" name="series6checkBox"> + <property name="geometry"> + <rect> + <x>70</x> + <y>750</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>6</string> + </property> + </widget> + <widget class="QCheckBox" name="series7checkBox"> + <property name="geometry"> + <rect> + <x>100</x> + <y>740</y> + <width>31</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>7</string> + </property> + </widget> + <widget class="QComboBox" name="themeComboBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>770</y> + <width>171</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <item> + <property name="text"> + <string>Theme: Light</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: Blue Cerulean</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: Dark</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: Brown Sand</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: Blue Ncs</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: High Contrast</string> + </property> + </item> + <item> + <property name="text"> + <string>Theme: Blue Icy</string> + </property> + </item> + </widget> + <widget class="QLabel" name="hoverLabel"> + <property name="geometry"> + <rect> + <x>10</x> + <y>800</y> + <width>171</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Hover coordinates here!</string> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1207</width> + <height>21</height> + </rect> + </property> + </widget> + <widget class="QToolBar" name="mainToolBar"> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + </widget> + <widget class="QStatusBar" name="statusBar"/> + </widget> + <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>ChartView</class> + <extends>QGraphicsView</extends> + <header>chartview.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/tests/polarcharttest/polarcharttest.pro b/tests/polarcharttest/polarcharttest.pro new file mode 100644 index 00000000..25f4b4e3 --- /dev/null +++ b/tests/polarcharttest/polarcharttest.pro @@ -0,0 +1,20 @@ +!include( ../tests.pri ) { + error( "Couldn't find the test.pri file!" ) +} + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = polarcharttest +TEMPLATE = app + + +SOURCES += main.cpp \ + mainwindow.cpp \ + chartview.cpp + +HEADERS += mainwindow.h \ + chartview.h + +FORMS += mainwindow.ui diff --git a/tests/tests.pro b/tests/tests.pro index 068ad6f3..f57bd3e0 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -7,7 +7,8 @@ SUBDIRS += \ auto \ qmlchartproperties \ qmlchartaxis \ - presenterchart + presenterchart \ + polarcharttest contains(QT_CONFIG, opengl) { SUBDIRS += chartwidgettest \ |