| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the demonstration applications of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:BSD$ |
| ** 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 https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** BSD License Usage |
| ** Alternatively, 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 The Qt Company Ltd 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 <QtCore> |
| #include <QtWidgets> |
| #include <QtNetwork> |
| #include <QtSvg> |
| |
| #define GET_DATA_ATTR(val) xml.attributes().value(val).toString() |
| #define GET_DATETIME(val) QDateTime::fromString(val, "yyyy-MM-ddThh:mm:ss") |
| #define FORMAT_TEMPERATURE(val) val + QChar(176) + "C" |
| #define TEXTCOLOR palette().color(QPalette::WindowText) |
| |
| class WeatherInfo: public QMainWindow |
| { |
| Q_OBJECT |
| |
| private: |
| |
| QGraphicsView *m_view; |
| QGraphicsScene m_scene; |
| QString city; |
| QGraphicsRectItem *m_statusItem; |
| QGraphicsTextItem *m_temperatureItem; |
| QGraphicsTextItem *m_conditionItem; |
| QGraphicsTextItem *m_cityItem; |
| QGraphicsTextItem *m_copyright; |
| 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 << "Moscow"; |
| cities << "Helsinki"; |
| cities << "Santa Clara"; |
| for (int i = 0; i < cities.count(); ++i) { |
| QAction *action = new QAction(cities[i], this); |
| connect(action, SIGNAL(triggered()), SLOT(chooseCity())); |
| addAction(action); |
| } |
| setContextMenuPolicy(Qt::ActionsContextMenu); |
| |
| connect(&m_manager, SIGNAL(finished(QNetworkReply*)), |
| this, SLOT(handleNetworkData(QNetworkReply*))); |
| |
| QTimer::singleShot(0, this, SLOT(delayedInit())); |
| } |
| |
| private slots: |
| void delayedInit() { |
| request("http://www.yr.no/place/Norge/Oslo/Oslo/Oslo/varsel.xml"); |
| } |
| |
| private slots: |
| |
| void chooseCity() { |
| QAction *action = qobject_cast<QAction*>(sender()); |
| if (action) { |
| if (action->text() == "Oslo") { |
| request("http://www.yr.no/place/Norge/Oslo/Oslo/Oslo/varsel.xml"); |
| } else if (action->text() == "Berlin") { |
| request("http://www.yr.no/place/Germany/Berlin/Berlin/varsel.xml"); |
| } else if (action->text() == "Moscow") { |
| request("http://www.yr.no/place/Russia/Moscow/Moscow/varsel.xml"); |
| } else if (action->text() == "Helsinki") { |
| request("http://www.yr.no/place/Finland/Southern_Finland/Helsinki/varsel.xml"); |
| } else if (action->text() == "Santa Clara") { |
| request("http://www.yr.no/place/United_States/California/Santa_Clara/varsel.xml"); |
| } |
| } |
| } |
| |
| 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; |
| m_iconItem->setOpacity(progress); |
| 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)); |
| m_conditionItems[i]->setOpacity(alpha); |
| 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() { |
| QFont textFont = font(); |
| textFont.setBold(true); |
| textFont.setPointSize(static_cast<int>(textFont.pointSize() * 1.5)); |
| |
| m_temperatureItem = m_scene.addText(QString(), textFont); |
| m_temperatureItem->setDefaultTextColor(TEXTCOLOR); |
| |
| m_conditionItem = m_scene.addText(QString(), textFont); |
| m_conditionItem->setDefaultTextColor(TEXTCOLOR); |
| |
| m_cityItem = m_scene.addText(QString(), textFont); |
| m_cityItem->setDefaultTextColor(TEXTCOLOR); |
| |
| m_copyright = m_scene.addText(QString()); |
| m_copyright->setDefaultTextColor(TEXTCOLOR); |
| m_copyright->setOpenExternalLinks(true); |
| m_copyright->setTextInteractionFlags(Qt::TextBrowserInteraction); |
| |
| 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); |
| m_copyright->setParentItem(m_statusItem); |
| |
| connect(&m_timeLine, SIGNAL(frameChanged(int)), SLOT(animate(int))); |
| m_timeLine.setDuration(1100); |
| m_timeLine.setFrameRange(0, 100); |
| m_timeLine.setEasingCurve(QEasingCurve::InCurve); |
| } |
| |
| void request(const QString &location) { |
| QUrl url(location); |
| m_manager.get(QNetworkRequest(url)); |
| |
| city = QString(); |
| setWindowTitle("Loading..."); |
| } |
| |
| QString extractIcon(const QString &data) { |
| if (m_icons.isEmpty()) { |
| m_icons["Partly cloudy"] = "weather-few-clouds"; |
| m_icons["Cloudy"] = "weather-overcast"; |
| m_icons["Fair"] = "weather-sunny-very-few-clouds"; |
| m_icons["Sun"] = "weather-sunny"; |
| m_icons["Sun/clear sky"] = "weather-sunny"; |
| m_icons["Clear sky"] = "weather-sunny"; |
| m_icons["Snow showers"] = "weather-snow"; |
| m_icons["Snow"] = "weather-snow"; |
| m_icons["Fog"] = "weather-fog"; |
| m_icons["Sleet"] = "weather-sleet"; |
| m_icons["Sleet showers"] = "weather-sleet"; |
| m_icons["Rain showers"] = "weather-showers"; |
| m_icons["Rain"] = "weather-showers"; |
| m_icons["Heavy rain"] = "weather-showers"; |
| m_icons["Rain showers with thunder"] = "weather-thundershower"; |
| m_icons["Rain and thunder"] = "weather-thundershower"; |
| m_icons["Sleet and thunder"] = "weather-thundershower"; |
| m_icons["Heavy rain and thunder"] = "weather-thundershower"; |
| m_icons["Snow and thunder"] = "weather-thundershower"; |
| m_icons["Sleet showers and thunder"] = "weather-thundershower"; |
| m_icons["Snow showers and thunder"] = "weather-thundershower"; |
| } |
| QString name = m_icons.value(data); |
| if (!name.isEmpty()) { |
| name.prepend(":/icons/"); |
| name.append(".svg"); |
| return name; |
| } |
| return QString(); |
| } |
| |
| void digest(const QString &data) { |
| delete m_iconItem; |
| m_iconItem = new QGraphicsSvgItem(); |
| m_scene.addItem(m_iconItem); |
| m_iconItem->setParentItem(m_statusItem); |
| m_conditionItem->setPlainText(QString()); |
| |
| 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); |
| |
| bool foundCurrentForecast = false; |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "location") { |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "name") { |
| city = xml.readElementText(); |
| m_cityItem->setPlainText(city); |
| setWindowTitle(city); |
| xml.skipCurrentElement(); |
| break; |
| } |
| } |
| } |
| } else if (xml.name() == "credit") { |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "link") { |
| m_copyright->setHtml(QString("<td align=\"center\">%1 <a href=\"%2\">(source)</a></td>").arg(GET_DATA_ATTR("text")).arg(GET_DATA_ATTR("url"))); |
| xml.skipCurrentElement(); |
| break; |
| } |
| } |
| } |
| } else if (xml.name() == "tabular") { |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "time") { |
| if (!foundCurrentForecast) { |
| QString temperature; |
| QString symbol; |
| getSymbolTemp(xml, symbol, temperature); |
| if (!symbol.isEmpty()) { |
| delete m_iconItem; |
| m_iconItem = new QGraphicsSvgItem(symbol); |
| m_scene.addItem(m_iconItem); |
| m_iconItem->setParentItem(m_statusItem); |
| } |
| QString s = FORMAT_TEMPERATURE(temperature); |
| m_temperatureItem->setPlainText(s); |
| foundCurrentForecast = true; |
| } else { |
| createNewDay(xml); |
| } |
| |
| } |
| } |
| } |
| } else if (xml.name() != "weatherdata" && xml.name() != "forecast" && xml.name() != "credit"){ |
| xml.skipCurrentElement(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| m_timeLine.stop(); |
| layoutItems(); |
| animate(0); |
| m_timeLine.start(); |
| } |
| |
| void createNewDay(QXmlStreamReader &xml) { |
| QGraphicsTextItem *dayItem = 0; |
| QString lowT; |
| QString highT; |
| QString period = GET_DATA_ATTR("period"); |
| QString datetime; |
| if (period == "0") |
| datetime = GET_DATA_ATTR("to"); |
| else |
| datetime = GET_DATA_ATTR("from"); |
| QString temperature; |
| QString symbol; |
| getSymbolTemp(xml, symbol, temperature); |
| lowT = highT = temperature; |
| QDateTime date = GET_DATETIME(datetime); |
| dayItem = m_scene.addText(date.date().toString("ddd")); |
| dayItem->setDefaultTextColor(TEXTCOLOR); |
| |
| // check for other info same day |
| bool saved = false; |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "time") { |
| QString period = GET_DATA_ATTR("period"); |
| // save data if new day starts |
| if (period == "0") { |
| saveDayItem(dayItem, lowT, highT, symbol); |
| createNewDay(xml); |
| saved = true; |
| } else { |
| updateDay(xml, lowT, highT, symbol, period == "2"); |
| } |
| } |
| } |
| } |
| if (!saved)// last Item |
| saveDayItem(dayItem, lowT, highT, symbol); |
| } |
| |
| void updateDay(QXmlStreamReader &xml, QString &lowT, QString &highT, QString &symbolToShow, bool updateSymbol) { |
| QString temperature; |
| QString symbol; |
| getSymbolTemp(xml, symbol, temperature); |
| if (lowT.toFloat() > temperature.toFloat()) |
| lowT = temperature; |
| if (highT.toFloat() < temperature.toFloat()) |
| highT = temperature; |
| if (updateSymbol) |
| symbolToShow = symbol; |
| } |
| |
| void saveDayItem(QGraphicsTextItem *dayItem, QString lowT, QString highT, QString symbolToShow) { |
| QGraphicsSvgItem *statusItem = 0; |
| if (!symbolToShow.isEmpty()) { |
| statusItem = new QGraphicsSvgItem(symbolToShow); |
| m_scene.addItem(statusItem); |
| } |
| if (m_dayItems.count() < 4 && dayItem && statusItem && // Show 4 days |
| !lowT.isEmpty() && !highT.isEmpty()) { |
| m_dayItems << dayItem; |
| m_conditionItems << statusItem; |
| QString txt = FORMAT_TEMPERATURE(lowT) + '/' + FORMAT_TEMPERATURE(highT); |
| 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; |
| } |
| } |
| |
| void getSymbolTemp(QXmlStreamReader &xml, QString &symbol, QString &temp) { |
| bool foundIcon = false; |
| bool foundTemp = false; |
| while (!xml.atEnd()) { |
| xml.readNext(); |
| if (xml.tokenType() == QXmlStreamReader::StartElement) { |
| if (xml.name() == "symbol") { |
| QString condition = GET_DATA_ATTR("name"); |
| symbol = extractIcon(condition); |
| if (m_conditionItem->toPlainText().isEmpty()) |
| m_conditionItem->setPlainText(condition); |
| foundIcon = true; |
| } |
| if (xml.name() == "temperature") { |
| temp = GET_DATA_ATTR("value"); |
| foundTemp = true; |
| } |
| if (foundIcon && foundTemp) |
| break; |
| } |
| } |
| } |
| |
| |
| 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() { |
| qreal statusItemWidth = width() / 2 - 1; |
| m_statusItem->setRect(0, 0, statusItemWidth, height() - 1); |
| |
| m_temperatureItem->setPos(10, 2); |
| qreal wtemp = m_temperatureItem->boundingRect().width(); |
| qreal h1 = m_conditionItem->boundingRect().height(); |
| m_conditionItem->setPos(wtemp + 20, 2); |
| |
| m_copyright->setTextWidth(statusItemWidth); |
| |
| qreal wcity = m_cityItem->boundingRect().width(); |
| m_cityItem->setPos(statusItemWidth - wcity - 1, 2);; |
| |
| qreal h2 = m_copyright->boundingRect().height(); |
| m_copyright->setPos(0, height() - h2); |
| |
| if (!m_iconItem->boundingRect().isEmpty()) { |
| qreal sizeLeft = qMin(statusItemWidth, height() - h2 - h1 - 10); |
| qreal sw = sizeLeft / m_iconItem->boundingRect().width(); |
| qreal sh = sizeLeft / m_iconItem->boundingRect().height(); |
| m_iconItem->setTransform(QTransform().scale(sw, sh)); |
| m_iconItem->setPos(statusItemWidth/2 - sizeLeft/2, h1 + 5); |
| } |
| |
| if (m_dayItems.count()) { |
| qreal left = width() * 0.6; |
| qreal statusWidth = 0; |
| qreal rangeWidth = 0; |
| qreal h = height() / m_dayItems.count(); |
| for (int i = 0; i < m_dayItems.count(); ++i) { |
| 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() { |
| qreal statusItemWidth = width() - 1; |
| m_statusItem->setRect(0, 0, statusItemWidth, height() / 2 - 1); |
| |
| m_temperatureItem->setPos(10, 2); |
| qreal wtemp = m_temperatureItem->boundingRect().width(); |
| qreal h1 = m_conditionItem->boundingRect().height(); |
| m_conditionItem->setPos(wtemp + 20, 2); |
| |
| m_copyright->setTextWidth(statusItemWidth); |
| |
| qreal wcity = m_cityItem->boundingRect().width(); |
| m_cityItem->setPos(statusItemWidth - wcity - 1, 2);; |
| |
| m_copyright->setTextWidth(statusItemWidth); |
| qreal h2 = m_copyright->boundingRect().height(); |
| m_copyright->setPos(0, height() - h2); |
| |
| 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; |
| |
| if (!m_iconItem->boundingRect().isEmpty()) { |
| qreal sizeLeft = qMin(statusItemWidth - 10, height() - top - 10); |
| qreal sw = sizeLeft / m_iconItem->boundingRect().width(); |
| qreal sh = sizeLeft / m_iconItem->boundingRect().height(); |
| m_iconItem->setTransform(QTransform().scale(sw, sh)); |
| m_iconItem->setPos(statusItemWidth/2 - sizeLeft/2, h1 + 5); |
| } |
| |
| 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) override { |
| Q_UNUSED(event); |
| layoutItems(); |
| } |
| |
| }; |
| |
| #include "weatherinfo.moc" |
| |
| int main(int argc, char *argv[]) |
| { |
| QApplication app(argc, argv); |
| |
| WeatherInfo w; |
| w.resize(520, 288); |
| w.show(); |
| |
| return app.exec(); |
| } |