1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the demonstration applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include <QtCore> |
52 | #include <QtWidgets> |
53 | #include <QtNetwork> |
54 | #include <QtSvg> |
55 | |
56 | #define GET_DATA_ATTR(val) xml.attributes().value(val).toString() |
57 | #define GET_DATETIME(val) QDateTime::fromString(val, "yyyy-MM-ddThh:mm:ss") |
58 | #define FORMAT_TEMPERATURE(val) val + QChar(176) + "C" |
59 | #define TEXTCOLOR palette().color(QPalette::WindowText) |
60 | |
61 | class WeatherInfo: public QMainWindow |
62 | { |
63 | Q_OBJECT |
64 | |
65 | private: |
66 | |
67 | QGraphicsView *m_view; |
68 | QGraphicsScene m_scene; |
69 | QString city; |
70 | QGraphicsRectItem *m_statusItem; |
71 | QGraphicsTextItem *m_temperatureItem; |
72 | QGraphicsTextItem *m_conditionItem; |
73 | QGraphicsTextItem *m_cityItem; |
74 | QGraphicsTextItem *m_copyright; |
75 | QGraphicsSvgItem *m_iconItem; |
76 | QList<QGraphicsRectItem*> m_forecastItems; |
77 | QList<QGraphicsTextItem*> m_dayItems; |
78 | QList<QGraphicsSvgItem*> m_conditionItems; |
79 | QList<QGraphicsTextItem*> m_rangeItems; |
80 | QTimeLine m_timeLine; |
81 | QHash<QString, QString> m_icons; |
82 | QNetworkAccessManager m_manager; |
83 | |
84 | public: |
85 | WeatherInfo(QWidget *parent = 0): QMainWindow(parent) { |
86 | |
87 | m_view = new QGraphicsView(this); |
88 | setCentralWidget(m_view); |
89 | |
90 | setupScene(); |
91 | m_view->setScene(&m_scene); |
92 | m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
93 | m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
94 | |
95 | m_view->setFrameShape(QFrame::NoFrame); |
96 | setWindowTitle("Weather Info" ); |
97 | |
98 | QStringList cities; |
99 | cities << "Oslo" ; |
100 | cities << "Berlin" ; |
101 | cities << "Moscow" ; |
102 | cities << "Helsinki" ; |
103 | cities << "Santa Clara" ; |
104 | for (int i = 0; i < cities.count(); ++i) { |
105 | QAction *action = new QAction(cities[i], this); |
106 | connect(asender: action, SIGNAL(triggered()), SLOT(chooseCity())); |
107 | addAction(action); |
108 | } |
109 | setContextMenuPolicy(Qt::ActionsContextMenu); |
110 | |
111 | connect(sender: &m_manager, SIGNAL(finished(QNetworkReply*)), |
112 | receiver: this, SLOT(handleNetworkData(QNetworkReply*))); |
113 | |
114 | QTimer::singleShot(msec: 0, receiver: this, SLOT(delayedInit())); |
115 | } |
116 | |
117 | private slots: |
118 | void delayedInit() { |
119 | request(location: "http://www.yr.no/place/Norge/Oslo/Oslo/Oslo/varsel.xml" ); |
120 | } |
121 | |
122 | private slots: |
123 | |
124 | void chooseCity() { |
125 | QAction *action = qobject_cast<QAction*>(object: sender()); |
126 | if (action) { |
127 | if (action->text() == "Oslo" ) { |
128 | request(location: "http://www.yr.no/place/Norge/Oslo/Oslo/Oslo/varsel.xml" ); |
129 | } else if (action->text() == "Berlin" ) { |
130 | request(location: "http://www.yr.no/place/Germany/Berlin/Berlin/varsel.xml" ); |
131 | } else if (action->text() == "Moscow" ) { |
132 | request(location: "http://www.yr.no/place/Russia/Moscow/Moscow/varsel.xml" ); |
133 | } else if (action->text() == "Helsinki" ) { |
134 | request(location: "http://www.yr.no/place/Finland/Southern_Finland/Helsinki/varsel.xml" ); |
135 | } else if (action->text() == "Santa Clara" ) { |
136 | request(location: "http://www.yr.no/place/United_States/California/Santa_Clara/varsel.xml" ); |
137 | } |
138 | } |
139 | } |
140 | |
141 | void handleNetworkData(QNetworkReply *networkReply) { |
142 | QUrl url = networkReply->url(); |
143 | if (!networkReply->error()) |
144 | digest(data: QString::fromUtf8(str: networkReply->readAll())); |
145 | networkReply->deleteLater(); |
146 | } |
147 | |
148 | void animate(int frame) { |
149 | qreal progress = static_cast<qreal>(frame) / 100; |
150 | m_iconItem->setOpacity(progress); |
151 | qreal hw = width() / 2.0; |
152 | m_statusItem->setPos(ax: -hw + hw * progress, ay: 0); |
153 | for (int i = 0; i < m_forecastItems.count(); ++i) { |
154 | qreal ofs = i * 0.5 / m_forecastItems.count(); |
155 | qreal alpha = qBound(min: qreal(0), val: 2 * (progress - ofs), max: qreal(1)); |
156 | m_conditionItems[i]->setOpacity(alpha); |
157 | QPointF pos = m_forecastItems[i]->pos(); |
158 | if (width() > height()) { |
159 | qreal fx = width() - width() * 0.4 * alpha; |
160 | m_forecastItems[i]->setPos(ax: fx, ay: pos.y()); |
161 | } else { |
162 | qreal fx = height() - height() * 0.5 * alpha; |
163 | m_forecastItems[i]->setPos(ax: pos.x(), ay: fx); |
164 | } |
165 | } |
166 | } |
167 | |
168 | private: |
169 | |
170 | void setupScene() { |
171 | QFont textFont = font(); |
172 | textFont.setBold(true); |
173 | textFont.setPointSize(static_cast<int>(textFont.pointSize() * 1.5)); |
174 | |
175 | m_temperatureItem = m_scene.addText(text: QString(), font: textFont); |
176 | m_temperatureItem->setDefaultTextColor(TEXTCOLOR); |
177 | |
178 | m_conditionItem = m_scene.addText(text: QString(), font: textFont); |
179 | m_conditionItem->setDefaultTextColor(TEXTCOLOR); |
180 | |
181 | m_cityItem = m_scene.addText(text: QString(), font: textFont); |
182 | m_cityItem->setDefaultTextColor(TEXTCOLOR); |
183 | |
184 | m_copyright = m_scene.addText(text: QString()); |
185 | m_copyright->setDefaultTextColor(TEXTCOLOR); |
186 | m_copyright->setOpenExternalLinks(true); |
187 | m_copyright->setTextInteractionFlags(Qt::TextBrowserInteraction); |
188 | |
189 | m_iconItem = new QGraphicsSvgItem; |
190 | m_scene.addItem(item: m_iconItem); |
191 | |
192 | m_statusItem = m_scene.addRect(x: 0, y: 0, w: 10, h: 10); |
193 | m_statusItem->setPen(Qt::NoPen); |
194 | m_statusItem->setBrush(Qt::NoBrush); |
195 | m_temperatureItem->setParentItem(m_statusItem); |
196 | m_conditionItem->setParentItem(m_statusItem); |
197 | m_iconItem->setParentItem(m_statusItem); |
198 | m_copyright->setParentItem(m_statusItem); |
199 | |
200 | connect(asender: &m_timeLine, SIGNAL(frameChanged(int)), SLOT(animate(int))); |
201 | m_timeLine.setDuration(1100); |
202 | m_timeLine.setFrameRange(startFrame: 0, endFrame: 100); |
203 | m_timeLine.setEasingCurve(QEasingCurve::InCurve); |
204 | } |
205 | |
206 | void request(const QString &location) { |
207 | QUrl url(location); |
208 | m_manager.get(request: QNetworkRequest(url)); |
209 | |
210 | city = QString(); |
211 | setWindowTitle("Loading..." ); |
212 | } |
213 | |
214 | QString (const QString &data) { |
215 | if (m_icons.isEmpty()) { |
216 | m_icons["Partly cloudy" ] = "weather-few-clouds" ; |
217 | m_icons["Cloudy" ] = "weather-overcast" ; |
218 | m_icons["Fair" ] = "weather-sunny-very-few-clouds" ; |
219 | m_icons["Sun" ] = "weather-sunny" ; |
220 | m_icons["Sun/clear sky" ] = "weather-sunny" ; |
221 | m_icons["Clear sky" ] = "weather-sunny" ; |
222 | m_icons["Snow showers" ] = "weather-snow" ; |
223 | m_icons["Snow" ] = "weather-snow" ; |
224 | m_icons["Fog" ] = "weather-fog" ; |
225 | m_icons["Sleet" ] = "weather-sleet" ; |
226 | m_icons["Sleet showers" ] = "weather-sleet" ; |
227 | m_icons["Rain showers" ] = "weather-showers" ; |
228 | m_icons["Rain" ] = "weather-showers" ; |
229 | m_icons["Heavy rain" ] = "weather-showers" ; |
230 | m_icons["Rain showers with thunder" ] = "weather-thundershower" ; |
231 | m_icons["Rain and thunder" ] = "weather-thundershower" ; |
232 | m_icons["Sleet and thunder" ] = "weather-thundershower" ; |
233 | m_icons["Heavy rain and thunder" ] = "weather-thundershower" ; |
234 | m_icons["Snow and thunder" ] = "weather-thundershower" ; |
235 | m_icons["Sleet showers and thunder" ] = "weather-thundershower" ; |
236 | m_icons["Snow showers and thunder" ] = "weather-thundershower" ; |
237 | } |
238 | QString name = m_icons.value(key: data); |
239 | if (!name.isEmpty()) { |
240 | name.prepend(s: ":/icons/" ); |
241 | name.append(s: ".svg" ); |
242 | return name; |
243 | } |
244 | return QString(); |
245 | } |
246 | |
247 | void digest(const QString &data) { |
248 | delete m_iconItem; |
249 | m_iconItem = new QGraphicsSvgItem(); |
250 | m_scene.addItem(item: m_iconItem); |
251 | m_iconItem->setParentItem(m_statusItem); |
252 | m_conditionItem->setPlainText(QString()); |
253 | |
254 | qDeleteAll(c: m_dayItems); |
255 | qDeleteAll(c: m_conditionItems); |
256 | qDeleteAll(c: m_rangeItems); |
257 | qDeleteAll(c: m_forecastItems); |
258 | m_dayItems.clear(); |
259 | m_conditionItems.clear(); |
260 | m_rangeItems.clear(); |
261 | m_forecastItems.clear(); |
262 | |
263 | QXmlStreamReader xml(data); |
264 | |
265 | bool foundCurrentForecast = false; |
266 | while (!xml.atEnd()) { |
267 | xml.readNext(); |
268 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
269 | if (xml.name() == "location" ) { |
270 | while (!xml.atEnd()) { |
271 | xml.readNext(); |
272 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
273 | if (xml.name() == "name" ) { |
274 | city = xml.readElementText(); |
275 | m_cityItem->setPlainText(city); |
276 | setWindowTitle(city); |
277 | xml.skipCurrentElement(); |
278 | break; |
279 | } |
280 | } |
281 | } |
282 | } else if (xml.name() == "credit" ) { |
283 | while (!xml.atEnd()) { |
284 | xml.readNext(); |
285 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
286 | if (xml.name() == "link" ) { |
287 | m_copyright->setHtml(QString("<td align=\"center\">%1 <a href=\"%2\">(source)</a></td>" ).arg(GET_DATA_ATTR("text" )).arg(GET_DATA_ATTR("url" ))); |
288 | xml.skipCurrentElement(); |
289 | break; |
290 | } |
291 | } |
292 | } |
293 | } else if (xml.name() == "tabular" ) { |
294 | while (!xml.atEnd()) { |
295 | xml.readNext(); |
296 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
297 | if (xml.name() == "time" ) { |
298 | if (!foundCurrentForecast) { |
299 | QString temperature; |
300 | QString symbol; |
301 | getSymbolTemp(xml, symbol, temp&: temperature); |
302 | if (!symbol.isEmpty()) { |
303 | delete m_iconItem; |
304 | m_iconItem = new QGraphicsSvgItem(symbol); |
305 | m_scene.addItem(item: m_iconItem); |
306 | m_iconItem->setParentItem(m_statusItem); |
307 | } |
308 | QString s = FORMAT_TEMPERATURE(temperature); |
309 | m_temperatureItem->setPlainText(s); |
310 | foundCurrentForecast = true; |
311 | } else { |
312 | createNewDay(xml); |
313 | } |
314 | |
315 | } |
316 | } |
317 | } |
318 | } else if (xml.name() != "weatherdata" && xml.name() != "forecast" && xml.name() != "credit" ){ |
319 | xml.skipCurrentElement(); |
320 | } |
321 | } |
322 | } |
323 | |
324 | |
325 | |
326 | |
327 | |
328 | m_timeLine.stop(); |
329 | layoutItems(); |
330 | animate(frame: 0); |
331 | m_timeLine.start(); |
332 | } |
333 | |
334 | void createNewDay(QXmlStreamReader &xml) { |
335 | QGraphicsTextItem *dayItem = 0; |
336 | QString lowT; |
337 | QString highT; |
338 | QString period = GET_DATA_ATTR("period" ); |
339 | QString datetime; |
340 | if (period == "0" ) |
341 | datetime = GET_DATA_ATTR("to" ); |
342 | else |
343 | datetime = GET_DATA_ATTR("from" ); |
344 | QString temperature; |
345 | QString symbol; |
346 | getSymbolTemp(xml, symbol, temp&: temperature); |
347 | lowT = highT = temperature; |
348 | QDateTime date = GET_DATETIME(datetime); |
349 | dayItem = m_scene.addText(text: date.date().toString(format: "ddd" )); |
350 | dayItem->setDefaultTextColor(TEXTCOLOR); |
351 | |
352 | // check for other info same day |
353 | bool saved = false; |
354 | while (!xml.atEnd()) { |
355 | xml.readNext(); |
356 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
357 | if (xml.name() == "time" ) { |
358 | QString period = GET_DATA_ATTR("period" ); |
359 | // save data if new day starts |
360 | if (period == "0" ) { |
361 | saveDayItem(dayItem, lowT, highT, symbolToShow: symbol); |
362 | createNewDay(xml); |
363 | saved = true; |
364 | } else { |
365 | updateDay(xml, lowT, highT, symbolToShow&: symbol, updateSymbol: period == "2" ); |
366 | } |
367 | } |
368 | } |
369 | } |
370 | if (!saved)// last Item |
371 | saveDayItem(dayItem, lowT, highT, symbolToShow: symbol); |
372 | } |
373 | |
374 | void updateDay(QXmlStreamReader &xml, QString &lowT, QString &highT, QString &symbolToShow, bool updateSymbol) { |
375 | QString temperature; |
376 | QString symbol; |
377 | getSymbolTemp(xml, symbol, temp&: temperature); |
378 | if (lowT.toFloat() > temperature.toFloat()) |
379 | lowT = temperature; |
380 | if (highT.toFloat() < temperature.toFloat()) |
381 | highT = temperature; |
382 | if (updateSymbol) |
383 | symbolToShow = symbol; |
384 | } |
385 | |
386 | void saveDayItem(QGraphicsTextItem *dayItem, QString lowT, QString highT, QString symbolToShow) { |
387 | QGraphicsSvgItem *statusItem = 0; |
388 | if (!symbolToShow.isEmpty()) { |
389 | statusItem = new QGraphicsSvgItem(symbolToShow); |
390 | m_scene.addItem(item: statusItem); |
391 | } |
392 | if (m_dayItems.count() < 4 && dayItem && statusItem && // Show 4 days |
393 | !lowT.isEmpty() && !highT.isEmpty()) { |
394 | m_dayItems << dayItem; |
395 | m_conditionItems << statusItem; |
396 | QString txt = FORMAT_TEMPERATURE(lowT) + '/' + FORMAT_TEMPERATURE(highT); |
397 | QGraphicsTextItem* rangeItem; |
398 | rangeItem = m_scene.addText(text: txt); |
399 | rangeItem->setDefaultTextColor(TEXTCOLOR); |
400 | m_rangeItems << rangeItem; |
401 | QGraphicsRectItem *box; |
402 | box = m_scene.addRect(x: 0, y: 0, w: 10, h: 10); |
403 | box->setPen(Qt::NoPen); |
404 | box->setBrush(Qt::NoBrush); |
405 | m_forecastItems << box; |
406 | dayItem->setParentItem(box); |
407 | statusItem->setParentItem(box); |
408 | rangeItem->setParentItem(box); |
409 | } else { |
410 | delete dayItem; |
411 | delete statusItem; |
412 | } |
413 | } |
414 | |
415 | void getSymbolTemp(QXmlStreamReader &xml, QString &symbol, QString &temp) { |
416 | bool foundIcon = false; |
417 | bool foundTemp = false; |
418 | while (!xml.atEnd()) { |
419 | xml.readNext(); |
420 | if (xml.tokenType() == QXmlStreamReader::StartElement) { |
421 | if (xml.name() == "symbol" ) { |
422 | QString condition = GET_DATA_ATTR("name" ); |
423 | symbol = extractIcon(data: condition); |
424 | if (m_conditionItem->toPlainText().isEmpty()) |
425 | m_conditionItem->setPlainText(condition); |
426 | foundIcon = true; |
427 | } |
428 | if (xml.name() == "temperature" ) { |
429 | temp = GET_DATA_ATTR("value" ); |
430 | foundTemp = true; |
431 | } |
432 | if (foundIcon && foundTemp) |
433 | break; |
434 | } |
435 | } |
436 | } |
437 | |
438 | |
439 | void layoutItems() { |
440 | m_scene.setSceneRect(x: 0, y: 0, w: width() - 1, h: height() - 1); |
441 | m_view->centerOn(ax: width() / 2, ay: height() / 2); |
442 | if (width() > height()) |
443 | layoutItemsLandscape(); |
444 | else |
445 | layoutItemsPortrait(); |
446 | } |
447 | |
448 | void layoutItemsLandscape() { |
449 | qreal statusItemWidth = width() / 2 - 1; |
450 | m_statusItem->setRect(ax: 0, ay: 0, w: statusItemWidth, h: height() - 1); |
451 | |
452 | m_temperatureItem->setPos(ax: 10, ay: 2); |
453 | qreal wtemp = m_temperatureItem->boundingRect().width(); |
454 | qreal h1 = m_conditionItem->boundingRect().height(); |
455 | m_conditionItem->setPos(ax: wtemp + 20, ay: 2); |
456 | |
457 | m_copyright->setTextWidth(statusItemWidth); |
458 | |
459 | qreal wcity = m_cityItem->boundingRect().width(); |
460 | m_cityItem->setPos(ax: statusItemWidth - wcity - 1, ay: 2);; |
461 | |
462 | qreal h2 = m_copyright->boundingRect().height(); |
463 | m_copyright->setPos(ax: 0, ay: height() - h2); |
464 | |
465 | if (!m_iconItem->boundingRect().isEmpty()) { |
466 | qreal sizeLeft = qMin(a: statusItemWidth, b: height() - h2 - h1 - 10); |
467 | qreal sw = sizeLeft / m_iconItem->boundingRect().width(); |
468 | qreal sh = sizeLeft / m_iconItem->boundingRect().height(); |
469 | m_iconItem->setTransform(matrix: QTransform().scale(sx: sw, sy: sh)); |
470 | m_iconItem->setPos(ax: statusItemWidth/2 - sizeLeft/2, ay: h1 + 5); |
471 | } |
472 | |
473 | if (m_dayItems.count()) { |
474 | qreal left = width() * 0.6; |
475 | qreal statusWidth = 0; |
476 | qreal rangeWidth = 0; |
477 | qreal h = height() / m_dayItems.count(); |
478 | for (int i = 0; i < m_dayItems.count(); ++i) { |
479 | QRectF brect = m_dayItems[i]->boundingRect(); |
480 | statusWidth = qMax(a: statusWidth, b: brect.width()); |
481 | brect = m_rangeItems[i]->boundingRect(); |
482 | rangeWidth = qMax(a: rangeWidth, b: brect.width()); |
483 | } |
484 | qreal space = width() - left - statusWidth - rangeWidth; |
485 | qreal dim = qMin(a: h, b: space); |
486 | qreal pad = statusWidth + (space - dim) / 2; |
487 | for (int i = 0; i < m_dayItems.count(); ++i) { |
488 | qreal base = h * i; |
489 | m_forecastItems[i]->setPos(ax: left, ay: base); |
490 | m_forecastItems[i]->setRect(ax: 0, ay: 0, w: width() - left, h); |
491 | QRectF brect = m_dayItems[i]->boundingRect(); |
492 | qreal ofs = (h - brect.height()) / 2; |
493 | m_dayItems[i]->setPos(ax: 0, ay: ofs); |
494 | brect = m_rangeItems[i]->boundingRect(); |
495 | ofs = (h - brect.height()) / 2; |
496 | m_rangeItems[i]->setPos(ax: width() - rangeWidth - left, ay: ofs); |
497 | brect = m_conditionItems[i]->boundingRect(); |
498 | ofs = (h - dim) / 2; |
499 | m_conditionItems[i]->setPos(ax: pad, ay: ofs); |
500 | if (brect.isEmpty()) |
501 | continue; |
502 | qreal sw = dim / brect.width(); |
503 | qreal sh = dim / brect.height(); |
504 | m_conditionItems[i]->setTransform(matrix: QTransform().scale(sx: sw, sy: sh)); |
505 | } |
506 | } |
507 | } |
508 | |
509 | void layoutItemsPortrait() { |
510 | qreal statusItemWidth = width() - 1; |
511 | m_statusItem->setRect(ax: 0, ay: 0, w: statusItemWidth, h: height() / 2 - 1); |
512 | |
513 | m_temperatureItem->setPos(ax: 10, ay: 2); |
514 | qreal wtemp = m_temperatureItem->boundingRect().width(); |
515 | qreal h1 = m_conditionItem->boundingRect().height(); |
516 | m_conditionItem->setPos(ax: wtemp + 20, ay: 2); |
517 | |
518 | m_copyright->setTextWidth(statusItemWidth); |
519 | |
520 | qreal wcity = m_cityItem->boundingRect().width(); |
521 | m_cityItem->setPos(ax: statusItemWidth - wcity - 1, ay: 2);; |
522 | |
523 | m_copyright->setTextWidth(statusItemWidth); |
524 | qreal h2 = m_copyright->boundingRect().height(); |
525 | m_copyright->setPos(ax: 0, ay: height() - h2); |
526 | |
527 | if (m_dayItems.count()) { |
528 | qreal top = height() * 0.5; |
529 | qreal w = width() / m_dayItems.count(); |
530 | qreal statusHeight = 0; |
531 | qreal rangeHeight = 0; |
532 | for (int i = 0; i < m_dayItems.count(); ++i) { |
533 | m_dayItems[i]->setFont(font()); |
534 | QRectF brect = m_dayItems[i]->boundingRect(); |
535 | statusHeight = qMax(a: statusHeight, b: brect.height()); |
536 | brect = m_rangeItems[i]->boundingRect(); |
537 | rangeHeight = qMax(a: rangeHeight, b: brect.height()); |
538 | } |
539 | qreal space = height() - top - statusHeight - rangeHeight; |
540 | qreal dim = qMin(a: w, b: space); |
541 | |
542 | qreal boxh = statusHeight + rangeHeight + dim; |
543 | qreal pad = (height() - top - boxh) / 2; |
544 | |
545 | if (!m_iconItem->boundingRect().isEmpty()) { |
546 | qreal sizeLeft = qMin(a: statusItemWidth - 10, b: height() - top - 10); |
547 | qreal sw = sizeLeft / m_iconItem->boundingRect().width(); |
548 | qreal sh = sizeLeft / m_iconItem->boundingRect().height(); |
549 | m_iconItem->setTransform(matrix: QTransform().scale(sx: sw, sy: sh)); |
550 | m_iconItem->setPos(ax: statusItemWidth/2 - sizeLeft/2, ay: h1 + 5); |
551 | } |
552 | |
553 | for (int i = 0; i < m_dayItems.count(); ++i) { |
554 | qreal base = w * i; |
555 | m_forecastItems[i]->setPos(ax: base, ay: top); |
556 | m_forecastItems[i]->setRect(ax: 0, ay: 0, w, h: boxh); |
557 | QRectF brect = m_dayItems[i]->boundingRect(); |
558 | qreal ofs = (w - brect.width()) / 2; |
559 | m_dayItems[i]->setPos(ax: ofs, ay: pad); |
560 | |
561 | brect = m_rangeItems[i]->boundingRect(); |
562 | ofs = (w - brect.width()) / 2; |
563 | m_rangeItems[i]->setPos(ax: ofs, ay: pad + statusHeight + dim); |
564 | |
565 | brect = m_conditionItems[i]->boundingRect(); |
566 | ofs = (w - dim) / 2; |
567 | m_conditionItems[i]->setPos(ax: ofs, ay: pad + statusHeight); |
568 | if (brect.isEmpty()) |
569 | continue; |
570 | qreal sw = dim / brect.width(); |
571 | qreal sh = dim / brect.height(); |
572 | m_conditionItems[i]->setTransform(matrix: QTransform().scale(sx: sw, sy: sh)); |
573 | } |
574 | } |
575 | } |
576 | |
577 | |
578 | void resizeEvent(QResizeEvent *event) override { |
579 | Q_UNUSED(event); |
580 | layoutItems(); |
581 | } |
582 | |
583 | }; |
584 | |
585 | #include "weatherinfo.moc" |
586 | |
587 | int main(int argc, char *argv[]) |
588 | { |
589 | QApplication app(argc, argv); |
590 | |
591 | WeatherInfo w; |
592 | w.resize(w: 520, h: 288); |
593 | w.show(); |
594 | |
595 | return app.exec(); |
596 | } |
597 | |