Merge lp:~jtasker/weather-indicator/cloudy into lp:weather-indicator

Proposed by Joshua Tasker
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
Reviewer Review Type Date Requested Status
Vadim Rutkovsky Approve
Review via email: mp+164598@code.launchpad.net

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://code.google.com/p/python-weather-api/downloads/detail?name=pywapi-0.3.1.tar.gz

deb packages for pywapi 0.3.1 are available here:
https://launchpad.net/~pywapi-devel

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.

To post a comment you must log in.
Revision history for this message
Joshua Tasker (jtasker) wrote :

The dbus-related bug I am talking about is this one: https://bugs.launchpad.net/ubuntu/+source/indicator-weather/+bug/743541

Revision history for this message
Vadim Rutkovsky (roignac) wrote :

Great, thanks!

Unfortunately, I can't test the changes, but it seems that you've tested them pretty good

review: Approve
lp:~jtasker/weather-indicator/cloudy updated
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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">&lt;b&gt;Home&lt;/b&gt;</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">&lt;b&gt;Orange, Texas&lt;/b&gt;</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">&lt;big&gt;Forecast information cannot be fetched. Connection cannot be established.&lt;/big&gt;</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">&lt;big&gt;Forecast information cannot be fetched. Connection cannot be established.&lt;/big&gt;</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">&lt;big&gt;Today (fri.)&lt;/big&gt;</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">&lt;big&gt;Tomorrow (sat.)&lt;/big&gt;</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">&lt;big&gt;Sunday&lt;/big&gt;</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">&lt;big&gt;Monday&lt;/big&gt;</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">&lt;b&gt;Notifications&lt;/b&gt;</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">&lt;b&gt;Weather Data Source&lt;/b&gt;</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">&lt;b&gt;Weather Conditions&lt;/b&gt;</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">&lt;b&gt;Relative Heat Formula&lt;/b&gt;</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">&lt;b&gt;Wind Chill Formula&lt;/b&gt;</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">&lt;b&gt;Temperature Scale&lt;/b&gt;</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">&lt;b&gt;Wind Speed Unit&lt;/b&gt;</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 diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: