diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2013-11-18 10:24:19 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-11-18 11:53:15 +0100 |
commit | 02e88e8349b4c516c855f2f373838a29e9d3e1bb (patch) | |
tree | d439538d57af42be14e22377d347eebdc3973dae /examples/positioning/weatherinfo/appmodel.cpp | |
parent | 898f292736ced9ae83e7fad2da3006196dfaa8c7 (diff) |
Reorder example dir structure according to Qt convention
Causes undesirable example structure in Qt release package
Task-number: QTBUG-34907
Change-Id: I47e2205aa6faef388792146b36cf6ae6cae863da
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Diffstat (limited to 'examples/positioning/weatherinfo/appmodel.cpp')
-rw-r--r-- | examples/positioning/weatherinfo/appmodel.cpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/examples/positioning/weatherinfo/appmodel.cpp b/examples/positioning/weatherinfo/appmodel.cpp new file mode 100644 index 00000000..d0a37265 --- /dev/null +++ b/examples/positioning/weatherinfo/appmodel.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "appmodel.h" + +#include <qgeopositioninfosource.h> +#include <qgeosatelliteinfosource.h> +#include <qnmeapositioninfosource.h> +#include <qgeopositioninfo.h> +#include <qnetworkconfigmanager.h> +#include <qnetworksession.h> + +#include <QSignalMapper> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QStringList> +#include <QUrlQuery> +#include <QElapsedTimer> + +/* + *This application uses http://openweathermap.org/api + **/ + +#define ZERO_KELVIN 273.15 + +WeatherData::WeatherData(QObject *parent) : + QObject(parent) +{ +} + +WeatherData::WeatherData(const WeatherData &other) : + QObject(0), + m_dayOfWeek(other.m_dayOfWeek), + m_weather(other.m_weather), + m_weatherDescription(other.m_weatherDescription), + m_temperature(other.m_temperature) +{ +} + +QString WeatherData::dayOfWeek() const +{ + return m_dayOfWeek; +} + +/*! + * The icon value is based on OpenWeatherMap.org icon set. For details + * see http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes + * + * e.g. 01d ->sunny day + * + * The icon string will be translated to + * http://openweathermap.org/img/w/01d.png + */ +QString WeatherData::weatherIcon() const +{ + return m_weather; +} + +QString WeatherData::weatherDescription() const +{ + return m_weatherDescription; +} + +QString WeatherData::temperature() const +{ + return m_temperature; +} + +void WeatherData::setDayOfWeek(const QString &value) +{ + m_dayOfWeek = value; + emit dataChanged(); +} + +void WeatherData::setWeatherIcon(const QString &value) +{ + m_weather = value; + emit dataChanged(); +} + +void WeatherData::setWeatherDescription(const QString &value) +{ + m_weatherDescription = value; + emit dataChanged(); +} + +void WeatherData::setTemperature(const QString &value) +{ + m_temperature = value; + emit dataChanged(); +} + +class AppModelPrivate +{ +public: + QGeoPositionInfoSource *src; + QGeoCoordinate coord; + QString city; + QNetworkAccessManager *nam; + QNetworkSession *ns; + WeatherData now; + QList<WeatherData*> forecast; + QQmlListProperty<WeatherData> *fcProp; + QSignalMapper *geoReplyMapper; + QSignalMapper *weatherReplyMapper, *forecastReplyMapper; + bool ready; + bool useGps; + QElapsedTimer throttle; + + AppModelPrivate() : + src(NULL), + nam(NULL), + ns(NULL), + fcProp(NULL), + ready(false), + useGps(true) + {} +}; + +static void forecastAppend(QQmlListProperty<WeatherData> *prop, WeatherData *val) +{ + Q_UNUSED(val); + Q_UNUSED(prop); +} + +static WeatherData *forecastAt(QQmlListProperty<WeatherData> *prop, int index) +{ + AppModelPrivate *d = static_cast<AppModelPrivate*>(prop->data); + return d->forecast.at(index); +} + +static int forecastCount(QQmlListProperty<WeatherData> *prop) +{ + AppModelPrivate *d = static_cast<AppModelPrivate*>(prop->data); + return d->forecast.size(); +} + +static void forecastClear(QQmlListProperty<WeatherData> *prop) +{ + static_cast<AppModelPrivate*>(prop->data)->forecast.clear(); +} + +//! [0] +AppModel::AppModel(QObject *parent) : + QObject(parent), + d(new AppModelPrivate) +{ +//! [0] + d->fcProp = new QQmlListProperty<WeatherData>(this, d, + forecastAppend, + forecastCount, + forecastAt, + forecastClear); + + d->geoReplyMapper = new QSignalMapper(this); + d->weatherReplyMapper = new QSignalMapper(this); + d->forecastReplyMapper = new QSignalMapper(this); + + connect(d->geoReplyMapper, SIGNAL(mapped(QObject*)), + this, SLOT(handleGeoNetworkData(QObject*))); + connect(d->weatherReplyMapper, SIGNAL(mapped(QObject*)), + this, SLOT(handleWeatherNetworkData(QObject*))); + connect(d->forecastReplyMapper, SIGNAL(mapped(QObject*)), + this, SLOT(handleForecastNetworkData(QObject*))); + +//! [1] + // make sure we have an active network session + d->nam = new QNetworkAccessManager(this); + + QNetworkConfigurationManager ncm; + d->ns = new QNetworkSession(ncm.defaultConfiguration(), this); + connect(d->ns, SIGNAL(opened()), this, SLOT(networkSessionOpened())); + // the session may be already open. if it is, run the slot directly + if (d->ns->isOpen()) + this->networkSessionOpened(); + // tell the system we want network + d->ns->open(); +} +//! [1] + +AppModel::~AppModel() +{ + d->ns->close(); + if (d->src) + d->src->stopUpdates(); + delete d; +} + +//! [2] +void AppModel::networkSessionOpened() +{ + d->src = QGeoPositionInfoSource::createDefaultSource(this); + + if (d->src) { + d->useGps = true; + connect(d->src, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + d->src->startUpdates(); + } else { + d->useGps = false; + d->city = "Brisbane"; + emit cityChanged(); + this->refreshWeather(); + } +} +//! [2] + +//! [3] +void AppModel::positionUpdated(QGeoPositionInfo gpsPos) +{ + d->coord = gpsPos.coordinate(); + + if (!(d->useGps)) + return; + + QString latitude, longitude; + longitude.setNum(d->coord.longitude()); + latitude.setNum(d->coord.latitude()); +//! [3] + + //don't update more often then once a minute + //to keep load on server low + if (d->throttle.isValid() && d->throttle.elapsed() < 1000*10 ) { + return; + } + d->throttle.restart(); + + QUrl url("http://api.openweathermap.org/data/2.5/weather"); + QUrlQuery query; + query.addQueryItem("lat", latitude); + query.addQueryItem("lon", longitude); + query.addQueryItem("mode", "json"); + url.setQuery(query); + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + d->geoReplyMapper->setMapping(rep, rep); + connect(rep, SIGNAL(finished()), + d->geoReplyMapper, SLOT(map())); +} + +void AppModel::handleGeoNetworkData(QObject *replyObj) +{ + QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj); + if (!networkReply) + return; + + if (!networkReply->error()) { + //convert coordinates to city name + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + QJsonObject jo = document.object(); + QJsonValue jv = jo.value(QStringLiteral("name")); + + const QString city = jv.toString(); + if (city != d->city) { + if (!d->throttle.isValid()) + d->throttle.start(); + d->city = city; + emit cityChanged(); + refreshWeather(); + } + } + networkReply->deleteLater(); +} + +void AppModel::refreshWeather() +{ + QUrl url("http://api.openweathermap.org/data/2.5/weather"); + QUrlQuery query; + + query.addQueryItem("q", d->city); + query.addQueryItem("mode", "json"); + url.setQuery(query); + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + d->weatherReplyMapper->setMapping(rep, rep); + connect(rep, SIGNAL(finished()), + d->weatherReplyMapper, SLOT(map())); +} + +static QString niceTemperatureString(double t) +{ + return QString::number(qRound(t-ZERO_KELVIN)) + QChar(0xB0); +} + +void AppModel::handleWeatherNetworkData(QObject *replyObj) +{ + QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj); + if (!networkReply) + return; + + if (!networkReply->error()) { + foreach (WeatherData *inf, d->forecast) + delete inf; + d->forecast.clear(); + + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + if (document.isObject()) { + QJsonObject obj = document.object(); + QJsonObject tempObject; + QJsonValue val; + + if (obj.contains(QStringLiteral("weather"))) { + val = obj.value(QStringLiteral("weather")); + QJsonArray weatherArray = val.toArray(); + val = weatherArray.at(0); + tempObject = val.toObject(); + d->now.setWeatherDescription(tempObject.value(QStringLiteral("description")).toString()); + d->now.setWeatherIcon(tempObject.value("icon").toString()); + } + if (obj.contains(QStringLiteral("main"))) { + val = obj.value(QStringLiteral("main")); + tempObject = val.toObject(); + val = tempObject.value(QStringLiteral("temp")); + d->now.setTemperature(niceTemperatureString(val.toDouble())); + } + } + } + networkReply->deleteLater(); + + //retrieve the forecast + QUrl url("http://api.openweathermap.org/data/2.5/forecast/daily"); + QUrlQuery query; + + query.addQueryItem("q", d->city); + query.addQueryItem("mode", "json"); + query.addQueryItem("cnt", "5"); + url.setQuery(query); + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + d->forecastReplyMapper->setMapping(rep, rep); + connect(rep, SIGNAL(finished()), d->forecastReplyMapper, SLOT(map())); +} + +void AppModel::handleForecastNetworkData(QObject *replyObj) +{ + QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj); + if (!networkReply) + return; + + if (!networkReply->error()) { + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + QJsonObject jo; + QJsonValue jv; + QJsonObject root = document.object(); + jv = root.value(QStringLiteral("list")); + if (!jv.isArray()) + qWarning() << "Invalid forecast object"; + QJsonArray ja = jv.toArray(); + //we need 4 days of forecast -> first entry is today + if (ja.count() != 5) + qWarning() << "Invalid forecast object"; + + QString data; + for (int i = 1; i<ja.count(); i++) { + WeatherData *forecastEntry = new WeatherData(); + + //min/max temperature + QJsonObject subtree = ja.at(i).toObject(); + jo = subtree.value(QStringLiteral("temp")).toObject(); + jv = jo.value(QStringLiteral("min")); + data.clear(); + data += niceTemperatureString(jv.toDouble()); + data += QChar('/'); + jv = jo.value(QStringLiteral("max")); + data += niceTemperatureString(jv.toDouble()); + forecastEntry->setTemperature(data); + + //get date + jv = subtree.value(QStringLiteral("dt")); + QDateTime dt = QDateTime::fromMSecsSinceEpoch((qint64)jv.toDouble()*1000); + forecastEntry->setDayOfWeek(dt.date().toString(QStringLiteral("ddd"))); + + //get icon + QJsonArray weatherArray = subtree.value(QStringLiteral("weather")).toArray(); + jo = weatherArray.at(0).toObject(); + forecastEntry->setWeatherIcon(jo.value(QStringLiteral("icon")).toString()); + + //get description + forecastEntry->setWeatherDescription(jo.value(QStringLiteral("description")).toString()); + + d->forecast.append(forecastEntry); + } + + if (!(d->ready)) { + d->ready = true; + emit readyChanged(); + } + + emit weatherChanged(); + } + networkReply->deleteLater(); +} + +bool AppModel::hasValidCity() const +{ + return (!(d->city.isEmpty()) && d->city.size() > 1 && d->city != ""); +} + +bool AppModel::hasValidWeather() const +{ + return hasValidCity() && (!(d->now.weatherIcon().isEmpty()) && + (d->now.weatherIcon().size() > 1) && + d->now.weatherIcon() != ""); +} + +WeatherData *AppModel::weather() const +{ + return &(d->now); +} + +QQmlListProperty<WeatherData> AppModel::forecast() const +{ + return *(d->fcProp); +} + +bool AppModel::ready() const +{ + return d->ready; +} + +bool AppModel::hasSource() const +{ + return (d->src != NULL); +} + +bool AppModel::useGps() const +{ + return d->useGps; +} + +void AppModel::setUseGps(bool value) +{ + d->useGps = value; + if (value) { + d->city = ""; + d->throttle.invalidate(); + emit cityChanged(); + emit weatherChanged(); + } + emit useGpsChanged(); +} + +QString AppModel::city() const +{ + return d->city; +} + +void AppModel::setCity(const QString &value) +{ + d->city = value; + emit cityChanged(); + refreshWeather(); +} |