|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the demonstration applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <QtCore> |
|
43 #include <QtGui> |
|
44 #include <QtNetwork> |
|
45 #include <QtSvg> |
|
46 |
|
47 #if defined (Q_OS_SYMBIAN) |
|
48 #include "sym_iap_util.h" |
|
49 #endif |
|
50 |
|
51 class WeatherInfo: public QMainWindow |
|
52 { |
|
53 Q_OBJECT |
|
54 |
|
55 private: |
|
56 |
|
57 QGraphicsView *m_view; |
|
58 QGraphicsScene m_scene; |
|
59 QString city; |
|
60 QGraphicsRectItem *m_statusItem; |
|
61 QGraphicsTextItem *m_temperatureItem; |
|
62 QGraphicsTextItem *m_conditionItem; |
|
63 QGraphicsSvgItem *m_iconItem; |
|
64 QList<QGraphicsRectItem*> m_forecastItems; |
|
65 QList<QGraphicsTextItem*> m_dayItems; |
|
66 QList<QGraphicsSvgItem*> m_conditionItems; |
|
67 QList<QGraphicsTextItem*> m_rangeItems; |
|
68 QTimeLine m_timeLine; |
|
69 QHash<QString, QString> m_icons; |
|
70 |
|
71 public: |
|
72 WeatherInfo(QWidget *parent = 0): QMainWindow(parent) { |
|
73 |
|
74 m_view = new QGraphicsView(this); |
|
75 setCentralWidget(m_view); |
|
76 |
|
77 setupScene(); |
|
78 m_view->setScene(&m_scene); |
|
79 m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
|
80 m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
|
81 |
|
82 m_view->setFrameShape(QFrame::NoFrame); |
|
83 setWindowTitle("Weather Info"); |
|
84 |
|
85 QStringList cities; |
|
86 cities << "Oslo"; |
|
87 cities << "Berlin"; |
|
88 cities << "Brisbane"; |
|
89 cities << "Helsinki"; |
|
90 cities << "San Diego"; |
|
91 for (int i = 0; i < cities.count(); ++i) { |
|
92 QAction *action = new QAction(cities[i], this); |
|
93 connect(action, SIGNAL(triggered()), SLOT(chooseCity())); |
|
94 addAction(action); |
|
95 #if defined(Q_OS_SYMBIAN) |
|
96 menuBar()->addAction(action); |
|
97 #endif |
|
98 } |
|
99 setContextMenuPolicy(Qt::ActionsContextMenu); |
|
100 |
|
101 QTimer::singleShot(0, this, SLOT(delayedInit())); |
|
102 } |
|
103 |
|
104 private slots: |
|
105 void delayedInit() { |
|
106 #if defined(Q_OS_SYMBIAN) |
|
107 qt_SetDefaultIap(); |
|
108 #else |
|
109 request("Oslo"); |
|
110 #endif |
|
111 } |
|
112 |
|
113 private slots: |
|
114 |
|
115 void chooseCity() { |
|
116 QAction *action = qobject_cast<QAction*>(sender()); |
|
117 if (action) |
|
118 request(action->text()); |
|
119 } |
|
120 |
|
121 void handleNetworkData(QNetworkReply *networkReply) { |
|
122 QUrl url = networkReply->url(); |
|
123 if (!networkReply->error()) |
|
124 digest(QString::fromUtf8(networkReply->readAll())); |
|
125 networkReply->deleteLater(); |
|
126 networkReply->manager()->deleteLater(); |
|
127 } |
|
128 |
|
129 void animate(int frame) { |
|
130 qreal progress = static_cast<qreal>(frame) / 100; |
|
131 #if QT_VERSION >= 0x040500 |
|
132 m_iconItem->setOpacity(progress); |
|
133 #endif |
|
134 qreal hw = width() / 2.0; |
|
135 m_statusItem->setPos(-hw + hw * progress, 0); |
|
136 for (int i = 0; i < m_forecastItems.count(); ++i) { |
|
137 qreal ofs = i * 0.5 / m_forecastItems.count(); |
|
138 qreal alpha = qBound(qreal(0), 2 * (progress - ofs), qreal(1)); |
|
139 #if QT_VERSION >= 0x040500 |
|
140 m_conditionItems[i]->setOpacity(alpha); |
|
141 #endif |
|
142 QPointF pos = m_forecastItems[i]->pos(); |
|
143 if (width() > height()) { |
|
144 qreal fx = width() - width() * 0.4 * alpha; |
|
145 m_forecastItems[i]->setPos(fx, pos.y()); |
|
146 } else { |
|
147 qreal fx = height() - height() * 0.5 * alpha; |
|
148 m_forecastItems[i]->setPos(pos.x(), fx); |
|
149 } |
|
150 } |
|
151 } |
|
152 |
|
153 private: |
|
154 |
|
155 void setupScene() { |
|
156 |
|
157 QColor textColor = palette().color(QPalette::WindowText); |
|
158 QFont textFont = font(); |
|
159 textFont.setBold(true); |
|
160 textFont.setPointSize(textFont.pointSize() * 2); |
|
161 |
|
162 m_temperatureItem = m_scene.addText(QString(), textFont); |
|
163 m_temperatureItem->setDefaultTextColor(textColor); |
|
164 |
|
165 m_conditionItem = m_scene.addText(QString(), textFont); |
|
166 m_conditionItem->setDefaultTextColor(textColor); |
|
167 |
|
168 m_iconItem = new QGraphicsSvgItem; |
|
169 m_scene.addItem(m_iconItem); |
|
170 |
|
171 m_statusItem = m_scene.addRect(0, 0, 10, 10); |
|
172 m_statusItem->setPen(Qt::NoPen); |
|
173 m_statusItem->setBrush(Qt::NoBrush); |
|
174 m_temperatureItem->setParentItem(m_statusItem); |
|
175 m_conditionItem->setParentItem(m_statusItem); |
|
176 m_iconItem->setParentItem(m_statusItem); |
|
177 |
|
178 connect(&m_timeLine, SIGNAL(frameChanged(int)), SLOT(animate(int))); |
|
179 m_timeLine.setDuration(1100); |
|
180 m_timeLine.setFrameRange(0, 100); |
|
181 m_timeLine.setCurveShape(QTimeLine::EaseInCurve); |
|
182 } |
|
183 |
|
184 void request(const QString &location) { |
|
185 QUrl url("http://www.google.com/ig/api"); |
|
186 url.addEncodedQueryItem("hl", "en"); |
|
187 url.addEncodedQueryItem("weather", QUrl::toPercentEncoding(location)); |
|
188 |
|
189 QNetworkAccessManager *manager = new QNetworkAccessManager(this); |
|
190 connect(manager, SIGNAL(finished(QNetworkReply*)), |
|
191 this, SLOT(handleNetworkData(QNetworkReply*))); |
|
192 manager->get(QNetworkRequest(url)); |
|
193 |
|
194 city = QString(); |
|
195 setWindowTitle("Loading..."); |
|
196 } |
|
197 |
|
198 QString extractIcon(const QString &data) { |
|
199 if (m_icons.isEmpty()) { |
|
200 m_icons["mostly_cloudy"] = "weather-few-clouds"; |
|
201 m_icons["cloudy"] = "weather-overcast"; |
|
202 m_icons["mostly_sunny"] = "weather-sunny-very-few-clouds"; |
|
203 m_icons["partly_cloudy"] = "weather-sunny-very-few-clouds"; |
|
204 m_icons["sunny"] = "weather-sunny"; |
|
205 m_icons["flurries"] = "weather-snow"; |
|
206 m_icons["fog"] = "weather-fog"; |
|
207 m_icons["haze"] = "weather-haze"; |
|
208 m_icons["icy"] = "weather-icy"; |
|
209 m_icons["sleet"] = "weather-sleet"; |
|
210 m_icons["chance_of_sleet"] = "weather-sleet"; |
|
211 m_icons["snow"] = "weather-snow"; |
|
212 m_icons["chance_of_snow"] = "weather-snow"; |
|
213 m_icons["mist"] = "weather-showers"; |
|
214 m_icons["rain"] = "weather-showers"; |
|
215 m_icons["chance_of_rain"] = "weather-showers"; |
|
216 m_icons["storm"] = "weather-storm"; |
|
217 m_icons["chance_of_storm"] = "weather-storm"; |
|
218 m_icons["thunderstorm"] = "weather-thundershower"; |
|
219 m_icons["chance_of_tstorm"] = "weather-thundershower"; |
|
220 } |
|
221 QRegExp regex("([\\w]+).gif$"); |
|
222 if (regex.indexIn(data) != -1) { |
|
223 QString i = regex.cap(); |
|
224 i = i.left(i.length() - 4); |
|
225 QString name = m_icons.value(i); |
|
226 if (!name.isEmpty()) { |
|
227 name.prepend(":/icons/"); |
|
228 name.append(".svg"); |
|
229 return name; |
|
230 } |
|
231 } |
|
232 return QString(); |
|
233 } |
|
234 |
|
235 static QString toCelcius(QString t, QString unit) { |
|
236 bool ok = false; |
|
237 int degree = t.toInt(&ok); |
|
238 if (!ok) |
|
239 return QString(); |
|
240 if (unit != "SI") |
|
241 degree = ((degree - 32) * 5 + 8)/ 9; |
|
242 return QString::number(degree) + QChar(176); |
|
243 } |
|
244 |
|
245 |
|
246 #define GET_DATA_ATTR xml.attributes().value("data").toString() |
|
247 |
|
248 void digest(const QString &data) { |
|
249 |
|
250 QColor textColor = palette().color(QPalette::WindowText); |
|
251 QString unitSystem; |
|
252 |
|
253 delete m_iconItem; |
|
254 m_iconItem = new QGraphicsSvgItem(); |
|
255 m_scene.addItem(m_iconItem); |
|
256 m_iconItem->setParentItem(m_statusItem); |
|
257 qDeleteAll(m_dayItems); |
|
258 qDeleteAll(m_conditionItems); |
|
259 qDeleteAll(m_rangeItems); |
|
260 qDeleteAll(m_forecastItems); |
|
261 m_dayItems.clear(); |
|
262 m_conditionItems.clear(); |
|
263 m_rangeItems.clear(); |
|
264 m_forecastItems.clear(); |
|
265 |
|
266 QXmlStreamReader xml(data); |
|
267 while (!xml.atEnd()) { |
|
268 xml.readNext(); |
|
269 if (xml.tokenType() == QXmlStreamReader::StartElement) { |
|
270 if (xml.name() == "city") { |
|
271 city = GET_DATA_ATTR; |
|
272 setWindowTitle(city); |
|
273 } |
|
274 if (xml.name() == "unit_system") |
|
275 unitSystem = xml.attributes().value("data").toString(); |
|
276 // Parse current weather conditions |
|
277 if (xml.name() == "current_conditions") { |
|
278 while (!xml.atEnd()) { |
|
279 xml.readNext(); |
|
280 if (xml.name() == "current_conditions") |
|
281 break; |
|
282 if (xml.tokenType() == QXmlStreamReader::StartElement) { |
|
283 if (xml.name() == "condition") { |
|
284 m_conditionItem->setPlainText(GET_DATA_ATTR); |
|
285 } |
|
286 if (xml.name() == "icon") { |
|
287 QString name = extractIcon(GET_DATA_ATTR); |
|
288 if (!name.isEmpty()) { |
|
289 delete m_iconItem; |
|
290 m_iconItem = new QGraphicsSvgItem(name); |
|
291 m_scene.addItem(m_iconItem); |
|
292 m_iconItem->setParentItem(m_statusItem); |
|
293 } |
|
294 } |
|
295 if (xml.name() == "temp_c") { |
|
296 QString s = GET_DATA_ATTR + QChar(176); |
|
297 m_temperatureItem->setPlainText(s); |
|
298 } |
|
299 } |
|
300 } |
|
301 } |
|
302 // Parse and collect the forecast conditions |
|
303 if (xml.name() == "forecast_conditions") { |
|
304 QGraphicsTextItem *dayItem = 0; |
|
305 QGraphicsSvgItem *statusItem = 0; |
|
306 QString lowT, highT; |
|
307 while (!xml.atEnd()) { |
|
308 xml.readNext(); |
|
309 if (xml.name() == "forecast_conditions") { |
|
310 if (dayItem && statusItem && |
|
311 !lowT.isEmpty() && !highT.isEmpty()) { |
|
312 m_dayItems << dayItem; |
|
313 m_conditionItems << statusItem; |
|
314 QString txt = highT + '/' + lowT; |
|
315 QGraphicsTextItem* rangeItem; |
|
316 rangeItem = m_scene.addText(txt); |
|
317 rangeItem->setDefaultTextColor(textColor); |
|
318 m_rangeItems << rangeItem; |
|
319 QGraphicsRectItem *box; |
|
320 box = m_scene.addRect(0, 0, 10, 10); |
|
321 box->setPen(Qt::NoPen); |
|
322 box->setBrush(Qt::NoBrush); |
|
323 m_forecastItems << box; |
|
324 dayItem->setParentItem(box); |
|
325 statusItem->setParentItem(box); |
|
326 rangeItem->setParentItem(box); |
|
327 } else { |
|
328 delete dayItem; |
|
329 delete statusItem; |
|
330 } |
|
331 break; |
|
332 } |
|
333 if (xml.tokenType() == QXmlStreamReader::StartElement) { |
|
334 if (xml.name() == "day_of_week") { |
|
335 QString s = GET_DATA_ATTR; |
|
336 dayItem = m_scene.addText(s.left(3)); |
|
337 dayItem->setDefaultTextColor(textColor); |
|
338 } |
|
339 if (xml.name() == "icon") { |
|
340 QString name = extractIcon(GET_DATA_ATTR); |
|
341 if (!name.isEmpty()) { |
|
342 statusItem = new QGraphicsSvgItem(name); |
|
343 m_scene.addItem(statusItem); |
|
344 } |
|
345 } |
|
346 if (xml.name() == "low") |
|
347 lowT = toCelcius(GET_DATA_ATTR, unitSystem); |
|
348 if (xml.name() == "high") |
|
349 highT = toCelcius(GET_DATA_ATTR, unitSystem); |
|
350 } |
|
351 } |
|
352 } |
|
353 |
|
354 } |
|
355 } |
|
356 |
|
357 m_timeLine.stop(); |
|
358 layoutItems(); |
|
359 animate(0); |
|
360 m_timeLine.start(); |
|
361 } |
|
362 |
|
363 void layoutItems() { |
|
364 m_scene.setSceneRect(0, 0, width() - 1, height() - 1); |
|
365 m_view->centerOn(width() / 2, height() / 2); |
|
366 if (width() > height()) |
|
367 layoutItemsLandscape(); |
|
368 else |
|
369 layoutItemsPortrait(); |
|
370 } |
|
371 |
|
372 void layoutItemsLandscape() { |
|
373 m_statusItem->setRect(0, 0, width() / 2 - 1, height() - 1); |
|
374 |
|
375 if (!m_iconItem->boundingRect().isEmpty()) { |
|
376 qreal dim = qMin(width() * 0.6, height() * 0.8); |
|
377 qreal pad = (height() - dim) / 2; |
|
378 qreal sw = dim / m_iconItem->boundingRect().width(); |
|
379 qreal sh = dim / m_iconItem->boundingRect().height(); |
|
380 m_iconItem->setTransform(QTransform().scale(sw, sh)); |
|
381 m_iconItem->setPos(1, pad); |
|
382 } |
|
383 |
|
384 m_temperatureItem->setPos(2, 2); |
|
385 qreal h = m_conditionItem->boundingRect().height(); |
|
386 m_conditionItem->setPos(10, height() - h); |
|
387 |
|
388 if (m_dayItems.count()) { |
|
389 qreal left = width() * 0.6; |
|
390 qreal h = height() / m_dayItems.count(); |
|
391 QFont textFont = font(); |
|
392 textFont.setPixelSize(static_cast<int>(h * 0.3)); |
|
393 qreal statusWidth = 0; |
|
394 qreal rangeWidth = 0; |
|
395 for (int i = 0; i < m_dayItems.count(); ++i) { |
|
396 m_dayItems[i]->setFont(textFont); |
|
397 QRectF brect = m_dayItems[i]->boundingRect(); |
|
398 statusWidth = qMax(statusWidth, brect.width()); |
|
399 brect = m_rangeItems[i]->boundingRect(); |
|
400 rangeWidth = qMax(rangeWidth, brect.width()); |
|
401 } |
|
402 qreal space = width() - left - statusWidth - rangeWidth; |
|
403 qreal dim = qMin(h, space); |
|
404 qreal pad = statusWidth + (space - dim) / 2; |
|
405 for (int i = 0; i < m_dayItems.count(); ++i) { |
|
406 qreal base = h * i; |
|
407 m_forecastItems[i]->setPos(left, base); |
|
408 m_forecastItems[i]->setRect(0, 0, width() - left, h); |
|
409 QRectF brect = m_dayItems[i]->boundingRect(); |
|
410 qreal ofs = (h - brect.height()) / 2; |
|
411 m_dayItems[i]->setPos(0, ofs); |
|
412 brect = m_rangeItems[i]->boundingRect(); |
|
413 ofs = (h - brect.height()) / 2; |
|
414 m_rangeItems[i]->setPos(width() - rangeWidth - left, ofs); |
|
415 brect = m_conditionItems[i]->boundingRect(); |
|
416 ofs = (h - dim) / 2; |
|
417 m_conditionItems[i]->setPos(pad, ofs); |
|
418 if (brect.isEmpty()) |
|
419 continue; |
|
420 qreal sw = dim / brect.width(); |
|
421 qreal sh = dim / brect.height(); |
|
422 m_conditionItems[i]->setTransform(QTransform().scale(sw, sh)); |
|
423 } |
|
424 } |
|
425 } |
|
426 |
|
427 void layoutItemsPortrait() { |
|
428 |
|
429 m_statusItem->setRect(0, 0, width() - 1, height() / 2 - 1); |
|
430 |
|
431 if (!m_iconItem->boundingRect().isEmpty()) { |
|
432 qreal dim = qMin(width() * 0.8, height() * 0.4); |
|
433 qreal ofsy = (height() / 2 - dim) / 2; |
|
434 qreal ofsx = (width() - dim) / 3; |
|
435 qreal sw = dim / m_iconItem->boundingRect().width(); |
|
436 qreal sh = dim / m_iconItem->boundingRect().height(); |
|
437 m_iconItem->setTransform(QTransform().scale(sw, sh)); |
|
438 m_iconItem->setPos(ofsx, ofsy); |
|
439 } |
|
440 |
|
441 m_temperatureItem->setPos(2, 2); |
|
442 qreal ch = m_conditionItem->boundingRect().height(); |
|
443 qreal cw = m_conditionItem->boundingRect().width(); |
|
444 m_conditionItem->setPos(width() - cw , height() / 2 - ch - 20); |
|
445 |
|
446 if (m_dayItems.count()) { |
|
447 qreal top = height() * 0.5; |
|
448 qreal w = width() / m_dayItems.count(); |
|
449 qreal statusHeight = 0; |
|
450 qreal rangeHeight = 0; |
|
451 for (int i = 0; i < m_dayItems.count(); ++i) { |
|
452 m_dayItems[i]->setFont(font()); |
|
453 QRectF brect = m_dayItems[i]->boundingRect(); |
|
454 statusHeight = qMax(statusHeight, brect.height()); |
|
455 brect = m_rangeItems[i]->boundingRect(); |
|
456 rangeHeight = qMax(rangeHeight, brect.height()); |
|
457 } |
|
458 qreal space = height() - top - statusHeight - rangeHeight; |
|
459 qreal dim = qMin(w, space); |
|
460 |
|
461 qreal boxh = statusHeight + rangeHeight + dim; |
|
462 qreal pad = (height() - top - boxh) / 2; |
|
463 |
|
464 for (int i = 0; i < m_dayItems.count(); ++i) { |
|
465 qreal base = w * i; |
|
466 m_forecastItems[i]->setPos(base, top); |
|
467 m_forecastItems[i]->setRect(0, 0, w, boxh); |
|
468 QRectF brect = m_dayItems[i]->boundingRect(); |
|
469 qreal ofs = (w - brect.width()) / 2; |
|
470 m_dayItems[i]->setPos(ofs, pad); |
|
471 |
|
472 brect = m_rangeItems[i]->boundingRect(); |
|
473 ofs = (w - brect.width()) / 2; |
|
474 m_rangeItems[i]->setPos(ofs, pad + statusHeight + dim); |
|
475 |
|
476 brect = m_conditionItems[i]->boundingRect(); |
|
477 ofs = (w - dim) / 2; |
|
478 m_conditionItems[i]->setPos(ofs, pad + statusHeight); |
|
479 if (brect.isEmpty()) |
|
480 continue; |
|
481 qreal sw = dim / brect.width(); |
|
482 qreal sh = dim / brect.height(); |
|
483 m_conditionItems[i]->setTransform(QTransform().scale(sw, sh)); |
|
484 } |
|
485 } |
|
486 } |
|
487 |
|
488 |
|
489 void resizeEvent(QResizeEvent *event) { |
|
490 Q_UNUSED(event); |
|
491 layoutItems(); |
|
492 } |
|
493 |
|
494 }; |
|
495 |
|
496 #include "weatherinfo.moc" |
|
497 |
|
498 int main(int argc, char *argv[]) |
|
499 { |
|
500 QApplication app(argc, argv); |
|
501 |
|
502 WeatherInfo w; |
|
503 #if defined(Q_OS_SYMBIAN) |
|
504 w.showMaximized(); |
|
505 #else |
|
506 w.resize(520, 288); |
|
507 w.show(); |
|
508 #endif |
|
509 |
|
510 return app.exec(); |
|
511 } |