weatherinfo.cpp Example File

demos/embedded/weatherinfo/weatherinfo.cpp
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore>
#include <QtGui>
#include <QtNetwork>
#include <QtSvg>

class WeatherInfo: public QMainWindow
{
    Q_OBJECT

private:

    QGraphicsView *m_view;
    QGraphicsScene m_scene;
    QString city;
    QGraphicsRectItem *m_statusItem;
    QGraphicsTextItem *m_temperatureItem;
    QGraphicsTextItem *m_conditionItem;
    QGraphicsSvgItem *m_iconItem;
    QList<QGraphicsRectItem*> m_forecastItems;
    QList<QGraphicsTextItem*> m_dayItems;
    QList<QGraphicsSvgItem*> m_conditionItems;
    QList<QGraphicsTextItem*> m_rangeItems;
    QTimeLine m_timeLine;
    QHash<QString, QString> m_icons;
    QNetworkAccessManager m_manager;

public:
    WeatherInfo(QWidget *parent = 0): QMainWindow(parent) {

        m_view = new QGraphicsView(this);
        setCentralWidget(m_view);

        setupScene();
        m_view->setScene(&m_scene);
        m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

        m_view->setFrameShape(QFrame::NoFrame);
        setWindowTitle("Weather Info");

        QStringList cities;
        cities << "Oslo";
        cities << "Berlin";
        cities << "Brisbane";
        cities << "Helsinki";
        cities << "San Diego";
        for (int i = 0; i < cities.count(); ++i) {
            QAction *action = new QAction(cities[i], this);
            connect(action, SIGNAL(triggered()), SLOT(chooseCity()));
            addAction(action);
#if defined(Q_OS_SYMBIAN)
            menuBar()->addAction(action);
#endif
        }
        setContextMenuPolicy(Qt::ActionsContextMenu);

        connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                this, SLOT(handleNetworkData(QNetworkReply*)));

        QTimer::singleShot(0, this, SLOT(delayedInit()));
    }

private slots:
    void delayedInit() {
        request("Oslo");
    }

private slots:

    void chooseCity() {
        QAction *action = qobject_cast<QAction*>(sender());
        if (action)
            request(action->text());
    }

    void handleNetworkData(QNetworkReply *networkReply) {
        QUrl url = networkReply->url();
        if (!networkReply->error())
            digest(QString::fromUtf8(networkReply->readAll()));
        networkReply->deleteLater();
    }

    void animate(int frame) {
        qreal progress = static_cast<qreal>(frame) / 100;
#if QT_VERSION >= 0x040500
        m_iconItem->setOpacity(progress);
#endif
        qreal hw = width() / 2.0;
        m_statusItem->setPos(-hw + hw * progress, 0);
        for (int i = 0; i < m_forecastItems.count(); ++i) {
            qreal ofs = i * 0.5 / m_forecastItems.count();
            qreal alpha = qBound(qreal(0), 2 * (progress - ofs), qreal(1));
#if QT_VERSION >= 0x040500
            m_conditionItems[i]->setOpacity(alpha);
#endif
            QPointF pos = m_forecastItems[i]->pos();
            if (width() > height()) {
                qreal fx = width() - width() * 0.4 * alpha;
                m_forecastItems[i]->setPos(fx, pos.y());
            } else {
                qreal fx = height() - height() * 0.5 * alpha;
                m_forecastItems[i]->setPos(pos.x(), fx);
            }
        }
    }

