Merge lp:~jtasker/weather-indicator/cloudy into lp:weather-indicator
- cloudy
- Merge into rainy
Status: | Merged |
---|---|
Approved by: | Joshua Tasker |
Approved revision: | 325 |
Merged at revision: | 312 |
Proposed branch: | lp:~jtasker/weather-indicator/cloudy |
Merge into: | lp:weather-indicator |
Diff against target: |
5722 lines (+2158/-964) 16 files modified
AUTHORS (+1/-0) bin/indicator-weather (+1225/-641) data/indicator-weather.gschema.xml (+25/-0) data/ui/Assistant.ui (+85/-40) data/ui/ExtendedForecast.ui (+129/-14) data/ui/PreferencesDialog.ui (+454/-97) debian/changelog (+35/-0) debian/control (+26/-19) debian/copyright (+29/-10) debian/indicator-weather.install (+1/-0) debian/postinst (+6/-7) debian/rules (+10/-4) indicator_weather/helpers.py (+9/-9) indicator_weather/indicator_weatherconfig.py (+2/-2) po/indicator-weather.pot (+117/-118) setup.py (+4/-3) |
To merge this branch: | bzr merge lp:~jtasker/weather-indicator/cloudy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vadim Rutkovsky | Approve | ||
Review via email: mp+164598@code.launchpad.net |
Commit message
Description of the change
This fixes all the "SIGSEGV" bugs related to glib/dbus; (libdbus is not thread-safe - I re-wrote some of the threading code to deal with this)
I also updated it to use Gtk3 and GObject, instead of the now-deprecated PyGtk.
Finally, I added code to calculate humidex/wind chill, and added a related tab to the Preferences Dialog.
Note that this version requires pywapi 0.3.1, which is available here:
http://
deb packages for pywapi 0.3.1 are available here:
https:/
From the changelog:
* Ported to GTK3 and GObject from PyGTK
* Rewrite threading code to avoid dbus-related crashes (LP: #743541)
* Added "feels like" temperature (humidex/heat index/wind chill)
* New "Conditions" tab in Preferences dialog to choose temperature
formulas and toggle display of some condition information
* Bumped version number to reflect massive changes
Note that it still sometimes gives LIBDBUSMENU-GLIB warnings to .xsession-errors, but they can be safely ignored - I have left it running for a week straight with no crashes.
Joshua Tasker (jtasker) wrote : | # |
Vadim Rutkovsky (roignac) wrote : | # |
Great, thanks!
Unfortunately, I can't test the changes, but it seems that you've tested them pretty good
- 320. By Joshua Tasker
-
* Missing comma in debian/control file
- 321. By Joshua Tasker
-
Update changelog
- 322. By Joshua Tasker
-
Fix 4-day forecast display (LP: #1182324)
'OK' button in Preferences Dialog is now more responsive - 323. By Joshua Tasker
-
update changelog
- 324. By Joshua Tasker
-
Prepare for release
- 325. By Joshua Tasker
-
typo in debian/control
Preview Diff
1 | === modified file 'AUTHORS' |
2 | --- AUTHORS 2011-03-19 09:27:37 +0000 |
3 | +++ AUTHORS 2013-05-22 05:08:27 +0000 |
4 | @@ -1,3 +1,4 @@ |
5 | Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com |
6 | Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com |
7 | Copyright (C) 2010 Vadim Rutkovsky roignac@gmail.com |
8 | +Copyright (C) 2013 Joshua Tasker jtasker@gmail.com |
9 | |
10 | === modified file 'bin/indicator-weather' |
11 | --- bin/indicator-weather 2012-07-30 04:02:24 +0000 |
12 | +++ bin/indicator-weather 2013-05-22 05:08:27 +0000 |
13 | @@ -4,6 +4,7 @@ |
14 | # Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com |
15 | # Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com |
16 | # Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com |
17 | +# Copyright (C) 2013 Joshua Tasker jtasker@gmail.com |
18 | # This program is free software: you can redistribute it and/or modify it |
19 | # under the terms of the GNU General Public License version 3, as published |
20 | # by the Free Software Foundation. |
21 | @@ -17,29 +18,26 @@ |
22 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | ### END LICENSE |
24 | |
25 | -try: |
26 | - from gi.repository import Gio |
27 | -except ImportError: |
28 | - pass |
29 | +from gi.repository import Gio, GLib, Gtk, Gdk, Notify, GObject, GdkPixbuf |
30 | +from gi.repository import AppIndicator3 as AppIndicator |
31 | + |
32 | import sys, os, shutil, tempfile |
33 | -import gtk, pygtk, gobject, pynotify |
34 | -pygtk.require('2.0') |
35 | -import appindicator |
36 | + |
37 | import urllib2, urllib |
38 | from urllib import urlencode |
39 | import re |
40 | import locale |
41 | from xml.dom.minidom import parseString |
42 | import datetime |
43 | -import dbus |
44 | +#import dbus |
45 | import time |
46 | import traceback |
47 | import types |
48 | -# Will be used for humidex |
49 | -#import math |
50 | +from math import exp |
51 | import commands, threading |
52 | import logging, logging.handlers |
53 | import pywapi |
54 | +import Queue |
55 | |
56 | import gettext |
57 | from gettext import gettext as _ |
58 | @@ -55,7 +53,7 @@ |
59 | sys.path.insert(0, PROJECT_ROOT_DIRECTORY) |
60 | os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses |
61 | |
62 | -VERSION = "12.07.30 'Cloudy 10'" |
63 | +VERSION = "13.05.26 'Rainy'" |
64 | |
65 | from indicator_weather.helpers import * |
66 | |
67 | @@ -69,11 +67,16 @@ |
68 | WEATHER_KEY = 'weather' |
69 | LOCATIONS_KEY = 'locations' |
70 | INDICATOR_DISPLAY = 'show_label' |
71 | + RELATIVE_DISPLAY = 'show_relative' |
72 | + WIND_DISPLAY = 'show_wind' |
73 | + SUNTIMES_DISPLAY = 'show_suntimes' |
74 | NOTIFICATIONS = 'notif' |
75 | WEATHER_SOURCE = 'data_source' |
76 | REFRESH_RATE = 'refresh_rate' |
77 | METRIC_SYSTEM = 'unit' |
78 | WIND_UNIT = 'wind' |
79 | + HEAT_ESTIMATE = 'heat' |
80 | + CHILL_ESTIMATE = 'chill' |
81 | PLACECHOSEN = 'placechosen' |
82 | PLACES = 'places' |
83 | |
84 | @@ -84,48 +87,70 @@ |
85 | INDICATOR_DISPLAY : { |
86 | INFO_TYPE : types.IntType, |
87 | INFO_SETTING : 'indicator-display' |
88 | - }, |
89 | + }, |
90 | + RELATIVE_DISPLAY : { |
91 | + INFO_TYPE : types.BooleanType, |
92 | + INFO_SETTING : 'relative-display' |
93 | + }, |
94 | + WIND_DISPLAY : { |
95 | + INFO_TYPE : types.BooleanType, |
96 | + INFO_SETTING : 'wind-display' |
97 | + }, |
98 | + SUNTIMES_DISPLAY : { |
99 | + INFO_TYPE : types.BooleanType, |
100 | + INFO_SETTING : 'suntimes-display' |
101 | + }, |
102 | NOTIFICATIONS : { |
103 | INFO_TYPE : types.StringType, |
104 | INFO_SETTING : 'notifications' |
105 | - }, |
106 | + }, |
107 | WEATHER_SOURCE : { |
108 | INFO_TYPE : types.StringType, |
109 | INFO_SETTING : 'weather-source' |
110 | - }, |
111 | + }, |
112 | REFRESH_RATE : { |
113 | INFO_TYPE : types.IntType, |
114 | INFO_SETTING : 'refresh-rate' |
115 | - }, |
116 | + }, |
117 | METRIC_SYSTEM : { |
118 | INFO_TYPE : types.StringType, |
119 | INFO_SETTING : 'metric-system' |
120 | - }, |
121 | + }, |
122 | WIND_UNIT : { |
123 | INFO_TYPE : types.StringType, |
124 | INFO_SETTING : 'wind-unit' |
125 | - }, |
126 | + }, |
127 | + HEAT_ESTIMATE: { |
128 | + INFO_TYPE : types.StringType, |
129 | + INFO_SETTING : 'heat-estimate' |
130 | + }, |
131 | + CHILL_ESTIMATE: { |
132 | + INFO_TYPE : types.StringType, |
133 | + INFO_SETTING : 'chill-estimate' |
134 | + }, |
135 | PLACECHOSEN : { |
136 | INFO_TYPE : types.IntType, |
137 | INFO_SETTING: 'placechosen' |
138 | - }, |
139 | + }, |
140 | PLACES : { |
141 | INFO_TYPE : types.ListType, |
142 | INFO_SETTING: 'places' |
143 | - }, |
144 | + }, |
145 | } |
146 | |
147 | - # Open the DB |
148 | def prepare_settings_store(self): |
149 | + """ Open the DB """ |
150 | log.debug("Settings: preparing settings store") |
151 | try: |
152 | self.db = Gio.Settings.new(self.BASE_KEY) |
153 | except Exception as e: |
154 | - log.debug("Settings: exception occurred while opening settings:\n %s" % str(e)) |
155 | + log.debug("Settings: exception occurred while " |
156 | + "opening settings:\n %s" % str(e)) |
157 | |
158 | - # Make sure autostart file is installed. Inspired by GTG. |
159 | def check_autostart(self): |
160 | - autostart_dir = os.path.join(os.path.expanduser("~"),".config/autostart/") |
161 | + """ Make sure autostart file is installed. Inspired by GTG. """ |
162 | + autostart_dir = os.path.join(os.path.expanduser("~"), |
163 | + ".config/autostart/") |
164 | autostart_file = "indicator-weather.desktop" |
165 | autostart_path = os.path.join(autostart_dir, autostart_file) |
166 | if not os.path.isfile(autostart_path): |
167 | @@ -135,12 +160,12 @@ |
168 | "/usr/local/share/applications"] |
169 | this_directory = os.path.dirname(os.path.abspath(__file__)) |
170 | for path in desktop_file_directories: |
171 | - fullpath = os.path.normpath(os.path.join(this_directory, path, \ |
172 | - autostart_file)) |
173 | + fullpath = os.path.normpath(os.path.join(this_directory, path, |
174 | + autostart_file)) |
175 | if os.path.isfile(fullpath): |
176 | desktop_file_path = fullpath |
177 | break |
178 | - #If we have found the desktop file, we make a link to in in |
179 | + # If we have found the desktop file, we make a link to in in |
180 | # autostart_path. |
181 | if desktop_file_path: |
182 | if not os.path.exists(autostart_dir): |
183 | @@ -149,8 +174,8 @@ |
184 | log.debug("Installing autostart file.") |
185 | os.symlink(desktop_file_path, autostart_path) |
186 | |
187 | - # Get a value of the setting |
188 | def get_value(self, setting, return_id = False): |
189 | + """ Get a value of the specified setting """ |
190 | log.debug("Settings: getting value for %s" % setting) |
191 | setting_name = Settings.INFO[setting][INFO_SETTING] |
192 | try: |
193 | @@ -162,15 +187,15 @@ |
194 | types.ListType: self.db.get_string, |
195 | types.DictType: self.db.get_string, |
196 | types.NoneType: self.db.get_value, |
197 | - }[setting_type] |
198 | + }[setting_type] |
199 | return get_func(setting_name) |
200 | except: |
201 | - self.log.debug("Settings: can't find value for %s" % setting) |
202 | + log.debug("Settings: can't find value for %s" % setting) |
203 | return None |
204 | |
205 | - # Set a setting value |
206 | + |
207 | def set_value(self, setting, value): |
208 | - |
209 | + """ Set a value for the specified setting """ |
210 | value = '' if value is None else value |
211 | value = str(value) if type(value) is types.ListType else value |
212 | log.debug("Settings: setting '%s'='%s'" % (setting, value)) |
213 | @@ -185,17 +210,20 @@ |
214 | types.ListType: self.db.set_string, |
215 | types.DictType: self.db.set_string, |
216 | types.NoneType: self.db.set_value, |
217 | - }[setting_type] |
218 | + }[setting_type] |
219 | set_func(setting_name, value) |
220 | except: |
221 | log.debug( \ |
222 | "Settings: schema for '%s' not found, aborting" % setting) |
223 | |
224 | - # Get cached weather by location code. |
225 | - # If return_id is True, only document id is returned, otherwise - full weather data |
226 | def get_weather(self, location_code, return_id = False): |
227 | - log.debug("Settings: getting cached weather for %s" % \ |
228 | - location_code) |
229 | + """Get cached weather by location code. |
230 | + If return_id is True, only document id is returned, |
231 | + otherwise full weather data is returned. |
232 | + |
233 | + """ |
234 | + log.debug("Settings: getting cached weather for %s" % |
235 | + location_code) |
236 | try: |
237 | cached_weather_string = self.db.get_string(self.WEATHER_KEY) |
238 | cached_weather = {} if cached_weather_string == ''\ |
239 | @@ -207,22 +235,22 @@ |
240 | "Settings: can't find value for %s" % location_code) |
241 | return None |
242 | except: |
243 | - log.debug("Settings: can't find %s setting" % WEATHER_KEY) |
244 | + log.debug("Settings: can't find %s setting" % self.WEATHER_KEY) |
245 | return None |
246 | |
247 | - # Save weather info in cache for specific location |
248 | def save_weather(self, weather, location_code): |
249 | - |
250 | + """ Save weather info in cache for specific location """ |
251 | record = { |
252 | - "label" : weather.get_temperature(needs_rounding=True), |
253 | - "condition": weather.get_condition_label(), |
254 | - "icon" : weather.get_icon_name(), |
255 | - "temper" : weather.get_temperature_label(), |
256 | - "humidex" : weather.get_humidex_label(), |
257 | - "humidity" : weather.get_humidity_label(), |
258 | - "wind" : weather.get_wind_label(), |
259 | - "sunrise" : weather.get_sunrise_label(), |
260 | - "sunset" : weather.get_sunset_label() |
261 | +## "label" : weather.get_temperature(needs_rounding=True), |
262 | + "label" : weather.get_temperature_string(), |
263 | + "condition" : weather.get_condition_label(), |
264 | + "icon" : weather.get_icon_name(), |
265 | + "temper" : weather.get_temperature_label(), |
266 | + "feelslike" : weather.get_relative_label(), |
267 | + "humidity" : weather.get_humidity_label(), |
268 | + "wind" : weather.get_wind_label(), |
269 | + "sunrise" : weather.get_sunrise_label(), |
270 | + "sunset" : weather.get_sunset_label() |
271 | } |
272 | log.debug("Settings: setting '%s'='%s'" % (location_code, record)) |
273 | |
274 | @@ -234,12 +262,17 @@ |
275 | cached_weather_string = str(cached_weather) |
276 | self.db.set_string(self.WEATHER_KEY, cached_weather_string) |
277 | except: |
278 | - log.debug(\ |
279 | - "Settings: schema for '%s' not found, aborting" % setting) |
280 | + log.debug( |
281 | + "Settings: schema for '%s' not found, aborting" % |
282 | + self.WEATHER_KEY |
283 | + ) |
284 | |
285 | - # Get location details by location code |
286 | - # If return_id is True, only document id is returned, otherwise - full location data |
287 | def get_location_details(self, location_code, return_id = False): |
288 | + """ Get location details by location code |
289 | + If return_id is True, only document id is returned, |
290 | + otherwise - full location data is returned |
291 | + |
292 | + """ |
293 | try: |
294 | locations_string = self.db.get_string(self.LOCATIONS_KEY) |
295 | locations = {} if locations_string == ''\ |
296 | @@ -247,19 +280,19 @@ |
297 | if location_code in locations.keys(): |
298 | return str(locations[location_code]) |
299 | else: |
300 | - log.debug(\ |
301 | - "Settings: can't find value for %s" % location_code) |
302 | + log.debug( |
303 | + "Settings: can't find value for %s" % location_code |
304 | + ) |
305 | return None |
306 | except: |
307 | - log.debug("Settings: can't find location details for %s" % \ |
308 | - location_code) |
309 | + log.debug("Settings: can't find location details for %s" % |
310 | + location_code) |
311 | return None |
312 | |
313 | - # Save location details |
314 | def save_location_details(self, location_details, location_code): |
315 | - log.debug("Settings: setting '%s'='%s'" %\ |
316 | - (location_code, location_details)) |
317 | - |
318 | + """ Save location details """ |
319 | + log.debug("Settings: setting '%s'='%s'" % |
320 | + (location_code, location_details)) |
321 | try: |
322 | locations_string = self.db.get_string(self.LOCATIONS_KEY) |
323 | locations = {} if locations_string == ''\ |
324 | @@ -270,8 +303,8 @@ |
325 | except: |
326 | pass |
327 | |
328 | -class MetricSystem: |
329 | - """ Class with available metric systems units """ |
330 | +class UnitSystem: |
331 | + """ Class with available measurement unit systems """ |
332 | SI = 1 |
333 | IMPERIAL = 2 |
334 | |
335 | @@ -285,52 +318,64 @@ |
336 | |
337 | class WeatherDataSource: |
338 | """ Class for available weather data sources """ |
339 | - GOOGLE = 1 |
340 | - YAHOO = 2 |
341 | + YAHOO = 1 |
342 | + WEATHER_COM = 2 |
343 | |
344 | +class RelativeFormula: |
345 | + """ Class for relative temperature formulas """ |
346 | + HEATINDEX = 1 |
347 | + HUMIDEX = 2 |
348 | + WINDCHILL = 3 |
349 | + APPARENT = 4 |
350 | + |
351 | class Location: |
352 | """ Data object to store location details """ |
353 | |
354 | - # Initialize an object with a label |
355 | - def __init__(self, metric_system, wind_unit, location_details = None): |
356 | + def __init__(self, metric_system, wind_unit, heat_index, |
357 | + chill_index, location_details = None): |
358 | + """ Initialize an object with a label """ |
359 | self.metric_system = metric_system |
360 | self.wind_unit = wind_unit |
361 | + self.heat_index = heat_index |
362 | + self.chill_index = chill_index |
363 | self.location_details = location_details |
364 | |
365 | - # Convert coordinate for google |
366 | def convert_coordinate_for_google(self, value): |
367 | - value = float(value) * 1e6 |
368 | - return int(round(value)) |
369 | + """ Convert coordinate for google """ |
370 | + value = float(value) * 1e6 |
371 | + return int(round(value)) |
372 | |
373 | - # Get necessary location details by its GeoNames details |
374 | def prepare_location(self, geonames_details): |
375 | + """ Get necessary location details by its GeoNames details """ |
376 | self.location_details = {} |
377 | self.location_details['full name'] = geonames_details[0] |
378 | self.location_details['latitude'] = geonames_details[2] |
379 | self.location_details['longitude'] = geonames_details[3] |
380 | self.prepare_location_for_google(geonames_details) |
381 | self.prepare_location_for_yahoo(geonames_details) |
382 | + self.prepare_location_for_weather_com(geonames_details) |
383 | #TODO: Get noaa id from geonames service |
384 | self.location_details['noaa id'] = "woot" |
385 | |
386 | # check mandatory attributes |
387 | if not hasattr(self, 'location_code') or \ |
388 | - 'latitude' not in self.location_details or \ |
389 | - 'longitude' not in self.location_details: |
390 | + 'latitude' not in self.location_details or \ |
391 | + 'longitude' not in self.location_details: |
392 | return False |
393 | |
394 | # check that we have at least one supported data source |
395 | if 'google id' not in self.location_details and \ |
396 | - 'yahoo id' not in self.location_details: |
397 | + 'yahoo id' not in self.location_details and \ |
398 | + 'weather-com id' not in self.location_details: |
399 | log.error(("Location '%s'" % |
400 | - self.location_details['full name'])) + \ |
401 | + self.location_details['full name'])) + \ |
402 | "is not supported by current data sources" |
403 | return False |
404 | |
405 | return True |
406 | |
407 | def prepare_location_for_google(self, geonames_details): |
408 | - # Format latitude and longitude for Google needs |
409 | + """ Format latitude and longitude for Google needs """ |
410 | try: |
411 | lat = self.convert_coordinate_for_google(geonames_details[2]) |
412 | lon = self.convert_coordinate_for_google(geonames_details[3]) |
413 | @@ -340,7 +385,7 @@ |
414 | log.error(e) |
415 | |
416 | def prepare_location_for_yahoo(self, geonames_details): |
417 | - # Get location details in english for Yahoo |
418 | + """ Get location details in English for Yahoo """ |
419 | baseurl = 'http://api.geonames.org/getJSON' |
420 | params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'} |
421 | url = '?'.join((baseurl, urlencode(params))) |
422 | @@ -356,19 +401,13 @@ |
423 | return |
424 | |
425 | # Get YAHOO WOEID by english name of location |
426 | - baseurl = 'http://where.yahooapis.com/geocode' |
427 | - params = {'location': displayed_city_name, 'appid': 'mOawLd4s', 'flags': 'J'} |
428 | - url = '?'.join((baseurl, urlencode(params))) |
429 | - log.debug("Location: Get Yahoo WOEID, url %s" % url) |
430 | - f = urllib2.urlopen(url) |
431 | - s=f.read() |
432 | - null = None |
433 | - yahoo_woeid_result = eval(s) |
434 | - if (yahoo_woeid_result['ResultSet']['Error'] != 0) and (yahoo_woeid_result['ResultSet']['Results'] != None): |
435 | - log.error("Location: Yahoo woeid return error. Full response:\n %s" % str(yahoo_woeid_result)) |
436 | + woeid_result = pywapi.get_woeid_from_yahoo(displayed_city_name) |
437 | + if woeid_result.has_key('error'): |
438 | + log.error("Location: Yahoo woeid return error. Full response:\n %s" % woeid_result['error']) |
439 | return |
440 | else: |
441 | - woeid = yahoo_woeid_result['ResultSet']['Results'][0]['woeid'] |
442 | + # only look at the the first woeid result |
443 | + woeid = woeid_result[0][0] |
444 | self.location_code = woeid |
445 | log.debug("Location: woeid is %s" % woeid) |
446 | |
447 | @@ -400,21 +439,54 @@ |
448 | except Exception, e: |
449 | log.error(e) |
450 | |
451 | - # Return lcoation code and location details |
452 | + def prepare_location_for_weather_com(self, geonames_details): |
453 | + """ Get location details in English for Weather.com """ |
454 | + baseurl = 'http://api.geonames.org/getJSON' |
455 | + params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'} |
456 | + url = '?'.join((baseurl, urlencode(params))) |
457 | + log.debug("Location: Get GeoNames location details, url %s" % url) |
458 | + try: |
459 | + city = eval(urllib2.urlopen(url).read()) |
460 | + if 'adminName1' in city: |
461 | + displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName']) |
462 | + elif 'name' in city: |
463 | + displayed_city_name = "%s, %s" % (city['name'], city['countryName']) |
464 | + else: |
465 | + log.error("Location: Cannot find GeoNames info for code %s Full Response:\n %s" % (geonames_details[1], str(city))) |
466 | + return |
467 | + |
468 | + # Get Weather.com Location ID by English name of location |
469 | + locid_result = pywapi.get_location_ids(displayed_city_name) |
470 | + if locid_result.has_key('error'): |
471 | + log.error("Location: Weather.com locid return error. Full response:\n %s" % locid_result['error']) |
472 | + return |
473 | + else: |
474 | + # only look at the the first locid result |
475 | + locid = locid_result[0][0] |
476 | + self.location_details['weather-com id'] = locid |
477 | + log.debug("Location: locid is %s" % locid) |
478 | + |
479 | + except urllib2.URLError: |
480 | + log.error("Location: error reaching url '%s'" % url) |
481 | + |
482 | + except Exception, e: |
483 | + log.error(e) |
484 | + |
485 | def export_location_details(self): |
486 | + """ Return location code and location details """ |
487 | return (self.location_code, self.location_details) |
488 | |
489 | - # Get fresh weather data and store it to weather object |
490 | def update_weather_data(self, source): |
491 | - # gather existing source keys |
492 | + """ Get fresh weather data and store it to weather object """ |
493 | valid_source = None |
494 | loc_ids = {} |
495 | |
496 | SOURCES = { |
497 | - WeatherDataSource.GOOGLE : ("google id", "Google"), |
498 | - WeatherDataSource.YAHOO : ("yahoo id", "Yahoo"), |
499 | + WeatherDataSource.WEATHER_COM : ("weather-com id", "Weather.com"), |
500 | + WeatherDataSource.YAHOO : ("yahoo id", "Yahoo") |
501 | } |
502 | |
503 | + # gather existing source keys |
504 | for source_id in SOURCES.keys(): |
505 | if SOURCES[source_id][0] in self.location_details: |
506 | loc_ids[source_id] = SOURCES[source_id][0] |
507 | @@ -423,158 +495,145 @@ |
508 | if source in loc_ids: |
509 | valid_source = source |
510 | log.debug(("Location: default weather source '%s' " + |
511 | - "chosen for '%s'") % (SOURCES[valid_source][1], |
512 | - self.location_details['label'])) |
513 | + "chosen for '%s'") % (SOURCES[valid_source][1], |
514 | + self.location_details['label'])) |
515 | |
516 | # try with the first alternative |
517 | elif len(loc_ids.keys()): |
518 | valid_source = loc_ids.keys()[0] |
519 | - log.debug(("Location: non default weather source '%s' " + |
520 | - "chosen for '%s'") % (SOURCES[valid_source][1], |
521 | - self.location_details['label'])) |
522 | |
523 | if valid_source is None: |
524 | log.error(("Location: no valid weather source can be " + |
525 | - "chosen for '%s'") % ( |
526 | - self.location_details['label'])) |
527 | + "chosen for '%s'") % ( |
528 | + self.location_details['label'])) |
529 | self.weather = None |
530 | else: |
531 | + log.debug(("Location: non default weather source '%s' " + |
532 | + "chosen for '%s'") % (SOURCES[valid_source][1], |
533 | + self.location_details['label'])) |
534 | self.weather = Weather( |
535 | self.location_details[loc_ids[valid_source]], |
536 | valid_source, self.metric_system, self.wind_unit, |
537 | + self.heat_index, self.chill_index, |
538 | self.location_details['latitude'], |
539 | self.location_details['longitude']) |
540 | |
541 | + |
542 | class Forecast: |
543 | """ Class to get forecast information """ |
544 | |
545 | - # Initialize a class with metric system, wind units, location and current user locale |
546 | - def __init__ (self, units, lat, lon, locale): |
547 | + def __init__ (self, units, location_id, locale): |
548 | + """Initialize a class with metric system, wind units, |
549 | + location code and current user locale |
550 | + |
551 | + """ |
552 | self.metric_system = units |
553 | - self.lat = self.convert_coordinate_for_google(lat) |
554 | - self.lon = self.convert_coordinate_for_google(lon) |
555 | + self.location_id = location_id |
556 | self.locale = locale |
557 | |
558 | - # Convert coordinate for google |
559 | - def convert_coordinate_for_google(self, value): |
560 | - value = float(value) * 1e6 |
561 | - return int(round(value)) |
562 | - |
563 | - # Get and store forecast data. For now using Google only |
564 | def prepare_forecast_data(self): |
565 | + """ Get and store forecast data. For now using Weather.com only. """ |
566 | + # TODO: Implement for NOAA |
567 | self.daysofweek = [] |
568 | self.icons = [] |
569 | self.conditions = [] |
570 | self.error_message = None |
571 | try: |
572 | - # Generate a fake location by current coordinates |
573 | - location_name = ",,,%s,%s" % (self.lat, self.lon) |
574 | - self.forecast = pywapi.get_weather_from_google (location_name, hl = self.locale) |
575 | - self.unitsystem = self.forecast['forecast_information']['unit_system'] |
576 | + log.debug("Forecast: units set to %s" % self.metric_system) |
577 | + # Check units, default to imperial |
578 | + if self.metric_system == UnitSystem.SI: |
579 | + self.unitsystem = 'metric' |
580 | + elif self.metric_system == UnitSystem.IMPERIAL: |
581 | + self.unitsystem = 'imperial' |
582 | + else: |
583 | + self.unitsystem = 'imperial' |
584 | + |
585 | + self.forecast = pywapi.get_weather_from_weather_com(self.location_id, self.unitsystem) |
586 | |
587 | for forecast in self.forecast['forecasts']: |
588 | self.daysofweek.append(forecast["day_of_week"]) |
589 | - forecast_icon = str(forecast["icon"]) |
590 | - if "/ig/images/weather/" in forecast_icon: |
591 | - self.icons.append(forecast_icon.split("/ig/images/weather/")[-1].split(".gif")[0]) |
592 | - elif "http://g0.gstatic.com/images/icons/onebox" in forecast_icon: |
593 | - self.icons.append(forecast_icon.split("http://g0.gstatic.com/images/icons/onebox/weather_")[-1].split("-40.gif")[0]) |
594 | - self.conditions.append(forecast["condition"]) |
595 | + |
596 | + # Yahoo forecast icon URL is "http://l.yimg.com/a/i/us/we/52/<code>.gif" |
597 | + self.icons.append(forecast["day"]["icon"]) |
598 | + self.conditions.append(forecast["day"]["brief_text"]) |
599 | + |
600 | self.error_message = None |
601 | |
602 | except urllib2.URLError: |
603 | - log.error("Forecast: error reading forecast for %s" % location_name) |
604 | + log.error("Forecast: error reading forecast for %s" % self.location_id) |
605 | except KeyError: |
606 | - log.error("Forecast: returned empty forecast %s" % location_name) |
607 | + log.error("Forecast: returned empty forecast for %s" % self.location_id) |
608 | self.error_message = _('Unknown error occurred while picking up weather data') |
609 | |
610 | - # Parse high values for forecast data |
611 | def get_forecast_data(self): |
612 | + """ Parse high and low values for forecast data """ |
613 | self.highdata = [] |
614 | self.lowdata = [] |
615 | |
616 | - if not hasattr(self, 'unitsystem'): |
617 | - return None |
618 | - |
619 | - if ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.SI)) or ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.IMPERIAL)): |
620 | - #correct scale selected |
621 | - for forecast in self.forecast['forecasts']: |
622 | - self.highdata.append(forecast["high"]) |
623 | - self.lowdata.append(forecast["low"]) |
624 | - |
625 | - elif ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.IMPERIAL)): |
626 | - #convert from SI to imperial |
627 | - for forecast in self.forecast['forecasts']: |
628 | - self.highdata.append(int(((int(forecast["high"])*9)/5)+32)) |
629 | - self.lowdata.append(int(((int(forecast["low"])*9)/5)+32)) |
630 | - |
631 | - elif ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.SI)): |
632 | - #convert from imperial to SI |
633 | - for forecast in self.forecast['forecasts']: |
634 | - self.highdata.append(int((((int(forecast["high"]))-32)*5)/9)) |
635 | - self.lowdata.append(int((((int(forecast["low"]))-32)*5)/9)) |
636 | + # Since we are now using Weather.com, forecast will always be in correct units |
637 | + for forecast in self.forecast['forecasts']: |
638 | + self.highdata.append(forecast["high"]) |
639 | + self.lowdata.append(forecast["low"]) |
640 | |
641 | return (self.highdata, self.lowdata) |
642 | |
643 | - # Parse a list of days of week with forecast data |
644 | def get_forecast_daysofweek(self): |
645 | + """ Parse a list of days of week with forecast data """ |
646 | return self.daysofweek |
647 | |
648 | - # Parse icons for forecast data |
649 | def get_forecast_icons(self): |
650 | + """ Parse icons for forecast data """ |
651 | return self.icons |
652 | |
653 | - # Parse conditions for forecast data |
654 | def get_forecast_conditions(self): |
655 | + """ Parse conditions for forecast data """ |
656 | return self.conditions |
657 | |
658 | class Weather: |
659 | - """ |
660 | - Data object to parse weather data with unit convertion |
661 | - """ |
662 | - |
663 | - #Available conditions by google icon |
664 | - #Format: Google icon name: (day icon, night icon, is a severe weather condition) |
665 | - #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/ |
666 | - _GoogleConditions = { |
667 | - "sunny" : ( "weather-clear", "weather-clear-night", False), |
668 | - "mostly_sunny" : ( "weather-clear", "weather-clear-night", False), |
669 | - "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
670 | - "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
671 | - "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
672 | - "cloudy" : ( "weather-clouds", "weather-clouds-night", False), |
673 | - "mostlycloudy" : ( "weather-overcast", "weather-overcast", False), |
674 | - "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False), |
675 | - "overcast" : ( "weather-overcast", "weather-overcast", False), |
676 | - "rain" : ( "weather-showers", "weather-showers", False), |
677 | - "chanceofrain" : ( "weather-showers", "weather-showers", False), |
678 | - "chance_of_rain" : ( "weather-showers", "weather-showers", False), |
679 | - "heavyrain" : ( "weather-showers", "weather-showers", False), |
680 | - "drizzle" : ( "weather-showers", "weather-showers", False), |
681 | - "sleet" : ( "weather-snow", "weather-snow", False), |
682 | - "rain_snow" : ( "weather-snow", "weather-snow", False), |
683 | - "rainsnow" : ( "weather-snow", "weather-snow", False), |
684 | - "snow" : ( "weather-snow", "weather-snow", False), |
685 | - "chanceofsnow" : ( "weather-snow", "weather-snow", False), |
686 | - "chance_of_snow" : ( "weather-snow", "weather-snow", False), |
687 | - "heavysnow" : ( "weather-snow", "weather-snow", False), |
688 | - "icy" : ( "weather-snow", "weather-snow", False), |
689 | - "snowflurries" : ( "weather-snow", "weather-snow", False), |
690 | - "flurries" : ( "weather-snow", "weather-snow", False), |
691 | - "dust" : ( "weather-fog", "weather-fog", False), |
692 | - "fog" : ( "weather-fog", "weather-fog", False), |
693 | - "smoke" : ( "weather-fog", "weather-fog", False), |
694 | - "haze" : ( "weather-fog", "weather-fog", False), |
695 | - "mist" : ( "weather-fog", "weather-fog", False), |
696 | - "thunderstorm" : ( "weather-storm", "weather-storm", True), |
697 | - "chance_of_storm" : ( "weather-storm", "weather-storm", True), |
698 | - "thunderstorms" : ( "weather-storm", "weather-storm", True), |
699 | - "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True), |
700 | - "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True), |
701 | - } |
702 | - |
703 | - #Available conditions by yahoo condition code |
704 | - #Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name) |
705 | + """Data object to parse weather data with unit conversion """ |
706 | + |
707 | +## #Available conditions by google icon |
708 | +## #Format: Google icon name: (day icon, night icon, is a severe weather condition) |
709 | +## #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/ |
710 | +## _GoogleConditions = { |
711 | +## "sunny" : ( "weather-clear", "weather-clear-night", False), |
712 | +## "mostly_sunny" : ( "weather-clear", "weather-clear-night", False), |
713 | +## "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
714 | +## "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
715 | +## "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False), |
716 | +## "cloudy" : ( "weather-clouds", "weather-clouds-night", False), |
717 | +## "mostlycloudy" : ( "weather-overcast", "weather-overcast", False), |
718 | +## "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False), |
719 | +## "overcast" : ( "weather-overcast", "weather-overcast", False), |
720 | +## "rain" : ( "weather-showers", "weather-showers", False), |
721 | +## "chanceofrain" : ( "weather-showers", "weather-showers", False), |
722 | +## "chance_of_rain" : ( "weather-showers", "weather-showers", False), |
723 | +## "heavyrain" : ( "weather-showers", "weather-showers", False), |
724 | +## "drizzle" : ( "weather-showers", "weather-showers", False), |
725 | +## "sleet" : ( "weather-snow", "weather-snow", False), |
726 | +## "rain_snow" : ( "weather-snow", "weather-snow", False), |
727 | +## "rainsnow" : ( "weather-snow", "weather-snow", False), |
728 | +## "snow" : ( "weather-snow", "weather-snow", False), |
729 | +## "chanceofsnow" : ( "weather-snow", "weather-snow", False), |
730 | +## "chance_of_snow" : ( "weather-snow", "weather-snow", False), |
731 | +## "heavysnow" : ( "weather-snow", "weather-snow", False), |
732 | +## "icy" : ( "weather-snow", "weather-snow", False), |
733 | +## "snowflurries" : ( "weather-snow", "weather-snow", False), |
734 | +## "flurries" : ( "weather-snow", "weather-snow", False), |
735 | +## "dust" : ( "weather-fog", "weather-fog", False), |
736 | +## "fog" : ( "weather-fog", "weather-fog", False), |
737 | +## "smoke" : ( "weather-fog", "weather-fog", False), |
738 | +## "haze" : ( "weather-fog", "weather-fog", False), |
739 | +## "mist" : ( "weather-fog", "weather-fog", False), |
740 | +## "thunderstorm" : ( "weather-storm", "weather-storm", True), |
741 | +## "chance_of_storm" : ( "weather-storm", "weather-storm", True), |
742 | +## "thunderstorms" : ( "weather-storm", "weather-storm", True), |
743 | +## "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True), |
744 | +## "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True), |
745 | +## } |
746 | + |
747 | + # Available conditions by yahoo condition code |
748 | + # Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name) |
749 | _YahooConditions = { |
750 | '0' : ("weather-storm", "weather-storm", True, _("Tornado")), |
751 | '1' : ("weather-storm", "weather-storm", True, _("Tropical storm")), |
752 | @@ -628,55 +687,60 @@ |
753 | '3200': (False, False, False, _("Unknown condition")) |
754 | } |
755 | |
756 | - # Initialize and get fresh data |
757 | - def __init__(self, location_id, weather_datasource, metric_system, wind_unit, lat, lon): |
758 | + # Available conditions by Weather.com condition code; same as Yahoo |
759 | + _WeathercomConditions = _YahooConditions |
760 | + |
761 | + def __init__(self, location_id, weather_datasource, metric_system, |
762 | + wind_unit, heat_index, chill_index, lat, lon): |
763 | + """ Initialize and get fresh weather data """ |
764 | self.__weather_datasource = weather_datasource |
765 | self.__metric_system = metric_system |
766 | self._wind_unit = wind_unit |
767 | self.__current_condition = None |
768 | + self.__heat_index = heat_index |
769 | + self.__chill_index = chill_index |
770 | self.__lat = lat |
771 | self.__lon = lon |
772 | |
773 | - # Get data from Google |
774 | - if self.__weather_datasource == WeatherDataSource.GOOGLE: |
775 | - # Get data in english locale, then - switch back |
776 | - self.__report = pywapi.get_weather_from_google (location_id, hl = 'en') |
777 | - # Get data in original locale for condition name |
778 | - self.__localized_report = pywapi.get_weather_from_google (location_id, hl = locale_name) |
779 | - |
780 | + if self.__metric_system == UnitSystem.SI: |
781 | + unit_system = 'metric' |
782 | + elif self.__metric_system == UnitSystem.IMPERIAL: |
783 | + unit_system = 'imperial' |
784 | + else: |
785 | + unit_system = 'imperial' |
786 | + |
787 | + # Get data from Weather.com |
788 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
789 | + self.__report = pywapi.get_weather_from_weather_com( |
790 | + location_id, unit_system) |
791 | + ##self.__localized_report = self.__report |
792 | + log.debug("Weather: checking Weather.com report for " |
793 | + "weather condition and icon name") |
794 | if 'current_conditions' not in self.__report.keys(): |
795 | - log.error("Weather: could not get Google weather condition from report") |
796 | + icon_name = "" |
797 | + log.error("Weather: could not get Weather.com " |
798 | + "weather condition from report") |
799 | log.error("Weather: got data '%s'" % str(self.__report)) |
800 | self.__current_condition = (False, False, False, _("Unknown condition")) |
801 | |
802 | - if 'current_conditions' not in self.__localized_report.keys(): |
803 | - log.error("Weather: could not get Google weather condition from localized report") |
804 | - log.error("Weather: got data '%s'" % str(self.__localized_report)) |
805 | - self.__current_condition = (False, False, False, _("Unknown condition")) |
806 | + elif 'icon' in self.__report['current_conditions'].keys(): |
807 | + icon_name = self.__report['current_conditions']['icon'] |
808 | + self.__current_condition = self._WeathercomConditions.get(icon_name) |
809 | |
810 | - if 'icon' in self.__report['current_conditions'].keys(): |
811 | - icon_path = self.__report['current_conditions']['icon'] |
812 | - if '/ig/images/weather/' in icon_path: |
813 | - icon_name = icon_path.replace('/ig/images/weather/', '').replace('.gif', '') |
814 | - elif 'http://g0.gstatic.com/images/icons/onebox' in icon_path: |
815 | - icon_name = icon_path.replace('http://g0.gstatic.com/images/icons/onebox/weather_', '').replace('-40.gif', '') |
816 | - else: |
817 | - icon_name = icon_path |
818 | else: |
819 | - log.error("Weather: could not get weather icon from report") |
820 | + icon_name = "" |
821 | + log.error("Weather: could not get icon name from Weather.com report") |
822 | log.error("Weather: got data '%s'" % str(self.__report['current_conditions'])) |
823 | - icon_name = "" |
824 | - |
825 | - self.__current_condition = self._GoogleConditions.get(icon_name) |
826 | - if self.__current_condition == None: |
827 | - log.error("ExtendedForecast: unknown Google weather condition '%s'" % icon_name) |
828 | self.__current_condition = (False, False, False, _("Unknown condition")) |
829 | - |
830 | + |
831 | # Get data from Yahoo |
832 | if self.__weather_datasource == WeatherDataSource.YAHOO: |
833 | - self.__report = pywapi.get_weather_from_yahoo (location_id, 'imperial') |
834 | - self.__localized_report = self.__report |
835 | + self.__report = pywapi.get_weather_from_yahoo(location_id, unit_system) |
836 | + ##self.__localized_report = self.__report |
837 | + log.debug("Weather: checking Yahoo report for " |
838 | + "weather condition and icon name") |
839 | if 'condition' not in self.__report.keys(): |
840 | + icon_name = "" |
841 | log.error("Weather: could not get Yahoo weather condition from report") |
842 | log.error("Weather: got data '%s'" % str(self.__report)) |
843 | self.__current_condition = (False, False, False, _("Unknown condition")) |
844 | @@ -695,8 +759,8 @@ |
845 | #Prepare sunrise/sunset data |
846 | self.get_sun_data() |
847 | |
848 | - #Get sunrise/sunset times, calculate whether it is night already |
849 | def get_sun_data(self): |
850 | + """ Get sunrise/sunset times and calculate whether it is night already """ |
851 | self.__night = False |
852 | self.__sunrise_t = None |
853 | self.__sunset_t = None |
854 | @@ -714,7 +778,7 @@ |
855 | "dst")[0].firstChild.nodeValue |
856 | # strip timezone info |
857 | localtime = datetime.datetime.strptime(localtime.rsplit(' ',1)[0], |
858 | - '%Y-%m-%d %H:%M:%S') |
859 | + '%Y-%m-%d %H:%M:%S') |
860 | dst = 1 if dst == "True" else 0 |
861 | |
862 | except urllib2.URLError: |
863 | @@ -725,6 +789,7 @@ |
864 | url = 'http://www.earthtools.org/sun/%s/%s/%s/%s/99/%s' % \ |
865 | (self.__lat, self.__lon, localtime.day, localtime.month, dst) |
866 | try: |
867 | + log.debug("Weather: get_sun_data: getting sunrise/sunset data") |
868 | f = urllib2.urlopen(url) |
869 | s=f.read() |
870 | parsed = parseString(s) |
871 | @@ -742,20 +807,20 @@ |
872 | else: |
873 | self.__night = False |
874 | log.debug("Weather: got localtime " + |
875 | - "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % ( |
876 | - localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night)) |
877 | + "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % ( |
878 | + localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night)) |
879 | |
880 | - # Return True, if weather condition is severe |
881 | def condition_is_severe(self): |
882 | + """ Return True if weather condition is severe """ |
883 | if self.__current_condition != None: |
884 | log.debug("Weather: got severe condition '%s'" % self.__current_condition[2]) |
885 | return self.__current_condition[2] |
886 | else: |
887 | log.error("Weather: condition is not set while condition severity check") |
888 | - return False; |
889 | + return False |
890 | |
891 | - # Get associated icon name |
892 | def get_icon_name(self): |
893 | + """ Get icon name associated with current condition """ |
894 | if self.__current_condition != None: |
895 | if self.__night: |
896 | log.debug("Weather: night, show '%s' icon" % self.__current_condition[1]) |
897 | @@ -767,217 +832,440 @@ |
898 | log.error("Weather: return 'offline' icon due to empty condition") |
899 | return False |
900 | |
901 | - # Get condition text |
902 | def get_condition_label(self): |
903 | - if self.__weather_datasource == WeatherDataSource.GOOGLE: |
904 | - if 'condition' in self.__localized_report['current_conditions'].keys(): |
905 | - condition = self.__localized_report['current_conditions']['condition'] |
906 | - else: |
907 | - condition = _("Unknown condition") |
908 | + """ Get text of current condition """ |
909 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
910 | + condition = self.__report['current_conditions']['text'] |
911 | if self.__weather_datasource == WeatherDataSource.YAHOO: |
912 | condition = self.__current_condition[3] |
913 | return condition |
914 | |
915 | - # Get humidity label |
916 | def get_humidity_label(self): |
917 | + """ Get text string for current humidity """ |
918 | humidity = "%s: ---%%" % (_("Humidity")) |
919 | - if self.__weather_datasource == WeatherDataSource.GOOGLE \ |
920 | - and 'humidity' in self.__localized_report['current_conditions']: |
921 | - humidity = self.__localized_report['current_conditions']['humidity'] |
922 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM \ |
923 | + and 'humidity' in self.__report['current_conditions']: |
924 | + humidity = "%s: %s%%" % (_("Humidity"), self.__report['current_conditions']['humidity']) |
925 | if self.__weather_datasource == WeatherDataSource.YAHOO \ |
926 | - and 'humidity' in self.__localized_report['atmosphere']: |
927 | - humidity = "%s: %s%%" % (_("Humidity"), self.__localized_report['atmosphere']['humidity']) |
928 | + and 'humidity' in self.__report['atmosphere']: |
929 | + humidity = "%s: %s%%" % (_("Humidity"), self.__report['atmosphere']['humidity']) |
930 | return humidity |
931 | |
932 | - # Get dew point - using in humidex calculation |
933 | - #TODO: Update with NOAA |
934 | def get_dew_point_label(self): |
935 | - if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO: |
936 | - # Not returned by Google and Yahoo |
937 | + """ Get dew point, which is used in humidex calculation """ |
938 | + #TODO: Update with NOAA |
939 | + _value = "---" |
940 | + _unit = "" |
941 | + if self.__weather_datasource == WeatherDataSource.YAHOO: |
942 | + # Not returned by Yahoo |
943 | return None |
944 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
945 | + _value = self.__report['current_conditions']['dewpoint'] |
946 | + _unit = self.__report['units']['temperature'] |
947 | + return u"%s: %s °%s" % (_("Dewpoint"), _value, _unit) |
948 | |
949 | - # Get pressure label |
950 | def get_pressure_label(self): |
951 | - if self.__weather_datasource == WeatherDataSource.GOOGLE: |
952 | - # TODO: Empty for Google, use NOAA data? |
953 | - value = "---" |
954 | - unit = "" |
955 | + """ Get text string for current air pressure """ |
956 | + _value = "---" |
957 | + _unit = "" |
958 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM \ |
959 | + and 'barometer' in self.__report['current_conditions'].keys() \ |
960 | + and 'pressure' in self.__report['units'].keys(): |
961 | + _value = self.__report['current_conditions']['barometer']['reading'] |
962 | + _unit = self.__report['units']['pressure'] |
963 | if self.__weather_datasource == WeatherDataSource.YAHOO \ |
964 | - and 'pressure' in self.__localized_report['atmosphere'].keys() \ |
965 | - and 'pressure' in self.__localized_report['units'].keys(): |
966 | - value = self.__localized_report['atmosphere']['pressure'] |
967 | - unit = self.__localized_report['units']['pressure'] |
968 | - return "%s: %s %s" % (_("Pressure"), value, units) |
969 | + and 'pressure' in self.__report['atmosphere'].keys() \ |
970 | + and 'pressure' in self.__report['units'].keys(): |
971 | + _value = self.__report['atmosphere']['pressure'] |
972 | + _unit = self.__report['units']['pressure'] |
973 | + return "%s: %s %s" % (_("Pressure"), _value, _unit) |
974 | |
975 | - # Get temperature with units value - doesn't include 'Temperature' label |
976 | - def get_temperature(self, needs_rounding = False): |
977 | - _value = "---" |
978 | +## def get_temperature(self, needs_rounding = False): |
979 | + def get_temperature(self): |
980 | + """ Get temperature value and units string """ |
981 | + _value = None |
982 | _unit = "" |
983 | - if self.__weather_datasource == WeatherDataSource.GOOGLE: |
984 | - if (self.__metric_system == MetricSystem.SI) \ |
985 | - and 'temp_c' in self.__report['current_conditions'].keys(): |
986 | - _value = self.__report['current_conditions']['temp_c'] |
987 | - _unit = "ËšC" |
988 | - elif 'temp_f' in self.__report['current_conditions'].keys(): |
989 | - _value = self.__report['current_conditions']['temp_f'] |
990 | - _unit = "ËšF" |
991 | - if self.__weather_datasource == WeatherDataSource.YAHOO: |
992 | - if (self.__metric_system == MetricSystem.SI) \ |
993 | - and 'temp' in self.__report['condition'].keys(): |
994 | - _value = NumberFormatter.format_float( |
995 | - ((float(self.__report['condition']['temp']) - 32) * 5/9), 1) |
996 | - _unit = "ËšC" |
997 | - else: |
998 | + if (self.__weather_datasource == WeatherDataSource.WEATHER_COM and |
999 | + 'temperature' in self.__report['current_conditions'].keys() and |
1000 | + 'temperature' in self.__report['units'].keys()): |
1001 | + if ((self.__metric_system == UnitSystem.SI and |
1002 | + self.__report['units']['temperature'] == u"C") or |
1003 | + (self.__metric_system == UnitSystem.IMPERIAL and |
1004 | + self.__report['units']['temperature' ] == u"F")): |
1005 | + _value = self.__report['current_conditions']['temperature'] |
1006 | + _unit = u"°%s" % self.__report['units']['temperature'] |
1007 | + if (self.__weather_datasource == WeatherDataSource.YAHOO and |
1008 | + 'temp' in self.__report['condition'].keys() and |
1009 | + 'temperature' in self.__report['units'].keys()): |
1010 | + if ((self.__metric_system == UnitSystem.SI and |
1011 | + self.__report['units']['temperature'] == u"C") or |
1012 | + (self.__metric_system == UnitSystem.IMPERIAL and |
1013 | + self.__report['units']['temperature'] == u"F")): |
1014 | _value = self.__report['condition']['temp'] |
1015 | - _unit = "ËšF" |
1016 | - # round the value if required |
1017 | - if needs_rounding and _value != "---": |
1018 | - _value = NumberFormatter.format_float(locale.atof(_value), 0) |
1019 | - return ("%s %s" % (_value, _unit)) |
1020 | - |
1021 | - # Get temperature label |
1022 | + _unit = u"°%s" % self.__report['units']['temperature'] |
1023 | +## # round the value if required |
1024 | +## if needs_rounding and _value != "---": |
1025 | +## _value = NumberFormatter.format_float(locale.atof(_value), 0) |
1026 | + return (_value, _unit) |
1027 | + |
1028 | + def get_temperature_string(self): |
1029 | + """ Get temperature with units value - doesn't include 'Temperature' string """ |
1030 | + (_value, _unit) = self.get_temperature() |
1031 | + if _value is None: |
1032 | + _value = "---" |
1033 | + _unit = "" |
1034 | + return (u"%s %s" % (_value, _unit)) |
1035 | + |
1036 | def get_temperature_label(self): |
1037 | - return "%s: %s" % (_("Temperature"), self.get_temperature()) |
1038 | - |
1039 | - # Get humidex parameter |
1040 | - def get_humidex_label(self): |
1041 | - if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO: |
1042 | - #Empty for Yahoo and Google |
1043 | - return None |
1044 | - #TODO: Update with NOAA data |
1045 | - #dewPoint=2 |
1046 | - #temp_c = 1 |
1047 | - #self.vapour_pressure = 6.11 * math.exp(5417.7530 * ( (1/273.16) - (1/(dewPoint+273.16)))) |
1048 | - #self.humidex = temp_c + (0.5555)*(self.vapour_pressure - 10.0); |
1049 | - #return ("%s: %.1f" % (_("Humidex"), self.humidex)).replace(".0", "") |
1050 | - |
1051 | - # Get wind label |
1052 | + """ Get text string for current temperature label """ |
1053 | + return "%s: %s" % (_("Temperature"), self.get_temperature_string()) |
1054 | + |
1055 | + def get_relative_string(self): |
1056 | + """ Get relative temperature with units value - doesn't include 'Feels Like' string """ |
1057 | + # try relative heat |
1058 | + if self.__heat_index == RelativeFormula.HUMIDEX: |
1059 | + (_value, _unit) = self.get_humidex() |
1060 | + if self.__heat_index == RelativeFormula.HEATINDEX: |
1061 | + (_value, _unit) = self.get_heat_index() |
1062 | + if _value is not None: |
1063 | + return (u"%s %s" % (_value, _unit)) |
1064 | + # try relative chill |
1065 | + if self.__chill_index == RelativeFormula.WINDCHILL: |
1066 | + (_value, _unit) = self.get_wind_chill() |
1067 | + if self.__chill_index == RelativeFormula.APPARENT: |
1068 | + (_value, _unit) = self.get_apparent_temp() |
1069 | + if _value is not None: |
1070 | + return (u"%s %s" % (_value, _unit)) |
1071 | + # use current temperature |
1072 | + return self.get_temperature_string() |
1073 | + |
1074 | + def get_relative_label(self): |
1075 | + """ Get text string for relative temperature ("feels like") label """ |
1076 | + return "%s: %s" % (_("Feels Like"), self.get_relative_string()) |
1077 | + |
1078 | + def get_humidex(self): |
1079 | + """ Calculate humidex and get value and units |
1080 | + |
1081 | + The standard Humidex formula used by Environment Canada is: |
1082 | + |
1083 | + humidex = (air temperature) + h |
1084 | + |
1085 | + h = (0.5555)*(e - 10.0); |
1086 | + e = vapour pressure in hPa (mbar), given by: |
1087 | + e = 6.11 * exp [5417.7530 * ( (1/273.16) - (1/dewpoint) ) ] |
1088 | + where dewpoint is expressed in Kelvins |
1089 | + (temperature in K = temperature in °C + 273.1) |
1090 | + and 5417.7530 is a rounded constant based on the molecular weight |
1091 | + of water, latent heat of evaporation, and the universal gas constant. |
1092 | + |
1093 | + """ |
1094 | + #TODO: Update with NOAA data |
1095 | + if self.__weather_datasource == WeatherDataSource.YAHOO: |
1096 | + # Empty for Yahoo |
1097 | + return (None, "") |
1098 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
1099 | + if self.__report['units']['temperature'] == "F": |
1100 | + # Humidex is calculated in C |
1101 | + dew_point_f = float(self.__report['current_conditions']['dewpoint']) |
1102 | + temp_f = float(self.__report['current_conditions']['temperature']) |
1103 | + dew_point_c = ((dew_point_f - 32.0) * 5.0/9.0) |
1104 | + temp_c = ((temp_f - 32.0) * 5.0/9.0) |
1105 | + else: |
1106 | + dew_point_c = float(self.__report['current_conditions']['dewpoint']) |
1107 | + temp_c = float(self.__report['current_conditions']['temperature']) |
1108 | + vapour_pressure = 6.11 * exp(5417.7530 * ( (1/273.16) - (1/(dew_point_c+273.16)))) |
1109 | + humidex = temp_c + (0.5555)*(vapour_pressure - 10.0) |
1110 | + # Humidex is meaningless if it is lower than temperature |
1111 | + if humidex < temp_c: |
1112 | + #return (u"%s: N/A" % _("Humidex")) |
1113 | + return (None, "") |
1114 | + if self.__report['units']['temperature'] == "F": |
1115 | + humidex_f = (humidex*9.0/5.0) + 32.0 |
1116 | + # Humidex is unitless (represents Celsius) so in F show units |
1117 | + #return (u"%s: %s°F" % (_("Humidex"), NumberFormatter.format_float(humidex_f, 1))).replace(".0", "") |
1118 | + #return (NumberFormatter.format_float(humidex_f, 1).replace(".0", ""), u"°F") |
1119 | + return (int(round(humidex_f)), u"°F") |
1120 | + else: |
1121 | + #return ("%s: %.1f" % (_("Humidex"), humidex)).replace(".0", "") |
1122 | + #return (u"%s: %s" % (_("Humidex"), NumberFormatter.format_float(humidex, 1))).replace(".0", "") |
1123 | + #return (NumberFormatter.format_float(humidex, 1).replace(".0", ""), u"°C") |
1124 | + return (int(round(humidex)), u"°C") |
1125 | + |
1126 | + def get_heat_index(self): |
1127 | + """ Calculate heat index and get value and units |
1128 | + |
1129 | + The formula below approximates the heat index in degrees |
1130 | + Fahrenheit, to within ±1.3 °F. It is the result of a |
1131 | + multivariate fit (temperature equal to or greater than |
1132 | + 80°F and relative humidity equal to or greater than 40%) |
1133 | + to a model of the human body. |
1134 | + |
1135 | + Heat Index = c_1 + (c_2 * T) + (c_3 * R) + (c_4 * T * R) + |
1136 | + (c_5 * T^2) + (c_6 * R^2) + (c_7 * T^2 * R) + |
1137 | + (c_8 * T * R^2) + (c_9 * T^2 * R^2) |
1138 | + where: |
1139 | + T = ambient dry-bulb temperature (in degrees Fahrenheit) |
1140 | + R = relative humidity (percentage value between 0 and 100) |
1141 | + |
1142 | + """ |
1143 | + #TODO: Update with NOAA data |
1144 | + _value = None |
1145 | + |
1146 | + if self.__weather_datasource == WeatherDataSource.YAHOO: |
1147 | + log.debug("Weather: get_heat_index: weather_datasource is Yahoo, " |
1148 | + "checking temp and humidity") |
1149 | + if self.__report['units']['temperature'] == "C": |
1150 | + units = 'metric' |
1151 | + elif self.__report['units']['temperature'] == "F": # else here? |
1152 | + units = 'imperial' |
1153 | + T = float(self.__report['condition']['temp']) |
1154 | + R = float(self.__report['atmosphere']['humidity']) |
1155 | + _value = pywapi.heat_index(T, R, units) |
1156 | + |
1157 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
1158 | + log.debug("Weather: get_heat_index: weather_datasource is " |
1159 | + "Weather.com, checking temp and humidity") |
1160 | + if self.__report['units']['temperature'] == "C": |
1161 | + units = 'metric' |
1162 | + elif self.__report['units']['temperature'] == "F": # else here? |
1163 | + units = 'imperial' |
1164 | + T = float(self.__report['current_conditions']['temperature']) |
1165 | + R = float(self.__report['current_conditions']['humidity']) |
1166 | + _value = pywapi.heat_index(T, R, units) |
1167 | + |
1168 | + # Heat Index is only valid for temp >= 80°F and humidity >= 40%) |
1169 | + if _value is None: |
1170 | + return (_value, "") |
1171 | + if units == 'metric': |
1172 | + #return (u"%s: %s°C" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "") |
1173 | + #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°C") |
1174 | + return (int(round(_value)), u"°C") |
1175 | + |
1176 | + elif units == 'imperial': # else here? |
1177 | + #return (u"%s: %s°F" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "") |
1178 | + #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°F") |
1179 | + return (int(round(_value)), u"°F") |
1180 | + |
1181 | + def get_wind_chill(self): |
1182 | + """ Calculate wind chill index and get text string for label |
1183 | + |
1184 | + The standard Wind Chill formula used by Environment Canada, |
1185 | + the 2001 JAG/TI Wind Chill Equivalent Temperature Index, is: |
1186 | + |
1187 | + T_wc = 13.12 + 0.6215 * T_a - |
1188 | + 11.37 * V^0.16 + |
1189 | + 0.3965 * T_a * V^0.16 |
1190 | + where: |
1191 | + T_wc is the wind chill index based on Celsius |
1192 | + T_a is the air temperature in °C |
1193 | + V is the wind speed in km/h, at 10 m (standard anemometer height) |
1194 | + |
1195 | + The equivalent formula in US customary units is: |
1196 | + |
1197 | + T_wc = 35.74 + 0.6215 * T_a - |
1198 | + 35.75 * V^0.16 + |
1199 | + 0.4275 * T_a * V^0.16 |
1200 | + |
1201 | + where: |
1202 | + T_wc is the wind chill index based on Fahrenheit |
1203 | + T_a is the air temperature °F |
1204 | + V is the wind speed in mph |
1205 | + |
1206 | + Windchill temperature is defined only for temperatures at or |
1207 | + below 10 °C (50 °F) and wind speeds above 4.8 kilometres per |
1208 | + hour (3.0 mph). |
1209 | + |
1210 | + """ |
1211 | + #TODO: Update with NOAA data |
1212 | + _value = None |
1213 | + |
1214 | + if self.__report['units']['temperature'] == "C": |
1215 | + units = 'metric' |
1216 | + elif self.__report['units']['temperature'] == "F": # else here? |
1217 | + units = 'imperial' |
1218 | + |
1219 | + if self.__weather_datasource == WeatherDataSource.YAHOO: |
1220 | + log.debug("Weather: get_wind_chill: weather_datasource is " |
1221 | + "Yahoo, checking temp and humidity") |
1222 | + T_a = float(self.__report['condition']['temp']) |
1223 | + V = float(self.__report['wind']['speed']) |
1224 | + |
1225 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
1226 | + log.debug("Weather: get_wind_chill: weather_datasource is " |
1227 | + "Weather.com, checking temp and humidity") |
1228 | + T_a = float(self.__report['current_conditions']['temperature']) |
1229 | + wind_speed = self.__report['current_conditions']['wind']['speed'] |
1230 | + V = (0.0 if wind_speed == 'calm' else float(wind_speed)) |
1231 | + |
1232 | + # move below to pywapi |
1233 | + if units == 'metric': |
1234 | + _value = 13.12 + 0.6215 * T_a - 11.37 * pow(V, 0.16) + 0.3965 * T_a * pow(V, 0.16) |
1235 | + return (int(round(_value)), u"°C") |
1236 | + elif units == 'imperial': |
1237 | + _value = 35.74 + 0.6215 * T_a - 35.75 * pow(V, 0.16) + 0.4275 * T_a * pow(V, 0.16) |
1238 | + return (int(round(_value)), u"°F") |
1239 | + |
1240 | + if _value is None: |
1241 | + return (_value, "") |
1242 | + |
1243 | + def get_apparent_temp(self): |
1244 | + """ Calculate Australian apparent temperature and get text string for label |
1245 | + |
1246 | + The standard formula for cooler temperatures used by the Australian |
1247 | + Bureau of Meteorology, the Australian Apparent Temperature, is: |
1248 | + |
1249 | + AT = T_a + 0.33e - 0.70ws - 4.00 |
1250 | + |
1251 | + Where: |
1252 | + T_a = Dry bulb temperature (°C) |
1253 | + e = Water vapour pressure (hPa) |
1254 | + ws = Wind speed (m/s) at an elevation of 10 meters |
1255 | + |
1256 | + The vapour pressure can be calculated from the temperature and |
1257 | + relative humidity using the equation: |
1258 | + |
1259 | + e = (rh / 100) * 6.105 * exp^[(17.27 * T_a) / (237.7 + T_a)] |
1260 | + |
1261 | + Where: |
1262 | + T_a = Dry bulb temperature (°C) |
1263 | + rh = Relative humidity [%] |
1264 | + exp^ represents the exponential function |
1265 | + |
1266 | + The Australian chill formula is only defined for temperatures |
1267 | + at or below 20°C (68°F) and wind speeds above 4.8 km/h (3.0 mph). |
1268 | + |
1269 | + """ |
1270 | + #TODO: Update with NOAA data |
1271 | + _value = None |
1272 | + |
1273 | + if self.__report['units']['temperature'] == "C": |
1274 | + units = 'metric' |
1275 | + elif self.__report['units']['temperature'] == "F": # else here? |
1276 | + units = 'imperial' |
1277 | + |
1278 | + if self.__weather_datasource == WeatherDataSource.YAHOO: |
1279 | + log.debug("Weather: get_wind_chill: weather_datasource is " |
1280 | + "Yahoo, checking temp and humidity") |
1281 | + T_a = float(self.__report['condition']['temp']) |
1282 | + rh = float(self.__report['atmosphere']['humidity']) |
1283 | + ws = float(self.__report['wind']['speed']) |
1284 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
1285 | + log.debug("Weather: get_wind_chill: weather_datasource is " |
1286 | + "Weather.com, checking temp and humidity") |
1287 | + T_a = float(self.__report['current_conditions']['temperature']) |
1288 | + rh = float(self.__report['current_conditions']['humidity']) |
1289 | + ws = float(self.__report['current_conditions']['wind']['speed']) |
1290 | + |
1291 | + # calculated in °C, so need to convert from °F |
1292 | + if units == 'imperial': |
1293 | + T_a = ((T_a - 32.0) * 5.0/9.0) |
1294 | + |
1295 | + e = (rh / 100) * 6.105 * exp((17.27 * T_a) / (237.7 + T_a)) |
1296 | + _value = T_a + 0.33 * e - 0.70 * ws - 4.00 |
1297 | + |
1298 | + # Now convert back to °F |
1299 | + if units == 'imperial': |
1300 | + _value = (_value*9.0/5.0) + 32.0 |
1301 | + return (int(round(_value)), u"°F") |
1302 | + if units == 'metric': |
1303 | + return (int(round(_value)), u"°C") |
1304 | + |
1305 | + if _value is None: |
1306 | + return (_value, "") |
1307 | + |
1308 | def get_wind_label(self): |
1309 | - if self.__weather_datasource == WeatherDataSource.GOOGLE: |
1310 | - # Convert units picked up from Google and replace units with currently configured |
1311 | - if 'wind_condition' in self.__localized_report['current_conditions'].keys(): |
1312 | - localized_wind_info = self.__localized_report['current_conditions']['wind_condition'].split(' ') |
1313 | - wind_direction = localized_wind_info[1] |
1314 | - wind_info = self.__report['current_conditions']['wind_condition'].split(' ') |
1315 | - wind_speed = wind_info[3] |
1316 | - else: |
1317 | - return _("Unknown") |
1318 | - |
1319 | + """ Get text string for current wind speed and direction """ |
1320 | + if self.__weather_datasource == WeatherDataSource.WEATHER_COM: |
1321 | + # Create a wind_info structure from Weather.com data |
1322 | + wind_direction = u"%s (%s°)" % (self.__report['current_conditions']['wind']['text'], |
1323 | + self.__report['current_conditions']['wind']['direction']) |
1324 | + wind_speed = self.__report['current_conditions']['wind']['speed'] |
1325 | + wind_units = self.__report['units']['speed'] |
1326 | + if wind_speed == "calm": |
1327 | + wind_speed = 0 |
1328 | + ##wind_direction = wind_direction.replace("CALM","Calm") |
1329 | + wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units] |
1330 | if self.__weather_datasource == WeatherDataSource.YAHOO: |
1331 | - # Create a similar to Google wind_info structure from Yahoo data |
1332 | - wind_direction = "%s (%sËš)" % (self.get_wind_direction(self.__localized_report['wind']['direction']), self.__localized_report['wind']['direction']) |
1333 | - wind_speed = self.__localized_report['wind']['speed'] |
1334 | - wind_units = self.__localized_report['units']['speed'] |
1335 | - localized_wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units] |
1336 | - |
1337 | + # Create a wind_info structure from Yahoo data |
1338 | + wind_direction = u"%s (%s°)" % (_(pywapi.get_wind_direction(self.__report['wind']['direction'])), self.__report['wind']['direction']) |
1339 | + wind_speed = self.__report['wind']['speed'] |
1340 | + wind_units = self.__report['units']['speed'] |
1341 | + wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units] |
1342 | |
1343 | try: |
1344 | _value = float(wind_speed) |
1345 | except ValueError as e: |
1346 | log.error("Could not parse '%s' as wind speed." % str(wind_speed)) |
1347 | _value = -1.0 |
1348 | - |
1349 | + |
1350 | # Parse Wind_direction - convert to selected scale |
1351 | - if (self._wind_unit == WindUnits.MPH): |
1352 | - _unit = __("mph", "mph", _value) |
1353 | - if (self._wind_unit == WindUnits.MPS): |
1354 | - _value *= 0.44704 |
1355 | - _unit = __("m/s", "m/s", _value) |
1356 | - if (self._wind_unit == WindUnits.BEAUFORT): |
1357 | - if _value >= 0.0: |
1358 | - _value = self.get_beaufort_from_mph(_value) |
1359 | - _unit = "" |
1360 | - if (self._wind_unit == WindUnits.KPH): |
1361 | - _value *= 1.609344 |
1362 | - _unit = __("km/h", "km/h", _value) |
1363 | - if (self._wind_unit == WindUnits.KNOTS): |
1364 | - _value *= 0.868976241900648 |
1365 | - _unit = __("knot", "knots", _value) |
1366 | - |
1367 | + if wind_units == "mph": |
1368 | + if (self._wind_unit == WindUnits.MPH): |
1369 | + _unit = __("mph", "mph", _value) |
1370 | + if (self._wind_unit == WindUnits.MPS): |
1371 | + _value *= 0.44704 |
1372 | + _unit = __("m/s", "m/s", _value) |
1373 | + if (self._wind_unit == WindUnits.BEAUFORT): |
1374 | + if _value >= 0.0: |
1375 | + _value = pywapi.wind_beaufort_scale(_value, WindUnits.MPH) |
1376 | + _unit = "" |
1377 | + if (self._wind_unit == WindUnits.KPH): |
1378 | + _value *= 1.609344 |
1379 | + _unit = __("km/h", "km/h", _value) |
1380 | + if (self._wind_unit == WindUnits.KNOTS): |
1381 | + _value *= 0.868976241900648 |
1382 | + _unit = __("knot", "knots", _value) |
1383 | + elif wind_units == "km/h": |
1384 | + if (self._wind_unit == WindUnits.MPH): |
1385 | + _value *= 0.621371 |
1386 | + _unit = __("mph", "mph", _value) |
1387 | + if (self._wind_unit == WindUnits.MPS): |
1388 | + _value *= 0.277778 |
1389 | + _unit = __("m/s", "m/s", _value) |
1390 | + if (self._wind_unit == WindUnits.BEAUFORT): |
1391 | + if _value >= 0.0: |
1392 | + _value = pywapi.wind_beaufort_scale(_value, WindUnits.KPH) |
1393 | + _unit = "" |
1394 | + if (self._wind_unit == WindUnits.KPH): |
1395 | + _unit = __("km/h", "km/h", _value) |
1396 | + if (self._wind_unit == WindUnits.KNOTS): |
1397 | + _value *= 0.539957 |
1398 | + _unit = __("knot", "knots", _value) |
1399 | + else: |
1400 | + log.error("Could not parse '%s' as wind units." % wind_units) |
1401 | + _value = -1.0 |
1402 | + |
1403 | # Join wind_info data in a label |
1404 | - localized_wind_info[len(localized_wind_info)-1] = _unit |
1405 | - localized_wind_info[len(localized_wind_info)-2] = \ |
1406 | + wind_info[len(wind_info)-1] = _unit |
1407 | + wind_info[len(wind_info)-2] = \ |
1408 | NumberFormatter.format_float(_value, 1) |
1409 | if _value < 0.0: |
1410 | - localized_wind_info[1:] = ["", "N\A", ""] |
1411 | - return "%s %s %s %s" % (localized_wind_info[0], localized_wind_info[1], \ |
1412 | - localized_wind_info[2], localized_wind_info[3]) |
1413 | + wind_info[1:] = ["", "N\A", ""] |
1414 | + if _value == 0.0: |
1415 | + wind_info[1:] = [_("Calm"), "", ""] |
1416 | + return "%s %s %s %s" % (wind_info[0], wind_info[1], \ |
1417 | + wind_info[2], wind_info[3]) |
1418 | |
1419 | - # Get sunrise label |
1420 | def get_sunrise_label(self): |
1421 | + """ Get text string for sunrise time """ |
1422 | return "%s: %s" % (_("Sunrise"), TimeFormatter.format_time(self.__sunrise_t)) |
1423 | |
1424 | - # Get sunset label |
1425 | def get_sunset_label(self): |
1426 | + """ Get text string for sunset time """ |
1427 | return "%s: %s" % (_("Sunset"), TimeFormatter.format_time(self.__sunset_t)) |
1428 | |
1429 | |
1430 | - # Additional functions |
1431 | - # Convert wind direction from degrees to localized direction |
1432 | - def get_wind_direction(self, degrees): |
1433 | - try: |
1434 | - degrees = int(degrees) |
1435 | - except ValueError: |
1436 | - return '' |
1437 | - |
1438 | - if degrees < 23 or degrees >= 338: |
1439 | - #Short wind direction - north |
1440 | - return _('N') |
1441 | - elif degrees < 68: |
1442 | - return _('NE') |
1443 | - elif degrees < 113: |
1444 | - return _('E') |
1445 | - elif degrees < 158: |
1446 | - return _('SE') |
1447 | - elif degrees < 203: |
1448 | - return _('S') |
1449 | - elif degrees < 248: |
1450 | - return _('SW') |
1451 | - elif degrees < 293: |
1452 | - return _('W') |
1453 | - elif degrees < 338: |
1454 | - return _('NW') |
1455 | - |
1456 | - # Convert mph to Beufort scale |
1457 | - def get_beaufort_from_mph(self, value): |
1458 | - if value < 1: |
1459 | - return 0 |
1460 | - elif value < 4: |
1461 | - return 1 |
1462 | - elif value < 8: |
1463 | - return 2 |
1464 | - elif value < 13: |
1465 | - return 3 |
1466 | - elif value < 18: |
1467 | - return 4 |
1468 | - elif value < 25: |
1469 | - return 5 |
1470 | - elif value < 27: |
1471 | - return 6 |
1472 | - elif value < 39: |
1473 | - return 7 |
1474 | - elif value < 47: |
1475 | - return 8 |
1476 | - elif value < 89: |
1477 | - return 9 |
1478 | - elif value < 64: |
1479 | - return 10 |
1480 | - elif value < 73: |
1481 | - return 11 |
1482 | - elif value >= 73: |
1483 | - return 12 |
1484 | - |
1485 | class indicator_weather(threading.Thread): |
1486 | """ Indicator class """ |
1487 | last_update_time = None |
1488 | |
1489 | # Settings values |
1490 | # Formats: setting value, object name (for preferences dialog), value assigned (optional) |
1491 | - metric_systems = { 'S': ('si', MetricSystem.SI), |
1492 | - 'I': ('imperial', MetricSystem.IMPERIAL)} |
1493 | + metric_systems = { 'S': ('si', UnitSystem.SI), |
1494 | + 'I': ('imperial', UnitSystem.IMPERIAL)} |
1495 | |
1496 | - weather_sources = { 'G': ('google', WeatherDataSource.GOOGLE), |
1497 | - 'Y': ('yahoo', WeatherDataSource.YAHOO)} |
1498 | + weather_sources = { 'Y': ('yahoo', WeatherDataSource.YAHOO), |
1499 | + 'W': ('weather-com', WeatherDataSource.WEATHER_COM)} |
1500 | |
1501 | notifications = {'N': 'nonotif', |
1502 | 'O': 'notifsevere', |
1503 | @@ -989,17 +1277,31 @@ |
1504 | 'beaufort': ("beaufort", WindUnits.BEAUFORT), |
1505 | 'knots': ("knots", WindUnits.KNOTS)} |
1506 | |
1507 | - # Initializing and reading settings |
1508 | + heat_estimates = {'heatindex': ("heatindex", RelativeFormula.HEATINDEX), |
1509 | + 'humidex': ("humidex", RelativeFormula.HUMIDEX)} |
1510 | + |
1511 | + chill_estimates = {'windchill': ("wctindex", RelativeFormula.WINDCHILL), |
1512 | + 'apparent': ("aatindex", RelativeFormula.APPARENT)} |
1513 | + |
1514 | def __init__(self): |
1515 | + """ Initializing and reading previously-saved settings """ |
1516 | log.debug("Indicator: creating") |
1517 | threading.Thread.__init__(self) |
1518 | self.main_icon = os.path.join |
1519 | - self.winder = appindicator.Indicator ("indicator-weather", "weather-indicator", appindicator.CATEGORY_OTHER) |
1520 | - self.winder.set_status (appindicator.STATUS_ACTIVE) |
1521 | - self.winder.set_attention_icon ("weather-indicator-error") |
1522 | - |
1523 | + self.winder = AppIndicator.Indicator.new("indicator-weather", "weather-indicator", AppIndicator.IndicatorCategory.OTHER) |
1524 | + |
1525 | + self.queue = Queue.Queue() |
1526 | + |
1527 | self.menu_update_lock = threading.Lock() |
1528 | + self.status_update_lock = threading.Lock() |
1529 | + |
1530 | + self.status_update_lock.acquire(True) |
1531 | + self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE) |
1532 | + self.winder.set_attention_icon("weather-indicator-error") |
1533 | + self.status_update_lock.release() |
1534 | + |
1535 | self.refreshed_minutes_ago = -1 |
1536 | + self.places_changed = False |
1537 | monitor_upower(self.on_system_sleep, self.on_system_resume, log) |
1538 | |
1539 | log.debug("Indicator: reading settings") |
1540 | @@ -1010,27 +1312,37 @@ |
1541 | self.unit = self.settings.get_value("unit") |
1542 | self.notif = self.settings.get_value("notif") |
1543 | self.wind = self.settings.get_value("wind") |
1544 | + self.heat = self.settings.get_value("heat") |
1545 | + self.chill = self.settings.get_value("chill") |
1546 | self.source = self.settings.get_value("data_source") |
1547 | self.placechosen = self.settings.get_value("placechosen") |
1548 | self.places = str(self.settings.get_value("places")) |
1549 | self.show_label = self.settings.get_value("show_label") |
1550 | - |
1551 | - log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, wind=%s, placechosen=%s, places=%s" % |
1552 | - (self.rate, self.unit, self.notif, self.wind, self.placechosen, self.places)) |
1553 | + self.show_relative = self.settings.get_value("show_relative") |
1554 | + self.show_wind = self.settings.get_value("show_wind") |
1555 | + self.show_suntimes = self.settings.get_value("show_suntimes") |
1556 | + |
1557 | + log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, " |
1558 | + "wind=%s, placechosen=%s, places=%s, heat=%s, chill=%s" % |
1559 | + (self.rate, self.unit, self.notif, self.wind, |
1560 | + self.placechosen, self.places, self.heat, self.chill)) |
1561 | |
1562 | #Setting default values |
1563 | - self.metric_system = MetricSystem.SI |
1564 | + self.metric_system = UnitSystem.SI |
1565 | self.wind_unit = WindUnits.MPH |
1566 | self.place = None |
1567 | self.menu = None |
1568 | self.condition = None |
1569 | self.icon = None |
1570 | - |
1571 | + self.heat_index = RelativeFormula.HUMIDEX |
1572 | + self.chill_index = RelativeFormula.WINDCHILL |
1573 | + |
1574 | #Parsing settings |
1575 | # Metric system |
1576 | if self.unit in (False, None): |
1577 | default_value = 'S' |
1578 | - log.debug("Indicator: could not parse unit, setting to %s" % default_value) |
1579 | + log.debug("Indicator: could not parse unit, " |
1580 | + "setting to %s" % default_value) |
1581 | self.settings.set_value("unit", default_value) |
1582 | self.unit = default_value |
1583 | self.metric_system = self.metric_systems[self.unit][1] |
1584 | @@ -1038,25 +1350,70 @@ |
1585 | # Notification |
1586 | if self.notif in (False, None): |
1587 | default_value = 'N' |
1588 | - log.debug("Indicator: could not parse notif, setting to %s" % default_value) |
1589 | + log.debug("Indicator: could not parse notif, " |
1590 | + "setting to %s" % default_value) |
1591 | self.settings.set_value("notif", default_value) |
1592 | self.notif = default_value |
1593 | + Notify.init("weather-indicator") |
1594 | |
1595 | # Wind units |
1596 | if self.wind in (False, None): |
1597 | default_value = 'mph' |
1598 | - log.debug("Indicator: could not parse wind, setting to %s" % default_value) |
1599 | + log.debug("Indicator: could not parse wind, " |
1600 | + "setting to %s" % default_value) |
1601 | self.settings.set_value("wind", default_value) |
1602 | self.wind = default_value |
1603 | self.wind_unit = self.wind_systems[self.wind][1] |
1604 | |
1605 | + # Heat estimate formula |
1606 | + if self.heat in (False, None): |
1607 | + default_value = 'humidex' |
1608 | + log.debug("Indicator: could not parse heat, " |
1609 | + "setting to %s" % default_value) |
1610 | + self.settings.set_value("heat", default_value) |
1611 | + self.heat = default_value |
1612 | + self.heat_index = self.heat_estimates[self.heat][1] |
1613 | + |
1614 | + # Chill estimate formula |
1615 | + if self.chill in (False, None): |
1616 | + default_value = 'windchill' |
1617 | + log.debug("Indicator: could not parse chill, " |
1618 | + "setting to %s" % default_value) |
1619 | + self.settings.set_value("chill", default_value) |
1620 | + self.chill = default_value |
1621 | + self.chill_index = self.chill_estimates[self.chill][1] |
1622 | + |
1623 | # Show label in indicator? |
1624 | - self.show_label = True if self.show_label == 1 else False |
1625 | + if self.show_label == 1: |
1626 | + self.show_label = True |
1627 | + self.label_guide = "100 ËšC" # Guide for width of label |
1628 | + else: |
1629 | + self.show_label = False |
1630 | + self.label_guide = " " |
1631 | |
1632 | + # Show relative temperature in dropdown? |
1633 | + if self.show_relative == 1: |
1634 | + self.show_relative = True |
1635 | + else: |
1636 | + self.show_relative = False |
1637 | + |
1638 | + # Show windspeed & direction in dropdown? |
1639 | + if self.show_wind == 1: |
1640 | + self.show_wind = True |
1641 | + else: |
1642 | + self.show_wind = False |
1643 | + |
1644 | + # Show sunrise & sunset times in dropdown? |
1645 | + if self.show_suntimes == 1: |
1646 | + self.show_suntimes = True |
1647 | + else: |
1648 | + self.show_suntimes = False |
1649 | + |
1650 | # Weather source |
1651 | - if self.source in (False, None): |
1652 | + if self.source in (False, None, 'G'): # If set to Google, reset it |
1653 | default_value = 'Y' |
1654 | - log.debug("Indicator: could not parse data source, setting to %s" % default_value) |
1655 | + log.debug("Indicator: could not parse data source, " |
1656 | + "setting to %s" % default_value) |
1657 | self.settings.set_value("data_source", default_value) |
1658 | self.source = default_value |
1659 | self.weather_source = self.weather_sources[self.source][1] |
1660 | @@ -1064,7 +1421,8 @@ |
1661 | # Rate |
1662 | if self.rate in (False, None): |
1663 | default_value = 15 |
1664 | - log.debug("Indicator: could not parse rate, setting to %s" % str(default_value)) |
1665 | + log.debug("Indicator: could not parse rate, " |
1666 | + "setting to %s" % str(default_value)) |
1667 | self.settings.set_value("refresh_rate", default_value) |
1668 | self.rate = default_value |
1669 | |
1670 | @@ -1077,9 +1435,11 @@ |
1671 | self.placechosen = int(self.placechosen) |
1672 | |
1673 | # Places list |
1674 | + self.menu_update_lock.acquire(True) |
1675 | if self.places in (False, None, '', '[]', "['']"): |
1676 | log.debug("Indicator: could not parse places") |
1677 | self.menu_noplace() |
1678 | + self.menu_update_lock.release() |
1679 | else: |
1680 | self.places = eval(self.places) |
1681 | if self.placechosen >= len(self.places): |
1682 | @@ -1089,89 +1449,96 @@ |
1683 | if self.location_details in (False, None, '', '[]', "['']"): |
1684 | log.debug("Indicator: could not parse current location details") |
1685 | self.menu_noplace() |
1686 | + self.menu_update_lock.release() |
1687 | else: |
1688 | self.location_details = eval(self.location_details) |
1689 | - self.menu_normal() |
1690 | +## self.menu_normal() |
1691 | + self.menu_update_lock.release() |
1692 | self.update_weather() |
1693 | |
1694 | - # Set a label of indicator |
1695 | def update_label(self, label): |
1696 | + """ Set the label of the indicator """ |
1697 | if (hasattr(self.winder, 'set_label')): |
1698 | log.debug("Indicator: update_label: setting label to '%s'" % label) |
1699 | self.previous_label_value = label |
1700 | - self.winder.set_label(label) if self.show_label else self.winder.set_label(" ") |
1701 | - self.winder.set_status(appindicator.STATUS_ATTENTION) |
1702 | - self.winder.set_status(appindicator.STATUS_ACTIVE) |
1703 | + self.status_update_lock.acquire(True) |
1704 | + self.winder.set_label(label, self.label_guide) if self.show_label else self.winder.set_label(" ", " ") |
1705 | + self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION) |
1706 | + self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE) |
1707 | + self.status_update_lock.release() |
1708 | |
1709 | - # Show a menu if no places specified |
1710 | def menu_noplace(self): |
1711 | + """ Show a menu if no places specified """ |
1712 | log.debug("Indicator: making a menu for no places") |
1713 | - menu_noplace = gtk.Menu() |
1714 | + menu_noplace = Gtk.Menu() |
1715 | |
1716 | - setup = gtk.MenuItem(_("Set Up Weather...")) |
1717 | + setup = Gtk.MenuItem(_("Set Up Weather...")) |
1718 | setup.connect("activate", self.prefs) |
1719 | setup.show() |
1720 | menu_noplace.append(setup) |
1721 | |
1722 | - quit = gtk.ImageMenuItem(gtk.STOCK_QUIT) |
1723 | + quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None) |
1724 | quit.connect("activate", self.quit) |
1725 | quit.show() |
1726 | menu_noplace.append(quit) |
1727 | |
1728 | + self.status_update_lock.acquire(True) |
1729 | self.winder.set_menu(menu_noplace) |
1730 | self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png")) |
1731 | - self.winder.set_status(appindicator.STATUS_ATTENTION) |
1732 | - self.winder.set_status(appindicator.STATUS_ACTIVE) |
1733 | - |
1734 | - # Show menu with data |
1735 | + self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION) |
1736 | + self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE) |
1737 | + self.status_update_lock.release() |
1738 | + |
1739 | def menu_normal(self): |
1740 | + """ Show a menu with weather and location data """ |
1741 | log.debug("Indicator: menu_normal: filling in a menu for found places") |
1742 | - self.menu = gtk.Menu() |
1743 | + |
1744 | + self.menu = Gtk.Menu() |
1745 | |
1746 | ##City |
1747 | - self.city_show = gtk.MenuItem() |
1748 | + self.city_show = Gtk.MenuItem() |
1749 | self.city_show.set_sensitive(True) |
1750 | self.menu.append(self.city_show) |
1751 | self.city_show.show() |
1752 | |
1753 | ##Condition |
1754 | - self.cond_show = gtk.MenuItem() |
1755 | + self.cond_show = Gtk.MenuItem() |
1756 | self.cond_show.set_sensitive(True) |
1757 | self.cond_show.show() |
1758 | self.menu.append(self.cond_show) |
1759 | |
1760 | ##Temperature |
1761 | - self.temp_show = gtk.MenuItem() |
1762 | + self.temp_show = Gtk.MenuItem() |
1763 | self.temp_show.set_sensitive(True) |
1764 | self.temp_show.show() |
1765 | self.menu.append(self.temp_show) |
1766 | |
1767 | - ##Humidex |
1768 | - self.humidex_show = gtk.MenuItem() |
1769 | - self.humidex_show.set_sensitive(True) |
1770 | - self.humidex_show.show() |
1771 | - self.menu.append(self.humidex_show) |
1772 | + ##Relative Temperature |
1773 | + self.relative_show = Gtk.MenuItem() |
1774 | + self.relative_show.set_sensitive(True) |
1775 | + self.relative_show.show() |
1776 | + self.menu.append(self.relative_show) |
1777 | |
1778 | ##Humidity |
1779 | - self.humid_show = gtk.MenuItem() |
1780 | + self.humid_show = Gtk.MenuItem() |
1781 | self.humid_show.set_sensitive(True) |
1782 | self.humid_show.show() |
1783 | self.menu.append(self.humid_show) |
1784 | |
1785 | ##Wind |
1786 | - self.wind_show = gtk.MenuItem() |
1787 | + self.wind_show = Gtk.MenuItem() |
1788 | self.wind_show.set_sensitive(True) |
1789 | self.wind_show.show() |
1790 | self.menu.append(self.wind_show) |
1791 | |
1792 | ##Sunrise |
1793 | - self.sunrise_show = gtk.MenuItem() |
1794 | + self.sunrise_show = Gtk.MenuItem() |
1795 | self.sunrise_show.set_sensitive(True) |
1796 | self.sunrise_show.show() |
1797 | self.menu.append(self.sunrise_show) |
1798 | |
1799 | ##Sunset |
1800 | - self.sunset_show = gtk.MenuItem() |
1801 | + self.sunset_show = Gtk.MenuItem() |
1802 | self.sunset_show.set_sensitive(True) |
1803 | self.sunset_show.show() |
1804 | self.menu.append(self.sunset_show) |
1805 | @@ -1179,65 +1546,68 @@ |
1806 | ##Cities |
1807 | if len(self.places) != 1: |
1808 | ##Breaker |
1809 | - breaker = gtk.SeparatorMenuItem() |
1810 | + breaker = Gtk.SeparatorMenuItem() |
1811 | breaker.show() |
1812 | self.menu.append(breaker) |
1813 | |
1814 | log.debug("Indicator: menu_normal: adding first location menu item '%s'" % self.places[0][1]) |
1815 | - loco1 = gtk.RadioMenuItem(None, self.places[0][1]) |
1816 | + loco1 = Gtk.RadioMenuItem.new_with_label([], self.places[0][1]) |
1817 | if self.placechosen == 0: |
1818 | loco1.set_active(True) |
1819 | loco1.connect("toggled", self.on_city_changed) |
1820 | loco1.show() |
1821 | self.menu.append(loco1) |
1822 | + group = loco1.get_group() |
1823 | for place in self.places[1:]: |
1824 | log.debug("Indicator: menu_normal: adding location menu item '%s'" % place[1]) |
1825 | - loco = gtk.RadioMenuItem(loco1, place[1]) |
1826 | + loco = Gtk.RadioMenuItem.new_with_label(group, place[1]) |
1827 | if self.places.index(place) == self.placechosen: |
1828 | loco.set_active(True) |
1829 | loco.connect("toggled", self.on_city_changed) |
1830 | loco.show() |
1831 | self.menu.append(loco) |
1832 | + group = loco.get_group() |
1833 | |
1834 | ##Breaker |
1835 | - breaker = gtk.SeparatorMenuItem() |
1836 | + breaker = Gtk.SeparatorMenuItem() |
1837 | breaker.show() |
1838 | self.menu.append(breaker) |
1839 | |
1840 | - self.refresh_show = gtk.MenuItem() |
1841 | + self.refresh_show = Gtk.MenuItem() |
1842 | #label will be set later |
1843 | self.refresh_show.connect("activate", self.update_weather) |
1844 | self.refresh_show.show() |
1845 | self.menu.append(self.refresh_show) |
1846 | |
1847 | - ext_show = gtk.MenuItem(_("Forecast")) |
1848 | + ext_show = Gtk.MenuItem(_("Forecast")) |
1849 | ext_show.connect("activate", self.extforecast) |
1850 | ext_show.show() |
1851 | self.menu.append(ext_show) |
1852 | |
1853 | ##Preferences |
1854 | - prefs_show = gtk.MenuItem(_("Preferences...")) |
1855 | + prefs_show = Gtk.MenuItem(_("Preferences...")) |
1856 | prefs_show.connect("activate", self.prefs) |
1857 | prefs_show.show() |
1858 | self.menu.append(prefs_show) |
1859 | |
1860 | ##About |
1861 | - about_show = gtk.MenuItem(_("About...")) |
1862 | + about_show = Gtk.MenuItem(_("About...")) |
1863 | about_show.connect("activate", self.about) |
1864 | about_show.show() |
1865 | self.menu.append(about_show) |
1866 | |
1867 | ##Quit |
1868 | - quit = gtk.ImageMenuItem(gtk.STOCK_QUIT) |
1869 | + quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None) |
1870 | quit.connect("activate", self.quit) |
1871 | quit.show() |
1872 | self.menu.append(quit) |
1873 | |
1874 | self.winder.set_menu(self.menu) |
1875 | self.update_label(" ") |
1876 | + |
1877 | |
1878 | - # Another city has been selected from radiobutton |
1879 | def on_city_changed(self,widget): |
1880 | + """ Another city has been selected from radiobutton """ |
1881 | if widget.get_active(): |
1882 | for place in self.places: |
1883 | if (place[1] == widget.get_label()): |
1884 | @@ -1251,28 +1621,30 @@ |
1885 | self.location_details = self.settings.get_location_details(self.place[0]) |
1886 | if self.location_details in (False, None, '', '[]', "['']"): |
1887 | log.debug("Indicator: could not parse location details for placechosen='%s'" % self.placechosen) |
1888 | + self.menu_update_lock.acquire(True) |
1889 | self.menu_noplace() |
1890 | + self.menu_update_lock.release() |
1891 | else: |
1892 | self.location_details = eval(self.location_details) |
1893 | self.settings.set_value("placechosen", self.placechosen) |
1894 | self.update_weather(False) |
1895 | |
1896 | def on_system_sleep(self): |
1897 | - """ |
1898 | - Callback from UPower that system suspends/hibernates |
1899 | + """Callback from UPower that system suspends/hibernates |
1900 | + |
1901 | """ |
1902 | # store time |
1903 | self.sleep_time = datetime.datetime.now() |
1904 | log.debug("Indicator: system goes to sleep at %s" % self.sleep_time) |
1905 | # remove gobject timeouts |
1906 | if hasattr(self, "refresh_id"): |
1907 | - gobject.source_remove(self.refresh_id) |
1908 | + GObject.source_remove(self.refresh_id) |
1909 | if hasattr(self, "rate_id"): |
1910 | - gobject.source_remove(self.rate_id) |
1911 | + GObject.source_remove(self.rate_id) |
1912 | |
1913 | def on_system_resume(self): |
1914 | - """ |
1915 | - Callback from UPower that system resumes |
1916 | + """Callback from UPower that system resumes |
1917 | + |
1918 | """ |
1919 | now = datetime.datetime.now() |
1920 | log.debug("Indicator: system resumes at %s" % now) |
1921 | @@ -1291,25 +1663,26 @@ |
1922 | else: |
1923 | self.update_weather() |
1924 | |
1925 | - # Schedule weather update |
1926 | def schedule_weather_update(self, rate_override = None): |
1927 | + """ Schedule the next weather update """ |
1928 | if hasattr(self, "rate_id"): |
1929 | - gobject.source_remove(self.rate_id) |
1930 | + GObject.source_remove(self.rate_id) |
1931 | if rate_override: |
1932 | - self.rate_id = gobject.timeout_add( |
1933 | - int(rate_override) * 60000, self.update_weather) |
1934 | + self.rate_id = GObject.timeout_add( |
1935 | +## int(rate_override) * 60000, self.update_weather) |
1936 | + int(rate_override * 60000), self.update_weather) |
1937 | else: |
1938 | - self.rate_id = gobject.timeout_add( |
1939 | + self.rate_id = GObject.timeout_add( |
1940 | int(self.rate) * 60000, self.update_weather) |
1941 | |
1942 | - # Schedule weather update |
1943 | def schedule_refresh_label_update(self): |
1944 | + """ Schedule the next 'Refresh' label update """ |
1945 | if hasattr(self, "refresh_id"): |
1946 | - gobject.source_remove(self.refresh_id) |
1947 | - self.refresh_id = gobject.timeout_add(60000, self.update_refresh_label) |
1948 | + GObject.source_remove(self.refresh_id) |
1949 | + self.refresh_id = GObject.timeout_add(60000, self.update_refresh_label) |
1950 | |
1951 | - # Update 'Refresh' label with time since last successful data refresh |
1952 | def update_refresh_label(self, reset_minutes = None): |
1953 | + """ Update 'Refresh' label with time since last successful data refresh """ |
1954 | if reset_minutes is not None: |
1955 | self.refreshed_minutes_ago = reset_minutes |
1956 | else: |
1957 | @@ -1319,6 +1692,7 @@ |
1958 | return False |
1959 | |
1960 | def set_refresh_label(self, refreshing=False): |
1961 | + """ Update the 'Refresh' label text """ |
1962 | if refreshing: |
1963 | refresh_label=_("Refreshing, please wait") |
1964 | elif self.refreshed_minutes_ago < 0: |
1965 | @@ -1329,68 +1703,86 @@ |
1966 | refresh_label = "%s (%s)" % (_("Refresh"), _("%d min. ago") % self.refreshed_minutes_ago) |
1967 | self.refresh_show.set_label(refresh_label) |
1968 | |
1969 | - # Load weather data from cache and display its values |
1970 | - def show_cached_weather(self): |
1971 | + def get_cached_weather(self, queue): |
1972 | + """ Load weather data from cache and put it onto the queue """ |
1973 | try: |
1974 | - self.menu_update_lock.acquire(True) |
1975 | + log.debug("Indicator: show_cached_weather: setting " |
1976 | + "previous_condition to None") |
1977 | self.previous_condition = None |
1978 | cached_weather = self.settings.get_weather(self.places[self.placechosen][0]) |
1979 | if cached_weather is not None: |
1980 | cached_weather = eval(cached_weather) |
1981 | - log.debug("Indicator: loading weather from cache for %s" % self.places[self.placechosen]) |
1982 | - self.menu_normal() |
1983 | - self.set_refresh_label(True) |
1984 | - self.icon = cached_weather['icon'] |
1985 | - if (self.icon == False): |
1986 | - self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png")) |
1987 | - else: |
1988 | - self.winder.set_icon(self.icon) |
1989 | + log.debug("Indicator: loading weather from cache " |
1990 | + "for %s" % self.places[self.placechosen]) |
1991 | + # Put the cached weather onto to the Queue |
1992 | + queue.put({'cached_weather': cached_weather}) |
1993 | + except Exception, e: |
1994 | + log.error(e) |
1995 | + log.debug(traceback.format_exc(e)) |
1996 | |
1997 | - self.city_show.set_label(self.places[self.placechosen][1]) |
1998 | - self.previous_condition = cached_weather['condition'] |
1999 | - self.cond_show.set_label(cached_weather['condition']) |
2000 | - self.temp_show.set_label(cached_weather['temper']) |
2001 | - if cached_weather['humidex'] != None: |
2002 | - self.humidex_show.set_label(cached_weather['humidex']) |
2003 | - else: |
2004 | - self.humidex_show.destroy() |
2005 | - self.humid_show.set_label(cached_weather['humidity']) |
2006 | + def show_cached_weather(self, cached_weather): |
2007 | + """ Update the indicator icon, label and menu with cached weather data """ |
2008 | + try: |
2009 | + self.menu_normal() |
2010 | + self.set_refresh_label(True) |
2011 | + self.icon = cached_weather['icon'] |
2012 | + if (self.icon == False): |
2013 | + self.winder.set_icon( |
2014 | + os.path.join(PROJECT_ROOT_DIRECTORY, |
2015 | + "share/indicator-weather/media/icon_unknown_condition.png") |
2016 | + ) |
2017 | + else: |
2018 | + self.winder.set_icon(self.icon) |
2019 | + |
2020 | + self.city_show.set_label(self.places[self.placechosen][1]) |
2021 | + self.previous_condition = cached_weather['condition'] |
2022 | + self.cond_show.set_label(cached_weather['condition']) |
2023 | + self.temp_show.set_label(cached_weather['temper']) |
2024 | + if (self.show_relative and cached_weather['feelslike'] != None): |
2025 | + self.relative_show.set_visible(True) |
2026 | + self.relative_show.set_label(cached_weather['feelslike']) |
2027 | + else: |
2028 | + self.relative_show.set_visible(False) |
2029 | + self.humid_show.set_label(cached_weather['humidity']) |
2030 | + if self.show_wind: |
2031 | + self.wind_show.set_visible(True) |
2032 | self.wind_show.set_label(cached_weather['wind']) |
2033 | + else: |
2034 | + self.wind_show.set_visible(False) |
2035 | + if self.show_suntimes: |
2036 | + self.sunrise_show.set_visible(True) |
2037 | self.sunrise_show.set_label(cached_weather['sunrise']) |
2038 | + self.sunset_show.set_visible(True) |
2039 | self.sunset_show.set_label(cached_weather['sunset']) |
2040 | - self.update_label(cached_weather['label']) |
2041 | - self.winder.set_status(appindicator.STATUS_ATTENTION) |
2042 | - self.winder.set_status(appindicator.STATUS_ACTIVE) |
2043 | + else: |
2044 | + self.sunrise_show.set_visible(False) |
2045 | + self.sunset_show.set_visible(False) |
2046 | |
2047 | + self.update_label(cached_weather['label']) |
2048 | + |
2049 | except Exception, e: |
2050 | log.error(e) |
2051 | log.debug(traceback.format_exc(e)) |
2052 | |
2053 | - self.menu_update_lock.release() |
2054 | - |
2055 | - # Get fresh weather data |
2056 | - def get_new_weather_data(self, notif = True): |
2057 | - |
2058 | + def get_new_weather_data(self, notif, queue): |
2059 | + """ Get fresh weather data from source and put it onto the queue """ |
2060 | # get weather and catch any exception |
2061 | weather = None |
2062 | try: |
2063 | weather = self.get_weather() |
2064 | - |
2065 | except urllib2.URLError, e: |
2066 | weather = None |
2067 | log.error("Indicator: networking error: %s" % e) |
2068 | - |
2069 | except Exception, e: |
2070 | weather = None |
2071 | log.error(e) |
2072 | log.debug(traceback.format_exc(e)) |
2073 | + # Put the new weather data onto the Queue |
2074 | + queue.put({'weather': weather}) |
2075 | |
2076 | + def show_new_weather_data(self, weather): |
2077 | + """ Update the indicator icon, label and menu with new weather data """ |
2078 | try: |
2079 | - # wait until cacher finishes |
2080 | - log.debug("Indicator: updateWeather: waiting for 'Cacher' thread to terminate") |
2081 | - self.menu_update_lock.acquire(True) |
2082 | - self.menu_update_lock.release() |
2083 | - |
2084 | if weather is None: |
2085 | # remove the "Refreshing" status |
2086 | self.set_refresh_label() |
2087 | @@ -1399,35 +1791,59 @@ |
2088 | # Repeat an attempt in one minute |
2089 | self.schedule_weather_update(1) |
2090 | return |
2091 | - |
2092 | + |
2093 | # Fill in menu with data |
2094 | log.debug("Indicator: updateWeather: got condition '%s', icon '%s'" % (self.condition, self.icon)) |
2095 | self.condition = weather.get_condition_label() |
2096 | self.icon = weather.get_icon_name() |
2097 | - log.debug("Indicator: fill in menu with params: city='%s', temp='%s', humid='%s', wind='%s', sunrise='%s', sunset='%s', puretemp=%s" % (self.places[self.placechosen][1], weather.get_temperature_label(), weather.get_humidity_label(), weather.get_wind_label(), weather.get_sunrise_label(), weather.get_sunset_label(), weather.get_temperature())) |
2098 | - |
2099 | + log.debug("Indicator: fill in menu with params: " \ |
2100 | + "city='%s', temp='%s', humid='%s', " \ |
2101 | + "wind='%s', sunrise='%s', sunset='%s', " \ |
2102 | + "puretemp=%s" % (self.places[self.placechosen][1], |
2103 | + weather.get_temperature_label(), |
2104 | + weather.get_humidity_label(), |
2105 | + weather.get_wind_label(), |
2106 | + weather.get_sunrise_label(), |
2107 | + weather.get_sunset_label(), |
2108 | + weather.get_temperature_string())) |
2109 | self.menu_normal() |
2110 | self.update_refresh_label(0) |
2111 | self.city_show.set_label(self.places[self.placechosen][1]) |
2112 | self.cond_show.set_label(self.condition) |
2113 | self.temp_show.set_label(weather.get_temperature_label()) |
2114 | - if (weather.get_humidex_label() != None): |
2115 | - self.humidex_show.set_label(weather.get_humidex_label()) |
2116 | + _relative_label = weather.get_relative_label() |
2117 | + if (self.show_relative and "---" not in _relative_label): |
2118 | + self.relative_show.set_visible(True) |
2119 | + self.relative_show.set_label(_relative_label) |
2120 | else: |
2121 | - self.humidex_show.destroy() |
2122 | + self.relative_show.set_visible(False) |
2123 | self.humid_show.set_label(weather.get_humidity_label()) |
2124 | - self.wind_show.set_label(weather.get_wind_label()) |
2125 | - self.sunrise_show.set_label(weather.get_sunrise_label()) |
2126 | - self.sunset_show.set_label(weather.get_sunset_label()) |
2127 | - |
2128 | - # Saving cached data, unless correct icon is supplied |
2129 | + if self.show_wind: |
2130 | + self.wind_show.set_visible(True) |
2131 | + self.wind_show.set_label(weather.get_wind_label()) |
2132 | + else: |
2133 | + self.wind_show.set_visible(False) |
2134 | + if self.show_suntimes: |
2135 | + self.sunrise_show.set_visible(True) |
2136 | + self.sunrise_show.set_label(weather.get_sunrise_label()) |
2137 | + self.sunset_show.set_visible(True) |
2138 | + self.sunset_show.set_label(weather.get_sunset_label()) |
2139 | + else: |
2140 | + self.sunrise_show.set_visible(False) |
2141 | + self.sunset_show.set_visible(False) |
2142 | + |
2143 | + # Save cached data if correct icon is supplied |
2144 | if (self.icon == False): |
2145 | - self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png")) |
2146 | + self.winder.set_icon( |
2147 | + os.path.join(PROJECT_ROOT_DIRECTORY, |
2148 | + "share/indicator-weather/media/icon_unknown_condition.png") |
2149 | + ) |
2150 | else: |
2151 | self.winder.set_icon(self.icon) |
2152 | self.settings.save_weather(weather, self.places[self.placechosen][0]) |
2153 | - self.update_label(weather.get_temperature(needs_rounding=True)) |
2154 | - |
2155 | + # Update the indicator label |
2156 | + self.update_label(weather.get_temperature_string()) |
2157 | + |
2158 | # Notify user, if notifications are enabled |
2159 | if self.condition != self.previous_condition and self.notif == 'U': |
2160 | # Weather condition has changed |
2161 | @@ -1437,106 +1853,135 @@ |
2162 | # Severe weather condition notification |
2163 | log.debug("Indicator: updateWeather: severe condition notification") |
2164 | self.notify(self.condition, self.icon, severe=True) |
2165 | - |
2166 | + # Save the current condition to track changes for notifications |
2167 | self.previous_condition = self.condition |
2168 | - |
2169 | except Exception, e: |
2170 | log.error(e) |
2171 | log.debug(traceback.format_exc(e)) |
2172 | |
2173 | + # Schedule the next weather fetch |
2174 | self.schedule_weather_update() |
2175 | - |
2176 | - # Update weather |
2177 | + |
2178 | def update_weather(self, notif=True, widget=None): |
2179 | - log.debug("Indicator: updateWeather: updating weather for %s" % self.places[self.placechosen]) |
2180 | + """ Update the displayed weather data from cache and fetch new data """ |
2181 | + log.debug("Indicator: updateWeather: updating weather for %s" % |
2182 | + self.places[self.placechosen]) |
2183 | # First, display cached data |
2184 | - threading.Thread(target=self.show_cached_weather, name='Cache').start() |
2185 | + cache_thread = threading.Thread(target=self.get_cached_weather, |
2186 | + name='Cache', args=(self.queue,)) |
2187 | + cache_thread.start() |
2188 | + |
2189 | # Then, start a new thread with real data pickup |
2190 | - threading.Thread(target=self.get_new_weather_data, name='Fetcher').start() |
2191 | - |
2192 | - # Get current weather for selected location |
2193 | + fetcher_thread = threading.Thread(target=self.get_new_weather_data, |
2194 | + name='Fetcher', |
2195 | + args=(self.notif, self.queue)) |
2196 | + fetcher_thread.start() |
2197 | + |
2198 | + # Update the menu with the cached weather |
2199 | + cache_thread.join() |
2200 | + cached_weather = self.queue.get() |
2201 | + if cached_weather is not None and cached_weather.has_key('cached_weather'): |
2202 | + self.show_cached_weather(cached_weather['cached_weather']) |
2203 | + |
2204 | + # Update the menu with the new weather |
2205 | + fetcher_thread.join() |
2206 | + weather = self.queue.get() |
2207 | + if weather is not None and weather.has_key('weather'): |
2208 | + self.show_new_weather_data(weather['weather']) |
2209 | + |
2210 | def get_weather(self): |
2211 | - log.debug("Indicator: getWeather for location '%s'" % self.location_details['full name']) |
2212 | - self.current_location = Location(self.metric_system, self.wind_unit, self.location_details) |
2213 | + """ Get current weather for selected location """ |
2214 | + log.debug("Indicator: getWeather for location '%s'" % |
2215 | + self.location_details['full name']) |
2216 | + self.current_location = Location(self.metric_system, self.wind_unit, |
2217 | + self.heat_index, self.chill_index, |
2218 | + self.location_details) |
2219 | log.debug("Indicator: getWeather: updating weather report") |
2220 | self.current_location.update_weather_data(self.weather_source) |
2221 | return self.current_location.weather |
2222 | |
2223 | - # Show notification to user |
2224 | - def notify(self,conditon,icon,severe=False): |
2225 | - log.debug("Indicator: Notify on weather condition, severe=%s, condition=%s, icon=%s" % (severe, self.condition, icon)) |
2226 | + def notify(self,condition,icon,severe=False): |
2227 | + """ Show notification to user according to preferences """ |
2228 | + log.debug("Indicator: Notify on weather condition, severe=%s, " |
2229 | + "condition=%s, icon=%s" % (severe, condition, icon)) |
2230 | if severe: |
2231 | - n = pynotify.Notification (_("Severe weather alert"), |
2232 | - self.condition, |
2233 | - icon) |
2234 | + n = Notify.Notification (_("Severe weather alert"), |
2235 | + condition, |
2236 | + icon) |
2237 | else: |
2238 | - n = pynotify.Notification (self.condition, "", icon) |
2239 | + n = Notify.Notification (condition, "", icon) |
2240 | n.show () |
2241 | |
2242 | - # Menu callbacks |
2243 | - # Open Preferences dialog |
2244 | def prefs(self, widget): |
2245 | + """ Menu callback to open Preferences dialog """ |
2246 | log.debug("Indicator: open Preferences") |
2247 | if ((not hasattr(self, 'prefswindow')) or (not self.prefswindow.get_visible())): |
2248 | self.prefswindow = PreferencesDialog() |
2249 | self.prefswindow.show() |
2250 | - |
2251 | + |
2252 | def about(self, widget): |
2253 | + """ Menu callback to open About dialog """ |
2254 | log.debug("Indicator: open About dialog") |
2255 | - self.aboutdialog = gtk.AboutDialog() |
2256 | + self.aboutdialog = Gtk.AboutDialog() |
2257 | self.aboutdialog.set_name(_("Weather Indicator")) |
2258 | self.aboutdialog.set_version(VERSION) |
2259 | |
2260 | - ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/doc/indicator-weather/AUTHORS"), "r") |
2261 | + ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, |
2262 | + "share/doc/indicator-weather/AUTHORS"), "r") |
2263 | self.aboutdialog.set_copyright(ifile.read().replace('\x0c', '')) |
2264 | ifile.close() |
2265 | |
2266 | - ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/common-licenses/GPL-3"), "r") |
2267 | + ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, |
2268 | + "share/common-licenses/GPL-3"), "r") |
2269 | self.aboutdialog.set_license(ifile.read().replace('\x0c', '')) |
2270 | ifile.close() |
2271 | |
2272 | self.aboutdialog.set_website("https://launchpad.net/weather-indicator") |
2273 | self.aboutdialog.set_translator_credits(_("translator-credits")) |
2274 | - logo_path = os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png") |
2275 | - self.aboutdialog.set_logo(gtk.gdk.pixbuf_new_from_file(logo_path)) |
2276 | - |
2277 | + logo_path = os.path.join(PROJECT_ROOT_DIRECTORY, |
2278 | + "share/indicator-weather/media/icon.png") |
2279 | + self.aboutdialog.set_logo(GdkPixbuf.Pixbuf.new_from_file(logo_path)) |
2280 | |
2281 | self.aboutdialog.connect("response", self.about_close) |
2282 | self.aboutdialog.show() |
2283 | |
2284 | def about_close(self, widget, event=None): |
2285 | + """ Menu callback to close About dialog """ |
2286 | log.debug("Indicator: closing About dialog") |
2287 | self.aboutdialog.destroy() |
2288 | |
2289 | - # Open Extended forecast window |
2290 | def extforecast(self, widget): |
2291 | + """ Menu callback to open Extended forecast window """ |
2292 | log.debug("Indicator: open Forecast") |
2293 | - if ((not hasattr(self, 'forecastwd')) or (not self.forecastwd.get_visible())): |
2294 | - self.forecastwd = ExtendedForecast() |
2295 | - self.forecastwd.show() |
2296 | + if ((not hasattr(self, 'forecastwd')) or \ |
2297 | + (not self.forecastwd.get_visible())): |
2298 | + self.forecastwd = ExtendedForecast() |
2299 | + self.forecastwd.show() |
2300 | |
2301 | - # Quit the applet |
2302 | def quit(self, widget, data=None): |
2303 | + """ Menu callback to quit the indicator applet """ |
2304 | log.debug("Indicator: Quitting") |
2305 | - gtk.main_quit() |
2306 | + Gtk.main_quit() |
2307 | |
2308 | -class PreferencesDialog(gtk.Dialog): |
2309 | +class PreferencesDialog(Gtk.Dialog): |
2310 | """ Class for preferences dialog """ |
2311 | __gtype_name__ = "PreferencesDialog" |
2312 | |
2313 | - # Creating a new preferences dialog |
2314 | def __new__(cls): |
2315 | + """ Creating a new preferences dialog """ |
2316 | log.debug("Preferences: creating") |
2317 | builder = get_builder('PreferencesDialog') |
2318 | new_object = builder.get_object("preferences_dialog") |
2319 | new_object.finish_initializing(builder) |
2320 | return new_object |
2321 | |
2322 | - # Fill in preferences dialog with currect data |
2323 | def finish_initializing(self, builder): |
2324 | + """ Fill in preferences dialog with correct data """ |
2325 | log.debug("Preferences: finishing initialization") |
2326 | - log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, rate=%s, source=%s" % |
2327 | - (wi.unit, wi.notif, wi.wind, wi.rate, wi.source)) |
2328 | + log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, " |
2329 | + "rate=%s, source=%s, heat=%s, chill=%s" % |
2330 | + (wi.unit, wi.notif, wi.wind, wi.rate, wi.source, |
2331 | + wi.heat, wi.chill)) |
2332 | self.builder = builder |
2333 | |
2334 | # Set correct wind_unit using dictionary of wind value and object name |
2335 | @@ -1547,11 +1992,16 @@ |
2336 | self.builder.get_object('show_label').set_active(wi.show_label) |
2337 | self.builder.get_object('show_label').set_visible(hasattr(wi.winder, 'set_label')) |
2338 | self.builder.get_object('rate').set_value(float(wi.rate)) |
2339 | + self.builder.get_object(wi.heat_estimates[wi.heat][0]).set_active(True) |
2340 | + self.builder.get_object(wi.chill_estimates[wi.chill][0]).set_active(True) |
2341 | + self.builder.get_object('show_relative').set_active(wi.show_relative) |
2342 | + self.builder.get_object('show_wind').set_active(wi.show_wind) |
2343 | + self.builder.get_object('show_suntimes').set_active(wi.show_suntimes) |
2344 | |
2345 | log.debug("Preferences: Loading places") |
2346 | if wi.places != None: |
2347 | for place in wi.places: |
2348 | - if len(place)>1: |
2349 | + if len(place)>1 and place[0] is not None: |
2350 | log.debug("Preferences: Places: got (%s, %s)" % (place[1], place[0])) |
2351 | newplace = list() |
2352 | newplace.append(place[1]) |
2353 | @@ -1563,32 +2013,45 @@ |
2354 | |
2355 | self.builder.connect_signals(self) |
2356 | |
2357 | - # 'Remove' clicked - remove location from list |
2358 | - #TODO: Update settings object |
2359 | def on_remove_location(self, widget): |
2360 | + """ 'Remove' clicked - remove location from list """ |
2361 | selection = self.builder.get_object('location_list').get_selection() |
2362 | model, iter = selection.get_selected() |
2363 | if iter != None: |
2364 | - log.debug("Preferences: Removing location %s (code %s)" % (model[iter][0], model[iter][1])) |
2365 | + log.debug("Preferences: Removing location %s (code %s)" % |
2366 | + (model[iter][0], model[iter][1])) |
2367 | + newplaces = [] |
2368 | + for place in wi.places: |
2369 | + if place[1] != model[iter][0] and \ |
2370 | + place[0] != model[iter][1]: |
2371 | + newplaces.append(place) |
2372 | + if newplaces != wi.places: |
2373 | + wi.places = newplaces |
2374 | + log.debug("Preferences: update settings object") |
2375 | + wi.settings.set_value("places", str(wi.places)) |
2376 | + |
2377 | model.remove(iter) |
2378 | - |
2379 | + wi.places_changed = True |
2380 | + |
2381 | if (self.builder.get_object('citieslist').get_iter_first() == None): |
2382 | self.builder.get_object('ok_button').set_sensitive(False) |
2383 | - |
2384 | - # 'Add' clicked - create a new Assistant |
2385 | + |
2386 | def on_add_location(self, widget): |
2387 | + """ 'Add' clicked - create a new Assistant """ |
2388 | log.debug("Preferences: Add location clicked") |
2389 | if ((not hasattr(self, 'assistant')) or (not self.assistant.get_visible())): |
2390 | self.assistant = Assistant() |
2391 | self.assistant.show() |
2392 | |
2393 | - # 'OK' clicked - save settings |
2394 | def ok(self, widget, data=None): |
2395 | + """ 'OK' clicked - save settings """ |
2396 | + #TODO: rewrite this - for better response to user clicks, |
2397 | + # it should first copy values to a queue, then destroy the window |
2398 | log.debug("Preferences: Saving settings") |
2399 | need_to_update_weather = False |
2400 | need_to_update_indicator = False |
2401 | |
2402 | - #Show label near icon |
2403 | + # Show label near icon |
2404 | new_show_label = self.builder.get_object('show_label').get_active() |
2405 | if (wi.show_label != new_show_label): |
2406 | wi.show_label = new_show_label |
2407 | @@ -1597,6 +2060,30 @@ |
2408 | need_to_update_indicator = True |
2409 | log.debug("Preferences: Show Label changed to '%s'" % wi.show_label) |
2410 | |
2411 | + # Show relative temperature |
2412 | + new_show_relative = self.builder.get_object('show_relative').get_active() |
2413 | + if (wi.show_relative != new_show_relative): |
2414 | + wi.show_relative = new_show_relative |
2415 | + wi.settings.set_value("show_relative", new_show_relative) |
2416 | + need_to_update_weather = True |
2417 | + log.debug("PreferencesDialog: Show Relative Temp changed to %s" % wi.show_relative) |
2418 | + |
2419 | + # Show wind speed & direction |
2420 | + new_show_wind = self.builder.get_object('show_wind').get_active() |
2421 | + if (wi.show_wind != new_show_wind): |
2422 | + wi.show_wind = new_show_wind |
2423 | + wi.settings.set_value("show_relative", new_show_wind) |
2424 | + need_to_update_weather = True |
2425 | + log.debug("PreferencesDialog: Show Wind Data changed to %s" % wi.show_wind) |
2426 | + |
2427 | + # Show sunrise & sunset times |
2428 | + new_show_suntimes = self.builder.get_object('show_suntimes').get_active() |
2429 | + if (wi.show_suntimes != new_show_suntimes): |
2430 | + wi.show_suntimes = new_show_suntimes |
2431 | + wi.settings.set_value("show_suntimes", new_show_suntimes) |
2432 | + need_to_update_weather = True |
2433 | + log.debug("PreferencesDialog: Show Sunrise/Sunset Times changed to %s" % wi.show_suntimes) |
2434 | + |
2435 | # Metric systems |
2436 | for k in wi.metric_systems.keys(): |
2437 | if self.builder.get_object(wi.metric_systems[k][0]).get_active(): |
2438 | @@ -1634,6 +2121,32 @@ |
2439 | need_to_update_weather = True |
2440 | log.debug("Preferences: Wind Unit changed to '%s'" % wi.wind) |
2441 | |
2442 | + # Heat estimate formula |
2443 | + for k in wi.heat_estimates.keys(): |
2444 | + if self.builder.get_object(wi.heat_estimates[k][0]).get_active(): |
2445 | + new_heat_index = k |
2446 | + new_heat_estimate = wi.heat_estimates[k][1] |
2447 | + |
2448 | + if (wi.heat != new_heat_index): |
2449 | + wi.heat = new_heat_index |
2450 | + wi.heat_index = new_heat_estimate |
2451 | + wi.settings.set_value("heat", wi.heat) |
2452 | + need_to_update_weather = True |
2453 | + log.debug("Preferences: Heat Estimate changed to '%s'" % wi.heat) |
2454 | + |
2455 | + # Chill estimate formula |
2456 | + for k in wi.chill_estimates.keys(): |
2457 | + if self.builder.get_object(wi.chill_estimates[k][0]).get_active(): |
2458 | + new_chill_index = k |
2459 | + new_chill_estimate = wi.chill_estimates[k][1] |
2460 | + |
2461 | + if (wi.chill != new_chill_index): |
2462 | + wi.chill = new_chill_index |
2463 | + wi.chill_index = new_chill_estimate |
2464 | + wi.settings.set_value("chill", wi.chill) |
2465 | + need_to_update_weather = True |
2466 | + log.debug("Preferences: Chill Estimate changed to '%s'" % wi.chill) |
2467 | + |
2468 | # Weather source |
2469 | for k in wi.weather_sources.keys(): |
2470 | if self.builder.get_object(wi.weather_sources[k][0]).get_active(): |
2471 | @@ -1655,18 +2168,19 @@ |
2472 | wi.schedule_weather_update() |
2473 | |
2474 | # Get places from location list |
2475 | - newplaces = list() |
2476 | - item = self.builder.get_object('citieslist').get_iter_first() |
2477 | - while (item != None): |
2478 | - newplace = list() |
2479 | - newplace.append(self.builder.get_object('citieslist').get_value (item, 1)) |
2480 | - newplace.append(self.builder.get_object('citieslist').get_value (item, 0)) |
2481 | - newplaces.append(newplace) |
2482 | - item = self.builder.get_object('citieslist').iter_next(item) |
2483 | +## newplaces = list() |
2484 | +## item = self.builder.get_object('citieslist').get_iter_first() |
2485 | +## while (item != None): |
2486 | +## newplace = list() |
2487 | +## newplace.append(self.builder.get_object('citieslist').get_value (item, 1)) |
2488 | +## newplace.append(self.builder.get_object('citieslist').get_value (item, 0)) |
2489 | +## newplaces.append(newplace) |
2490 | +## item = self.builder.get_object('citieslist').iter_next(item) |
2491 | |
2492 | # If places have changed - update weather data |
2493 | - if newplaces != wi.places: |
2494 | - wi.places = newplaces |
2495 | +## if newplaces != wi.places: |
2496 | +## wi.places = newplaces |
2497 | + if wi.places_changed: |
2498 | log.debug("Preferences: Places changed to '%s'" % str(wi.places)) |
2499 | wi.settings.set_value("places", str(wi.places)) |
2500 | if (type(wi.place) != None) and (wi.place in wi.places): |
2501 | @@ -1677,60 +2191,68 @@ |
2502 | log.debug("Preferences: Place Chosen changed to '%s'" % wi.placechosen) |
2503 | wi.settings.set_value("placechosen", wi.placechosen) |
2504 | wi.location_details = eval(wi.settings.get_location_details(wi.place[0])) |
2505 | - wi.menu_normal() |
2506 | - wi.set_refresh_label() |
2507 | +## wi.menu_normal() |
2508 | +## wi.set_refresh_label() |
2509 | need_to_update_weather = True |
2510 | + wi.places_changed = False |
2511 | |
2512 | if need_to_update_weather: |
2513 | - wi.update_weather(False) |
2514 | +#### wi.update_weather(False) |
2515 | + wi.schedule_weather_update(0.003) |
2516 | |
2517 | if need_to_update_indicator: |
2518 | wi.update_label(wi.previous_label_value) |
2519 | |
2520 | self.destroy() |
2521 | |
2522 | - # 'Cancel' click - forget all changes |
2523 | def cancel(self, widget, data=None): |
2524 | + """ 'Cancel' clicked - forget all changes """ |
2525 | log.debug("Preferences: Cancelling") |
2526 | self.destroy() |
2527 | |
2528 | -class ExtendedForecast(gtk.Window): |
2529 | +class ExtendedForecast(Gtk.Window): |
2530 | """ Class for forecast window """ |
2531 | __gtype_name__ = "ExtendedForecast" |
2532 | |
2533 | - # Create forecast |
2534 | def __new__(cls): |
2535 | + """ Create forecast """ |
2536 | log.debug("ExtendedForecast: creating") |
2537 | builder = get_builder('ExtendedForecast') |
2538 | new_object = builder.get_object("extended_forecast") |
2539 | new_object.finish_initializing(builder) |
2540 | return new_object |
2541 | |
2542 | - # Fill in forecast parameters |
2543 | def finish_initializing(self, builder): |
2544 | + """ Fill in forecast parameters """ |
2545 | log.debug("ExtendedForecast: finishing initialization") |
2546 | self.builder = builder |
2547 | self.builder.connect_signals(self) |
2548 | |
2549 | # Get forecast data using Forecast object |
2550 | - log.debug("ExtendedForecast: chosen place: %s (code %s)" % (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0])) |
2551 | - self.builder.get_object('extended_forecast').set_title("%s %s" % (_('Weather Forecast for '), wi.places[wi.placechosen][1])) |
2552 | + log.debug("ExtendedForecast: chosen place: %s (code %s)" % |
2553 | + (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0])) |
2554 | + self.builder.get_object('extended_forecast').set_title("%s %s" % |
2555 | + (_('Weather Forecast for'), wi.places[wi.placechosen][1])) |
2556 | log.debug("ExtendedForecast: getting forecast data") |
2557 | - forecast = Forecast(wi.metric_system, wi.current_location.location_details['latitude'], wi.current_location.location_details['longitude'], locale_name) |
2558 | + forecast = Forecast(wi.metric_system, |
2559 | + wi.current_location.location_details['yahoo id'], |
2560 | + locale_name) |
2561 | forecast.prepare_forecast_data() |
2562 | if forecast.error_message != None: |
2563 | #Error occurred while getting forecast data |
2564 | - self.builder.get_object('connection_error').set_text("%s" % forecast.error_message) |
2565 | + self.builder.get_object('connection_error').set_text("%s" % |
2566 | + forecast.error_message) |
2567 | self.builder.get_object('connection_error').set_visible(True) |
2568 | self.builder.get_object('hbox1').set_visible(False) |
2569 | else: |
2570 | daysofweek = forecast.get_forecast_daysofweek() |
2571 | forecast_data = forecast.get_forecast_data() |
2572 | if forecast_data == None: |
2573 | - # Forecast data unavailable - hide elements and show 'connection_error' label |
2574 | - self.builder.get_object('connection_error').set_visible(True); |
2575 | - self.builder.get_object('hbox1').set_visible(False); |
2576 | - self.builder.get_object('hseparator1').set_visible(False); |
2577 | + # Forecast data unavailable - hide elements |
2578 | + # and show 'connection_error' label |
2579 | + self.builder.get_object('connection_error').set_visible(True) |
2580 | + self.builder.get_object('hbox1').set_visible(False) |
2581 | + self.builder.get_object('hseparator1').set_visible(False) |
2582 | return |
2583 | (highdata, lowdata) = forecast_data |
2584 | icons = forecast.get_forecast_icons() |
2585 | @@ -1738,68 +2260,81 @@ |
2586 | |
2587 | log.debug("ExtendedForecast: parsing forecast data") |
2588 | # Create labels for each weekday |
2589 | - self.builder.get_object('day1lbl').set_label('<big>%s</big>' % daysofweek[0].capitalize()) |
2590 | - self.builder.get_object('day2lbl').set_label('<big>%s</big>' % daysofweek[1].capitalize()) |
2591 | - self.builder.get_object('day3lbl').set_label('<big>%s</big>' % daysofweek[2].capitalize()) |
2592 | - self.builder.get_object('day4lbl').set_label('<big>%s</big>' % daysofweek[3].capitalize()) |
2593 | - |
2594 | + for i in xrange(1,5): |
2595 | + try: |
2596 | + lbl_name = 'day%slbl' % (i) |
2597 | + self.builder.get_object(lbl_name).set_label('<big>%s</big>' % |
2598 | + daysofweek[i].capitalize()) |
2599 | + except IndexError: |
2600 | + log.error("ExtendedForecast: Yahoo didn't return " |
2601 | + "forecast for %s days" % i) |
2602 | + log.error(forecast.forecast) |
2603 | + |
2604 | # Fill in icons |
2605 | for i in xrange(1,5): |
2606 | - # Get icon name from dictionary in Weather object for Google icons |
2607 | + # Get icon name from dictionary in Weather object |
2608 | + # for Yahoo condition codes |
2609 | try: |
2610 | - conds = Weather._GoogleConditions.get(icons[i-1]) |
2611 | + conds = Weather._YahooConditions.get(icons[i]) |
2612 | if conds != None: |
2613 | - google_icon = conds[0] |
2614 | + yahoo_icon = conds[0] |
2615 | else: |
2616 | - log.error("ExtendedForecast: unknown Google weather condition '%s'" % icons[i-1]) |
2617 | + log.error("ExtendedForecast: unknown Yahoo weather " |
2618 | + "condition code '%s'" % icons[i]) |
2619 | log.error(forecast.forecast) |
2620 | - google_icon = 'weather-indicator-unknown' |
2621 | - self.builder.get_object('day%simage' % str(i)).set_from_icon_name(google_icon,gtk.ICON_SIZE_BUTTON) |
2622 | + yahoo_icon = 'weather-indicator-unknown' |
2623 | + self.builder.get_object('day%simage' % |
2624 | + str(i)).set_from_icon_name(yahoo_icon, |
2625 | + Gtk.IconSize.BUTTON) |
2626 | except IndexError: |
2627 | - log.error("ExtendedForecast: Google didn't return condition for %s days" % i-1) |
2628 | - log.error(forecast.forecast) |
2629 | + log.error("ExtendedForecast: Yahoo didn't return " |
2630 | + "condition for %s days" % i) |
2631 | + log.error(forecast.forecast) |
2632 | |
2633 | # Fill in condition labels |
2634 | for i in xrange(1,5): |
2635 | - if conditions[i-1] != '': |
2636 | - condition = conditions[i-1] |
2637 | + if conditions[i] != '': |
2638 | + condition = conditions[i] |
2639 | else: |
2640 | condition = _("Unknown condition") |
2641 | - self.builder.get_object('day%scond' % str(i)).set_label(condition) |
2642 | + self.builder.get_object('day%scond' % |
2643 | + str(i)).set_label(condition) |
2644 | |
2645 | # Fill in High and Low temperatures |
2646 | - if wi.metric_system == MetricSystem.SI: |
2647 | - tempunit = '°C' |
2648 | + if wi.metric_system == UnitSystem.SI: |
2649 | + tempunit = u"°C" |
2650 | else: |
2651 | - tempunit = '°F' |
2652 | + tempunit = u"°F" |
2653 | for i in xrange(1,5): |
2654 | - label = "%s: %s%s" % (_('High'), highdata[i-1],tempunit) |
2655 | - self.builder.get_object('day%stemphigh' % str(i)).set_label(label) |
2656 | - label = "%s: %s%s" % (_('Low'), lowdata[i-1],tempunit) |
2657 | - self.builder.get_object('day%stemplow' % str(i)).set_label(label) |
2658 | + label = "%s: %s%s" % (_('High'), highdata[i],tempunit) |
2659 | + self.builder.get_object('day%stemphigh' % |
2660 | + str(i)).set_label(label) |
2661 | + label = "%s: %s%s" % (_('Low'), lowdata[i],tempunit) |
2662 | + self.builder.get_object('day%stemplow' % |
2663 | + str(i)).set_label(label) |
2664 | |
2665 | - # Closing forecast window |
2666 | def close(self, widget, data=None): |
2667 | + """ 'Close' clicked - close forecast window """ |
2668 | log.debug("ExtendedForecast: closing window") |
2669 | self.destroy() |
2670 | |
2671 | def on_destroy(self, widget): |
2672 | pass |
2673 | |
2674 | -class Assistant(gtk.Assistant): |
2675 | +class Assistant(Gtk.Assistant): |
2676 | """ Class for a wizard, which helps to add a new location in location list """ |
2677 | __gtype_name__ = "Assistant" |
2678 | |
2679 | - # Create new object |
2680 | def __new__(cls): |
2681 | + """ Create new object """ |
2682 | log.debug("Assistant: creating new Assistance instance") |
2683 | builder = get_builder('Assistant') |
2684 | new_object = builder.get_object("assistant") |
2685 | new_object.finish_initializing(builder) |
2686 | return new_object |
2687 | |
2688 | - # Finish UI initialization - prepare combobox |
2689 | def finish_initializing(self, builder): |
2690 | + """ Finish UI initialization - prepare combobox """ |
2691 | log.debug("Assistant: finishing initialization") |
2692 | self.builder = builder |
2693 | self.builder.connect_signals(self) |
2694 | @@ -1809,18 +2344,18 @@ |
2695 | |
2696 | # Set up combobox |
2697 | log.debug("Assistant: setting up location combobox") |
2698 | - self.store = gtk.ListStore(str, str, str, str, str) |
2699 | + self.store = Gtk.ListStore(str, str, str, str, str) |
2700 | self.location_input_combo = self.builder.get_object("combolocations") |
2701 | self.location_input_combo.set_model(self.store) |
2702 | - self.location_input_combo.set_text_column(0) |
2703 | + self.location_input_combo.set_entry_text_column(0) |
2704 | self.location_entry = self.builder.get_object("entrylocation") |
2705 | self.place_selected = None |
2706 | self.location = None |
2707 | |
2708 | - self.assistant.set_forward_page_func(self.next_page) |
2709 | + self.assistant.set_forward_page_func(self.next_page, None) |
2710 | |
2711 | - # 'Get cities' button clicked - get suggested cities list |
2712 | def on_get_city_names(self, widget): |
2713 | + """ 'Get cities' button clicked - get suggested cities list """ |
2714 | new_text = self.location_entry.get_text() |
2715 | log.debug("Assistant: looking for location '%s'" % new_text) |
2716 | try: |
2717 | @@ -1833,18 +2368,26 @@ |
2718 | for city in cities['geonames']: |
2719 | # Create a full city name, consisting of city name, administrative areas names and country name |
2720 | if 'adminName2' in city: |
2721 | - displayed_city_name = "%s, %s, %s, %s" % (city['name'], city['adminName1'], city['adminName1'], city['countryName']) |
2722 | + displayed_city_name = "%s, %s, %s, %s" % ( |
2723 | + city['name'], city['adminName1'], |
2724 | + city['adminName1'], city['countryName'] |
2725 | + ) |
2726 | elif 'adminName1' in city: |
2727 | - displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName']) |
2728 | + displayed_city_name = "%s, %s, %s" % ( |
2729 | + city['name'], city['adminName1'], city['countryName'] |
2730 | + ) |
2731 | else: |
2732 | - displayed_city_name = "%s, %s" % (city['name'], city['countryName']) |
2733 | - self.store.append([displayed_city_name, str(city['geonameId']), str(city['lat']), str(city['lng']), str(city['name'])]) |
2734 | + displayed_city_name = "%s, %s" % (city['name'], |
2735 | + city['countryName']) |
2736 | + self.store.append([displayed_city_name, |
2737 | + str(city['geonameId']), str(city['lat']), |
2738 | + str(city['lng']), str(city['name'])]) |
2739 | self.location_input_combo.popup() |
2740 | except urllib2.URLError: |
2741 | log.error("Assistant: error reaching url '%s'" % url) |
2742 | |
2743 | - # A city is selected from suggested list |
2744 | def on_select_city(self, entry): |
2745 | + """ A city is selected from suggested list """ |
2746 | if self.location_input_combo.get_active() != -1: |
2747 | self.place_selected = self.store[self.location_input_combo.get_active()] |
2748 | self.assistant.set_page_complete(self.builder.get_object("placeinput"),True) |
2749 | @@ -1853,50 +2396,74 @@ |
2750 | self.location = None |
2751 | self.assistant.set_page_complete(self.builder.get_object("placeinput"), False) |
2752 | |
2753 | - # Create a location object out of a selected location |
2754 | - def next_page(self, current_page): |
2755 | + def next_page(self, current_page, data): |
2756 | + """ Create a location object out of a selected location """ |
2757 | log.debug("Assistant: moved to page %s" % current_page) |
2758 | - if (self.assistant.get_current_page() == 0) and not self.location and self.place_selected: |
2759 | + if (self.assistant.get_current_page() == 0) and \ |
2760 | + not self.location and self.place_selected: |
2761 | # Label input page |
2762 | - log.debug("Assistant: Page %s: got location with code %s" % (current_page, self.place_selected[1])) |
2763 | - self.location = Location(wi.metric_system, wi.wind_unit) |
2764 | + log.debug("Assistant: Page %s: got location with code %s" % |
2765 | + (current_page, self.place_selected[1])) |
2766 | + self.location = Location(wi.metric_system, wi.wind_unit, |
2767 | + wi.heat_index, wi.chill_index) |
2768 | if self.location.prepare_location(self.place_selected): |
2769 | - log.debug("Assistant: Page %s: City %s found" % (current_page, self.place_selected[0])) |
2770 | + log.debug("Assistant: Page %s: City %s found" % |
2771 | + (current_page, self.place_selected[0])) |
2772 | # Set a short city name as default label |
2773 | self.builder.get_object("entrylbl").set_text(self.place_selected[4]) |
2774 | else: |
2775 | - log.error("Assistant: Page %s: City with code %s was NOT found" % (current_page, self.place_selected[0])) |
2776 | + log.error("Assistant: Page %s: City with code %s was NOT " |
2777 | + "found" % (current_page, self.place_selected[0])) |
2778 | return 3 |
2779 | elif self.assistant.get_current_page() == 1: |
2780 | # Confirmation page |
2781 | lbl = self.builder.get_object("entrylbl").get_text() |
2782 | - log.debug("Assistant: Page %s: City label is %s" % (current_page, lbl)) |
2783 | + log.debug("Assistant: Page %s: City label is %s" % |
2784 | + (current_page, lbl)) |
2785 | # If empty label was input, set label to short city name |
2786 | if lbl == '': |
2787 | - log.debug("Assistant: Page %s: Empty label found, setting lbl to short name - %s" % (current_page, self.place_selected[4])) |
2788 | + log.debug("Assistant: Page %s: Empty label found, setting " |
2789 | + "lbl to short name - %s" % |
2790 | + (current_page, self.place_selected[4])) |
2791 | lbl = self.place_selected[4] |
2792 | self.location.location_details['label'] = lbl |
2793 | self.builder.get_object("lbl3").set_label(_('Label:')) |
2794 | self.builder.get_object("labellbl").set_label('<b>%s</b>' % lbl) |
2795 | - self.builder.get_object("placelbl").set_label('<b>%s</b>' % self.place_selected[0]) |
2796 | + self.builder.get_object("placelbl").set_label( |
2797 | + '<b>%s</b>' % self.place_selected[0]) |
2798 | |
2799 | return self.assistant.get_current_page() + 1 |
2800 | |
2801 | - # 'Cancel' clicked |
2802 | def on_cancel(self,widget): |
2803 | + """ 'Cancel' clicked """ |
2804 | log.debug("Assistant: Cancelled") |
2805 | self.destroy() |
2806 | |
2807 | - # 'Apply' clicked - save location details, add an entry in a location list |
2808 | def on_apply(self,widget): |
2809 | + """ 'Apply' clicked - save location details, add an entry in a location list """ |
2810 | (location_code, location_details) = self.location.export_location_details() |
2811 | - log.debug("Assistant: Apply: adding location ('%s', '%s')" % (self.location.location_details['label'], location_code)) |
2812 | + log.debug("Assistant: Apply: adding location ('%s', '%s')" % |
2813 | + (self.location.location_details['label'], location_code)) |
2814 | newplace = list() |
2815 | newplace.append(self.location.location_details['label']) |
2816 | newplace.append(str(location_code)) |
2817 | newplace.append(str(location_details)) |
2818 | - wi.settings.save_location_details(eval(str(location_details)), str(location_code)) |
2819 | + wi.settings.save_location_details( |
2820 | + eval(str(location_details)), str(location_code) |
2821 | + ) |
2822 | wi.prefswindow.builder.get_object('citieslist').append(newplace) |
2823 | + |
2824 | + newplaces = list() |
2825 | + item = wi.prefswindow.builder.get_object('citieslist').get_iter_first() |
2826 | + while (item != None): |
2827 | + newplace = list() |
2828 | + newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 1)) |
2829 | + newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 0)) |
2830 | + newplaces.append(newplace) |
2831 | + item = wi.prefswindow.builder.get_object('citieslist').iter_next(item) |
2832 | + wi.places = newplaces |
2833 | + wi.places_changed = True |
2834 | + |
2835 | # Enable 'OK' button in Preferences |
2836 | wi.prefswindow.builder.get_object('ok_button').set_sensitive(True) |
2837 | self.hide() |
2838 | @@ -1904,8 +2471,8 @@ |
2839 | class SingleInstance(object): |
2840 | """ Class to ensure, that single instance of the applet is run for each user """ |
2841 | |
2842 | - # Initialize, specifying a path to store pids |
2843 | def __init__(self, pidPath): |
2844 | + """ Initialize, specifying a path to store pids """ |
2845 | self.pidPath=pidPath |
2846 | # See if pidFile exists |
2847 | if os.path.exists(pidPath): |
2848 | @@ -1928,7 +2495,8 @@ |
2849 | shutil.copy(temp_path, pidPath) |
2850 | os.unlink(temp_path) |
2851 | except Exception as e: |
2852 | - log.error("SingleInstance: exception while renaming '%s' to '%s':\n %s" % (temp_path, pidPath, str(e))) |
2853 | + log.error("SingleInstance: exception while renaming '%s' " |
2854 | + "to '%s':\n %s" % (temp_path, pidPath, str(e))) |
2855 | |
2856 | def is_already_running(self): |
2857 | return self.lasterror |
2858 | @@ -1939,7 +2507,8 @@ |
2859 | os.unlink(self.pidPath) |
2860 | |
2861 | def main(): |
2862 | - gtk.main() |
2863 | + Gtk.main() |
2864 | + |
2865 | return 0 |
2866 | |
2867 | if __name__ == "__main__": |
2868 | @@ -1952,8 +2521,10 @@ |
2869 | log = logging.getLogger('IndicatorWeather') |
2870 | log.propagate = False |
2871 | log.setLevel(logging.DEBUG) |
2872 | - log_handler = logging.handlers.RotatingFileHandler(log_filename, maxBytes=1024*1024, backupCount=5) |
2873 | - log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - %(levelname)s - %(message)s") |
2874 | + log_handler = logging.handlers.RotatingFileHandler( |
2875 | + log_filename, maxBytes=1024*1024, backupCount=5) |
2876 | + log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - " |
2877 | + "%(levelname)s - %(message)s") |
2878 | log_handler.setFormatter(log_formatter) |
2879 | log.addHandler(log_handler) |
2880 | |
2881 | @@ -1974,8 +2545,7 @@ |
2882 | TimeFormatter.monitor_indicator_datetime(log) |
2883 | |
2884 | # not running, safe to continue... |
2885 | - gtk.gdk.threads_init() |
2886 | - gtk.gdk.threads_enter() |
2887 | + |
2888 | # Remember locale name |
2889 | global locale_name |
2890 | locale_name = locale.getlocale()[0] |
2891 | @@ -1984,6 +2554,20 @@ |
2892 | else: |
2893 | locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale |
2894 | locale_name = "en" |
2895 | + |
2896 | + # init GLib/GObject |
2897 | + GObject.threads_init() |
2898 | + |
2899 | + # init Gdk threads and get Gdk lock |
2900 | + Gdk.threads_init() |
2901 | + Gdk.threads_enter() |
2902 | + |
2903 | + # init Gtk |
2904 | + Gtk.init(None) |
2905 | + |
2906 | + # create main thread and enter main loop |
2907 | wi = indicator_weather() |
2908 | main() |
2909 | - gtk.gdk.threads_leave() |
2910 | + |
2911 | + # release Gdk lock |
2912 | + Gdk.threads_leave() |
2913 | \ No newline at end of file |
2914 | |
2915 | === modified file 'data/indicator-weather.gschema.xml' |
2916 | --- data/indicator-weather.gschema.xml 2011-05-31 09:49:23 +0000 |
2917 | +++ data/indicator-weather.gschema.xml 2013-05-22 05:08:27 +0000 |
2918 | @@ -6,6 +6,21 @@ |
2919 | <summary>Display icon/label in the indicator</summary> |
2920 | <description>0 - display icon only, 1 - display label only, 2 - display both icon and text</description> |
2921 | </key> |
2922 | + <key type="b" name="relative-display"> |
2923 | + <default>false</default> |
2924 | + <summary>Display relative temperature</summary> |
2925 | + <description>Display relative temperature</description> |
2926 | + </key> |
2927 | + <key type="b" name="wind-display"> |
2928 | + <default>true</default> |
2929 | + <summary>Display wind speed and direction</summary> |
2930 | + <description>Display wind speed and direction</description> |
2931 | + </key> |
2932 | + <key type="b" name="suntimes-display"> |
2933 | + <default>true</default> |
2934 | + <summary>Display sunrise and sunset times</summary> |
2935 | + <description>Display sunrise and sunset times</description> |
2936 | + </key> |
2937 | <key type="s" name="notifications"> |
2938 | <default>'N'</default> |
2939 | <summary>Notifications</summary> |
2940 | @@ -36,6 +51,16 @@ |
2941 | <summary>wind unit</summary> |
2942 | <description>wind unit</description> |
2943 | </key> |
2944 | + <key type="s" name="heat-estimate"> |
2945 | + <default>'humidex'</default> |
2946 | + <summary>heat estimate formula</summary> |
2947 | + <description>heat estimate formula</description> |
2948 | + </key> |
2949 | + <key type="s" name="chill-estimate"> |
2950 | + <default>'windchill'</default> |
2951 | + <summary>chill estimate formula</summary> |
2952 | + <description>chill estimate formula</description> |
2953 | + </key> |
2954 | <key type="b" name="show-forecast"> |
2955 | <default>true</default> |
2956 | <summary>show forecast</summary> |
2957 | |
2958 | === modified file 'data/ui/Assistant.ui' |
2959 | --- data/ui/Assistant.ui 2011-04-03 05:00:19 +0000 |
2960 | +++ data/ui/Assistant.ui 2013-05-22 05:08:27 +0000 |
2961 | @@ -1,29 +1,28 @@ |
2962 | <?xml version="1.0" encoding="UTF-8"?> |
2963 | <interface> |
2964 | - <requires lib="gtk+" version="2.16"/> |
2965 | + <!-- interface-requires gtk+ 3.0 --> |
2966 | <!-- interface-requires assistant 1.0 --> |
2967 | - <!-- interface-naming-policy project-wide --> |
2968 | <!-- interface-local-resource-path ../media --> |
2969 | <object class="Assistant" id="assistant"> |
2970 | + <property name="can_focus">False</property> |
2971 | <property name="border_width">12</property> |
2972 | <property name="title" translatable="yes">Add a location</property> |
2973 | + <property name="resizable">False</property> |
2974 | <property name="window_position">mouse</property> |
2975 | <property name="default_width">400</property> |
2976 | <property name="default_height">200</property> |
2977 | - <property name="resizable">False</property> |
2978 | - <signal name="apply" handler="on_apply"/> |
2979 | - <signal name="cancel" handler="on_cancel"/> |
2980 | - <!--<child> |
2981 | - <placeholder/> |
2982 | - </child> --> |
2983 | + <signal name="apply" handler="on_apply" swapped="no"/> |
2984 | + <signal name="cancel" handler="on_cancel" swapped="no"/> |
2985 | <child> |
2986 | <object class="GtkVBox" id="placeinput"> |
2987 | <property name="visible">True</property> |
2988 | + <property name="can_focus">False</property> |
2989 | <property name="border_width">12</property> |
2990 | <property name="spacing">6</property> |
2991 | <child> |
2992 | <object class="GtkLabel" id="lblplaceinput"> |
2993 | <property name="visible">True</property> |
2994 | + <property name="can_focus">False</property> |
2995 | <property name="xalign">0</property> |
2996 | <property name="yalign">0</property> |
2997 | <property name="ypad">6</property> |
2998 | @@ -32,43 +31,56 @@ |
2999 | <property name="wrap_mode">word-char</property> |
3000 | </object> |
3001 | <packing> |
3002 | + <property name="expand">True</property> |
3003 | + <property name="fill">True</property> |
3004 | <property name="position">0</property> |
3005 | </packing> |
3006 | </child> |
3007 | <child> |
3008 | - <object class="GtkHBox" id="city_input_vbox"> |
3009 | - <property name="visible">True</property> |
3010 | - <property name="border_width">12</property> |
3011 | - <property name="spacing">6</property> |
3012 | - <child> |
3013 | - <object class="GtkComboBoxEntry" id="combolocations"> |
3014 | - <property name="visible">True</property> |
3015 | - <signal name="changed" handler="on_select_city"/> |
3016 | - <child internal-child="entry"> |
3017 | - <object class="GtkEntry" id="entrylocation"> |
3018 | - <property name="visible">True</property> |
3019 | - <property name="can_focus">True</property> |
3020 | - </object> |
3021 | - </child> |
3022 | - </object> |
3023 | - <packing> |
3024 | - <property name="position">0</property> |
3025 | - </packing> |
3026 | - </child> |
3027 | - <child> |
3028 | - <object class="GtkButton" id="getcities"> |
3029 | - <property name="visible">True</property> |
3030 | - <property name="label" translatable="yes">Search</property> |
3031 | - <signal name="clicked" handler="on_get_city_names"/> |
3032 | - </object> |
3033 | - <packing> |
3034 | - <property name="position">1</property> |
3035 | - </packing> |
3036 | - </child> |
3037 | - </object> |
3038 | - <packing> |
3039 | + <object class="GtkHBox" id="city_input_vbox"> |
3040 | + <property name="visible">True</property> |
3041 | + <property name="can_focus">False</property> |
3042 | + <property name="border_width">12</property> |
3043 | + <property name="spacing">6</property> |
3044 | + <child> |
3045 | + <object class="GtkComboBox" id="combolocations"> |
3046 | + <property name="visible">True</property> |
3047 | + <property name="can_focus">False</property> |
3048 | + <property name="has_entry">True</property> |
3049 | + <signal name="changed" handler="on_select_city" swapped="no"/> |
3050 | + <child internal-child="entry"> |
3051 | + <object class="GtkEntry" id="entrylocation"> |
3052 | + <property name="visible">True</property> |
3053 | + <property name="can_focus">True</property> |
3054 | + </object> |
3055 | + </child> |
3056 | + </object> |
3057 | + <packing> |
3058 | + <property name="expand">True</property> |
3059 | + <property name="fill">True</property> |
3060 | + <property name="position">0</property> |
3061 | + </packing> |
3062 | + </child> |
3063 | + <child> |
3064 | + <object class="GtkButton" id="getcities"> |
3065 | + <property name="label" translatable="yes">Search</property> |
3066 | + <property name="visible">True</property> |
3067 | + <property name="can_focus">False</property> |
3068 | + <property name="receives_default">False</property> |
3069 | + <signal name="clicked" handler="on_get_city_names" swapped="no"/> |
3070 | + </object> |
3071 | + <packing> |
3072 | + <property name="expand">True</property> |
3073 | + <property name="fill">True</property> |
3074 | <property name="position">1</property> |
3075 | - </packing> |
3076 | + </packing> |
3077 | + </child> |
3078 | + </object> |
3079 | + <packing> |
3080 | + <property name="expand">True</property> |
3081 | + <property name="fill">True</property> |
3082 | + <property name="position">1</property> |
3083 | + </packing> |
3084 | </child> |
3085 | </object> |
3086 | <packing> |
3087 | @@ -78,16 +90,19 @@ |
3088 | <child> |
3089 | <object class="GtkVBox" id="label"> |
3090 | <property name="visible">True</property> |
3091 | + <property name="can_focus">False</property> |
3092 | <property name="border_width">12</property> |
3093 | <property name="spacing">6</property> |
3094 | <child> |
3095 | <object class="GtkLabel" id="lblwould"> |
3096 | <property name="visible">True</property> |
3097 | + <property name="can_focus">False</property> |
3098 | <property name="xalign">0</property> |
3099 | <property name="label" translatable="yes">Please enter a name for this location:</property> |
3100 | </object> |
3101 | <packing> |
3102 | <property name="expand">False</property> |
3103 | + <property name="fill">True</property> |
3104 | <property name="position">0</property> |
3105 | </packing> |
3106 | </child> |
3107 | @@ -99,6 +114,7 @@ |
3108 | </object> |
3109 | <packing> |
3110 | <property name="expand">False</property> |
3111 | + <property name="fill">True</property> |
3112 | <property name="position">1</property> |
3113 | </packing> |
3114 | </child> |
3115 | @@ -110,10 +126,12 @@ |
3116 | <child> |
3117 | <object class="GtkVBox" id="review"> |
3118 | <property name="visible">True</property> |
3119 | + <property name="can_focus">False</property> |
3120 | <property name="border_width">12</property> |
3121 | <child> |
3122 | <object class="GtkLabel" id="lblreview"> |
3123 | <property name="visible">True</property> |
3124 | + <property name="can_focus">False</property> |
3125 | <property name="xalign">0</property> |
3126 | <property name="ypad">6</property> |
3127 | <property name="label" translatable="yes">Please review the choices below. If anything is not correct, please go back and select the correct options.</property> |
3128 | @@ -122,99 +140,120 @@ |
3129 | </object> |
3130 | <packing> |
3131 | <property name="expand">False</property> |
3132 | + <property name="fill">True</property> |
3133 | <property name="position">0</property> |
3134 | </packing> |
3135 | </child> |
3136 | <child> |
3137 | <object class="GtkAlignment" id="alignment1"> |
3138 | <property name="visible">True</property> |
3139 | + <property name="can_focus">False</property> |
3140 | <property name="left_padding">12</property> |
3141 | <child> |
3142 | <object class="GtkVBox" id="vbox1"> |
3143 | <property name="visible">True</property> |
3144 | + <property name="can_focus">False</property> |
3145 | <child> |
3146 | <object class="GtkHBox" id="revlabel"> |
3147 | <property name="visible">True</property> |
3148 | + <property name="can_focus">False</property> |
3149 | <property name="spacing">5</property> |
3150 | <child> |
3151 | <object class="GtkArrow" id="arrow3"> |
3152 | <property name="visible">True</property> |
3153 | <property name="sensitive">False</property> |
3154 | + <property name="can_focus">False</property> |
3155 | </object> |
3156 | <packing> |
3157 | <property name="expand">False</property> |
3158 | + <property name="fill">True</property> |
3159 | <property name="position">0</property> |
3160 | </packing> |
3161 | </child> |
3162 | <child> |
3163 | <object class="GtkLabel" id="lbl3"> |
3164 | <property name="visible">True</property> |
3165 | + <property name="can_focus">False</property> |
3166 | <property name="xalign">0</property> |
3167 | <property name="label" translatable="yes">Label:</property> |
3168 | <property name="use_markup">True</property> |
3169 | </object> |
3170 | <packing> |
3171 | <property name="expand">False</property> |
3172 | + <property name="fill">True</property> |
3173 | <property name="position">1</property> |
3174 | </packing> |
3175 | </child> |
3176 | <child> |
3177 | <object class="GtkLabel" id="labellbl"> |
3178 | <property name="visible">True</property> |
3179 | + <property name="can_focus">False</property> |
3180 | <property name="xalign">0</property> |
3181 | <property name="label" translatable="yes"><b>Home</b></property> |
3182 | <property name="use_markup">True</property> |
3183 | </object> |
3184 | <packing> |
3185 | + <property name="expand">True</property> |
3186 | + <property name="fill">True</property> |
3187 | <property name="position">2</property> |
3188 | </packing> |
3189 | </child> |
3190 | </object> |
3191 | <packing> |
3192 | <property name="expand">False</property> |
3193 | + <property name="fill">True</property> |
3194 | <property name="position">0</property> |
3195 | </packing> |
3196 | </child> |
3197 | <child> |
3198 | <object class="GtkHBox" id="revplace"> |
3199 | <property name="visible">True</property> |
3200 | + <property name="can_focus">False</property> |
3201 | <property name="spacing">5</property> |
3202 | <child> |
3203 | <object class="GtkArrow" id="arrow2"> |
3204 | <property name="visible">True</property> |
3205 | <property name="sensitive">False</property> |
3206 | + <property name="can_focus">False</property> |
3207 | </object> |
3208 | <packing> |
3209 | <property name="expand">False</property> |
3210 | + <property name="fill">True</property> |
3211 | <property name="position">0</property> |
3212 | </packing> |
3213 | </child> |
3214 | <child> |
3215 | <object class="GtkLabel" id="lbl2"> |
3216 | <property name="visible">True</property> |
3217 | + <property name="can_focus">False</property> |
3218 | <property name="xalign">0</property> |
3219 | <property name="label" translatable="yes">Location:</property> |
3220 | <property name="use_markup">True</property> |
3221 | </object> |
3222 | <packing> |
3223 | <property name="expand">False</property> |
3224 | + <property name="fill">True</property> |
3225 | <property name="position">1</property> |
3226 | </packing> |
3227 | </child> |
3228 | <child> |
3229 | <object class="GtkLabel" id="placelbl"> |
3230 | <property name="visible">True</property> |
3231 | + <property name="can_focus">False</property> |
3232 | <property name="xalign">0</property> |
3233 | <property name="label" translatable="yes"><b>Orange, Texas</b></property> |
3234 | <property name="use_markup">True</property> |
3235 | </object> |
3236 | <packing> |
3237 | + <property name="expand">True</property> |
3238 | + <property name="fill">True</property> |
3239 | <property name="position">2</property> |
3240 | </packing> |
3241 | </child> |
3242 | </object> |
3243 | <packing> |
3244 | <property name="expand">False</property> |
3245 | + <property name="fill">True</property> |
3246 | <property name="position">1</property> |
3247 | </packing> |
3248 | </child> |
3249 | @@ -223,6 +262,7 @@ |
3250 | </object> |
3251 | <packing> |
3252 | <property name="expand">False</property> |
3253 | + <property name="fill">True</property> |
3254 | <property name="position">1</property> |
3255 | </packing> |
3256 | </child> |
3257 | @@ -235,5 +275,10 @@ |
3258 | <property name="title" translatable="yes">Review choices</property> |
3259 | </packing> |
3260 | </child> |
3261 | + <child internal-child="action_area"> |
3262 | + <object class="GtkBox" id="Assistant-action_area1"> |
3263 | + <property name="can_focus">False</property> |
3264 | + </object> |
3265 | + </child> |
3266 | </object> |
3267 | </interface> |
3268 | |
3269 | === modified file 'data/ui/ExtendedForecast.ui' |
3270 | --- data/ui/ExtendedForecast.ui 2011-11-27 07:38:08 +0000 |
3271 | +++ data/ui/ExtendedForecast.ui 2013-05-22 05:08:27 +0000 |
3272 | @@ -1,64 +1,76 @@ |
3273 | <?xml version="1.0" encoding="UTF-8"?> |
3274 | <interface> |
3275 | - <requires lib="gtk+" version="2.16"/> |
3276 | + <!-- interface-requires gtk+ 3.0 --> |
3277 | <!-- interface-requires extended_forecast 1.0 --> |
3278 | - <!-- interface-naming-policy project-wide --> |
3279 | <!-- interface-local-resource-path ../media --> |
3280 | <object class="ExtendedForecast" id="extended_forecast"> |
3281 | + <property name="can_focus">False</property> |
3282 | <property name="border_width">12</property> |
3283 | <property name="title" translatable="yes">Extended Forecast</property> |
3284 | <property name="resizable">False</property> |
3285 | <property name="window_position">mouse</property> |
3286 | <property name="icon">../media/icon.png</property> |
3287 | - <signal name="destroy" handler="on_destroy"/> |
3288 | + <signal name="destroy" handler="on_destroy" swapped="no"/> |
3289 | <child> |
3290 | <object class="GtkVBox" id="mainvbox"> |
3291 | <property name="visible">True</property> |
3292 | + <property name="can_focus">False</property> |
3293 | <child> |
3294 | - <object class="GtkLabel" id="connection_error"> |
3295 | - <property name="visible">False</property> |
3296 | - <property name="ypad">10</property> |
3297 | - <property name="label"><big>Forecast information cannot be fetched. Connection cannot be established.</big></property> |
3298 | - <property name="use_markup">True</property> |
3299 | - <property name="wrap">False</property> |
3300 | - </object> |
3301 | - <packing> |
3302 | - <property name="position">0</property> |
3303 | - </packing> |
3304 | + <object class="GtkLabel" id="connection_error"> |
3305 | + <property name="can_focus">False</property> |
3306 | + <property name="ypad">10</property> |
3307 | + <property name="label"><big>Forecast information cannot be fetched. Connection cannot be established.</big></property> |
3308 | + <property name="use_markup">True</property> |
3309 | + </object> |
3310 | + <packing> |
3311 | + <property name="expand">True</property> |
3312 | + <property name="fill">True</property> |
3313 | + <property name="position">0</property> |
3314 | + </packing> |
3315 | </child> |
3316 | <child> |
3317 | <object class="GtkHBox" id="hbox1"> |
3318 | <property name="visible">True</property> |
3319 | + <property name="can_focus">False</property> |
3320 | <property name="spacing">5</property> |
3321 | <property name="homogeneous">True</property> |
3322 | <child> |
3323 | <object class="GtkHBox" id="hbox2"> |
3324 | <property name="visible">True</property> |
3325 | + <property name="can_focus">False</property> |
3326 | <child> |
3327 | <object class="GtkAlignment" id="alignment3"> |
3328 | <property name="visible">True</property> |
3329 | + <property name="can_focus">False</property> |
3330 | <property name="right_padding">6</property> |
3331 | <child> |
3332 | <object class="GtkVBox" id="day1vbox"> |
3333 | <property name="visible">True</property> |
3334 | + <property name="can_focus">False</property> |
3335 | <child> |
3336 | <object class="GtkLabel" id="day1lbl"> |
3337 | <property name="visible">True</property> |
3338 | + <property name="can_focus">False</property> |
3339 | <property name="ypad">10</property> |
3340 | <property name="label"><big>Today (fri.)</big></property> |
3341 | <property name="use_markup">True</property> |
3342 | </object> |
3343 | <packing> |
3344 | + <property name="expand">True</property> |
3345 | + <property name="fill">True</property> |
3346 | <property name="position">0</property> |
3347 | </packing> |
3348 | </child> |
3349 | <child> |
3350 | <object class="GtkImage" id="day1image"> |
3351 | <property name="visible">True</property> |
3352 | + <property name="can_focus">False</property> |
3353 | <property name="pixel_size">50</property> |
3354 | <property name="icon_name">weather-showers</property> |
3355 | </object> |
3356 | <packing> |
3357 | + <property name="expand">True</property> |
3358 | + <property name="fill">True</property> |
3359 | <property name="position">1</property> |
3360 | </packing> |
3361 | </child> |
3362 | @@ -66,6 +78,7 @@ |
3363 | <object class="GtkLabel" id="day1cond"> |
3364 | <property name="height_request">80</property> |
3365 | <property name="visible">True</property> |
3366 | + <property name="can_focus">False</property> |
3367 | <property name="label">Partially cloudy</property> |
3368 | <property name="justify">center</property> |
3369 | <property name="wrap">True</property> |
3370 | @@ -73,33 +86,44 @@ |
3371 | <property name="width_chars">15</property> |
3372 | </object> |
3373 | <packing> |
3374 | + <property name="expand">True</property> |
3375 | + <property name="fill">True</property> |
3376 | <property name="position">2</property> |
3377 | </packing> |
3378 | </child> |
3379 | <child> |
3380 | <object class="GtkVBox" id="vbox1"> |
3381 | <property name="visible">True</property> |
3382 | + <property name="can_focus">False</property> |
3383 | <property name="homogeneous">True</property> |
3384 | <child> |
3385 | <object class="GtkLabel" id="day1temphigh"> |
3386 | <property name="visible">True</property> |
3387 | + <property name="can_focus">False</property> |
3388 | <property name="label">High : 23°C</property> |
3389 | </object> |
3390 | <packing> |
3391 | + <property name="expand">True</property> |
3392 | + <property name="fill">True</property> |
3393 | <property name="position">0</property> |
3394 | </packing> |
3395 | </child> |
3396 | <child> |
3397 | <object class="GtkLabel" id="day1templow"> |
3398 | <property name="visible">True</property> |
3399 | + <property name="can_focus">False</property> |
3400 | <property name="label">Low : 19°C</property> |
3401 | </object> |
3402 | <packing> |
3403 | + <property name="expand">True</property> |
3404 | + <property name="fill">True</property> |
3405 | <property name="position">1</property> |
3406 | </packing> |
3407 | </child> |
3408 | </object> |
3409 | <packing> |
3410 | + <property name="expand">True</property> |
3411 | + <property name="fill">True</property> |
3412 | <property name="position">3</property> |
3413 | </packing> |
3414 | </child> |
3415 | @@ -107,6 +131,8 @@ |
3416 | </child> |
3417 | </object> |
3418 | <packing> |
3419 | + <property name="expand">True</property> |
3420 | + <property name="fill">True</property> |
3421 | <property name="position">0</property> |
3422 | </packing> |
3423 | </child> |
3424 | @@ -114,45 +140,58 @@ |
3425 | <object class="GtkVSeparator" id="vseparator1"> |
3426 | <property name="width_request">2</property> |
3427 | <property name="visible">True</property> |
3428 | + <property name="can_focus">False</property> |
3429 | </object> |
3430 | <packing> |
3431 | <property name="expand">False</property> |
3432 | + <property name="fill">True</property> |
3433 | <property name="position">1</property> |
3434 | </packing> |
3435 | </child> |
3436 | </object> |
3437 | <packing> |
3438 | + <property name="expand">True</property> |
3439 | + <property name="fill">True</property> |
3440 | <property name="position">0</property> |
3441 | </packing> |
3442 | </child> |
3443 | <child> |
3444 | <object class="GtkHBox" id="hbox3"> |
3445 | <property name="visible">True</property> |
3446 | + <property name="can_focus">False</property> |
3447 | <child> |
3448 | <object class="GtkAlignment" id="alignment2"> |
3449 | <property name="visible">True</property> |
3450 | + <property name="can_focus">False</property> |
3451 | <property name="right_padding">6</property> |
3452 | <child> |
3453 | <object class="GtkVBox" id="day2vbox"> |
3454 | <property name="visible">True</property> |
3455 | + <property name="can_focus">False</property> |
3456 | <child> |
3457 | <object class="GtkLabel" id="day2lbl"> |
3458 | <property name="visible">True</property> |
3459 | + <property name="can_focus">False</property> |
3460 | <property name="ypad">10</property> |
3461 | <property name="label"><big>Tomorrow (sat.)</big></property> |
3462 | <property name="use_markup">True</property> |
3463 | </object> |
3464 | <packing> |
3465 | + <property name="expand">True</property> |
3466 | + <property name="fill">True</property> |
3467 | <property name="position">0</property> |
3468 | </packing> |
3469 | </child> |
3470 | <child> |
3471 | <object class="GtkImage" id="day2image"> |
3472 | <property name="visible">True</property> |
3473 | + <property name="can_focus">False</property> |
3474 | <property name="pixel_size">50</property> |
3475 | <property name="icon_name">weather-storm</property> |
3476 | </object> |
3477 | <packing> |
3478 | + <property name="expand">True</property> |
3479 | + <property name="fill">True</property> |
3480 | <property name="position">1</property> |
3481 | </packing> |
3482 | </child> |
3483 | @@ -160,6 +199,7 @@ |
3484 | <object class="GtkLabel" id="day2cond"> |
3485 | <property name="height_request">80</property> |
3486 | <property name="visible">True</property> |
3487 | + <property name="can_focus">False</property> |
3488 | <property name="label">Couverture nuageuse partielle</property> |
3489 | <property name="justify">center</property> |
3490 | <property name="wrap">True</property> |
3491 | @@ -167,33 +207,44 @@ |
3492 | <property name="width_chars">15</property> |
3493 | </object> |
3494 | <packing> |
3495 | + <property name="expand">True</property> |
3496 | + <property name="fill">True</property> |
3497 | <property name="position">2</property> |
3498 | </packing> |
3499 | </child> |
3500 | <child> |
3501 | <object class="GtkVBox" id="vbox2"> |
3502 | <property name="visible">True</property> |
3503 | + <property name="can_focus">False</property> |
3504 | <property name="homogeneous">True</property> |
3505 | <child> |
3506 | <object class="GtkLabel" id="day2temphigh"> |
3507 | <property name="visible">True</property> |
3508 | + <property name="can_focus">False</property> |
3509 | <property name="label">High : 26°C</property> |
3510 | </object> |
3511 | <packing> |
3512 | + <property name="expand">True</property> |
3513 | + <property name="fill">True</property> |
3514 | <property name="position">0</property> |
3515 | </packing> |
3516 | </child> |
3517 | <child> |
3518 | <object class="GtkLabel" id="day2templow"> |
3519 | <property name="visible">True</property> |
3520 | + <property name="can_focus">False</property> |
3521 | <property name="label">Low : 16°C</property> |
3522 | </object> |
3523 | <packing> |
3524 | + <property name="expand">True</property> |
3525 | + <property name="fill">True</property> |
3526 | <property name="position">1</property> |
3527 | </packing> |
3528 | </child> |
3529 | </object> |
3530 | <packing> |
3531 | + <property name="expand">True</property> |
3532 | + <property name="fill">True</property> |
3533 | <property name="position">3</property> |
3534 | </packing> |
3535 | </child> |
3536 | @@ -201,6 +252,8 @@ |
3537 | </child> |
3538 | </object> |
3539 | <packing> |
3540 | + <property name="expand">True</property> |
3541 | + <property name="fill">True</property> |
3542 | <property name="position">0</property> |
3543 | </packing> |
3544 | </child> |
3545 | @@ -208,47 +261,58 @@ |
3546 | <object class="GtkVSeparator" id="vseparator2"> |
3547 | <property name="width_request">2</property> |
3548 | <property name="visible">True</property> |
3549 | + <property name="can_focus">False</property> |
3550 | </object> |
3551 | <packing> |
3552 | <property name="expand">False</property> |
3553 | + <property name="fill">True</property> |
3554 | <property name="position">1</property> |
3555 | </packing> |
3556 | </child> |
3557 | </object> |
3558 | <packing> |
3559 | + <property name="expand">True</property> |
3560 | + <property name="fill">True</property> |
3561 | <property name="position">1</property> |
3562 | </packing> |
3563 | </child> |
3564 | <child> |
3565 | <object class="GtkHBox" id="hbox4"> |
3566 | <property name="visible">True</property> |
3567 | + <property name="can_focus">False</property> |
3568 | <child> |
3569 | <object class="GtkAlignment" id="alignment4"> |
3570 | <property name="visible">True</property> |
3571 | + <property name="can_focus">False</property> |
3572 | <property name="right_padding">6</property> |
3573 | <child> |
3574 | <object class="GtkVBox" id="day3vbox"> |
3575 | <property name="visible">True</property> |
3576 | + <property name="can_focus">False</property> |
3577 | <child> |
3578 | <object class="GtkLabel" id="day3lbl"> |
3579 | <property name="visible">True</property> |
3580 | + <property name="can_focus">False</property> |
3581 | <property name="ypad">10</property> |
3582 | <property name="label"><big>Sunday</big></property> |
3583 | <property name="use_markup">True</property> |
3584 | </object> |
3585 | <packing> |
3586 | <property name="expand">False</property> |
3587 | + <property name="fill">True</property> |
3588 | <property name="position">0</property> |
3589 | </packing> |
3590 | </child> |
3591 | <child> |
3592 | <object class="GtkImage" id="day3image"> |
3593 | <property name="visible">True</property> |
3594 | + <property name="can_focus">False</property> |
3595 | <property name="pixel_size">50</property> |
3596 | <property name="icon_name">weather-fog</property> |
3597 | </object> |
3598 | <packing> |
3599 | <property name="expand">False</property> |
3600 | + <property name="fill">True</property> |
3601 | <property name="position">1</property> |
3602 | </packing> |
3603 | </child> |
3604 | @@ -256,6 +320,7 @@ |
3605 | <object class="GtkLabel" id="day3cond"> |
3606 | <property name="height_request">80</property> |
3607 | <property name="visible">True</property> |
3608 | + <property name="can_focus">False</property> |
3609 | <property name="label">Fogs</property> |
3610 | <property name="justify">center</property> |
3611 | <property name="wrap">True</property> |
3612 | @@ -263,32 +328,43 @@ |
3613 | <property name="width_chars">15</property> |
3614 | </object> |
3615 | <packing> |
3616 | + <property name="expand">True</property> |
3617 | + <property name="fill">True</property> |
3618 | <property name="position">2</property> |
3619 | </packing> |
3620 | </child> |
3621 | <child> |
3622 | <object class="GtkVBox" id="vbox3"> |
3623 | <property name="visible">True</property> |
3624 | + <property name="can_focus">False</property> |
3625 | <child> |
3626 | <object class="GtkLabel" id="day3temphigh"> |
3627 | <property name="visible">True</property> |
3628 | + <property name="can_focus">False</property> |
3629 | <property name="label">High : 28°C</property> |
3630 | </object> |
3631 | <packing> |
3632 | + <property name="expand">True</property> |
3633 | + <property name="fill">True</property> |
3634 | <property name="position">0</property> |
3635 | </packing> |
3636 | </child> |
3637 | <child> |
3638 | <object class="GtkLabel" id="day3templow"> |
3639 | <property name="visible">True</property> |
3640 | + <property name="can_focus">False</property> |
3641 | <property name="label">Low : 16°C</property> |
3642 | </object> |
3643 | <packing> |
3644 | + <property name="expand">True</property> |
3645 | + <property name="fill">True</property> |
3646 | <property name="position">1</property> |
3647 | </packing> |
3648 | </child> |
3649 | </object> |
3650 | <packing> |
3651 | + <property name="expand">True</property> |
3652 | + <property name="fill">True</property> |
3653 | <property name="position">3</property> |
3654 | </packing> |
3655 | </child> |
3656 | @@ -296,50 +372,65 @@ |
3657 | </child> |
3658 | </object> |
3659 | <packing> |
3660 | + <property name="expand">True</property> |
3661 | + <property name="fill">True</property> |
3662 | <property name="position">0</property> |
3663 | </packing> |
3664 | </child> |
3665 | <child> |
3666 | <object class="GtkVSeparator" id="vseparator3"> |
3667 | <property name="visible">True</property> |
3668 | + <property name="can_focus">False</property> |
3669 | </object> |
3670 | <packing> |
3671 | <property name="expand">False</property> |
3672 | + <property name="fill">True</property> |
3673 | <property name="position">1</property> |
3674 | </packing> |
3675 | </child> |
3676 | </object> |
3677 | <packing> |
3678 | + <property name="expand">True</property> |
3679 | + <property name="fill">True</property> |
3680 | <property name="position">2</property> |
3681 | </packing> |
3682 | </child> |
3683 | <child> |
3684 | <object class="GtkHBox" id="hbox5"> |
3685 | <property name="visible">True</property> |
3686 | + <property name="can_focus">False</property> |
3687 | <child> |
3688 | <object class="GtkAlignment" id="alignment5"> |
3689 | <property name="visible">True</property> |
3690 | + <property name="can_focus">False</property> |
3691 | <child> |
3692 | <object class="GtkVBox" id="day4vbox"> |
3693 | <property name="visible">True</property> |
3694 | + <property name="can_focus">False</property> |
3695 | <child> |
3696 | <object class="GtkLabel" id="day4lbl"> |
3697 | <property name="visible">True</property> |
3698 | + <property name="can_focus">False</property> |
3699 | <property name="ypad">10</property> |
3700 | <property name="label"><big>Monday</big></property> |
3701 | <property name="use_markup">True</property> |
3702 | </object> |
3703 | <packing> |
3704 | + <property name="expand">True</property> |
3705 | + <property name="fill">True</property> |
3706 | <property name="position">0</property> |
3707 | </packing> |
3708 | </child> |
3709 | <child> |
3710 | <object class="GtkImage" id="day4image"> |
3711 | <property name="visible">True</property> |
3712 | + <property name="can_focus">False</property> |
3713 | <property name="pixel_size">50</property> |
3714 | <property name="icon_name">weather-clear</property> |
3715 | </object> |
3716 | <packing> |
3717 | + <property name="expand">True</property> |
3718 | + <property name="fill">True</property> |
3719 | <property name="position">1</property> |
3720 | </packing> |
3721 | </child> |
3722 | @@ -347,6 +438,7 @@ |
3723 | <object class="GtkLabel" id="day4cond"> |
3724 | <property name="height_request">80</property> |
3725 | <property name="visible">True</property> |
3726 | + <property name="can_focus">False</property> |
3727 | <property name="label">Clear</property> |
3728 | <property name="justify">center</property> |
3729 | <property name="wrap">True</property> |
3730 | @@ -354,32 +446,43 @@ |
3731 | <property name="width_chars">15</property> |
3732 | </object> |
3733 | <packing> |
3734 | + <property name="expand">True</property> |
3735 | + <property name="fill">True</property> |
3736 | <property name="position">2</property> |
3737 | </packing> |
3738 | </child> |
3739 | <child> |
3740 | <object class="GtkVBox" id="vbox4"> |
3741 | <property name="visible">True</property> |
3742 | + <property name="can_focus">False</property> |
3743 | <child> |
3744 | <object class="GtkLabel" id="day4temphigh"> |
3745 | <property name="visible">True</property> |
3746 | + <property name="can_focus">False</property> |
3747 | <property name="label">High : 26°C</property> |
3748 | </object> |
3749 | <packing> |
3750 | + <property name="expand">True</property> |
3751 | + <property name="fill">True</property> |
3752 | <property name="position">0</property> |
3753 | </packing> |
3754 | </child> |
3755 | <child> |
3756 | <object class="GtkLabel" id="day4templow"> |
3757 | <property name="visible">True</property> |
3758 | + <property name="can_focus">False</property> |
3759 | <property name="label">Low : 14°C</property> |
3760 | </object> |
3761 | <packing> |
3762 | + <property name="expand">True</property> |
3763 | + <property name="fill">True</property> |
3764 | <property name="position">1</property> |
3765 | </packing> |
3766 | </child> |
3767 | </object> |
3768 | <packing> |
3769 | + <property name="expand">True</property> |
3770 | + <property name="fill">True</property> |
3771 | <property name="position">3</property> |
3772 | </packing> |
3773 | </child> |
3774 | @@ -387,16 +490,21 @@ |
3775 | </child> |
3776 | </object> |
3777 | <packing> |
3778 | + <property name="expand">True</property> |
3779 | + <property name="fill">True</property> |
3780 | <property name="position">0</property> |
3781 | </packing> |
3782 | </child> |
3783 | </object> |
3784 | <packing> |
3785 | + <property name="expand">True</property> |
3786 | + <property name="fill">True</property> |
3787 | <property name="position">3</property> |
3788 | </packing> |
3789 | </child> |
3790 | </object> |
3791 | <packing> |
3792 | + <property name="expand">True</property> |
3793 | <property name="fill">False</property> |
3794 | <property name="position">1</property> |
3795 | </packing> |
3796 | @@ -404,26 +512,31 @@ |
3797 | <child> |
3798 | <object class="GtkHSeparator" id="hseparator1"> |
3799 | <property name="visible">True</property> |
3800 | + <property name="can_focus">False</property> |
3801 | </object> |
3802 | <packing> |
3803 | <property name="expand">False</property> |
3804 | + <property name="fill">True</property> |
3805 | <property name="position">2</property> |
3806 | </packing> |
3807 | </child> |
3808 | <child> |
3809 | <object class="GtkAlignment" id="alignment1"> |
3810 | <property name="visible">True</property> |
3811 | + <property name="can_focus">False</property> |
3812 | <property name="xalign">1</property> |
3813 | <child> |
3814 | <object class="GtkVButtonBox" id="hbuttonbox1"> |
3815 | <property name="visible">True</property> |
3816 | + <property name="can_focus">False</property> |
3817 | <child> |
3818 | <object class="GtkButton" id="closebtn"> |
3819 | <property name="label">gtk-close</property> |
3820 | <property name="visible">True</property> |
3821 | + <property name="can_focus">False</property> |
3822 | <property name="receives_default">True</property> |
3823 | <property name="use_stock">True</property> |
3824 | - <signal name="clicked" handler="close"/> |
3825 | + <signal name="clicked" handler="close" swapped="no"/> |
3826 | </object> |
3827 | <packing> |
3828 | <property name="expand">False</property> |
3829 | @@ -435,6 +548,8 @@ |
3830 | </child> |
3831 | </object> |
3832 | <packing> |
3833 | + <property name="expand">True</property> |
3834 | + <property name="fill">True</property> |
3835 | <property name="position">3</property> |
3836 | </packing> |
3837 | </child> |
3838 | |
3839 | === modified file 'data/ui/PreferencesDialog.ui' |
3840 | --- data/ui/PreferencesDialog.ui 2012-03-12 04:53:24 +0000 |
3841 | +++ data/ui/PreferencesDialog.ui 2013-05-22 05:08:27 +0000 |
3842 | @@ -1,19 +1,25 @@ |
3843 | <?xml version="1.0" encoding="UTF-8"?> |
3844 | <interface> |
3845 | - <requires lib="gtk+" version="2.16"/> |
3846 | + <!-- interface-requires gtk+ 3.0 --> |
3847 | <!-- interface-requires preferences_dialog 1.0 --> |
3848 | - <!-- interface-naming-policy project-wide --> |
3849 | <object class="GtkListStore" id="citieslist"> |
3850 | <columns> |
3851 | <!-- column-name Label --> |
3852 | <column type="gchararray"/> |
3853 | <!-- column-name City --> |
3854 | <column type="gchararray"/> |
3855 | - <!-- column-name Location Details --> |
3856 | + <!-- column-name Location --> |
3857 | <column type="gchararray"/> |
3858 | </columns> |
3859 | </object> |
3860 | + <object class="GtkAdjustment" id="rate"> |
3861 | + <property name="lower">1</property> |
3862 | + <property name="upper">30</property> |
3863 | + <property name="step_increment">1</property> |
3864 | + <property name="page_increment">10</property> |
3865 | + </object> |
3866 | <object class="PreferencesDialog" id="preferences_dialog"> |
3867 | + <property name="can_focus">False</property> |
3868 | <property name="border_width">12</property> |
3869 | <property name="title" translatable="yes">Weather Indicator Preferences</property> |
3870 | <property name="window_position">mouse</property> |
3871 | @@ -22,62 +28,118 @@ |
3872 | <property name="icon">../media/icon.png</property> |
3873 | <property name="type_hint">normal</property> |
3874 | <child internal-child="vbox"> |
3875 | - <object class="GtkVBox" id="dialog-vbox1"> |
3876 | + <object class="GtkBox" id="dialog-vbox1"> |
3877 | <property name="visible">True</property> |
3878 | + <property name="can_focus">False</property> |
3879 | + <property name="orientation">vertical</property> |
3880 | <property name="spacing">6</property> |
3881 | + <child internal-child="action_area"> |
3882 | + <object class="GtkButtonBox" id="dialog-action_area1"> |
3883 | + <property name="visible">True</property> |
3884 | + <property name="can_focus">False</property> |
3885 | + <property name="layout_style">end</property> |
3886 | + <child> |
3887 | + <object class="GtkButton" id="cancel_button"> |
3888 | + <property name="label">gtk-cancel</property> |
3889 | + <property name="visible">True</property> |
3890 | + <property name="can_focus">False</property> |
3891 | + <property name="receives_default">True</property> |
3892 | + <property name="use_stock">True</property> |
3893 | + <signal name="clicked" handler="cancel" swapped="no"/> |
3894 | + </object> |
3895 | + <packing> |
3896 | + <property name="expand">False</property> |
3897 | + <property name="fill">False</property> |
3898 | + <property name="position">0</property> |
3899 | + </packing> |
3900 | + </child> |
3901 | + <child> |
3902 | + <object class="GtkButton" id="ok_button"> |
3903 | + <property name="label">gtk-ok</property> |
3904 | + <property name="visible">True</property> |
3905 | + <property name="sensitive">False</property> |
3906 | + <property name="can_focus">False</property> |
3907 | + <property name="receives_default">True</property> |
3908 | + <property name="use_stock">True</property> |
3909 | + <signal name="clicked" handler="ok" swapped="no"/> |
3910 | + </object> |
3911 | + <packing> |
3912 | + <property name="expand">False</property> |
3913 | + <property name="fill">False</property> |
3914 | + <property name="position">1</property> |
3915 | + </packing> |
3916 | + </child> |
3917 | + </object> |
3918 | + <packing> |
3919 | + <property name="expand">False</property> |
3920 | + <property name="fill">False</property> |
3921 | + <property name="pack_type">end</property> |
3922 | + <property name="position">0</property> |
3923 | + </packing> |
3924 | + </child> |
3925 | <child> |
3926 | <object class="GtkNotebook" id="notebook1"> |
3927 | <property name="visible">True</property> |
3928 | <property name="can_focus">True</property> |
3929 | <child> |
3930 | - <object class="GtkVBox" id="vboxpreferences"> |
3931 | + <object class="GtkBox" id="vboxpreferences"> |
3932 | <property name="visible">True</property> |
3933 | + <property name="can_focus">False</property> |
3934 | <property name="border_width">12</property> |
3935 | + <property name="orientation">vertical</property> |
3936 | <property name="spacing">12</property> |
3937 | <child> |
3938 | - <object class="GtkVBox" id="vbox10"> |
3939 | + <object class="GtkBox" id="vbox10"> |
3940 | <property name="visible">True</property> |
3941 | + <property name="can_focus">False</property> |
3942 | + <property name="orientation">vertical</property> |
3943 | <property name="homogeneous">True</property> |
3944 | <child> |
3945 | <object class="GtkCheckButton" id="enableindicator"> |
3946 | <property name="label" translatable="yes">Enable the Weather Indicator Applet</property> |
3947 | <property name="can_focus">True</property> |
3948 | <property name="receives_default">False</property> |
3949 | - <property name="xalign">0.02</property> |
3950 | + <property name="xalign">0.019999999552965164</property> |
3951 | <property name="active">True</property> |
3952 | <property name="draw_indicator">True</property> |
3953 | </object> |
3954 | <packing> |
3955 | <property name="expand">False</property> |
3956 | + <property name="fill">True</property> |
3957 | <property name="position">0</property> |
3958 | </packing> |
3959 | </child> |
3960 | <child> |
3961 | <object class="GtkCheckButton" id="show_label"> |
3962 | - <property name="visible">True</property> |
3963 | <property name="label" translatable="yes">Show temperature near indicator</property> |
3964 | + <property name="visible">True</property> |
3965 | <property name="can_focus">True</property> |
3966 | <property name="receives_default">False</property> |
3967 | + <property name="xalign">0</property> |
3968 | <property name="draw_indicator">True</property> |
3969 | </object> |
3970 | <packing> |
3971 | <property name="expand">False</property> |
3972 | + <property name="fill">True</property> |
3973 | <property name="position">1</property> |
3974 | </packing> |
3975 | </child> |
3976 | <child> |
3977 | - <object class="GtkHBox" id="hbox_updateevery"> |
3978 | + <object class="GtkBox" id="hbox_updateevery"> |
3979 | <property name="visible">True</property> |
3980 | + <property name="can_focus">False</property> |
3981 | <property name="spacing">6</property> |
3982 | <child> |
3983 | <object class="GtkLabel" id="updateevery"> |
3984 | <property name="visible">True</property> |
3985 | + <property name="can_focus">False</property> |
3986 | <property name="xalign">1</property> |
3987 | <property name="label" translatable="yes">Update every</property> |
3988 | <property name="use_markup">True</property> |
3989 | </object> |
3990 | <packing> |
3991 | <property name="expand">False</property> |
3992 | + <property name="fill">True</property> |
3993 | <property name="position">0</property> |
3994 | </packing> |
3995 | </child> |
3996 | @@ -86,6 +148,7 @@ |
3997 | <property name="visible">True</property> |
3998 | <property name="can_focus">True</property> |
3999 | <property name="invisible_char">•</property> |
4000 | + <property name="invisible_char_set">True</property> |
4001 | <property name="adjustment">rate</property> |
4002 | </object> |
4003 | <packing> |
4004 | @@ -97,31 +160,39 @@ |
4005 | <child> |
4006 | <object class="GtkLabel" id="label4"> |
4007 | <property name="visible">True</property> |
4008 | + <property name="can_focus">False</property> |
4009 | <property name="xalign">0</property> |
4010 | <property name="label" translatable="yes">minutes</property> |
4011 | </object> |
4012 | <packing> |
4013 | <property name="expand">False</property> |
4014 | + <property name="fill">True</property> |
4015 | <property name="position">2</property> |
4016 | </packing> |
4017 | </child> |
4018 | </object> |
4019 | <packing> |
4020 | + <property name="expand">True</property> |
4021 | + <property name="fill">True</property> |
4022 | <property name="position">2</property> |
4023 | </packing> |
4024 | </child> |
4025 | </object> |
4026 | <packing> |
4027 | <property name="expand">False</property> |
4028 | + <property name="fill">True</property> |
4029 | <property name="position">0</property> |
4030 | </packing> |
4031 | </child> |
4032 | <child> |
4033 | - <object class="GtkVBox" id="vbox1"> |
4034 | + <object class="GtkBox" id="vbox1"> |
4035 | <property name="visible">True</property> |
4036 | + <property name="can_focus">False</property> |
4037 | + <property name="orientation">vertical</property> |
4038 | <child> |
4039 | <object class="GtkLabel" id="notificationslabel"> |
4040 | <property name="visible">True</property> |
4041 | + <property name="can_focus">False</property> |
4042 | <property name="xalign">0</property> |
4043 | <property name="ypad">3</property> |
4044 | <property name="label" translatable="yes"><b>Notifications</b></property> |
4045 | @@ -129,16 +200,20 @@ |
4046 | </object> |
4047 | <packing> |
4048 | <property name="expand">False</property> |
4049 | + <property name="fill">True</property> |
4050 | <property name="position">0</property> |
4051 | </packing> |
4052 | </child> |
4053 | <child> |
4054 | <object class="GtkAlignment" id="alignment1"> |
4055 | <property name="visible">True</property> |
4056 | + <property name="can_focus">False</property> |
4057 | <property name="left_padding">12</property> |
4058 | <child> |
4059 | - <object class="GtkVBox" id="vbox4"> |
4060 | + <object class="GtkBox" id="vbox4"> |
4061 | <property name="visible">True</property> |
4062 | + <property name="can_focus">False</property> |
4063 | + <property name="orientation">vertical</property> |
4064 | <property name="homogeneous">True</property> |
4065 | <child> |
4066 | <object class="GtkRadioButton" id="nonotif"> |
4067 | @@ -146,6 +221,7 @@ |
4068 | <property name="visible">True</property> |
4069 | <property name="can_focus">True</property> |
4070 | <property name="receives_default">False</property> |
4071 | + <property name="xalign">0.5</property> |
4072 | <property name="active">True</property> |
4073 | <property name="draw_indicator">True</property> |
4074 | </object> |
4075 | @@ -161,6 +237,7 @@ |
4076 | <property name="visible">True</property> |
4077 | <property name="can_focus">True</property> |
4078 | <property name="receives_default">False</property> |
4079 | + <property name="xalign">0.5</property> |
4080 | <property name="draw_indicator">True</property> |
4081 | <property name="group">nonotif</property> |
4082 | </object> |
4083 | @@ -176,6 +253,7 @@ |
4084 | <property name="visible">True</property> |
4085 | <property name="can_focus">True</property> |
4086 | <property name="receives_default">False</property> |
4087 | + <property name="xalign">0.5</property> |
4088 | <property name="draw_indicator">True</property> |
4089 | <property name="group">nonotif</property> |
4090 | </object> |
4091 | @@ -189,12 +267,15 @@ |
4092 | </child> |
4093 | </object> |
4094 | <packing> |
4095 | + <property name="expand">True</property> |
4096 | + <property name="fill">True</property> |
4097 | <property name="position">1</property> |
4098 | </packing> |
4099 | </child> |
4100 | <child> |
4101 | <object class="GtkLabel" id="weathersourcelabel"> |
4102 | <property name="visible">True</property> |
4103 | + <property name="can_focus">False</property> |
4104 | <property name="xalign">0</property> |
4105 | <property name="ypad">3</property> |
4106 | <property name="label" translatable="yes"><b>Weather Data Source</b></property> |
4107 | @@ -202,40 +283,46 @@ |
4108 | </object> |
4109 | <packing> |
4110 | <property name="expand">False</property> |
4111 | + <property name="fill">True</property> |
4112 | <property name="position">2</property> |
4113 | </packing> |
4114 | </child> |
4115 | <child> |
4116 | <object class="GtkAlignment" id="alignment2"> |
4117 | <property name="visible">True</property> |
4118 | + <property name="can_focus">False</property> |
4119 | <property name="left_padding">12</property> |
4120 | <child> |
4121 | - <object class="GtkVBox" id="vbox5"> |
4122 | + <object class="GtkBox" id="vbox5"> |
4123 | <property name="visible">True</property> |
4124 | + <property name="can_focus">False</property> |
4125 | + <property name="orientation">vertical</property> |
4126 | <property name="homogeneous">True</property> |
4127 | <child> |
4128 | - <object class="GtkRadioButton" id="google"> |
4129 | - <property name="label" translatable="yes">Google</property> |
4130 | - <property name="visible">True</property> |
4131 | - <property name="can_focus">True</property> |
4132 | - <property name="receives_default">False</property> |
4133 | - <property name="active">True</property> |
4134 | - <property name="draw_indicator">True</property> |
4135 | - </object> |
4136 | - <packing> |
4137 | - <property name="expand">False</property> |
4138 | - <property name="fill">False</property> |
4139 | - <property name="position">0</property> |
4140 | - </packing> |
4141 | - </child> |
4142 | - <child> |
4143 | <object class="GtkRadioButton" id="yahoo"> |
4144 | <property name="label" translatable="yes">Yahoo!</property> |
4145 | <property name="visible">True</property> |
4146 | <property name="can_focus">True</property> |
4147 | <property name="receives_default">False</property> |
4148 | - <property name="draw_indicator">True</property> |
4149 | - <property name="group">google</property> |
4150 | + <property name="xalign">0.5</property> |
4151 | + <property name="draw_indicator">True</property> |
4152 | + </object> |
4153 | + <packing> |
4154 | + <property name="expand">False</property> |
4155 | + <property name="fill">False</property> |
4156 | + <property name="position">0</property> |
4157 | + </packing> |
4158 | + </child> |
4159 | + <child> |
4160 | + <object class="GtkRadioButton" id="weather-com"> |
4161 | + <property name="label" translatable="yes">Weather.com</property> |
4162 | + <property name="visible">True</property> |
4163 | + <property name="can_focus">True</property> |
4164 | + <property name="receives_default">False</property> |
4165 | + <property name="xalign">0.5</property> |
4166 | + <property name="active">True</property> |
4167 | + <property name="draw_indicator">True</property> |
4168 | + <property name="group">yahoo</property> |
4169 | </object> |
4170 | <packing> |
4171 | <property name="expand">False</property> |
4172 | @@ -247,12 +334,15 @@ |
4173 | </child> |
4174 | </object> |
4175 | <packing> |
4176 | + <property name="expand">True</property> |
4177 | + <property name="fill">True</property> |
4178 | <property name="position">3</property> |
4179 | </packing> |
4180 | </child> |
4181 | </object> |
4182 | <packing> |
4183 | <property name="expand">False</property> |
4184 | + <property name="fill">True</property> |
4185 | <property name="position">1</property> |
4186 | </packing> |
4187 | </child> |
4188 | @@ -261,6 +351,7 @@ |
4189 | <child type="tab"> |
4190 | <object class="GtkLabel" id="label1"> |
4191 | <property name="visible">True</property> |
4192 | + <property name="can_focus">False</property> |
4193 | <property name="label" translatable="yes">General</property> |
4194 | </object> |
4195 | <packing> |
4196 | @@ -268,16 +359,296 @@ |
4197 | </packing> |
4198 | </child> |
4199 | <child> |
4200 | - <object class="GtkVBox" id="vbox9"> |
4201 | - <property name="visible">True</property> |
4202 | - <property name="border_width">12</property> |
4203 | - <property name="spacing">12</property> |
4204 | - <child> |
4205 | - <object class="GtkVBox" id="vbox2"> |
4206 | - <property name="visible">True</property> |
4207 | + <object class="GtkBox" id="boxdisplay"> |
4208 | + <property name="visible">True</property> |
4209 | + <property name="can_focus">False</property> |
4210 | + <property name="border_width">12</property> |
4211 | + <property name="orientation">vertical</property> |
4212 | + <property name="spacing">12</property> |
4213 | + <child> |
4214 | + <object class="GtkBox" id="box1"> |
4215 | + <property name="visible">True</property> |
4216 | + <property name="can_focus">False</property> |
4217 | + <property name="orientation">vertical</property> |
4218 | + <child> |
4219 | + <object class="GtkLabel" id="conditionslabel"> |
4220 | + <property name="visible">True</property> |
4221 | + <property name="can_focus">False</property> |
4222 | + <property name="xalign">0</property> |
4223 | + <property name="ypad">3</property> |
4224 | + <property name="label" translatable="yes"><b>Weather Conditions</b></property> |
4225 | + <property name="use_markup">True</property> |
4226 | + </object> |
4227 | + <packing> |
4228 | + <property name="expand">False</property> |
4229 | + <property name="fill">True</property> |
4230 | + <property name="position">0</property> |
4231 | + </packing> |
4232 | + </child> |
4233 | + <child> |
4234 | + <object class="GtkAlignment" id="alignment5"> |
4235 | + <property name="visible">True</property> |
4236 | + <property name="can_focus">False</property> |
4237 | + <property name="left_padding">12</property> |
4238 | + <child> |
4239 | + <object class="GtkBox" id="box3"> |
4240 | + <property name="visible">True</property> |
4241 | + <property name="can_focus">False</property> |
4242 | + <property name="orientation">vertical</property> |
4243 | + <property name="homogeneous">True</property> |
4244 | + <child> |
4245 | + <object class="GtkCheckButton" id="show_relative"> |
4246 | + <property name="label" translatable="yes">Relative temperature ("Feels like")</property> |
4247 | + <property name="visible">True</property> |
4248 | + <property name="can_focus">True</property> |
4249 | + <property name="receives_default">False</property> |
4250 | + <property name="xalign">0</property> |
4251 | + <property name="draw_indicator">True</property> |
4252 | + </object> |
4253 | + <packing> |
4254 | + <property name="expand">False</property> |
4255 | + <property name="fill">False</property> |
4256 | + <property name="position">0</property> |
4257 | + </packing> |
4258 | + </child> |
4259 | + <child> |
4260 | + <object class="GtkCheckButton" id="show_wind"> |
4261 | + <property name="label" translatable="yes">Wind speed and direction</property> |
4262 | + <property name="visible">True</property> |
4263 | + <property name="can_focus">True</property> |
4264 | + <property name="receives_default">False</property> |
4265 | + <property name="xalign">0</property> |
4266 | + <property name="active">True</property> |
4267 | + <property name="draw_indicator">True</property> |
4268 | + </object> |
4269 | + <packing> |
4270 | + <property name="expand">False</property> |
4271 | + <property name="fill">False</property> |
4272 | + <property name="position">1</property> |
4273 | + </packing> |
4274 | + </child> |
4275 | + <child> |
4276 | + <object class="GtkCheckButton" id="show_suntimes"> |
4277 | + <property name="label" translatable="yes">Sunrise and sunset times</property> |
4278 | + <property name="visible">True</property> |
4279 | + <property name="can_focus">True</property> |
4280 | + <property name="receives_default">False</property> |
4281 | + <property name="xalign">0</property> |
4282 | + <property name="active">True</property> |
4283 | + <property name="draw_indicator">True</property> |
4284 | + </object> |
4285 | + <packing> |
4286 | + <property name="expand">False</property> |
4287 | + <property name="fill">False</property> |
4288 | + <property name="position">2</property> |
4289 | + </packing> |
4290 | + </child> |
4291 | + </object> |
4292 | + </child> |
4293 | + </object> |
4294 | + <packing> |
4295 | + <property name="expand">True</property> |
4296 | + <property name="fill">True</property> |
4297 | + <property name="position">1</property> |
4298 | + </packing> |
4299 | + </child> |
4300 | + </object> |
4301 | + <packing> |
4302 | + <property name="expand">False</property> |
4303 | + <property name="fill">True</property> |
4304 | + <property name="position">0</property> |
4305 | + </packing> |
4306 | + </child> |
4307 | + <child> |
4308 | + <object class="GtkBox" id="box6"> |
4309 | + <property name="visible">True</property> |
4310 | + <property name="can_focus">False</property> |
4311 | + <property name="orientation">vertical</property> |
4312 | + <child> |
4313 | + <object class="GtkLabel" id="heatlabel"> |
4314 | + <property name="visible">True</property> |
4315 | + <property name="can_focus">False</property> |
4316 | + <property name="xalign">0</property> |
4317 | + <property name="ypad">3</property> |
4318 | + <property name="label" translatable="yes"><b>Relative Heat Formula</b></property> |
4319 | + <property name="use_markup">True</property> |
4320 | + </object> |
4321 | + <packing> |
4322 | + <property name="expand">False</property> |
4323 | + <property name="fill">True</property> |
4324 | + <property name="position">0</property> |
4325 | + </packing> |
4326 | + </child> |
4327 | + <child> |
4328 | + <object class="GtkAlignment" id="alignment6"> |
4329 | + <property name="visible">True</property> |
4330 | + <property name="can_focus">False</property> |
4331 | + <property name="left_padding">12</property> |
4332 | + <child> |
4333 | + <object class="GtkBox" id="box4"> |
4334 | + <property name="visible">True</property> |
4335 | + <property name="can_focus">False</property> |
4336 | + <property name="orientation">vertical</property> |
4337 | + <property name="homogeneous">True</property> |
4338 | + <child> |
4339 | + <object class="GtkRadioButton" id="heatindex"> |
4340 | + <property name="label" translatable="yes">Heat Index (US)</property> |
4341 | + <property name="visible">True</property> |
4342 | + <property name="can_focus">True</property> |
4343 | + <property name="receives_default">False</property> |
4344 | + <property name="xalign">0</property> |
4345 | + <property name="active">True</property> |
4346 | + <property name="draw_indicator">True</property> |
4347 | + </object> |
4348 | + <packing> |
4349 | + <property name="expand">False</property> |
4350 | + <property name="fill">False</property> |
4351 | + <property name="position">0</property> |
4352 | + </packing> |
4353 | + </child> |
4354 | + <child> |
4355 | + <object class="GtkRadioButton" id="humidex"> |
4356 | + <property name="label" translatable="yes">Humidex (Canada)</property> |
4357 | + <property name="visible">True</property> |
4358 | + <property name="can_focus">True</property> |
4359 | + <property name="receives_default">False</property> |
4360 | + <property name="xalign">0</property> |
4361 | + <property name="draw_indicator">True</property> |
4362 | + <property name="group">heatindex</property> |
4363 | + </object> |
4364 | + <packing> |
4365 | + <property name="expand">False</property> |
4366 | + <property name="fill">False</property> |
4367 | + <property name="position">1</property> |
4368 | + </packing> |
4369 | + </child> |
4370 | + </object> |
4371 | + </child> |
4372 | + </object> |
4373 | + <packing> |
4374 | + <property name="expand">False</property> |
4375 | + <property name="fill">True</property> |
4376 | + <property name="position">1</property> |
4377 | + </packing> |
4378 | + </child> |
4379 | + </object> |
4380 | + <packing> |
4381 | + <property name="expand">False</property> |
4382 | + <property name="fill">True</property> |
4383 | + <property name="position">1</property> |
4384 | + </packing> |
4385 | + </child> |
4386 | + <child> |
4387 | + <object class="GtkBox" id="box2"> |
4388 | + <property name="visible">True</property> |
4389 | + <property name="can_focus">False</property> |
4390 | + <property name="orientation">vertical</property> |
4391 | + <child> |
4392 | + <object class="GtkLabel" id="chilllabel"> |
4393 | + <property name="visible">True</property> |
4394 | + <property name="can_focus">False</property> |
4395 | + <property name="xalign">0</property> |
4396 | + <property name="ypad">3</property> |
4397 | + <property name="label" translatable="yes"><b>Wind Chill Formula</b></property> |
4398 | + <property name="use_markup">True</property> |
4399 | + </object> |
4400 | + <packing> |
4401 | + <property name="expand">False</property> |
4402 | + <property name="fill">True</property> |
4403 | + <property name="position">0</property> |
4404 | + </packing> |
4405 | + </child> |
4406 | + <child> |
4407 | + <object class="GtkAlignment" id="alignment7"> |
4408 | + <property name="visible">True</property> |
4409 | + <property name="can_focus">False</property> |
4410 | + <property name="left_padding">12</property> |
4411 | + <child> |
4412 | + <object class="GtkBox" id="box7"> |
4413 | + <property name="visible">True</property> |
4414 | + <property name="can_focus">False</property> |
4415 | + <property name="orientation">vertical</property> |
4416 | + <property name="homogeneous">True</property> |
4417 | + <child> |
4418 | + <object class="GtkRadioButton" id="wctindex"> |
4419 | + <property name="label" translatable="yes">JAG/TI Wind Chill Index (US/UK/Canada)</property> |
4420 | + <property name="visible">True</property> |
4421 | + <property name="can_focus">True</property> |
4422 | + <property name="receives_default">False</property> |
4423 | + <property name="xalign">0</property> |
4424 | + <property name="active">True</property> |
4425 | + <property name="draw_indicator">True</property> |
4426 | + </object> |
4427 | + <packing> |
4428 | + <property name="expand">False</property> |
4429 | + <property name="fill">False</property> |
4430 | + <property name="position">0</property> |
4431 | + </packing> |
4432 | + </child> |
4433 | + <child> |
4434 | + <object class="GtkRadioButton" id="aatindex"> |
4435 | + <property name="label" translatable="yes">Apparent Temperature (Australia)</property> |
4436 | + <property name="visible">True</property> |
4437 | + <property name="can_focus">True</property> |
4438 | + <property name="receives_default">False</property> |
4439 | + <property name="xalign">0</property> |
4440 | + <property name="draw_indicator">True</property> |
4441 | + <property name="group">wctindex</property> |
4442 | + </object> |
4443 | + <packing> |
4444 | + <property name="expand">False</property> |
4445 | + <property name="fill">False</property> |
4446 | + <property name="position">1</property> |
4447 | + </packing> |
4448 | + </child> |
4449 | + </object> |
4450 | + </child> |
4451 | + </object> |
4452 | + <packing> |
4453 | + <property name="expand">False</property> |
4454 | + <property name="fill">True</property> |
4455 | + <property name="position">1</property> |
4456 | + </packing> |
4457 | + </child> |
4458 | + </object> |
4459 | + <packing> |
4460 | + <property name="expand">False</property> |
4461 | + <property name="fill">True</property> |
4462 | + <property name="position">2</property> |
4463 | + </packing> |
4464 | + </child> |
4465 | + </object> |
4466 | + <packing> |
4467 | + <property name="position">1</property> |
4468 | + </packing> |
4469 | + </child> |
4470 | + <child type="tab"> |
4471 | + <object class="GtkLabel" id="label5"> |
4472 | + <property name="visible">True</property> |
4473 | + <property name="can_focus">False</property> |
4474 | + <property name="label" translatable="yes">Conditions</property> |
4475 | + </object> |
4476 | + <packing> |
4477 | + <property name="position">1</property> |
4478 | + <property name="tab_fill">False</property> |
4479 | + </packing> |
4480 | + </child> |
4481 | + <child> |
4482 | + <object class="GtkBox" id="vbox9"> |
4483 | + <property name="visible">True</property> |
4484 | + <property name="can_focus">False</property> |
4485 | + <property name="border_width">12</property> |
4486 | + <property name="orientation">vertical</property> |
4487 | + <property name="spacing">12</property> |
4488 | + <child> |
4489 | + <object class="GtkBox" id="vbox2"> |
4490 | + <property name="visible">True</property> |
4491 | + <property name="can_focus">False</property> |
4492 | + <property name="orientation">vertical</property> |
4493 | <child> |
4494 | <object class="GtkLabel" id="Metric System Unit"> |
4495 | <property name="visible">True</property> |
4496 | + <property name="can_focus">False</property> |
4497 | <property name="xalign">0</property> |
4498 | <property name="ypad">3</property> |
4499 | <property name="label" translatable="yes"><b>Temperature Scale</b></property> |
4500 | @@ -285,16 +656,20 @@ |
4501 | </object> |
4502 | <packing> |
4503 | <property name="expand">False</property> |
4504 | + <property name="fill">True</property> |
4505 | <property name="position">0</property> |
4506 | </packing> |
4507 | </child> |
4508 | <child> |
4509 | <object class="GtkAlignment" id="alignment3"> |
4510 | <property name="visible">True</property> |
4511 | + <property name="can_focus">False</property> |
4512 | <property name="left_padding">12</property> |
4513 | <child> |
4514 | - <object class="GtkVBox" id="vbox6"> |
4515 | + <object class="GtkBox" id="box5"> |
4516 | <property name="visible">True</property> |
4517 | + <property name="can_focus">False</property> |
4518 | + <property name="orientation">vertical</property> |
4519 | <property name="homogeneous">True</property> |
4520 | <child> |
4521 | <object class="GtkRadioButton" id="imperial"> |
4522 | @@ -302,6 +677,7 @@ |
4523 | <property name="visible">True</property> |
4524 | <property name="can_focus">True</property> |
4525 | <property name="receives_default">False</property> |
4526 | + <property name="xalign">0</property> |
4527 | <property name="active">True</property> |
4528 | <property name="draw_indicator">True</property> |
4529 | </object> |
4530 | @@ -317,6 +693,7 @@ |
4531 | <property name="visible">True</property> |
4532 | <property name="can_focus">True</property> |
4533 | <property name="receives_default">False</property> |
4534 | + <property name="xalign">0</property> |
4535 | <property name="draw_indicator">True</property> |
4536 | <property name="group">imperial</property> |
4537 | </object> |
4538 | @@ -331,21 +708,26 @@ |
4539 | </object> |
4540 | <packing> |
4541 | <property name="expand">False</property> |
4542 | + <property name="fill">True</property> |
4543 | <property name="position">1</property> |
4544 | </packing> |
4545 | </child> |
4546 | </object> |
4547 | <packing> |
4548 | <property name="expand">False</property> |
4549 | + <property name="fill">True</property> |
4550 | <property name="position">0</property> |
4551 | </packing> |
4552 | </child> |
4553 | <child> |
4554 | - <object class="GtkVBox" id="vbox3"> |
4555 | + <object class="GtkBox" id="vbox3"> |
4556 | <property name="visible">True</property> |
4557 | + <property name="can_focus">False</property> |
4558 | + <property name="orientation">vertical</property> |
4559 | <child> |
4560 | <object class="GtkLabel" id="Wind Speed Unit"> |
4561 | <property name="visible">True</property> |
4562 | + <property name="can_focus">False</property> |
4563 | <property name="xalign">0</property> |
4564 | <property name="ypad">3</property> |
4565 | <property name="label" translatable="yes"><b>Wind Speed Unit</b></property> |
4566 | @@ -353,23 +735,28 @@ |
4567 | </object> |
4568 | <packing> |
4569 | <property name="expand">False</property> |
4570 | + <property name="fill">True</property> |
4571 | <property name="position">0</property> |
4572 | </packing> |
4573 | </child> |
4574 | <child> |
4575 | <object class="GtkAlignment" id="alignment4"> |
4576 | <property name="visible">True</property> |
4577 | + <property name="can_focus">False</property> |
4578 | <property name="left_padding">12</property> |
4579 | <child> |
4580 | - <object class="GtkVBox" id="vbox7"> |
4581 | + <object class="GtkBox" id="vbox7"> |
4582 | <property name="visible">True</property> |
4583 | + <property name="can_focus">False</property> |
4584 | + <property name="orientation">vertical</property> |
4585 | <property name="homogeneous">True</property> |
4586 | <child> |
4587 | <object class="GtkRadioButton" id="mps"> |
4588 | - <property name="label" translatable="yes">Meter per second (m/s)</property> |
4589 | + <property name="label" translatable="yes">Meters per second (m/s)</property> |
4590 | <property name="visible">True</property> |
4591 | <property name="can_focus">True</property> |
4592 | <property name="receives_default">False</property> |
4593 | + <property name="xalign">0.5</property> |
4594 | <property name="active">True</property> |
4595 | <property name="draw_indicator">True</property> |
4596 | </object> |
4597 | @@ -385,6 +772,7 @@ |
4598 | <property name="visible">True</property> |
4599 | <property name="can_focus">True</property> |
4600 | <property name="receives_default">False</property> |
4601 | + <property name="xalign">0.5</property> |
4602 | <property name="draw_indicator">True</property> |
4603 | <property name="group">mps</property> |
4604 | </object> |
4605 | @@ -400,6 +788,7 @@ |
4606 | <property name="visible">True</property> |
4607 | <property name="can_focus">True</property> |
4608 | <property name="receives_default">False</property> |
4609 | + <property name="xalign">0.5</property> |
4610 | <property name="draw_indicator">True</property> |
4611 | <property name="group">mps</property> |
4612 | </object> |
4613 | @@ -411,10 +800,11 @@ |
4614 | </child> |
4615 | <child> |
4616 | <object class="GtkRadioButton" id="beaufort"> |
4617 | - <property name="label" translatable="yes">Beaufort</property> |
4618 | + <property name="label" translatable="yes">Beaufort number</property> |
4619 | <property name="visible">True</property> |
4620 | <property name="can_focus">True</property> |
4621 | <property name="receives_default">False</property> |
4622 | + <property name="xalign">0.5</property> |
4623 | <property name="draw_indicator">True</property> |
4624 | <property name="group">mps</property> |
4625 | </object> |
4626 | @@ -430,6 +820,7 @@ |
4627 | <property name="visible">True</property> |
4628 | <property name="can_focus">True</property> |
4629 | <property name="receives_default">False</property> |
4630 | + <property name="xalign">0.5</property> |
4631 | <property name="draw_indicator">True</property> |
4632 | <property name="group">mps</property> |
4633 | </object> |
4634 | @@ -444,41 +835,44 @@ |
4635 | </object> |
4636 | <packing> |
4637 | <property name="expand">False</property> |
4638 | + <property name="fill">True</property> |
4639 | <property name="position">1</property> |
4640 | </packing> |
4641 | </child> |
4642 | </object> |
4643 | <packing> |
4644 | <property name="expand">False</property> |
4645 | + <property name="fill">True</property> |
4646 | <property name="position">1</property> |
4647 | </packing> |
4648 | </child> |
4649 | </object> |
4650 | <packing> |
4651 | - <property name="position">1</property> |
4652 | + <property name="position">2</property> |
4653 | </packing> |
4654 | </child> |
4655 | <child type="tab"> |
4656 | <object class="GtkLabel" id="label2"> |
4657 | <property name="visible">True</property> |
4658 | + <property name="can_focus">False</property> |
4659 | <property name="label" translatable="yes">Units</property> |
4660 | </object> |
4661 | <packing> |
4662 | - <property name="position">1</property> |
4663 | + <property name="position">2</property> |
4664 | <property name="tab_fill">False</property> |
4665 | </packing> |
4666 | </child> |
4667 | <child> |
4668 | - <object class="GtkVBox" id="vbox8"> |
4669 | + <object class="GtkBox" id="vbox8"> |
4670 | <property name="visible">True</property> |
4671 | + <property name="can_focus">False</property> |
4672 | <property name="border_width">12</property> |
4673 | + <property name="orientation">vertical</property> |
4674 | <property name="spacing">6</property> |
4675 | <child> |
4676 | <object class="GtkScrolledWindow" id="scrolledwindow1"> |
4677 | <property name="visible">True</property> |
4678 | <property name="can_focus">True</property> |
4679 | - <property name="hscrollbar_policy">automatic</property> |
4680 | - <property name="vscrollbar_policy">automatic</property> |
4681 | <property name="window_placement_set">True</property> |
4682 | <property name="shadow_type">in</property> |
4683 | <child> |
4684 | @@ -490,6 +884,9 @@ |
4685 | <property name="headers_clickable">False</property> |
4686 | <property name="reorderable">True</property> |
4687 | <property name="search_column">0</property> |
4688 | + <child internal-child="selection"> |
4689 | + <object class="GtkTreeSelection" id="treeview-selection1"/> |
4690 | + </child> |
4691 | <child> |
4692 | <object class="GtkTreeViewColumn" id="City"> |
4693 | <property name="title">City</property> |
4694 | @@ -506,12 +903,15 @@ |
4695 | </child> |
4696 | </object> |
4697 | <packing> |
4698 | + <property name="expand">True</property> |
4699 | + <property name="fill">True</property> |
4700 | <property name="position">0</property> |
4701 | </packing> |
4702 | </child> |
4703 | <child> |
4704 | <object class="GtkHButtonBox" id="hbuttonbox1"> |
4705 | <property name="visible">True</property> |
4706 | + <property name="can_focus">False</property> |
4707 | <property name="spacing">6</property> |
4708 | <property name="layout_style">start</property> |
4709 | <child> |
4710 | @@ -521,7 +921,7 @@ |
4711 | <property name="can_focus">True</property> |
4712 | <property name="receives_default">True</property> |
4713 | <property name="use_stock">True</property> |
4714 | - <signal name="clicked" handler="on_add_location"/> |
4715 | + <signal name="clicked" handler="on_add_location" swapped="no"/> |
4716 | </object> |
4717 | <packing> |
4718 | <property name="expand">False</property> |
4719 | @@ -536,7 +936,7 @@ |
4720 | <property name="can_focus">True</property> |
4721 | <property name="receives_default">True</property> |
4722 | <property name="use_stock">True</property> |
4723 | - <signal name="clicked" handler="on_remove_location"/> |
4724 | + <signal name="clicked" handler="on_remove_location" swapped="no"/> |
4725 | </object> |
4726 | <packing> |
4727 | <property name="expand">False</property> |
4728 | @@ -547,70 +947,33 @@ |
4729 | </object> |
4730 | <packing> |
4731 | <property name="expand">False</property> |
4732 | + <property name="fill">True</property> |
4733 | <property name="position">1</property> |
4734 | </packing> |
4735 | </child> |
4736 | </object> |
4737 | <packing> |
4738 | - <property name="position">2</property> |
4739 | + <property name="position">3</property> |
4740 | </packing> |
4741 | </child> |
4742 | <child type="tab"> |
4743 | <object class="GtkLabel" id="label3"> |
4744 | <property name="visible">True</property> |
4745 | + <property name="can_focus">False</property> |
4746 | <property name="label" translatable="yes">Locations</property> |
4747 | </object> |
4748 | <packing> |
4749 | - <property name="position">2</property> |
4750 | + <property name="position">3</property> |
4751 | <property name="tab_fill">False</property> |
4752 | </packing> |
4753 | </child> |
4754 | </object> |
4755 | <packing> |
4756 | + <property name="expand">False</property> |
4757 | + <property name="fill">True</property> |
4758 | <property name="position">1</property> |
4759 | </packing> |
4760 | </child> |
4761 | - <child internal-child="action_area"> |
4762 | - <object class="GtkHButtonBox" id="dialog-action_area1"> |
4763 | - <property name="visible">True</property> |
4764 | - <property name="layout_style">end</property> |
4765 | - <child> |
4766 | - <object class="GtkButton" id="cancel_button"> |
4767 | - <property name="label">gtk-cancel</property> |
4768 | - <property name="visible">True</property> |
4769 | - <property name="receives_default">True</property> |
4770 | - <property name="use_stock">True</property> |
4771 | - <signal name="clicked" handler="cancel"/> |
4772 | - </object> |
4773 | - <packing> |
4774 | - <property name="expand">False</property> |
4775 | - <property name="fill">False</property> |
4776 | - <property name="position">0</property> |
4777 | - </packing> |
4778 | - </child> |
4779 | - <child> |
4780 | - <object class="GtkButton" id="ok_button"> |
4781 | - <property name="label">gtk-ok</property> |
4782 | - <property name="visible">True</property> |
4783 | - <property name="sensitive">False</property> |
4784 | - <property name="receives_default">True</property> |
4785 | - <property name="use_stock">True</property> |
4786 | - <signal name="clicked" handler="ok"/> |
4787 | - </object> |
4788 | - <packing> |
4789 | - <property name="expand">False</property> |
4790 | - <property name="fill">False</property> |
4791 | - <property name="position">1</property> |
4792 | - </packing> |
4793 | - </child> |
4794 | - </object> |
4795 | - <packing> |
4796 | - <property name="expand">False</property> |
4797 | - <property name="fill">False</property> |
4798 | - <property name="pack_type">end</property> |
4799 | - <property name="position">0</property> |
4800 | - </packing> |
4801 | - </child> |
4802 | </object> |
4803 | </child> |
4804 | <action-widgets> |
4805 | @@ -618,10 +981,4 @@ |
4806 | <action-widget response="-5">ok_button</action-widget> |
4807 | </action-widgets> |
4808 | </object> |
4809 | - <object class="GtkAdjustment" id="rate"> |
4810 | - <property name="lower">1</property> |
4811 | - <property name="upper">30</property> |
4812 | - <property name="step_increment">1</property> |
4813 | - <property name="page_increment">10</property> |
4814 | - </object> |
4815 | </interface> |
4816 | |
4817 | === modified file 'debian/changelog' |
4818 | --- debian/changelog 2012-07-30 04:02:24 +0000 |
4819 | +++ debian/changelog 2013-05-22 05:08:27 +0000 |
4820 | @@ -1,3 +1,38 @@ |
4821 | +indicator-weather (13.05.17-quantal2) quantal; urgency=low |
4822 | + |
4823 | + * Fix for 4-day Forecast display (LP: #1182324) |
4824 | + * 'OK' button in Preferences Dialog is now more responsive |
4825 | + |
4826 | + -- Joshua Tasker <jtasker@gmail.com> Wed, 22 May 2013 00:32:54 +0500 |
4827 | + |
4828 | +indicator-weather (13.05.17~quantal1) quantal; urgency=low |
4829 | + |
4830 | + * Ported to GTK3 and GObject from PyGTK |
4831 | + * Rewrite threading code to avoid dbus-related crashes (LP: #743541) |
4832 | + * Added "feels like" temperature (humidex/heat index/wind chill) |
4833 | + * New "Conditions" tab in Preferences dialog, choose temperature formulas, toggle display of conditions |
4834 | + * Bumped version number to reflect massive changes |
4835 | + |
4836 | + -- Joshua Tasker <jtasker@gmail.com> Sat, 18 May 2013 12:59:03 +0500 |
4837 | + |
4838 | +indicator-weather (12.07.30~quantal2) quantal; urgency=low |
4839 | + |
4840 | + * Don't crash if Yahoo doesn't return conditions |
4841 | + * Fixed a crash when reading saved Places with no location IDs |
4842 | + * Fix units in Forecast when metric is selected |
4843 | + * One-line fix for outdated data sources |
4844 | + |
4845 | + -- Joshua Tasker <jtasker@gmail.com> Sat, 04 May 2013 01:20:38 +0500 |
4846 | + |
4847 | +indicator-weather (12.07.30~quantal1) quantal; urgency=low |
4848 | + |
4849 | + * Fix adding location, now uses Yahoo's YQL service |
4850 | + * Fix for "Forecast", now uses Yahoo instead of Google |
4851 | + * Bump dependency for python-pywapi to 0.3 |
4852 | + * Hide Google radio button on Preferences UI |
4853 | + |
4854 | + -- Joshua Tasker <jtasker@gmail.com> Tue, 09 Apr 2013 02:27:04 +0500 |
4855 | + |
4856 | indicator-weather (12.07.30~precise1) precise; urgency=low |
4857 | * Skip sunset and sunrise check as Earthtools.org is down (LP: #964365) |
4858 | |
4859 | |
4860 | === modified file 'debian/control' |
4861 | --- debian/control 2011-06-22 09:52:46 +0000 |
4862 | +++ debian/control 2013-05-22 05:08:27 +0000 |
4863 | @@ -1,26 +1,33 @@ |
4864 | Source: indicator-weather |
4865 | Section: python |
4866 | Priority: extra |
4867 | -Build-Depends: cdbs (>= 0.4.90-1~), |
4868 | - debhelper (>= 6), |
4869 | - python (>= 2.6.6-3~), |
4870 | - gobject-introspection, |
4871 | - python-distutils-extra (>= 2.10) |
4872 | -Maintainer: Vadim Rutkovsky <roignac@gmail.com> |
4873 | -Standards-Version: 3.8.3 |
4874 | +Build-Depends: debhelper (>= 7.0.50~), |
4875 | + gobject-introspection, |
4876 | + python (>= 2.6.6-3~), |
4877 | + python-distutils-extra (>= 2.10) |
4878 | +X-Python-Version: >= 2.6 |
4879 | +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
4880 | +Standards-Version: 3.9.3 |
4881 | +Homepage: https://launchpad.net/weather-indicator |
4882 | |
4883 | Package: indicator-weather |
4884 | Architecture: all |
4885 | -Depends: ${misc:Depends}, |
4886 | - ${python:Depends}, |
4887 | - libglib2.0-bin, |
4888 | - python-appindicator, |
4889 | - python-notify, |
4890 | - python-gobject, |
4891 | - python-gtk2, |
4892 | - python-gconf, |
4893 | - python-pywapi |
4894 | +Depends: gir1.2-glib-2.0, |
4895 | + gir1.2-gtk-3.0, |
4896 | + gir1.2-appindicator3-0.1, |
4897 | + gnome-icon-theme, |
4898 | + libglib2.0-bin, |
4899 | + libgtk-3-bin, |
4900 | + python-gconf, |
4901 | + python-gi, |
4902 | + python-pywapi (>= 0.3.2 |
4903 | + xdg-utils, |
4904 | + ${misc:Depends}, |
4905 | + ${python:Depends} |
4906 | Recommends: python-apport |
4907 | -Description: A weather indicator for Ubuntu's Indicator Applet |
4908 | - A weather indicator that displays information for one or multiple places |
4909 | - in the world |
4910 | +Description: indicator that displays weather information |
4911 | + Indicator-Weather displays information for one or multiple places |
4912 | + in the world. Current weather status is displayed directly on your |
4913 | + panel and detailed forecasts are no more than a click away. |
4914 | + . |
4915 | + It is implemented using the Indicator Applet API. |
4916 | |
4917 | === modified file 'debian/copyright' |
4918 | --- debian/copyright 2011-06-22 09:52:46 +0000 |
4919 | +++ debian/copyright 2013-05-22 05:08:27 +0000 |
4920 | @@ -1,12 +1,31 @@ |
4921 | -Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat |
4922 | -Upstream-Name: indicator-weather |
4923 | -Upstream-Maintainer: Vadim Rutkovsky <roignac@gmail.com> |
4924 | -Upstream-Source: https://launchpad.net/weather-indicator |
4925 | +Format: http://dep.debian.net/deps/dep5 |
4926 | +Upstream-Name: Indicator-Weather |
4927 | +Upstream-Contact: Vadim Rutkovsky <roignac@gmail.com> |
4928 | +Source: https://launchpad.net/weather-indicator/+download |
4929 | |
4930 | Files: * |
4931 | -Copyright: (C) 2010 Mehdi Rejraji mehd36@gmail.com |
4932 | -Copyright: (C) 2010 Sebastian MacDonald Sebas310@gmail.com |
4933 | -Copyright: (C) 2011 Vadim Rutkovsky <roignac@gmail.com> |
4934 | -License: GPL-3 |
4935 | - The full text of the GPL is distributed in |
4936 | - /usr/share/common-licenses/GPL-3 on Debian systems. |
4937 | +Copyright: 2010, Mehdi Rejraji <mehd36@gmail.com> |
4938 | + 2010, Sebastian MacDonald <Sebas310@gmail.com> |
4939 | + 2011, Vadim Rutkovsky <roignac@gmail.com> |
4940 | + 2013, Joshua Tasker <jtasker@gmail.com> |
4941 | +License: GPL-3 |
4942 | + |
4943 | +Files: debian/* |
4944 | +Copyright: 2011, Andrew Starr-Bochicchio <a.starr.b@gmail.com> |
4945 | +License: GPL-3 |
4946 | + |
4947 | +License: GPL-3 |
4948 | + This package is free software; you can redistribute it and/or modify |
4949 | + it under the terms of the GNU General Public License as published by |
4950 | + the Free Software Foundation; version 3 of the License. |
4951 | + . |
4952 | + This package is distributed in the hope that it will be useful, |
4953 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
4954 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4955 | + GNU General Public License for more details. |
4956 | + . |
4957 | + You should have received a copy of the GNU General Public License |
4958 | + along with this package; if not, write to the Free Software |
4959 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
4960 | + . |
4961 | + On Debian systems, see `/usr/share/common-licenses/GPL-3' |
4962 | |
4963 | === modified file 'debian/indicator-weather.install' |
4964 | --- debian/indicator-weather.install 2011-01-23 14:08:21 +0000 |
4965 | +++ debian/indicator-weather.install 2013-05-22 05:08:27 +0000 |
4966 | @@ -1,1 +1,2 @@ |
4967 | +AUTHORS /usr/share/doc/indicator-weather |
4968 | debian/source_indicator-weather.py usr/share/apport/package-hooks |
4969 | |
4970 | === modified file 'debian/postinst' |
4971 | --- debian/postinst 2011-11-02 15:34:54 +0000 |
4972 | +++ debian/postinst 2013-05-22 05:08:27 +0000 |
4973 | @@ -1,19 +1,18 @@ |
4974 | #!/bin/sh |
4975 | - |
4976 | + |
4977 | +set -e |
4978 | + |
4979 | #DEBHELPER# |
4980 | + |
4981 | echo "Installing indicator-specific icons..." |
4982 | xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon.png weather-indicator |
4983 | xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_unknown_condition.png weather-indicator-unknown |
4984 | xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_connection_error.png weather-indicator-error |
4985 | |
4986 | -#installing dconf schema |
4987 | -echo "Installing indicator dconf schema..." |
4988 | -cp /usr/share/indicator-weather/indicator-weather.gschema.xml /usr/share/glib-2.0/schemas |
4989 | -glib-compile-schemas /usr/share/glib-2.0/schemas |
4990 | - |
4991 | #quick fix for incomplete icon themes |
4992 | echo "Fixing incomplete weather icons..." |
4993 | -if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then |
4994 | +if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ] && \ |
4995 | + [ ! -L "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then |
4996 | ln -s /usr/share/icons/gnome/22x22/status/weather-few-clouds.png /usr/share/icons/gnome/22x22/status/weather-clouds.png |
4997 | ln -s /usr/share/icons/gnome/22x22/status/weather-clouds-night.png /usr/share/icons/gnome/22x22/status/weather-clouds-night.png |
4998 | |
4999 | |
5000 | === modified file 'debian/rules' |
The dbus-related bug I am talking about is this one: https:/ /bugs.launchpad .net/ubuntu/ +source/ indicator- weather/ +bug/743541