From c64ea33f6f52d7dc416aae6c3ab880f60f85763b 2012-02-03 07:39:24 From: Tero Ahola Date: 2012-02-03 07:39:24 Subject: [PATCH] Proof-of-concept for QML api Line, scatter and pie series now shown on a QML app. Uses hard-coded test data. --- diff --git a/charts.pro b/charts.pro index a7d70df..18d71ab 100644 --- a/charts.pro +++ b/charts.pro @@ -3,7 +3,7 @@ } TEMPLATE = subdirs -SUBDIRS += src example test +SUBDIRS += src qmlplugin example test CONFIG += ordered QMAKE_CXXFLAGS += -g -Wall unix:QMAKE_DISTCLEAN += -r build bin diff --git a/common.pri b/common.pri index acf75e5..38921c2 100644 --- a/common.pri +++ b/common.pri @@ -6,12 +6,11 @@ CHART_BUILD_DIR = $$PWD/build CHART_BUILD_BIN_DIR = $$PWD/bin # hack to fix windows builds - win32:{ -CHART_BUILD_HEADER_DIR = $$replace(CHART_BUILD_HEADER_DIR, "/","\\") -CHART_BUILD_LIB_DIR = $$replace(CHART_BUILD_LIB_DIR, "/","\\") -CHART_BUILD_BUILD_DIR = $$replace(CHART_BUILD_BUILD_DIR, "/","\\") -CHART_BUILD_BIN_DIR = $$replace(CHART_BUILD_BIN_DIR, "/","\\") + CHART_BUILD_HEADER_DIR = $$replace(CHART_BUILD_HEADER_DIR, "/","\\") + CHART_BUILD_LIB_DIR = $$replace(CHART_BUILD_LIB_DIR, "/","\\") + CHART_BUILD_BUILD_DIR = $$replace(CHART_BUILD_BUILD_DIR, "/","\\") + CHART_BUILD_BIN_DIR = $$replace(CHART_BUILD_BIN_DIR, "/","\\") } mac: { diff --git a/features/qtcommercialchart.prf b/features/qtcommercialchart.prf index d60608e..72f4a2b 100644 --- a/features/qtcommercialchart.prf +++ b/features/qtcommercialchart.prf @@ -1,5 +1,3 @@ -message( "Configuring with QtCommercialChart addon..." ) - INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtCommercialChart CONFIG(debug, debug|release) { diff --git a/qmlplugin/declarativechart.cpp b/qmlplugin/declarativechart.cpp index 5fcedc5..6151af8 100644 --- a/qmlplugin/declarativechart.cpp +++ b/qmlplugin/declarativechart.cpp @@ -1,27 +1,29 @@ #include "declarativechart.h" -#include -#ifndef QTQUICK2 +QTCOMMERCIALCHART_BEGIN_NAMESPACE + DeclarativeChart::DeclarativeChart(QDeclarativeItem *parent) - : QDeclarativeItem(parent) + : QDeclarativeItem(parent), + m_chart(new QChart(this)) { - // need to disable this flag to draw inside a QDeclarativeItem setFlag(QGraphicsItem::ItemHasNoContents, false); + m_chart->resize(QSize(height(), width())); + m_chart->setMargin(25); // TODO: should not be needed? } -#else -DeclarativeChart::DeclarativeChart(QQuickItem *parent) - : QQuickPaintedItem(parent) -{ +DeclarativeChart::ChartTheme DeclarativeChart::theme() +{ + if (m_chart) + return (ChartTheme) m_chart->theme(); + else + return ThemeInvalid; } -#endif -#ifndef QTQUICK2 -void DeclarativeChart::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -#else -void DeclarativeChart::paint(QPainter *painter) -#endif +void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - drawChart(painter, boundingRect()); + m_chart->resize(QSize(newGeometry.width(), newGeometry.height())); } +#include "moc_declarativechart.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/qmlplugin/declarativechart.h b/qmlplugin/declarativechart.h index 89d7f8b..cef5a05 100644 --- a/qmlplugin/declarativechart.h +++ b/qmlplugin/declarativechart.h @@ -2,36 +2,44 @@ #define DECLARATIVECHART_H #include -#ifndef QTQUICK2 - #include -#else - #include -#endif -#include -#include "../src/chart.h" - -#ifndef QTQUICK2 -class DeclarativeChart : public QDeclarativeItem, public Chart -#else -class DeclarativeChart : public QQuickPaintedItem, public Chart -#endif +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class DeclarativeChart : public QDeclarativeItem +// TODO: for QTQUICK2: extend QQuickPainterItem instead +//class DeclarativeChart : public QQuickPaintedItem, public Chart { Q_OBJECT - Q_PROPERTY(QColor color READ color WRITE setColor) + Q_ENUMS(ChartTheme) + Q_PROPERTY(ChartTheme theme READ theme WRITE setTheme) public: -#ifndef QTQUICK2 + enum ChartTheme { + ThemeInvalid = QChart::ChartThemeInvalid, + ThemeDefault, + ThemeVanilla, + ThemeIcy, + ThemeGrayscale, + //ThemeScientific, + ThemeUnnamed1 + }; DeclarativeChart(QDeclarativeItem *parent = 0); -#else - DeclarativeChart(QQuickItem *parent = 0); -#endif - -#ifndef QTQUICK2 - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); -#else - void paint(QPainter *painter); -#endif + +public: // From QDeclarativeItem/QGraphicsItem + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + +public: + void setTheme(ChartTheme theme) {m_chart->setTheme((QChart::ChartThemeId) theme);} + ChartTheme theme(); + +public: + // 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; }; +QTCOMMERCIALCHART_END_NAMESPACE #endif // DECLARATIVECHART_H diff --git a/qmlplugin/declarativeseries.cpp b/qmlplugin/declarativeseries.cpp new file mode 100644 index 0000000..fa0044d --- /dev/null +++ b/qmlplugin/declarativeseries.cpp @@ -0,0 +1,96 @@ +#include "declarativeseries.h" +#include "declarativechart.h" +#include +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +DeclarativeSeries::DeclarativeSeries(QDeclarativeItem *parent) : + QDeclarativeItem(parent), + m_seriesType(SeriesTypeInvalid), // TODO: default type? + m_chart(0), + m_series(0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + connect(this, SIGNAL(parentChanged()), + this, SLOT(setParentForSeries())); +} + +void DeclarativeSeries::setSeriesType(SeriesType type) +{ + if (!m_series || type != m_seriesType) { + m_seriesType = type; + initSeries(); + } +} + +void DeclarativeSeries::setParentForSeries() +{ + initSeries(); +} + +void DeclarativeSeries::initSeries() +{ + DeclarativeChart *declarativeChart = qobject_cast(parent()); + + if (declarativeChart && m_seriesType != SeriesTypeInvalid) { + delete m_series; + m_series = 0; + + QChart *chart = qobject_cast(declarativeChart->m_chart); + qDebug() << "creating series for chart: " << chart; + Q_ASSERT(chart); + + switch (m_seriesType) { + case SeriesTypeLine: { + m_series = QXYChartSeries::create(this); + for (qreal i(0.0); i < 100.0; i += 1.0) + ((QXYChartSeries *)m_series)->add(i, i); + chart->addSeries(m_series); + break; + } + case SeriesTypeBar: + // fallthrough; bar and scatter use the same test data + case SeriesTypeScatter: { + m_series = chart->createSeries((QChartSeries::QChartSeriesType) m_seriesType); + QList datax; + QList datay; + for (qreal i = 0; i < 100; i += 0.1) { + datax.append(i + (rand() % 5)); + datay.append(abs(sin(3.14159265358979 / 50 * i) * 100) + (rand() % 5)); + } + Q_ASSERT(m_series->setData(datax, datay)); + break; + } + case SeriesTypeStackedBar: + break; + case SeriesTypePercentBar: + break; + case SeriesTypePie: { + m_series = chart->createSeries((QChartSeries::QChartSeriesType) m_seriesType); + QList data; + data << 1.0; + data << 12.0; + data << 4.0; + Q_ASSERT(m_series->setData(data)); + break; + } + default: + break; + } + } +} + +QVariant DeclarativeSeries::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + // For debugging purposes only: +// qDebug() << QString::number(change) << " : " << value.toString(); + return QGraphicsItem::itemChange(change, value); +} + + +#include "moc_declarativeseries.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/qmlplugin/declarativeseries.h b/qmlplugin/declarativeseries.h new file mode 100644 index 0000000..0d57425 --- /dev/null +++ b/qmlplugin/declarativeseries.h @@ -0,0 +1,53 @@ +#ifndef DECLARATIVESERIES_H +#define DECLARATIVESERIES_H + +#include +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class DeclarativeSeries : public QDeclarativeItem +{ + Q_OBJECT + Q_ENUMS(SeriesType) + Q_PROPERTY(SeriesType seriesType READ seriesType WRITE setSeriesType) + +public: + // TODO: how to re-use the existing enum from QChart? + enum SeriesType { + SeriesTypeInvalid = QChartSeries::SeriesTypeInvalid, + SeriesTypeLine, +// SeriesTypeArea, + SeriesTypeBar, + SeriesTypeStackedBar, + SeriesTypePercentBar, + SeriesTypePie, + SeriesTypeScatter +// SeriesTypeSpline + }; + + explicit DeclarativeSeries(QDeclarativeItem *parent = 0); + +signals: + +public slots: + void setParentForSeries(); + +public: // from QDeclarativeItem + QVariant itemChange(GraphicsItemChange, const QVariant &); + +public: + void setSeriesType(SeriesType type); + SeriesType seriesType() { return m_seriesType; } + +private: + void initSeries(); + SeriesType m_seriesType; + QChart *m_chart; + QChartSeries *m_series; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // DECLARATIVESERIES_H diff --git a/qmlplugin/plugin.cpp b/qmlplugin/plugin.cpp index 1b8f64e..d9140f3 100644 --- a/qmlplugin/plugin.cpp +++ b/qmlplugin/plugin.cpp @@ -1,22 +1,27 @@ #include #include #include "declarativechart.h" +#include "declarativeseries.h" -QT_BEGIN_NAMESPACE +QTCOMMERCIALCHART_BEGIN_NAMESPACE -class QmlChartPlugin : public QDeclarativeExtensionPlugin +class ChartQmlPlugin : public QDeclarativeExtensionPlugin { Q_OBJECT public: virtual void registerTypes(const char *uri) { - Q_ASSERT(QLatin1String(uri) == QLatin1String("com.digia.charts")); + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtCommercial.Chart")); qmlRegisterType(uri, 1, 0, "Chart"); + qmlRegisterType(uri, 1, 0, "Series"); + //qmlRegisterUncreatableType(uri, 1, 0, "Series.Se", QLatin1String("Do not create objects of this type.")); } }; -QT_END_NAMESPACE - #include "plugin.moc" -Q_EXPORT_PLUGIN2(qmlchartplugin, QT_PREPEND_NAMESPACE(QmlChartPlugin)); +QTCOMMERCIALCHART_END_NAMESPACE + +QTCOMMERCIALCHART_USE_NAMESPACE + +Q_EXPORT_PLUGIN2(qtcommercialchartqml, QT_PREPEND_NAMESPACE(ChartQmlPlugin)) diff --git a/qmlplugin/qmldir b/qmlplugin/qmldir index 0d373d6..d4b6ae7 100644 --- a/qmlplugin/qmldir +++ b/qmlplugin/qmldir @@ -1 +1 @@ -plugin qmlchartplugin +plugin qtcommercialchartqml diff --git a/qmlplugin/qmlplugin.pro b/qmlplugin/qmlplugin.pro index fb143f0..cde7f93 100644 --- a/qmlplugin/qmlplugin.pro +++ b/qmlplugin/qmlplugin.pro @@ -1,30 +1,39 @@ +!include( ../common.pri ) { + error( "Couldn't find the common.pri file!" ) +} +!include( ../integrated.pri ) { + error( "Couldn't find the integrated.pri file !") +} + TEMPLATE = lib -TARGET = qmlchartplugin +TARGET = qtcommercialchartqml CONFIG += qt plugin QT += declarative contains(QT_MAJOR_VERSION, 5) { + # TODO: QtQuick2 not supported by the implementation currently DEFINES += QTQUICK2 } -OBJECTS_DIR = tmp -MOC_DIR = tmp +OBJECTS_DIR = $$CHART_BUILD_DIR/lib +MOC_DIR = $$CHART_BUILD_DIR/lib +UI_DIR = $$CHART_BUILD_DIR/lib +RCC_DIR = $$CHART_BUILD_DIR/lib SOURCES += \ plugin.cpp \ - declarativechart.cpp + declarativechart.cpp \ + declarativeseries.cpp HEADERS += \ - declarativechart.h -include(../src/chart.pri) + declarativechart.h \ + declarativeseries.h -TARGETPATH = com/digia/charts - -#DESTDIR = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH +TARGETPATH = QtCommercial/Chart target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH - qmldir.files += $$PWD/qmldir qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH INSTALLS += target qmldir + diff --git a/src/qchart.cpp b/src/qchart.cpp index 4c64ce5..492afc1 100644 --- a/src/qchart.cpp +++ b/src/qchart.cpp @@ -47,6 +47,8 @@ QChart::~QChart(){} void QChart::addSeries(QChartSeries* series) { // TODO: we should check the series not already added + Q_ASSERT(series); + Q_ASSERT(series->type() != QChartSeries::SeriesTypeInvalid); m_chartSeries << series; @@ -178,6 +180,8 @@ void QChart::addSeries(QChartSeries* series) m_chartTheme->addObserver(pieSeries->d); break; } + default: + break; } // Update all the items to match the new visible area of the chart @@ -288,6 +292,11 @@ void QChart::setTheme(QChart::ChartThemeId theme) update(); } +QChart::ChartThemeId QChart::theme() +{ + return (QChart::ChartThemeId) m_chartTheme->d->m_currentTheme; +} + void QChart::zoomInToRect(const QRectF& rectangle) { diff --git a/src/qchart.h b/src/qchart.h index 763f448..3e2ee69 100644 --- a/src/qchart.h +++ b/src/qchart.h @@ -58,6 +58,7 @@ public: void setMargin(int margin); int margin() const; void setTheme(QChart::ChartThemeId theme); + QChart::ChartThemeId theme(); void setTitle(const QString& title,const QFont& font = QFont()); void setBackground(const QColor& startColor, const QColor& endColor = Qt::white, GradientOrientation orientation = VerticalGradientOrientation); diff --git a/src/qchartseries.h b/src/qchartseries.h index 816311b..bbe6c3e 100644 --- a/src/qchartseries.h +++ b/src/qchartseries.h @@ -12,7 +12,8 @@ class QTCOMMERCIALCHART_EXPORT QChartSeries : public QObject Q_OBJECT public: enum QChartSeriesType { - SeriesTypeLine = 0, + SeriesTypeInvalid = -1, + SeriesTypeLine, // SeriesTypeArea, SeriesTypeBar, SeriesTypeStackedBar, diff --git a/test/qmlchart/chart.h b/test/qmlchart/chart.h new file mode 100644 index 0000000..fa20070 --- /dev/null +++ b/test/qmlchart/chart.h @@ -0,0 +1,59 @@ +#ifndef CHART_H +#define CHART_H + +#include +#include + +class Chart : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(int theme READ theme WRITE setTheme NOTIFY themeChanged) + +public: + Chart(QDeclarativeItem *parent = 0) : + QDeclarativeItem(parent), m_theme(0) + { + setFlag(QGraphicsItem::ItemHasNoContents, false); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + // TODO: remove + QPen pen(QColor(0, 0, 0), 10); + painter->setPen(pen); + if(smooth() == true) { + painter->setRenderHint(QPainter::Antialiasing, true); + } + painter->drawLine(0, 0, 100, 100); + } + + int theme() const { return m_theme; } + + // Set methods + void setTheme(int theme) { + if (theme != m_theme) { + m_theme = theme; + updateSize(); + emit themeChanged(); + update(); + } + } + +signals: + void themeChanged(); + +private: + void updateSize() { +// setX(qMin(m_x1, m_x2) - m_penWidth/2); +// setY(qMin(m_y1, m_y2) - m_penWidth/2); +// setWidth(qAbs(m_x2 - m_x1) + m_penWidth); +// setHeight(qAbs(m_y2 - m_y1) + m_penWidth); + } + +private: + int m_theme; +}; + +QML_DECLARE_TYPE(Chart) + +#endif // CHART_H diff --git a/test/qmlchart/main.cpp b/test/qmlchart/main.cpp new file mode 100644 index 0000000..125111a --- /dev/null +++ b/test/qmlchart/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "qmlapplicationviewer.h" +#include "chart.h" + +Q_DECL_EXPORT int main(int argc, char *argv[]) +{ + QScopedPointer app(createApplication(argc, argv)); + QScopedPointer viewer(QmlApplicationViewer::create()); + + viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto); + viewer->setMainQmlFile(QLatin1String("qml/qmlchart/main.qml")); + viewer->showExpanded(); + + return app->exec(); +} diff --git a/test/qmlchart/qml/qmlchart/main.qml b/test/qmlchart/qml/qmlchart/main.qml new file mode 100644 index 0000000..e068b65 --- /dev/null +++ b/test/qmlchart/qml/qmlchart/main.qml @@ -0,0 +1,76 @@ +import QtQuick 1.0 +import QtCommercial.Chart 1.0 + +Rectangle { + width: 360 + height: 360 + Text { + text: qsTr("Hello World") + anchors.centerIn: parent + } + +// Component.onCompleted: { +// for (var i = 0.0; i < 100.0; i += 0.1) { +// var x = i + Math.random() * 5; +// var y = Math.abs(Math.sin(3.14159 / 50 * x) * 100) + (Math.random() * 5); +// myData.append({'x':x, 'y':y}); +// } +// } + +// ChartModel { +// id: chartData +// ChartElement { +// y: 1.2 +// } +// ChartElement { +// x: 1.1 +// y: 1.2 +// } +// ChartElement { +// label: "February" +// y: 1.2 +// } +// ChartElement { +// label: "January" +// x: 0.2 +// y: 2.1 +// } +// } + +// Series { +// model: chartData +// seriesType: pie +// axis: Series.AxisSecondaryY +// } + + ListModel { + id: myData + ListElement { + nnn: 55.2 + mmm: 13.1 + } + ListElement { + nnn: 15.3 + mmm: 3.4 + } + } + + Chart { + anchors.fill: parent + theme: Chart.ThemeIcy + + Series { + seriesType: Series.SeriesTypePie + } + Series { + seriesType: Series.SeriesTypeScatter + } + Series { + seriesType: Series.SeriesTypeLine + } + // TODO: +// Series { +// seriesType: Series.SeriesTypeBar +// } + } +} diff --git a/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.cpp b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.cpp new file mode 100644 index 0000000..8ba6e88 --- /dev/null +++ b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.cpp @@ -0,0 +1,200 @@ +// checksum 0x78c version 0x60010 +/* + 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 +#include +#include +#include +#include +#include + +#include // MEEGO_EDITION_HARMATTAN + +#ifdef HARMATTAN_BOOSTER +#include +#endif + +#if defined(QMLJSDEBUGGER) && QT_VERSION < 0x040800 + +#include + +#if !defined(NO_JSDEBUGGER) +#include +#endif +#if !defined(NO_QMLOBSERVER) +#include +#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 +{ + QmlApplicationViewerPrivate(QDeclarativeView *view_) : view(view_) {} + + QString mainQmlFile; + QDeclarativeView *view; + friend class QmlApplicationViewer; + QString adjustPath(const QString &path); +}; + +QString QmlApplicationViewerPrivate::adjustPath(const QString &path) +{ +#ifdef Q_OS_UNIX +#ifdef Q_OS_MAC + if (!QDir::isAbsolutePath(path)) + return QCoreApplication::applicationDirPath() + + QLatin1String("/../Resources/") + path; +#else + QString pathInInstallDir; + const QString applicationDirPath = QCoreApplication::applicationDirPath(); + pathInInstallDir = QString::fromAscii("%1/../%2").arg(applicationDirPath, path); + + if (QFileInfo(pathInInstallDir).exists()) + return pathInInstallDir; +#endif +#endif + return path; +} + +QmlApplicationViewer::QmlApplicationViewer(QWidget *parent) + : QDeclarativeView(parent) + , d(new QmlApplicationViewerPrivate(this)) +{ + 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(d->view->engine()); +#endif +#if !defined(NO_QMLOBSERVER) + new QmlJSDebugger::QDeclarativeViewObserver(d->view, d->view); +#endif +#endif +} + +QmlApplicationViewer::QmlApplicationViewer(QDeclarativeView *view, QWidget *parent) + : QDeclarativeView(parent) + , d(new QmlApplicationViewerPrivate(view)) +{ + connect(view->engine(), SIGNAL(quit()), view, SLOT(close())); + view->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(d->view->engine()); +#endif +#if !defined(NO_QMLOBSERVER) + new QmlJSDebugger::QDeclarativeViewObserver(d->view, d->view); +#endif +#endif +} + +QmlApplicationViewer::~QmlApplicationViewer() +{ + delete d; +} + +QmlApplicationViewer *QmlApplicationViewer::create() +{ +#ifdef HARMATTAN_BOOSTER + return new QmlApplicationViewer(MDeclarativeCache::qDeclarativeView(), 0); +#else + return new QmlApplicationViewer(); +#endif +} + +void QmlApplicationViewer::setMainQmlFile(const QString &file) +{ + d->mainQmlFile = d->adjustPath(file); + d->view->setSource(QUrl::fromLocalFile(d->mainQmlFile)); +} + +void QmlApplicationViewer::addImportPath(const QString &path) +{ + d->view->engine()->addImportPath(d->adjustPath(path)); +} + +void QmlApplicationViewer::setOrientation(ScreenOrientation orientation) +{ +#if defined(Q_OS_SYMBIAN) + // If the version of Qt on the device is < 4.7.2, that attribute won't work + if (orientation != ScreenOrientationAuto) { + const QStringList v = QString::fromAscii(qVersion()).split(QLatin1Char('.')); + if (v.count() == 3 && (v.at(0).toInt() << 16 | v.at(1).toInt() << 8 | v.at(2).toInt()) < 0x040702) { + qWarning("Screen orientation locking only supported with Qt 4.7.2 and above"); + return; + } + } +#endif // Q_OS_SYMBIAN + + 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(128); + break; + case ScreenOrientationLockLandscape: + attribute = static_cast(129); + break; + default: + case ScreenOrientationAuto: + attribute = static_cast(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); +} + +void QmlApplicationViewer::showExpanded() +{ +#if defined(Q_OS_SYMBIAN) || defined(MEEGO_EDITION_HARMATTAN) || defined(Q_WS_SIMULATOR) + d->view->showFullScreen(); +#elif defined(Q_WS_MAEMO_5) + d->view->showMaximized(); +#else + d->view->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/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.h b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.h new file mode 100644 index 0000000..f8008f5 --- /dev/null +++ b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.h @@ -0,0 +1,47 @@ +// checksum 0x82ed version 0x60010 +/* + 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 + +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 Symbian and Fremantle. + void setOrientation(ScreenOrientation orientation); + + void showExpanded(); + +private: + explicit QmlApplicationViewer(QDeclarativeView *view, QWidget *parent); + class QmlApplicationViewerPrivate *d; +}; + +QApplication *createApplication(int &argc, char **argv); + +#endif // QMLAPPLICATIONVIEWER_H diff --git a/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.pri b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.pri new file mode 100644 index 0000000..1b90206 --- /dev/null +++ b/test/qmlchart/qmlapplicationviewer/qmlapplicationviewer.pri @@ -0,0 +1,146 @@ +# checksum 0x368d version 0x60010 +# This file was generated by the Qt Quick Application wizard of Qt Creator. +# The code below adds the QmlApplicationViewer to the project and handles the +# activation of QML debugging. +# It is recommended not to modify this file, since newer versions of Qt Creator +# may offer an updated version of it. + +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 +} + +contains(CONFIG,qdeclarative-boostable):contains(MEEGO_EDITION,harmattan) { + DEFINES += HARMATTAN_BOOSTER +} +# This file was generated by an application wizard of Qt Creator. +# The code below handles deployment to Symbian and Maemo, aswell as copying +# of the application data to shadow build directories on desktop. +# It is recommended not to modify this file, since newer versions of Qt Creator +# may offer an updated version of it. + +defineTest(qtcAddDeployment) { +for(deploymentfolder, DEPLOYMENTFOLDERS) { + item = item$${deploymentfolder} + itemsources = $${item}.sources + $$itemsources = $$eval($${deploymentfolder}.source) + itempath = $${item}.path + $$itempath= $$eval($${deploymentfolder}.target) + export($$itemsources) + export($$itempath) + DEPLOYMENT += $$item +} + +MAINPROFILEPWD = $$PWD + +symbian { + isEmpty(ICON):exists($${TARGET}.svg):ICON = $${TARGET}.svg + isEmpty(TARGET.EPOCHEAPSIZE):TARGET.EPOCHEAPSIZE = 0x20000 0x2000000 +} else:win32 { + copyCommand = + for(deploymentfolder, DEPLOYMENTFOLDERS) { + source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) + source = $$replace(source, /, \\) + sourcePathSegments = $$split(source, \\) + target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) + target = $$replace(target, /, \\) + !isEqual(source,$$target) { + !isEmpty(copyCommand):copyCommand += && + isEqual(QMAKE_DIR_SEP, \\) { + copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" + } else { + source = $$replace(source, \\\\, /) + target = $$OUT_PWD/$$eval($${deploymentfolder}.target) + target = $$replace(target, \\\\, /) + copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" + } + } + } + !isEmpty(copyCommand) { + copyCommand = @echo Copying application data... && $$copyCommand + copydeploymentfolders.commands = $$copyCommand + first.depends = $(first) copydeploymentfolders + export(first.depends) + export(copydeploymentfolders.commands) + QMAKE_EXTRA_TARGETS += first copydeploymentfolders + } +} else:unix { + maemo5 { + desktopfile.files = $${TARGET}.desktop + desktopfile.path = /usr/share/applications/hildon + icon.files = $${TARGET}64.png + icon.path = /usr/share/icons/hicolor/64x64/apps + } else:!isEmpty(MEEGO_VERSION_MAJOR) { + desktopfile.files = $${TARGET}_harmattan.desktop + desktopfile.path = /usr/share/applications + icon.files = $${TARGET}80.png + icon.path = /usr/share/icons/hicolor/80x80/apps + } else { # Assumed to be a Desktop Unix + copyCommand = + for(deploymentfolder, DEPLOYMENTFOLDERS) { + source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) + source = $$replace(source, \\\\, /) + macx { + target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) + } else { + target = $$OUT_PWD/$$eval($${deploymentfolder}.target) + } + target = $$replace(target, \\\\, /) + sourcePathSegments = $$split(source, /) + targetFullPath = $$target/$$last(sourcePathSegments) + !isEqual(source,$$targetFullPath) { + !isEmpty(copyCommand):copyCommand += && + copyCommand += $(MKDIR) \"$$target\" + copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" + } + } + !isEmpty(copyCommand) { + copyCommand = @echo Copying application data... && $$copyCommand + copydeploymentfolders.commands = $$copyCommand + first.depends = $(first) copydeploymentfolders + export(first.depends) + export(copydeploymentfolders.commands) + QMAKE_EXTRA_TARGETS += first copydeploymentfolders + } + } + installPrefix = /opt/$${TARGET} + for(deploymentfolder, DEPLOYMENTFOLDERS) { + item = item$${deploymentfolder} + itemfiles = $${item}.files + $$itemfiles = $$eval($${deploymentfolder}.source) + itempath = $${item}.path + $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) + export($$itemfiles) + export($$itempath) + INSTALLS += $$item + } + + !isEmpty(desktopfile.path) { + export(icon.files) + export(icon.path) + export(desktopfile.files) + export(desktopfile.path) + INSTALLS += icon desktopfile + } + + target.path = $${installPrefix}/bin + export(target.path) + INSTALLS += target +} + +export (ICON) +export (INSTALLS) +export (DEPLOYMENT) +export (TARGET.EPOCHEAPSIZE) +export (TARGET.CAPABILITY) +export (LIBS) +export (QMAKE_EXTRA_TARGETS) +} diff --git a/test/qmlchart/qmlchart.desktop b/test/qmlchart/qmlchart.desktop new file mode 100644 index 0000000..99ebae7 --- /dev/null +++ b/test/qmlchart/qmlchart.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Type=Application +Terminal=false +Name=qmlchart +Exec=/opt/qmlchart/bin/qmlchart +Icon=qmlchart64 +X-Window-Icon= +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable diff --git a/test/qmlchart/qmlchart.pro b/test/qmlchart/qmlchart.pro new file mode 100644 index 0000000..3927027 --- /dev/null +++ b/test/qmlchart/qmlchart.pro @@ -0,0 +1,56 @@ +!include( ../../common.pri ) { + error( "Couldn't find the common.pri file!" ) +} +!include( ../../integrated.pri ) { + error( "Couldn't find the integrated.pri file !") +} + +integrated_build: { + # cannot use integrated build for now; we would need the qml files copied to + # charts/bin folder also to make this work. And even in that case I'm not sure if + # the chart qml plugin can be found or if it needs to be installed to the qt's plugin + # folder always. + warning("TODO: Charts qml test app does not work with integrated builds") +} + +# Add more folders to ship with the application, here +folder_01.source = qml/qmlchart +folder_01.target = qml +DEPLOYMENTFOLDERS = folder_01 + +# Additional import path used to resolve QML modules in Creator's code model +QML_IMPORT_PATH = + +symbian:TARGET.UID3 = 0xE421236E + +# Smart Installer package's UID +# This UID is from the protected range and therefore the package will +# fail to install if self-signed. By default qmake uses the unprotected +# range value if unprotected UID is defined for the application and +# 0x2002CCCF value if protected UID is given to the application +#symbian:DEPLOYMENT.installer_header = 0x2002CCCF + +# Allow network access on Symbian +symbian:TARGET.CAPABILITY += NetworkServices + +# If your application uses the Qt Mobility libraries, uncomment the following +# lines and add the respective components to the MOBILITY variable. +# CONFIG += mobility +# MOBILITY += + +# Speed up launching on MeeGo/Harmattan when using applauncherd daemon +# CONFIG += qdeclarative-boostable + +# Add dependency to Symbian components +# CONFIG += qt-components + +# The .cpp file which was generated for your project. Feel free to hack it. +SOURCES += main.cpp + +# Please do not modify the following two lines. Required for deployment. +include(qmlapplicationviewer/qmlapplicationviewer.pri) +qtcAddDeployment() + +#HEADERS += \ +# chart.h + diff --git a/test/qmlchart/qmlchart.svg b/test/qmlchart/qmlchart.svg new file mode 100644 index 0000000..566acfa --- /dev/null +++ b/test/qmlchart/qmlchart.svg @@ -0,0 +1,93 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/test/qmlchart/qmlchart64.png b/test/qmlchart/qmlchart64.png new file mode 100644 index 0000000000000000000000000000000000000000..707d5c4e85d82959740b243a8a36d5071c277299 GIT binary patch literal 3400 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 IC|gJU4?aG7DgXcg literal 0 Hc$@