private:

    void setupScene() {

        QColor textColor = palette().color(QPalette::WindowText);
        QFont textFont = font();
        textFont.setBold(true);
        textFont.setPointSize(textFont.pointSize() * 2);

        m_temperatureItem = m_scene.addText(QString(), textFont);
        m_temperatureItem->setDefaultTextColor(textColor);

        m_conditionItem = m_scene.addText(QString(), textFont);
        m_conditionItem->setDefaultTextColor(textColor);

        m_iconItem = new QGraphicsSvgItem;
        m_scene.addItem(m_iconItem);

        m_statusItem = m_scene.addRect(0, 0, 10, 10);
        m_statusItem->setPen(Qt::NoPen);
        m_statusItem->setBrush(Qt::NoBrush);
        m_temperatureItem->setParentItem(m_statusItem);
        m_conditionItem->setParentItem(m_statusItem);
        m_iconItem->setParentItem(m_statusItem);

        connect(&m_timeLine, SIGNAL(frameChanged(int)), SLOT(animate(int)));
        m_timeLine.setDuration(1100);
        m_timeLine.setFrameRange(0, 100);
        m_timeLine.setCurveShape(QTimeLine::EaseInCurve);
    }

    void request(const QString &location) {
        QUrl url("http://www.google.com/ig/api");
        url.addEncodedQueryItem("hl", "en");
        url.addEncodedQueryItem("weather", QUrl::toPercentEncoding(location));

        m_manager.get(QNetworkRequest(url));

        city = QString();
        setWindowTitle("Loading...");
    }

    QString extractIcon(const QString &data) {
        if (m_icons.isEmpty()) {
            m_icons["mostly_cloudy"]    = "weather-few-clouds";
            m_icons["cloudy"]           = "weather-overcast";
            m_icons["mostly_sunny"]     = "weather-sunny-very-few-clouds";
            m_icons["partly_cloudy"]    = "weather-sunny-very-few-clouds";
            m_icons["sunny"]            = "weather-sunny";
            m_icons["flurries"]         = "weather-snow";
            m_icons["fog"]              = "weather-fog";
            m_icons["haze"]             = "weather-haze";
            m_icons["icy"]              = "weather-icy";
            m_icons["sleet"]            = "weather-sleet";
            m_icons["chance_of_sleet"]  = "weather-sleet";
            m_icons["snow"]             = "weather-snow";
            m_icons["chance_of_snow"]   = "weather-snow";
            m_icons["mist"]             = "weather-showers";
            m_icons["rain"]             = "weather-showers";
            m_icons["chance_of_rain"]   = "weather-showers";
            m_icons["storm"]            = "weather-storm";
            m_icons["chance_of_storm"]  = "weather-storm";
            m_icons["thunderstorm"]     = "weather-thundershower";
            m_icons["chance_of_tstorm"] = "weather-thundershower";
        }
        QRegExp regex("([\\w]+).gif$");
        if (regex.indexIn(data) != -1) {
            QString i = regex.cap();
            i = i.left(i.length() - 4);
            QString name = m_icons.value(i);
            if (!name.isEmpty()) {
                name.prepend(":/icons/");
                name.append(".svg");
                return name;
            }
        }
        return QString();
    }

    static QString toCelcius(QString t, QString unit) {
        bool ok = false;
        int degree = t.toInt(&ok);
        if (!ok)
            return QString();
        if (unit != "SI")
            degree = ((degree - 32) * 5 + 8)/ 9;
        return QString::number(degree) + QChar(176);
    }

#define GET_DATA_ATTR xml.attributes().value("data").toString()

    void digest(const QString &data) {

        QColor textColor = palette().color(QPalette::WindowText);
        QString unitSystem;

        delete m_iconItem;
        m_iconItem = new QGraphicsSvgItem();
        m_scene.addItem(m_iconItem);
        m_iconItem->setParentItem(m_statusItem);
        qDeleteAll(m_dayItems);
        qDeleteAll(m_conditionItems);
        qDeleteAll(m_rangeItems);
        qDeleteAll(m_forecastItems);
        m_dayItems.clear();
        m_conditionItems.clear();
        m_rangeItems.clear();
        m_forecastItems.clear();

        QXmlStreamReader xml(data);
        while (!xml.atEnd()) {
            xml.readNext();
            if (xml.tokenType() == QXmlStreamReader::StartElement) {
                if (xml.name() == "city") {
                    city = GET_DATA_ATTR;
                    setWindowTitle(city);
                }
                if (xml.name() == "unit_system")
                    unitSystem = xml.attributes().value("data").toString();
                // Parse current weather conditions
                if (xml.name() == "current_conditions") {
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "current_conditions")
                            break;
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "condition") {
                                m_conditionItem->setPlainText(GET_DATA_ATTR);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    delete m_iconItem;
                                    m_iconItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(m_iconItem);
                                    m_iconItem->setParentItem(m_statusItem);
                                }
                            }
                            if (xml.name() == "temp_c") {
                                QString s = GET_DATA_ATTR + QChar(176);
                                m_temperatureItem->setPlainText(s);
                            }
                        }
                    }
                }
                // Parse and collect the forecast conditions
                if (xml.name() == "forecast_conditions") {
                    QGraphicsTextItem *dayItem  = 0;
                    QGraphicsSvgItem *statusItem = 0;
                    QString lowT, highT;
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "forecast_conditions") {
                            if (dayItem && statusItem &&
                                !lowT.isEmpty() && !highT.isEmpty()) {
                                m_dayItems << dayItem;
                                m_conditionItems << statusItem;
                                QString txt = highT + '/' + lowT;
                                QGraphicsTextItem* rangeItem;
                                rangeItem = m_scene.addText(txt);
                                rangeItem->setDefaultTextColor(textColor);
                                m_rangeItems << rangeItem;
                                QGraphicsRectItem *box;
                                box = m_scene.addRect(0, 0, 10, 10);
                                box->setPen(Qt::NoPen);
                                box->setBrush(Qt::NoBrush);
                                m_forecastItems << box;
                                dayItem->setParentItem(box);
                                statusItem->setParentItem(box);
                                rangeItem->setParentItem(box);
                            } else {
                                delete dayItem;
                                delete statusItem;
                            }
                            break;
                        }
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "day_of_week") {
                                QString s = GET_DATA_ATTR;
                                dayItem = m_scene.addText(s.left(3));
                                dayItem->setDefaultTextColor(textColor);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    statusItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(statusItem);
                                }
                            }
                            if (xml.name() == "low")
                                lowT = toCelcius(GET_DATA_ATTR, unitSystem);
                            if (xml.name() == "high")
                                highT = toCelcius(GET_DATA_ATTR, unitSystem);
                        }
                    }
                }

            }
        }

        m_timeLine.stop();
        layoutItems();
        animate(0);
        m_timeLine.start();
    }

    void layoutItems() {
        m_scene.setSceneRect(0, 0, width() - 1, height() - 1);
        m_view->centerOn(width() / 2, height() / 2);
        if (width() > height())
            layoutItemsLandscape();
        else
            layoutItemsPortrait();
    }

    void layoutItemsLandscape() {
        m_statusItem->setRect(0, 0, width() / 2 - 1, height() - 1);

        if (!m_iconItem->boundingRect().isEmpty()) {
            qreal dim = qMin(width() * 0.6, height() * 0.8);
            qreal pad = (height()  - dim) / 2;
            qreal sw = dim / m_iconItem->boundingRect().width();
            qreal sh = dim / m_iconItem->boundingRect().height();
            m_iconItem->setTransform(QTransform().scale(sw, sh));
            m_iconItem->setPos(1, pad);
        }

        m_temperatureItem->setPos(2, 2);
        qreal h = m_conditionItem->boundingRect().height();
        m_conditionItem->setPos(10, height() - h);

        if (m_dayItems.count()) {
            qreal left = width() * 0.6;
            qreal h = height() / m_dayItems.count();
            QFont textFont = font();
            textFont.setPixelSize(static_cast<int>(h * 0.3));
            qreal statusWidth = 0;
            qreal rangeWidth = 0;
            for (int i = 0; i < m_dayItems.count(); ++i) {
                m_dayItems[i]->setFont(textFont);
                QRectF brect = m_dayItems[i]->boundingRect();
                statusWidth = qMax(statusWidth, brect.width());
                brect = m_rangeItems[i]->boundingRect();
                rangeWidth = qMax(rangeWidth, brect.width());
            }
            qreal space = width() - left - statusWidth - rangeWidth;
            qreal dim = qMin(h, space);
            qreal pad = statusWidth + (space  - dim) / 2;
            for (int i = 0; i < m_dayItems.count(); ++i) {
                qreal base = h * i;
                m_forecastItems[i]->setPos(left, base);
                m_forecastItems[i]->setRect(0, 0, width() - left, h);
                QRectF brect = m_dayItems[i]->boundingRect();
                qreal ofs = (h - brect.height()) / 2;
                m_dayItems[i]->setPos(0, ofs);
                brect = m_rangeItems[i]->boundingRect();
                ofs = (h - brect.height()) / 2;
                m_rangeItems[i]->setPos(width() - rangeWidth - left, ofs);
                brect = m_conditionItems[i]->boundingRect();
                ofs = (h - dim) / 2;
                m_conditionItems[i]->setPos(pad, ofs);
                if (brect.isEmpty())
                    continue;
                qreal sw = dim / brect.width();
                qreal sh = dim / brect.height();
                m_conditionItems[i]->setTransform(QTransform().scale(sw, sh));
            }
        }
    }

    void layoutItemsPortrait() {

        m_statusItem->setRect(0, 0, width() - 1, height() / 2 - 1);

        if (!m_iconItem->boundingRect().isEmpty()) {
            qreal dim = qMin(width() * 0.8, height() * 0.4);
            qreal ofsy = (height() / 2  - dim) / 2;
            qreal ofsx = (width() - dim) / 3;
            qreal sw = dim / m_iconItem->boundingRect().width();
            qreal sh = dim / m_iconItem->boundingRect().height();
            m_iconItem->setTransform(QTransform().scale(sw, sh));
            m_iconItem->setPos(ofsx, ofsy);
        }

        m_temperatureItem->setPos(2, 2);
        qreal ch = m_conditionItem->boundingRect().height();
        qreal cw = m_conditionItem->boundingRect().width();
        m_conditionItem->setPos(width() - cw , height() / 2 - ch - 20);

        if (m_dayItems.count()) {
            qreal top = height() * 0.5;
            qreal w = width() / m_dayItems.count();
            qreal statusHeight = 0;
            qreal rangeHeight = 0;
            for (int i = 0; i < m_dayItems.count(); ++i) {
                m_dayItems[i]->setFont(font());
                QRectF brect = m_dayItems[i]->boundingRect();
                statusHeight = qMax(statusHeight, brect.height());
                brect = m_rangeItems[i]->boundingRect();
                rangeHeight = qMax(rangeHeight, brect.height());
            }
            qreal space = height() - top - statusHeight - rangeHeight;
            qreal dim = qMin(w, space);

            qreal boxh = statusHeight + rangeHeight + dim;
            qreal pad = (height() - top - boxh) / 2;

            for (int i = 0; i < m_dayItems.count(); ++i) {
                qreal base = w * i;
                m_forecastItems[i]->setPos(base, top);
                m_forecastItems[i]->setRect(0, 0, w, boxh);
                QRectF brect = m_dayItems[i]->boundingRect();
                qreal ofs = (w - brect.width()) / 2;
                m_dayItems[i]->setPos(ofs, pad);

                brect = m_rangeItems[i]->boundingRect();
                ofs = (w - brect.width()) / 2;
                m_rangeItems[i]->setPos(ofs, pad + statusHeight + dim);

                brect = m_conditionItems[i]->boundingRect();
                ofs = (w - dim) / 2;
                m_conditionItems[i]->setPos(ofs, pad + statusHeight);
                if (brect.isEmpty())
                    continue;
                qreal sw = dim / brect.width();
                qreal sh = dim / brect.height();
                m_conditionItems[i]->setTransform(QTransform().scale(sw, sh));
            }
        }
    }

    void resizeEvent(QResizeEvent *event) {
        Q_UNUSED(event);
        layoutItems();
    }

};

#include "weatherinfo.moc"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    WeatherInfo w;
#if defined(Q_OS_SYMBIAN)
    w.showMaximized();
#else
    w.resize(520, 288);
    w.show();
#endif

    return app.exec();
}

© 2016 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.