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
=== modified file 'AUTHORS'
--- AUTHORS 2011-03-19 09:27:37 +0000
+++ AUTHORS 2013-05-22 05:08:27 +0000
@@ -1,3 +1,4 @@
1Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com1Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
2Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com2Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
3Copyright (C) 2010 Vadim Rutkovsky roignac@gmail.com3Copyright (C) 2010 Vadim Rutkovsky roignac@gmail.com
4Copyright (C) 2013 Joshua Tasker jtasker@gmail.com
45
=== modified file 'bin/indicator-weather'
--- bin/indicator-weather 2012-07-30 04:02:24 +0000
+++ bin/indicator-weather 2013-05-22 05:08:27 +0000
@@ -4,6 +4,7 @@
4# Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com4# Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
5# Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com5# Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
6# Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com6# Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com
7# Copyright (C) 2013 Joshua Tasker jtasker@gmail.com
7# This program is free software: you can redistribute it and/or modify it8# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published9# under the terms of the GNU General Public License version 3, as published
9# by the Free Software Foundation.10# by the Free Software Foundation.
@@ -17,29 +18,26 @@
17# with this program. If not, see <http://www.gnu.org/licenses/>.18# with this program. If not, see <http://www.gnu.org/licenses/>.
18### END LICENSE19### END LICENSE
1920
20try:21from gi.repository import Gio, GLib, Gtk, Gdk, Notify, GObject, GdkPixbuf
21 from gi.repository import Gio22from gi.repository import AppIndicator3 as AppIndicator
22except ImportError:23
23 pass
24import sys, os, shutil, tempfile24import sys, os, shutil, tempfile
25import gtk, pygtk, gobject, pynotify25
26pygtk.require('2.0')
27import appindicator
28import urllib2, urllib26import urllib2, urllib
29from urllib import urlencode27from urllib import urlencode
30import re28import re
31import locale29import locale
32from xml.dom.minidom import parseString30from xml.dom.minidom import parseString
33import datetime31import datetime
34import dbus32#import dbus
35import time33import time
36import traceback34import traceback
37import types35import types
38# Will be used for humidex36from math import exp
39#import math
40import commands, threading37import commands, threading
41import logging, logging.handlers38import logging, logging.handlers
42import pywapi39import pywapi
40import Queue
4341
44import gettext42import gettext
45from gettext import gettext as _43from gettext import gettext as _
@@ -55,7 +53,7 @@
55 sys.path.insert(0, PROJECT_ROOT_DIRECTORY)53 sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
56 os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses54 os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses
5755
58VERSION = "12.07.30 'Cloudy 10'"56VERSION = "13.05.26 'Rainy'"
5957
60from indicator_weather.helpers import *58from indicator_weather.helpers import *
6159
@@ -69,11 +67,16 @@
69 WEATHER_KEY = 'weather'67 WEATHER_KEY = 'weather'
70 LOCATIONS_KEY = 'locations'68 LOCATIONS_KEY = 'locations'
71 INDICATOR_DISPLAY = 'show_label'69 INDICATOR_DISPLAY = 'show_label'
70 RELATIVE_DISPLAY = 'show_relative'
71 WIND_DISPLAY = 'show_wind'
72 SUNTIMES_DISPLAY = 'show_suntimes'
72 NOTIFICATIONS = 'notif'73 NOTIFICATIONS = 'notif'
73 WEATHER_SOURCE = 'data_source'74 WEATHER_SOURCE = 'data_source'
74 REFRESH_RATE = 'refresh_rate'75 REFRESH_RATE = 'refresh_rate'
75 METRIC_SYSTEM = 'unit'76 METRIC_SYSTEM = 'unit'
76 WIND_UNIT = 'wind'77 WIND_UNIT = 'wind'
78 HEAT_ESTIMATE = 'heat'
79 CHILL_ESTIMATE = 'chill'
77 PLACECHOSEN = 'placechosen'80 PLACECHOSEN = 'placechosen'
78 PLACES = 'places'81 PLACES = 'places'
7982
@@ -84,48 +87,70 @@
84 INDICATOR_DISPLAY : {87 INDICATOR_DISPLAY : {
85 INFO_TYPE : types.IntType,88 INFO_TYPE : types.IntType,
86 INFO_SETTING : 'indicator-display'89 INFO_SETTING : 'indicator-display'
87 },90 },
91 RELATIVE_DISPLAY : {
92 INFO_TYPE : types.BooleanType,
93 INFO_SETTING : 'relative-display'
94 },
95 WIND_DISPLAY : {
96 INFO_TYPE : types.BooleanType,
97 INFO_SETTING : 'wind-display'
98 },
99 SUNTIMES_DISPLAY : {
100 INFO_TYPE : types.BooleanType,
101 INFO_SETTING : 'suntimes-display'
102 },
88 NOTIFICATIONS : {103 NOTIFICATIONS : {
89 INFO_TYPE : types.StringType,104 INFO_TYPE : types.StringType,
90 INFO_SETTING : 'notifications'105 INFO_SETTING : 'notifications'
91 },106 },
92 WEATHER_SOURCE : {107 WEATHER_SOURCE : {
93 INFO_TYPE : types.StringType,108 INFO_TYPE : types.StringType,
94 INFO_SETTING : 'weather-source'109 INFO_SETTING : 'weather-source'
95 },110 },
96 REFRESH_RATE : {111 REFRESH_RATE : {
97 INFO_TYPE : types.IntType,112 INFO_TYPE : types.IntType,
98 INFO_SETTING : 'refresh-rate'113 INFO_SETTING : 'refresh-rate'
99 },114 },
100 METRIC_SYSTEM : {115 METRIC_SYSTEM : {
101 INFO_TYPE : types.StringType,116 INFO_TYPE : types.StringType,
102 INFO_SETTING : 'metric-system'117 INFO_SETTING : 'metric-system'
103 },118 },
104 WIND_UNIT : {119 WIND_UNIT : {
105 INFO_TYPE : types.StringType,120 INFO_TYPE : types.StringType,
106 INFO_SETTING : 'wind-unit'121 INFO_SETTING : 'wind-unit'
107 },122 },
123 HEAT_ESTIMATE: {
124 INFO_TYPE : types.StringType,
125 INFO_SETTING : 'heat-estimate'
126 },
127 CHILL_ESTIMATE: {
128 INFO_TYPE : types.StringType,
129 INFO_SETTING : 'chill-estimate'
130 },
108 PLACECHOSEN : {131 PLACECHOSEN : {
109 INFO_TYPE : types.IntType,132 INFO_TYPE : types.IntType,
110 INFO_SETTING: 'placechosen'133 INFO_SETTING: 'placechosen'
111 },134 },
112 PLACES : {135 PLACES : {
113 INFO_TYPE : types.ListType,136 INFO_TYPE : types.ListType,
114 INFO_SETTING: 'places'137 INFO_SETTING: 'places'
115 },138 },
116 }139 }
117140
118 # Open the DB
119 def prepare_settings_store(self):141 def prepare_settings_store(self):
142 """ Open the DB """
120 log.debug("Settings: preparing settings store")143 log.debug("Settings: preparing settings store")
121 try:144 try:
122 self.db = Gio.Settings.new(self.BASE_KEY)145 self.db = Gio.Settings.new(self.BASE_KEY)
123 except Exception as e:146 except Exception as e:
124 log.debug("Settings: exception occurred while opening settings:\n %s" % str(e))147 log.debug("Settings: exception occurred while "
148 "opening settings:\n %s" % str(e))
125149
126 # Make sure autostart file is installed. Inspired by GTG.
127 def check_autostart(self):150 def check_autostart(self):
128 autostart_dir = os.path.join(os.path.expanduser("~"),".config/autostart/")151 """ Make sure autostart file is installed. Inspired by GTG. """
152 autostart_dir = os.path.join(os.path.expanduser("~"),
153 ".config/autostart/")
129 autostart_file = "indicator-weather.desktop"154 autostart_file = "indicator-weather.desktop"
130 autostart_path = os.path.join(autostart_dir, autostart_file)155 autostart_path = os.path.join(autostart_dir, autostart_file)
131 if not os.path.isfile(autostart_path):156 if not os.path.isfile(autostart_path):
@@ -135,12 +160,12 @@
135 "/usr/local/share/applications"]160 "/usr/local/share/applications"]
136 this_directory = os.path.dirname(os.path.abspath(__file__))161 this_directory = os.path.dirname(os.path.abspath(__file__))
137 for path in desktop_file_directories:162 for path in desktop_file_directories:
138 fullpath = os.path.normpath(os.path.join(this_directory, path, \163 fullpath = os.path.normpath(os.path.join(this_directory, path,
139 autostart_file))164 autostart_file))
140 if os.path.isfile(fullpath):165 if os.path.isfile(fullpath):
141 desktop_file_path = fullpath166 desktop_file_path = fullpath
142 break167 break
143 #If we have found the desktop file, we make a link to in in168 # If we have found the desktop file, we make a link to in in
144 # autostart_path.169 # autostart_path.
145 if desktop_file_path:170 if desktop_file_path:
146 if not os.path.exists(autostart_dir):171 if not os.path.exists(autostart_dir):
@@ -149,8 +174,8 @@
149 log.debug("Installing autostart file.")174 log.debug("Installing autostart file.")
150 os.symlink(desktop_file_path, autostart_path)175 os.symlink(desktop_file_path, autostart_path)
151176
152 # Get a value of the setting
153 def get_value(self, setting, return_id = False):177 def get_value(self, setting, return_id = False):
178 """ Get a value of the specified setting """
154 log.debug("Settings: getting value for %s" % setting)179 log.debug("Settings: getting value for %s" % setting)
155 setting_name = Settings.INFO[setting][INFO_SETTING]180 setting_name = Settings.INFO[setting][INFO_SETTING]
156 try:181 try:
@@ -162,15 +187,15 @@
162 types.ListType: self.db.get_string,187 types.ListType: self.db.get_string,
163 types.DictType: self.db.get_string,188 types.DictType: self.db.get_string,
164 types.NoneType: self.db.get_value,189 types.NoneType: self.db.get_value,
165 }[setting_type]190 }[setting_type]
166 return get_func(setting_name)191 return get_func(setting_name)
167 except:192 except:
168 self.log.debug("Settings: can't find value for %s" % setting)193 log.debug("Settings: can't find value for %s" % setting)
169 return None194 return None
170195
171 # Set a setting value196
172 def set_value(self, setting, value):197 def set_value(self, setting, value):
173198 """ Set a value for the specified setting """
174 value = '' if value is None else value199 value = '' if value is None else value
175 value = str(value) if type(value) is types.ListType else value200 value = str(value) if type(value) is types.ListType else value
176 log.debug("Settings: setting '%s'='%s'" % (setting, value))201 log.debug("Settings: setting '%s'='%s'" % (setting, value))
@@ -185,17 +210,20 @@
185 types.ListType: self.db.set_string,210 types.ListType: self.db.set_string,
186 types.DictType: self.db.set_string,211 types.DictType: self.db.set_string,
187 types.NoneType: self.db.set_value,212 types.NoneType: self.db.set_value,
188 }[setting_type]213 }[setting_type]
189 set_func(setting_name, value)214 set_func(setting_name, value)
190 except:215 except:
191 log.debug( \216 log.debug( \
192 "Settings: schema for '%s' not found, aborting" % setting)217 "Settings: schema for '%s' not found, aborting" % setting)
193218
194 # Get cached weather by location code.
195 # If return_id is True, only document id is returned, otherwise - full weather data
196 def get_weather(self, location_code, return_id = False):219 def get_weather(self, location_code, return_id = False):
197 log.debug("Settings: getting cached weather for %s" % \220 """Get cached weather by location code.
198 location_code)221 If return_id is True, only document id is returned,
222 otherwise full weather data is returned.
223
224 """
225 log.debug("Settings: getting cached weather for %s" %
226 location_code)
199 try:227 try:
200 cached_weather_string = self.db.get_string(self.WEATHER_KEY)228 cached_weather_string = self.db.get_string(self.WEATHER_KEY)
201 cached_weather = {} if cached_weather_string == ''\229 cached_weather = {} if cached_weather_string == ''\
@@ -207,22 +235,22 @@
207 "Settings: can't find value for %s" % location_code)235 "Settings: can't find value for %s" % location_code)
208 return None236 return None
209 except:237 except:
210 log.debug("Settings: can't find %s setting" % WEATHER_KEY)238 log.debug("Settings: can't find %s setting" % self.WEATHER_KEY)
211 return None239 return None
212240
213 # Save weather info in cache for specific location
214 def save_weather(self, weather, location_code):241 def save_weather(self, weather, location_code):
215242 """ Save weather info in cache for specific location """
216 record = {243 record = {
217 "label" : weather.get_temperature(needs_rounding=True),244## "label" : weather.get_temperature(needs_rounding=True),
218 "condition": weather.get_condition_label(),245 "label" : weather.get_temperature_string(),
219 "icon" : weather.get_icon_name(),246 "condition" : weather.get_condition_label(),
220 "temper" : weather.get_temperature_label(),247 "icon" : weather.get_icon_name(),
221 "humidex" : weather.get_humidex_label(),248 "temper" : weather.get_temperature_label(),
222 "humidity" : weather.get_humidity_label(),249 "feelslike" : weather.get_relative_label(),
223 "wind" : weather.get_wind_label(),250 "humidity" : weather.get_humidity_label(),
224 "sunrise" : weather.get_sunrise_label(),251 "wind" : weather.get_wind_label(),
225 "sunset" : weather.get_sunset_label()252 "sunrise" : weather.get_sunrise_label(),
253 "sunset" : weather.get_sunset_label()
226 }254 }
227 log.debug("Settings: setting '%s'='%s'" % (location_code, record))255 log.debug("Settings: setting '%s'='%s'" % (location_code, record))
228256
@@ -234,12 +262,17 @@
234 cached_weather_string = str(cached_weather)262 cached_weather_string = str(cached_weather)
235 self.db.set_string(self.WEATHER_KEY, cached_weather_string)263 self.db.set_string(self.WEATHER_KEY, cached_weather_string)
236 except:264 except:
237 log.debug(\265 log.debug(
238 "Settings: schema for '%s' not found, aborting" % setting)266 "Settings: schema for '%s' not found, aborting" %
267 self.WEATHER_KEY
268 )
239269
240 # Get location details by location code
241 # If return_id is True, only document id is returned, otherwise - full location data
242 def get_location_details(self, location_code, return_id = False):270 def get_location_details(self, location_code, return_id = False):
271 """ Get location details by location code
272 If return_id is True, only document id is returned,
273 otherwise - full location data is returned
274
275 """
243 try:276 try:
244 locations_string = self.db.get_string(self.LOCATIONS_KEY)277 locations_string = self.db.get_string(self.LOCATIONS_KEY)
245 locations = {} if locations_string == ''\278 locations = {} if locations_string == ''\
@@ -247,19 +280,19 @@
247 if location_code in locations.keys():280 if location_code in locations.keys():
248 return str(locations[location_code])281 return str(locations[location_code])
249 else:282 else:
250 log.debug(\283 log.debug(
251 "Settings: can't find value for %s" % location_code)284 "Settings: can't find value for %s" % location_code
285 )
252 return None286 return None
253 except:287 except:
254 log.debug("Settings: can't find location details for %s" % \288 log.debug("Settings: can't find location details for %s" %
255 location_code)289 location_code)
256 return None290 return None
257291
258 # Save location details
259 def save_location_details(self, location_details, location_code):292 def save_location_details(self, location_details, location_code):
260 log.debug("Settings: setting '%s'='%s'" %\293 """ Save location details """
261 (location_code, location_details))294 log.debug("Settings: setting '%s'='%s'" %
262295 (location_code, location_details))
263 try:296 try:
264 locations_string = self.db.get_string(self.LOCATIONS_KEY)297 locations_string = self.db.get_string(self.LOCATIONS_KEY)
265 locations = {} if locations_string == ''\298 locations = {} if locations_string == ''\
@@ -270,8 +303,8 @@
270 except:303 except:
271 pass304 pass
272305
273class MetricSystem:306class UnitSystem:
274 """ Class with available metric systems units """307 """ Class with available measurement unit systems """
275 SI = 1308 SI = 1
276 IMPERIAL = 2309 IMPERIAL = 2
277310
@@ -285,52 +318,64 @@
285318
286class WeatherDataSource:319class WeatherDataSource:
287 """ Class for available weather data sources """320 """ Class for available weather data sources """
288 GOOGLE = 1321 YAHOO = 1
289 YAHOO = 2322 WEATHER_COM = 2
290323
324class RelativeFormula:
325 """ Class for relative temperature formulas """
326 HEATINDEX = 1
327 HUMIDEX = 2
328 WINDCHILL = 3
329 APPARENT = 4
330
291class Location:331class Location:
292 """ Data object to store location details """332 """ Data object to store location details """
293333
294 # Initialize an object with a label334 def __init__(self, metric_system, wind_unit, heat_index,
295 def __init__(self, metric_system, wind_unit, location_details = None):335 chill_index, location_details = None):
336 """ Initialize an object with a label """
296 self.metric_system = metric_system337 self.metric_system = metric_system
297 self.wind_unit = wind_unit338 self.wind_unit = wind_unit
339 self.heat_index = heat_index
340 self.chill_index = chill_index
298 self.location_details = location_details341 self.location_details = location_details
299342
300 # Convert coordinate for google
301 def convert_coordinate_for_google(self, value):343 def convert_coordinate_for_google(self, value):
302 value = float(value) * 1e6344 """ Convert coordinate for google """
303 return int(round(value))345 value = float(value) * 1e6
346 return int(round(value))
304347
305 # Get necessary location details by its GeoNames details
306 def prepare_location(self, geonames_details):348 def prepare_location(self, geonames_details):
349 """ Get necessary location details by its GeoNames details """
307 self.location_details = {}350 self.location_details = {}
308 self.location_details['full name'] = geonames_details[0]351 self.location_details['full name'] = geonames_details[0]
309 self.location_details['latitude'] = geonames_details[2]352 self.location_details['latitude'] = geonames_details[2]
310 self.location_details['longitude'] = geonames_details[3]353 self.location_details['longitude'] = geonames_details[3]
311 self.prepare_location_for_google(geonames_details)354 self.prepare_location_for_google(geonames_details)
312 self.prepare_location_for_yahoo(geonames_details)355 self.prepare_location_for_yahoo(geonames_details)
356 self.prepare_location_for_weather_com(geonames_details)
313 #TODO: Get noaa id from geonames service357 #TODO: Get noaa id from geonames service
314 self.location_details['noaa id'] = "woot"358 self.location_details['noaa id'] = "woot"
315359
316 # check mandatory attributes360 # check mandatory attributes
317 if not hasattr(self, 'location_code') or \361 if not hasattr(self, 'location_code') or \
318 'latitude' not in self.location_details or \362 'latitude' not in self.location_details or \
319 'longitude' not in self.location_details:363 'longitude' not in self.location_details:
320 return False364 return False
321365
322 # check that we have at least one supported data source366 # check that we have at least one supported data source
323 if 'google id' not in self.location_details and \367 if 'google id' not in self.location_details and \
324 'yahoo id' not in self.location_details:368 'yahoo id' not in self.location_details and \
369 'weather-com id' not in self.location_details:
325 log.error(("Location '%s'" %370 log.error(("Location '%s'" %
326 self.location_details['full name'])) + \371 self.location_details['full name'])) + \
327 "is not supported by current data sources"372 "is not supported by current data sources"
328 return False373 return False
329374
330 return True375 return True
331376
332 def prepare_location_for_google(self, geonames_details):377 def prepare_location_for_google(self, geonames_details):
333 # Format latitude and longitude for Google needs378 """ Format latitude and longitude for Google needs """
334 try:379 try:
335 lat = self.convert_coordinate_for_google(geonames_details[2])380 lat = self.convert_coordinate_for_google(geonames_details[2])
336 lon = self.convert_coordinate_for_google(geonames_details[3])381 lon = self.convert_coordinate_for_google(geonames_details[3])
@@ -340,7 +385,7 @@
340 log.error(e)385 log.error(e)
341386
342 def prepare_location_for_yahoo(self, geonames_details):387 def prepare_location_for_yahoo(self, geonames_details):
343 # Get location details in english for Yahoo388 """ Get location details in English for Yahoo """
344 baseurl = 'http://api.geonames.org/getJSON'389 baseurl = 'http://api.geonames.org/getJSON'
345 params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}390 params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}
346 url = '?'.join((baseurl, urlencode(params)))391 url = '?'.join((baseurl, urlencode(params)))
@@ -356,19 +401,13 @@
356 return401 return
357402
358 # Get YAHOO WOEID by english name of location403 # Get YAHOO WOEID by english name of location
359 baseurl = 'http://where.yahooapis.com/geocode'404 woeid_result = pywapi.get_woeid_from_yahoo(displayed_city_name)
360 params = {'location': displayed_city_name, 'appid': 'mOawLd4s', 'flags': 'J'}405 if woeid_result.has_key('error'):
361 url = '?'.join((baseurl, urlencode(params)))406 log.error("Location: Yahoo woeid return error. Full response:\n %s" % woeid_result['error'])
362 log.debug("Location: Get Yahoo WOEID, url %s" % url)
363 f = urllib2.urlopen(url)
364 s=f.read()
365 null = None
366 yahoo_woeid_result = eval(s)
367 if (yahoo_woeid_result['ResultSet']['Error'] != 0) and (yahoo_woeid_result['ResultSet']['Results'] != None):
368 log.error("Location: Yahoo woeid return error. Full response:\n %s" % str(yahoo_woeid_result))
369 return407 return
370 else:408 else:
371 woeid = yahoo_woeid_result['ResultSet']['Results'][0]['woeid']409 # only look at the the first woeid result
410 woeid = woeid_result[0][0]
372 self.location_code = woeid411 self.location_code = woeid
373 log.debug("Location: woeid is %s" % woeid)412 log.debug("Location: woeid is %s" % woeid)
374413
@@ -400,21 +439,54 @@
400 except Exception, e:439 except Exception, e:
401 log.error(e)440 log.error(e)
402441
403 # Return lcoation code and location details442 def prepare_location_for_weather_com(self, geonames_details):
443 """ Get location details in English for Weather.com """
444 baseurl = 'http://api.geonames.org/getJSON'
445 params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}
446 url = '?'.join((baseurl, urlencode(params)))
447 log.debug("Location: Get GeoNames location details, url %s" % url)
448 try:
449 city = eval(urllib2.urlopen(url).read())
450 if 'adminName1' in city:
451 displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])
452 elif 'name' in city:
453 displayed_city_name = "%s, %s" % (city['name'], city['countryName'])
454 else:
455 log.error("Location: Cannot find GeoNames info for code %s Full Response:\n %s" % (geonames_details[1], str(city)))
456 return
457
458 # Get Weather.com Location ID by English name of location
459 locid_result = pywapi.get_location_ids(displayed_city_name)
460 if locid_result.has_key('error'):
461 log.error("Location: Weather.com locid return error. Full response:\n %s" % locid_result['error'])
462 return
463 else:
464 # only look at the the first locid result
465 locid = locid_result[0][0]
466 self.location_details['weather-com id'] = locid
467 log.debug("Location: locid is %s" % locid)
468
469 except urllib2.URLError:
470 log.error("Location: error reaching url '%s'" % url)
471
472 except Exception, e:
473 log.error(e)
474
404 def export_location_details(self):475 def export_location_details(self):
476 """ Return location code and location details """
405 return (self.location_code, self.location_details)477 return (self.location_code, self.location_details)
406478
407 # Get fresh weather data and store it to weather object
408 def update_weather_data(self, source):479 def update_weather_data(self, source):
409 # gather existing source keys480 """ Get fresh weather data and store it to weather object """
410 valid_source = None481 valid_source = None
411 loc_ids = {}482 loc_ids = {}
412483
413 SOURCES = {484 SOURCES = {
414 WeatherDataSource.GOOGLE : ("google id", "Google"),485 WeatherDataSource.WEATHER_COM : ("weather-com id", "Weather.com"),
415 WeatherDataSource.YAHOO : ("yahoo id", "Yahoo"),486 WeatherDataSource.YAHOO : ("yahoo id", "Yahoo")
416 }487 }
417488
489 # gather existing source keys
418 for source_id in SOURCES.keys():490 for source_id in SOURCES.keys():
419 if SOURCES[source_id][0] in self.location_details:491 if SOURCES[source_id][0] in self.location_details:
420 loc_ids[source_id] = SOURCES[source_id][0]492 loc_ids[source_id] = SOURCES[source_id][0]
@@ -423,158 +495,145 @@
423 if source in loc_ids:495 if source in loc_ids:
424 valid_source = source496 valid_source = source
425 log.debug(("Location: default weather source '%s' " +497 log.debug(("Location: default weather source '%s' " +
426 "chosen for '%s'") % (SOURCES[valid_source][1],498 "chosen for '%s'") % (SOURCES[valid_source][1],
427 self.location_details['label']))499 self.location_details['label']))
428500
429 # try with the first alternative501 # try with the first alternative
430 elif len(loc_ids.keys()):502 elif len(loc_ids.keys()):
431 valid_source = loc_ids.keys()[0]503 valid_source = loc_ids.keys()[0]
432 log.debug(("Location: non default weather source '%s' " +
433 "chosen for '%s'") % (SOURCES[valid_source][1],
434 self.location_details['label']))
435504
436 if valid_source is None:505 if valid_source is None:
437 log.error(("Location: no valid weather source can be " +506 log.error(("Location: no valid weather source can be " +
438 "chosen for '%s'") % (507 "chosen for '%s'") % (
439 self.location_details['label']))508 self.location_details['label']))
440 self.weather = None509 self.weather = None
441 else:510 else:
511 log.debug(("Location: non default weather source '%s' " +
512 "chosen for '%s'") % (SOURCES[valid_source][1],
513 self.location_details['label']))
442 self.weather = Weather(514 self.weather = Weather(
443 self.location_details[loc_ids[valid_source]],515 self.location_details[loc_ids[valid_source]],
444 valid_source, self.metric_system, self.wind_unit,516 valid_source, self.metric_system, self.wind_unit,
517 self.heat_index, self.chill_index,
445 self.location_details['latitude'],518 self.location_details['latitude'],
446 self.location_details['longitude'])519 self.location_details['longitude'])
447520
521
448class Forecast:522class Forecast:
449 """ Class to get forecast information """523 """ Class to get forecast information """
450524
451 # Initialize a class with metric system, wind units, location and current user locale525 def __init__ (self, units, location_id, locale):
452 def __init__ (self, units, lat, lon, locale):526 """Initialize a class with metric system, wind units,
527 location code and current user locale
528
529 """
453 self.metric_system = units530 self.metric_system = units
454 self.lat = self.convert_coordinate_for_google(lat)531 self.location_id = location_id
455 self.lon = self.convert_coordinate_for_google(lon)
456 self.locale = locale532 self.locale = locale
457533
458 # Convert coordinate for google
459 def convert_coordinate_for_google(self, value):
460 value = float(value) * 1e6
461 return int(round(value))
462
463 # Get and store forecast data. For now using Google only
464 def prepare_forecast_data(self):534 def prepare_forecast_data(self):
535 """ Get and store forecast data. For now using Weather.com only. """
536 # TODO: Implement for NOAA
465 self.daysofweek = []537 self.daysofweek = []
466 self.icons = []538 self.icons = []
467 self.conditions = []539 self.conditions = []
468 self.error_message = None540 self.error_message = None
469 try:541 try:
470 # Generate a fake location by current coordinates542 log.debug("Forecast: units set to %s" % self.metric_system)
471 location_name = ",,,%s,%s" % (self.lat, self.lon)543 # Check units, default to imperial
472 self.forecast = pywapi.get_weather_from_google (location_name, hl = self.locale)544 if self.metric_system == UnitSystem.SI:
473 self.unitsystem = self.forecast['forecast_information']['unit_system']545 self.unitsystem = 'metric'
546 elif self.metric_system == UnitSystem.IMPERIAL:
547 self.unitsystem = 'imperial'
548 else:
549 self.unitsystem = 'imperial'
550
551 self.forecast = pywapi.get_weather_from_weather_com(self.location_id, self.unitsystem)
474552
475 for forecast in self.forecast['forecasts']:553 for forecast in self.forecast['forecasts']:
476 self.daysofweek.append(forecast["day_of_week"])554 self.daysofweek.append(forecast["day_of_week"])
477 forecast_icon = str(forecast["icon"])555
478 if "/ig/images/weather/" in forecast_icon:556 # Yahoo forecast icon URL is "http://l.yimg.com/a/i/us/we/52/<code>.gif"
479 self.icons.append(forecast_icon.split("/ig/images/weather/")[-1].split(".gif")[0])557 self.icons.append(forecast["day"]["icon"])
480 elif "http://g0.gstatic.com/images/icons/onebox" in forecast_icon:558 self.conditions.append(forecast["day"]["brief_text"])
481 self.icons.append(forecast_icon.split("http://g0.gstatic.com/images/icons/onebox/weather_")[-1].split("-40.gif")[0])559
482 self.conditions.append(forecast["condition"])
483 self.error_message = None560 self.error_message = None
484561
485 except urllib2.URLError:562 except urllib2.URLError:
486 log.error("Forecast: error reading forecast for %s" % location_name)563 log.error("Forecast: error reading forecast for %s" % self.location_id)
487 except KeyError:564 except KeyError:
488 log.error("Forecast: returned empty forecast %s" % location_name)565 log.error("Forecast: returned empty forecast for %s" % self.location_id)
489 self.error_message = _('Unknown error occurred while picking up weather data')566 self.error_message = _('Unknown error occurred while picking up weather data')
490567
491 # Parse high values for forecast data
492 def get_forecast_data(self):568 def get_forecast_data(self):
569 """ Parse high and low values for forecast data """
493 self.highdata = []570 self.highdata = []
494 self.lowdata = []571 self.lowdata = []
495572
496 if not hasattr(self, 'unitsystem'):573 # Since we are now using Weather.com, forecast will always be in correct units
497 return None574 for forecast in self.forecast['forecasts']:
498575 self.highdata.append(forecast["high"])
499 if ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.SI)) or ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.IMPERIAL)):576 self.lowdata.append(forecast["low"])
500 #correct scale selected
501 for forecast in self.forecast['forecasts']:
502 self.highdata.append(forecast["high"])
503 self.lowdata.append(forecast["low"])
504
505 elif ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.IMPERIAL)):
506 #convert from SI to imperial
507 for forecast in self.forecast['forecasts']:
508 self.highdata.append(int(((int(forecast["high"])*9)/5)+32))
509 self.lowdata.append(int(((int(forecast["low"])*9)/5)+32))
510
511 elif ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.SI)):
512 #convert from imperial to SI
513 for forecast in self.forecast['forecasts']:
514 self.highdata.append(int((((int(forecast["high"]))-32)*5)/9))
515 self.lowdata.append(int((((int(forecast["low"]))-32)*5)/9))
516577
517 return (self.highdata, self.lowdata)578 return (self.highdata, self.lowdata)
518579
519 # Parse a list of days of week with forecast data
520 def get_forecast_daysofweek(self):580 def get_forecast_daysofweek(self):
581 """ Parse a list of days of week with forecast data """
521 return self.daysofweek582 return self.daysofweek
522583
523 # Parse icons for forecast data
524 def get_forecast_icons(self):584 def get_forecast_icons(self):
585 """ Parse icons for forecast data """
525 return self.icons586 return self.icons
526587
527 # Parse conditions for forecast data
528 def get_forecast_conditions(self):588 def get_forecast_conditions(self):
589 """ Parse conditions for forecast data """
529 return self.conditions590 return self.conditions
530591
531class Weather:592class Weather:
532 """593 """Data object to parse weather data with unit conversion """
533 Data object to parse weather data with unit convertion594
534 """595## #Available conditions by google icon
535596## #Format: Google icon name: (day icon, night icon, is a severe weather condition)
536 #Available conditions by google icon597## #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/
537 #Format: Google icon name: (day icon, night icon, is a severe weather condition)598## _GoogleConditions = {
538 #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/599## "sunny" : ( "weather-clear", "weather-clear-night", False),
539 _GoogleConditions = {600## "mostly_sunny" : ( "weather-clear", "weather-clear-night", False),
540 "sunny" : ( "weather-clear", "weather-clear-night", False),601## "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
541 "mostly_sunny" : ( "weather-clear", "weather-clear-night", False),602## "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
542 "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),603## "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
543 "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),604## "cloudy" : ( "weather-clouds", "weather-clouds-night", False),
544 "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False),605## "mostlycloudy" : ( "weather-overcast", "weather-overcast", False),
545 "cloudy" : ( "weather-clouds", "weather-clouds-night", False),606## "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False),
546 "mostlycloudy" : ( "weather-overcast", "weather-overcast", False),607## "overcast" : ( "weather-overcast", "weather-overcast", False),
547 "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False),608## "rain" : ( "weather-showers", "weather-showers", False),
548 "overcast" : ( "weather-overcast", "weather-overcast", False),609## "chanceofrain" : ( "weather-showers", "weather-showers", False),
549 "rain" : ( "weather-showers", "weather-showers", False),610## "chance_of_rain" : ( "weather-showers", "weather-showers", False),
550 "chanceofrain" : ( "weather-showers", "weather-showers", False),611## "heavyrain" : ( "weather-showers", "weather-showers", False),
551 "chance_of_rain" : ( "weather-showers", "weather-showers", False),612## "drizzle" : ( "weather-showers", "weather-showers", False),
552 "heavyrain" : ( "weather-showers", "weather-showers", False),613## "sleet" : ( "weather-snow", "weather-snow", False),
553 "drizzle" : ( "weather-showers", "weather-showers", False),614## "rain_snow" : ( "weather-snow", "weather-snow", False),
554 "sleet" : ( "weather-snow", "weather-snow", False),615## "rainsnow" : ( "weather-snow", "weather-snow", False),
555 "rain_snow" : ( "weather-snow", "weather-snow", False),616## "snow" : ( "weather-snow", "weather-snow", False),
556 "rainsnow" : ( "weather-snow", "weather-snow", False),617## "chanceofsnow" : ( "weather-snow", "weather-snow", False),
557 "snow" : ( "weather-snow", "weather-snow", False),618## "chance_of_snow" : ( "weather-snow", "weather-snow", False),
558 "chanceofsnow" : ( "weather-snow", "weather-snow", False),619## "heavysnow" : ( "weather-snow", "weather-snow", False),
559 "chance_of_snow" : ( "weather-snow", "weather-snow", False),620## "icy" : ( "weather-snow", "weather-snow", False),
560 "heavysnow" : ( "weather-snow", "weather-snow", False),621## "snowflurries" : ( "weather-snow", "weather-snow", False),
561 "icy" : ( "weather-snow", "weather-snow", False),622## "flurries" : ( "weather-snow", "weather-snow", False),
562 "snowflurries" : ( "weather-snow", "weather-snow", False),623## "dust" : ( "weather-fog", "weather-fog", False),
563 "flurries" : ( "weather-snow", "weather-snow", False),624## "fog" : ( "weather-fog", "weather-fog", False),
564 "dust" : ( "weather-fog", "weather-fog", False),625## "smoke" : ( "weather-fog", "weather-fog", False),
565 "fog" : ( "weather-fog", "weather-fog", False),626## "haze" : ( "weather-fog", "weather-fog", False),
566 "smoke" : ( "weather-fog", "weather-fog", False),627## "mist" : ( "weather-fog", "weather-fog", False),
567 "haze" : ( "weather-fog", "weather-fog", False),628## "thunderstorm" : ( "weather-storm", "weather-storm", True),
568 "mist" : ( "weather-fog", "weather-fog", False),629## "chance_of_storm" : ( "weather-storm", "weather-storm", True),
569 "thunderstorm" : ( "weather-storm", "weather-storm", True),630## "thunderstorms" : ( "weather-storm", "weather-storm", True),
570 "chance_of_storm" : ( "weather-storm", "weather-storm", True),631## "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True),
571 "thunderstorms" : ( "weather-storm", "weather-storm", True),632## "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True),
572 "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True),633## }
573 "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True),634
574 }635 # Available conditions by yahoo condition code
575636 # Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name)
576 #Available conditions by yahoo condition code
577 #Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name)
578 _YahooConditions = {637 _YahooConditions = {
579 '0' : ("weather-storm", "weather-storm", True, _("Tornado")),638 '0' : ("weather-storm", "weather-storm", True, _("Tornado")),
580 '1' : ("weather-storm", "weather-storm", True, _("Tropical storm")),639 '1' : ("weather-storm", "weather-storm", True, _("Tropical storm")),
@@ -628,55 +687,60 @@
628 '3200': (False, False, False, _("Unknown condition"))687 '3200': (False, False, False, _("Unknown condition"))
629 }688 }
630689
631 # Initialize and get fresh data690 # Available conditions by Weather.com condition code; same as Yahoo
632 def __init__(self, location_id, weather_datasource, metric_system, wind_unit, lat, lon):691 _WeathercomConditions = _YahooConditions
692
693 def __init__(self, location_id, weather_datasource, metric_system,
694 wind_unit, heat_index, chill_index, lat, lon):
695 """ Initialize and get fresh weather data """
633 self.__weather_datasource = weather_datasource696 self.__weather_datasource = weather_datasource
634 self.__metric_system = metric_system697 self.__metric_system = metric_system
635 self._wind_unit = wind_unit698 self._wind_unit = wind_unit
636 self.__current_condition = None699 self.__current_condition = None
700 self.__heat_index = heat_index
701 self.__chill_index = chill_index
637 self.__lat = lat702 self.__lat = lat
638 self.__lon = lon703 self.__lon = lon
639704
640 # Get data from Google705 if self.__metric_system == UnitSystem.SI:
641 if self.__weather_datasource == WeatherDataSource.GOOGLE:706 unit_system = 'metric'
642 # Get data in english locale, then - switch back707 elif self.__metric_system == UnitSystem.IMPERIAL:
643 self.__report = pywapi.get_weather_from_google (location_id, hl = 'en')708 unit_system = 'imperial'
644 # Get data in original locale for condition name709 else:
645 self.__localized_report = pywapi.get_weather_from_google (location_id, hl = locale_name)710 unit_system = 'imperial'
646711
712 # Get data from Weather.com
713 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
714 self.__report = pywapi.get_weather_from_weather_com(
715 location_id, unit_system)
716 ##self.__localized_report = self.__report
717 log.debug("Weather: checking Weather.com report for "
718 "weather condition and icon name")
647 if 'current_conditions' not in self.__report.keys():719 if 'current_conditions' not in self.__report.keys():
648 log.error("Weather: could not get Google weather condition from report")720 icon_name = ""
721 log.error("Weather: could not get Weather.com "
722 "weather condition from report")
649 log.error("Weather: got data '%s'" % str(self.__report))723 log.error("Weather: got data '%s'" % str(self.__report))
650 self.__current_condition = (False, False, False, _("Unknown condition"))724 self.__current_condition = (False, False, False, _("Unknown condition"))
651725
652 if 'current_conditions' not in self.__localized_report.keys():726 elif 'icon' in self.__report['current_conditions'].keys():
653 log.error("Weather: could not get Google weather condition from localized report")727 icon_name = self.__report['current_conditions']['icon']
654 log.error("Weather: got data '%s'" % str(self.__localized_report))728 self.__current_condition = self._WeathercomConditions.get(icon_name)
655 self.__current_condition = (False, False, False, _("Unknown condition"))
656729
657 if 'icon' in self.__report['current_conditions'].keys():
658 icon_path = self.__report['current_conditions']['icon']
659 if '/ig/images/weather/' in icon_path:
660 icon_name = icon_path.replace('/ig/images/weather/', '').replace('.gif', '')
661 elif 'http://g0.gstatic.com/images/icons/onebox' in icon_path:
662 icon_name = icon_path.replace('http://g0.gstatic.com/images/icons/onebox/weather_', '').replace('-40.gif', '')
663 else:
664 icon_name = icon_path
665 else:730 else:
666 log.error("Weather: could not get weather icon from report")731 icon_name = ""
732 log.error("Weather: could not get icon name from Weather.com report")
667 log.error("Weather: got data '%s'" % str(self.__report['current_conditions']))733 log.error("Weather: got data '%s'" % str(self.__report['current_conditions']))
668 icon_name = ""
669
670 self.__current_condition = self._GoogleConditions.get(icon_name)
671 if self.__current_condition == None:
672 log.error("ExtendedForecast: unknown Google weather condition '%s'" % icon_name)
673 self.__current_condition = (False, False, False, _("Unknown condition"))734 self.__current_condition = (False, False, False, _("Unknown condition"))
674735
675 # Get data from Yahoo736 # Get data from Yahoo
676 if self.__weather_datasource == WeatherDataSource.YAHOO:737 if self.__weather_datasource == WeatherDataSource.YAHOO:
677 self.__report = pywapi.get_weather_from_yahoo (location_id, 'imperial')738 self.__report = pywapi.get_weather_from_yahoo(location_id, unit_system)
678 self.__localized_report = self.__report739 ##self.__localized_report = self.__report
740 log.debug("Weather: checking Yahoo report for "
741 "weather condition and icon name")
679 if 'condition' not in self.__report.keys():742 if 'condition' not in self.__report.keys():
743 icon_name = ""
680 log.error("Weather: could not get Yahoo weather condition from report")744 log.error("Weather: could not get Yahoo weather condition from report")
681 log.error("Weather: got data '%s'" % str(self.__report))745 log.error("Weather: got data '%s'" % str(self.__report))
682 self.__current_condition = (False, False, False, _("Unknown condition"))746 self.__current_condition = (False, False, False, _("Unknown condition"))
@@ -695,8 +759,8 @@
695 #Prepare sunrise/sunset data759 #Prepare sunrise/sunset data
696 self.get_sun_data()760 self.get_sun_data()
697761
698 #Get sunrise/sunset times, calculate whether it is night already
699 def get_sun_data(self):762 def get_sun_data(self):
763 """ Get sunrise/sunset times and calculate whether it is night already """
700 self.__night = False764 self.__night = False
701 self.__sunrise_t = None765 self.__sunrise_t = None
702 self.__sunset_t = None766 self.__sunset_t = None
@@ -714,7 +778,7 @@
714 "dst")[0].firstChild.nodeValue778 "dst")[0].firstChild.nodeValue
715 # strip timezone info779 # strip timezone info
716 localtime = datetime.datetime.strptime(localtime.rsplit(' ',1)[0],780 localtime = datetime.datetime.strptime(localtime.rsplit(' ',1)[0],
717 '%Y-%m-%d %H:%M:%S')781 '%Y-%m-%d %H:%M:%S')
718 dst = 1 if dst == "True" else 0782 dst = 1 if dst == "True" else 0
719783
720 except urllib2.URLError:784 except urllib2.URLError:
@@ -725,6 +789,7 @@
725 url = 'http://www.earthtools.org/sun/%s/%s/%s/%s/99/%s' % \789 url = 'http://www.earthtools.org/sun/%s/%s/%s/%s/99/%s' % \
726 (self.__lat, self.__lon, localtime.day, localtime.month, dst)790 (self.__lat, self.__lon, localtime.day, localtime.month, dst)
727 try:791 try:
792 log.debug("Weather: get_sun_data: getting sunrise/sunset data")
728 f = urllib2.urlopen(url)793 f = urllib2.urlopen(url)
729 s=f.read()794 s=f.read()
730 parsed = parseString(s)795 parsed = parseString(s)
@@ -742,20 +807,20 @@
742 else:807 else:
743 self.__night = False808 self.__night = False
744 log.debug("Weather: got localtime " +809 log.debug("Weather: got localtime " +
745 "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % (810 "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % (
746 localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night))811 localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night))
747812
748 # Return True, if weather condition is severe
749 def condition_is_severe(self):813 def condition_is_severe(self):
814 """ Return True if weather condition is severe """
750 if self.__current_condition != None:815 if self.__current_condition != None:
751 log.debug("Weather: got severe condition '%s'" % self.__current_condition[2])816 log.debug("Weather: got severe condition '%s'" % self.__current_condition[2])
752 return self.__current_condition[2]817 return self.__current_condition[2]
753 else:818 else:
754 log.error("Weather: condition is not set while condition severity check")819 log.error("Weather: condition is not set while condition severity check")
755 return False;820 return False
756821
757 # Get associated icon name
758 def get_icon_name(self):822 def get_icon_name(self):
823 """ Get icon name associated with current condition """
759 if self.__current_condition != None:824 if self.__current_condition != None:
760 if self.__night:825 if self.__night:
761 log.debug("Weather: night, show '%s' icon" % self.__current_condition[1])826 log.debug("Weather: night, show '%s' icon" % self.__current_condition[1])
@@ -767,217 +832,440 @@
767 log.error("Weather: return 'offline' icon due to empty condition")832 log.error("Weather: return 'offline' icon due to empty condition")
768 return False833 return False
769834
770 # Get condition text
771 def get_condition_label(self):835 def get_condition_label(self):
772 if self.__weather_datasource == WeatherDataSource.GOOGLE:836 """ Get text of current condition """
773 if 'condition' in self.__localized_report['current_conditions'].keys():837 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
774 condition = self.__localized_report['current_conditions']['condition']838 condition = self.__report['current_conditions']['text']
775 else:
776 condition = _("Unknown condition")
777 if self.__weather_datasource == WeatherDataSource.YAHOO:839 if self.__weather_datasource == WeatherDataSource.YAHOO:
778 condition = self.__current_condition[3]840 condition = self.__current_condition[3]
779 return condition841 return condition
780842
781 # Get humidity label
782 def get_humidity_label(self):843 def get_humidity_label(self):
844 """ Get text string for current humidity """
783 humidity = "%s: ---%%" % (_("Humidity"))845 humidity = "%s: ---%%" % (_("Humidity"))
784 if self.__weather_datasource == WeatherDataSource.GOOGLE \846 if self.__weather_datasource == WeatherDataSource.WEATHER_COM \
785 and 'humidity' in self.__localized_report['current_conditions']:847 and 'humidity' in self.__report['current_conditions']:
786 humidity = self.__localized_report['current_conditions']['humidity']848 humidity = "%s: %s%%" % (_("Humidity"), self.__report['current_conditions']['humidity'])
787 if self.__weather_datasource == WeatherDataSource.YAHOO \849 if self.__weather_datasource == WeatherDataSource.YAHOO \
788 and 'humidity' in self.__localized_report['atmosphere']:850 and 'humidity' in self.__report['atmosphere']:
789 humidity = "%s: %s%%" % (_("Humidity"), self.__localized_report['atmosphere']['humidity'])851 humidity = "%s: %s%%" % (_("Humidity"), self.__report['atmosphere']['humidity'])
790 return humidity852 return humidity
791853
792 # Get dew point - using in humidex calculation
793 #TODO: Update with NOAA
794 def get_dew_point_label(self):854 def get_dew_point_label(self):
795 if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:855 """ Get dew point, which is used in humidex calculation """
796 # Not returned by Google and Yahoo856 #TODO: Update with NOAA
857 _value = "---"
858 _unit = ""
859 if self.__weather_datasource == WeatherDataSource.YAHOO:
860 # Not returned by Yahoo
797 return None861 return None
862 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
863 _value = self.__report['current_conditions']['dewpoint']
864 _unit = self.__report['units']['temperature']
865 return u"%s: %s °%s" % (_("Dewpoint"), _value, _unit)
798866
799 # Get pressure label
800 def get_pressure_label(self):867 def get_pressure_label(self):
801 if self.__weather_datasource == WeatherDataSource.GOOGLE:868 """ Get text string for current air pressure """
802 # TODO: Empty for Google, use NOAA data?869 _value = "---"
803 value = "---"870 _unit = ""
804 unit = ""871 if self.__weather_datasource == WeatherDataSource.WEATHER_COM \
872 and 'barometer' in self.__report['current_conditions'].keys() \
873 and 'pressure' in self.__report['units'].keys():
874 _value = self.__report['current_conditions']['barometer']['reading']
875 _unit = self.__report['units']['pressure']
805 if self.__weather_datasource == WeatherDataSource.YAHOO \876 if self.__weather_datasource == WeatherDataSource.YAHOO \
806 and 'pressure' in self.__localized_report['atmosphere'].keys() \877 and 'pressure' in self.__report['atmosphere'].keys() \
807 and 'pressure' in self.__localized_report['units'].keys():878 and 'pressure' in self.__report['units'].keys():
808 value = self.__localized_report['atmosphere']['pressure']879 _value = self.__report['atmosphere']['pressure']
809 unit = self.__localized_report['units']['pressure']880 _unit = self.__report['units']['pressure']
810 return "%s: %s %s" % (_("Pressure"), value, units)881 return "%s: %s %s" % (_("Pressure"), _value, _unit)
811882
812 # Get temperature with units value - doesn't include 'Temperature' label883## def get_temperature(self, needs_rounding = False):
813 def get_temperature(self, needs_rounding = False):884 def get_temperature(self):
814 _value = "---"885 """ Get temperature value and units string """
886 _value = None
815 _unit = ""887 _unit = ""
816 if self.__weather_datasource == WeatherDataSource.GOOGLE:888 if (self.__weather_datasource == WeatherDataSource.WEATHER_COM and
817 if (self.__metric_system == MetricSystem.SI) \889 'temperature' in self.__report['current_conditions'].keys() and
818 and 'temp_c' in self.__report['current_conditions'].keys():890 'temperature' in self.__report['units'].keys()):
819 _value = self.__report['current_conditions']['temp_c']891 if ((self.__metric_system == UnitSystem.SI and
820 _unit = "ËšC"892 self.__report['units']['temperature'] == u"C") or
821 elif 'temp_f' in self.__report['current_conditions'].keys():893 (self.__metric_system == UnitSystem.IMPERIAL and
822 _value = self.__report['current_conditions']['temp_f']894 self.__report['units']['temperature' ] == u"F")):
823 _unit = "ËšF"895 _value = self.__report['current_conditions']['temperature']
824 if self.__weather_datasource == WeatherDataSource.YAHOO:896 _unit = u"°%s" % self.__report['units']['temperature']
825 if (self.__metric_system == MetricSystem.SI) \897 if (self.__weather_datasource == WeatherDataSource.YAHOO and
826 and 'temp' in self.__report['condition'].keys():898 'temp' in self.__report['condition'].keys() and
827 _value = NumberFormatter.format_float(899 'temperature' in self.__report['units'].keys()):
828 ((float(self.__report['condition']['temp']) - 32) * 5/9), 1)900 if ((self.__metric_system == UnitSystem.SI and
829 _unit = "ËšC"901 self.__report['units']['temperature'] == u"C") or
830 else:902 (self.__metric_system == UnitSystem.IMPERIAL and
903 self.__report['units']['temperature'] == u"F")):
831 _value = self.__report['condition']['temp']904 _value = self.__report['condition']['temp']
832 _unit = "˚F"905 _unit = u"°%s" % self.__report['units']['temperature']
833 # round the value if required906## # round the value if required
834 if needs_rounding and _value != "---":907## if needs_rounding and _value != "---":
835 _value = NumberFormatter.format_float(locale.atof(_value), 0)908## _value = NumberFormatter.format_float(locale.atof(_value), 0)
836 return ("%s %s" % (_value, _unit))909 return (_value, _unit)
837910
838 # Get temperature label911 def get_temperature_string(self):
912 """ Get temperature with units value - doesn't include 'Temperature' string """
913 (_value, _unit) = self.get_temperature()
914 if _value is None:
915 _value = "---"
916 _unit = ""
917 return (u"%s %s" % (_value, _unit))
918
839 def get_temperature_label(self):919 def get_temperature_label(self):
840 return "%s: %s" % (_("Temperature"), self.get_temperature())920 """ Get text string for current temperature label """
841921 return "%s: %s" % (_("Temperature"), self.get_temperature_string())
842 # Get humidex parameter922
843 def get_humidex_label(self):923 def get_relative_string(self):
844 if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:924 """ Get relative temperature with units value - doesn't include 'Feels Like' string """
845 #Empty for Yahoo and Google925 # try relative heat
846 return None926 if self.__heat_index == RelativeFormula.HUMIDEX:
847 #TODO: Update with NOAA data927 (_value, _unit) = self.get_humidex()
848 #dewPoint=2928 if self.__heat_index == RelativeFormula.HEATINDEX:
849 #temp_c = 1929 (_value, _unit) = self.get_heat_index()
850 #self.vapour_pressure = 6.11 * math.exp(5417.7530 * ( (1/273.16) - (1/(dewPoint+273.16))))930 if _value is not None:
851 #self.humidex = temp_c + (0.5555)*(self.vapour_pressure - 10.0);931 return (u"%s %s" % (_value, _unit))
852 #return ("%s: %.1f" % (_("Humidex"), self.humidex)).replace(".0", "")932 # try relative chill
853933 if self.__chill_index == RelativeFormula.WINDCHILL:
854 # Get wind label934 (_value, _unit) = self.get_wind_chill()
935 if self.__chill_index == RelativeFormula.APPARENT:
936 (_value, _unit) = self.get_apparent_temp()
937 if _value is not None:
938 return (u"%s %s" % (_value, _unit))
939 # use current temperature
940 return self.get_temperature_string()
941
942 def get_relative_label(self):
943 """ Get text string for relative temperature ("feels like") label """
944 return "%s: %s" % (_("Feels Like"), self.get_relative_string())
945
946 def get_humidex(self):
947 """ Calculate humidex and get value and units
948
949 The standard Humidex formula used by Environment Canada is:
950
951 humidex = (air temperature) + h
952
953 h = (0.5555)*(e - 10.0);
954 e = vapour pressure in hPa (mbar), given by:
955 e = 6.11 * exp [5417.7530 * ( (1/273.16) - (1/dewpoint) ) ]
956 where dewpoint is expressed in Kelvins
957 (temperature in K = temperature in °C + 273.1)
958 and 5417.7530 is a rounded constant based on the molecular weight
959 of water, latent heat of evaporation, and the universal gas constant.
960
961 """
962 #TODO: Update with NOAA data
963 if self.__weather_datasource == WeatherDataSource.YAHOO:
964 # Empty for Yahoo
965 return (None, "")
966 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
967 if self.__report['units']['temperature'] == "F":
968 # Humidex is calculated in C
969 dew_point_f = float(self.__report['current_conditions']['dewpoint'])
970 temp_f = float(self.__report['current_conditions']['temperature'])
971 dew_point_c = ((dew_point_f - 32.0) * 5.0/9.0)
972 temp_c = ((temp_f - 32.0) * 5.0/9.0)
973 else:
974 dew_point_c = float(self.__report['current_conditions']['dewpoint'])
975 temp_c = float(self.__report['current_conditions']['temperature'])
976 vapour_pressure = 6.11 * exp(5417.7530 * ( (1/273.16) - (1/(dew_point_c+273.16))))
977 humidex = temp_c + (0.5555)*(vapour_pressure - 10.0)
978 # Humidex is meaningless if it is lower than temperature
979 if humidex < temp_c:
980 #return (u"%s: N/A" % _("Humidex"))
981 return (None, "")
982 if self.__report['units']['temperature'] == "F":
983 humidex_f = (humidex*9.0/5.0) + 32.0
984 # Humidex is unitless (represents Celsius) so in F show units
985 #return (u"%s: %s°F" % (_("Humidex"), NumberFormatter.format_float(humidex_f, 1))).replace(".0", "")
986 #return (NumberFormatter.format_float(humidex_f, 1).replace(".0", ""), u"°F")
987 return (int(round(humidex_f)), u"°F")
988 else:
989 #return ("%s: %.1f" % (_("Humidex"), humidex)).replace(".0", "")
990 #return (u"%s: %s" % (_("Humidex"), NumberFormatter.format_float(humidex, 1))).replace(".0", "")
991 #return (NumberFormatter.format_float(humidex, 1).replace(".0", ""), u"°C")
992 return (int(round(humidex)), u"°C")
993
994 def get_heat_index(self):
995 """ Calculate heat index and get value and units
996
997 The formula below approximates the heat index in degrees
998 Fahrenheit, to within ±1.3 °F. It is the result of a
999 multivariate fit (temperature equal to or greater than
1000 80°F and relative humidity equal to or greater than 40%)
1001 to a model of the human body.
1002
1003 Heat Index = c_1 + (c_2 * T) + (c_3 * R) + (c_4 * T * R) +
1004 (c_5 * T^2) + (c_6 * R^2) + (c_7 * T^2 * R) +
1005 (c_8 * T * R^2) + (c_9 * T^2 * R^2)
1006 where:
1007 T = ambient dry-bulb temperature (in degrees Fahrenheit)
1008 R = relative humidity (percentage value between 0 and 100)
1009
1010 """
1011 #TODO: Update with NOAA data
1012 _value = None
1013
1014 if self.__weather_datasource == WeatherDataSource.YAHOO:
1015 log.debug("Weather: get_heat_index: weather_datasource is Yahoo, "
1016 "checking temp and humidity")
1017 if self.__report['units']['temperature'] == "C":
1018 units = 'metric'
1019 elif self.__report['units']['temperature'] == "F": # else here?
1020 units = 'imperial'
1021 T = float(self.__report['condition']['temp'])
1022 R = float(self.__report['atmosphere']['humidity'])
1023 _value = pywapi.heat_index(T, R, units)
1024
1025 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
1026 log.debug("Weather: get_heat_index: weather_datasource is "
1027 "Weather.com, checking temp and humidity")
1028 if self.__report['units']['temperature'] == "C":
1029 units = 'metric'
1030 elif self.__report['units']['temperature'] == "F": # else here?
1031 units = 'imperial'
1032 T = float(self.__report['current_conditions']['temperature'])
1033 R = float(self.__report['current_conditions']['humidity'])
1034 _value = pywapi.heat_index(T, R, units)
1035
1036 # Heat Index is only valid for temp >= 80°F and humidity >= 40%)
1037 if _value is None:
1038 return (_value, "")
1039 if units == 'metric':
1040 #return (u"%s: %s°C" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "")
1041 #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°C")
1042 return (int(round(_value)), u"°C")
1043
1044 elif units == 'imperial': # else here?
1045 #return (u"%s: %s°F" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "")
1046 #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°F")
1047 return (int(round(_value)), u"°F")
1048
1049 def get_wind_chill(self):
1050 """ Calculate wind chill index and get text string for label
1051
1052 The standard Wind Chill formula used by Environment Canada,
1053 the 2001 JAG/TI Wind Chill Equivalent Temperature Index, is:
1054
1055 T_wc = 13.12 + 0.6215 * T_a -
1056 11.37 * V^0.16 +
1057 0.3965 * T_a * V^0.16
1058 where:
1059 T_wc is the wind chill index based on Celsius
1060 T_a is the air temperature in °C
1061 V is the wind speed in km/h, at 10 m (standard anemometer height)
1062
1063 The equivalent formula in US customary units is:
1064
1065 T_wc = 35.74 + 0.6215 * T_a -
1066 35.75 * V^0.16 +
1067 0.4275 * T_a * V^0.16
1068
1069 where:
1070 T_wc is the wind chill index based on Fahrenheit
1071 T_a is the air temperature °F
1072 V is the wind speed in mph
1073
1074 Windchill temperature is defined only for temperatures at or
1075 below 10 °C (50 °F) and wind speeds above 4.8 kilometres per
1076 hour (3.0 mph).
1077
1078 """
1079 #TODO: Update with NOAA data
1080 _value = None
1081
1082 if self.__report['units']['temperature'] == "C":
1083 units = 'metric'
1084 elif self.__report['units']['temperature'] == "F": # else here?
1085 units = 'imperial'
1086
1087 if self.__weather_datasource == WeatherDataSource.YAHOO:
1088 log.debug("Weather: get_wind_chill: weather_datasource is "
1089 "Yahoo, checking temp and humidity")
1090 T_a = float(self.__report['condition']['temp'])
1091 V = float(self.__report['wind']['speed'])
1092
1093 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
1094 log.debug("Weather: get_wind_chill: weather_datasource is "
1095 "Weather.com, checking temp and humidity")
1096 T_a = float(self.__report['current_conditions']['temperature'])
1097 wind_speed = self.__report['current_conditions']['wind']['speed']
1098 V = (0.0 if wind_speed == 'calm' else float(wind_speed))
1099
1100 # move below to pywapi
1101 if units == 'metric':
1102 _value = 13.12 + 0.6215 * T_a - 11.37 * pow(V, 0.16) + 0.3965 * T_a * pow(V, 0.16)
1103 return (int(round(_value)), u"°C")
1104 elif units == 'imperial':
1105 _value = 35.74 + 0.6215 * T_a - 35.75 * pow(V, 0.16) + 0.4275 * T_a * pow(V, 0.16)
1106 return (int(round(_value)), u"°F")
1107
1108 if _value is None:
1109 return (_value, "")
1110
1111 def get_apparent_temp(self):
1112 """ Calculate Australian apparent temperature and get text string for label
1113
1114 The standard formula for cooler temperatures used by the Australian
1115 Bureau of Meteorology, the Australian Apparent Temperature, is:
1116
1117 AT = T_a + 0.33e - 0.70ws - 4.00
1118
1119 Where:
1120 T_a = Dry bulb temperature (°C)
1121 e = Water vapour pressure (hPa)
1122 ws = Wind speed (m/s) at an elevation of 10 meters
1123
1124 The vapour pressure can be calculated from the temperature and
1125 relative humidity using the equation:
1126
1127 e = (rh / 100) * 6.105 * exp^[(17.27 * T_a) / (237.7 + T_a)]
1128
1129 Where:
1130 T_a = Dry bulb temperature (°C)
1131 rh = Relative humidity [%]
1132 exp^ represents the exponential function
1133
1134 The Australian chill formula is only defined for temperatures
1135 at or below 20°C (68°F) and wind speeds above 4.8 km/h (3.0 mph).
1136
1137 """
1138 #TODO: Update with NOAA data
1139 _value = None
1140
1141 if self.__report['units']['temperature'] == "C":
1142 units = 'metric'
1143 elif self.__report['units']['temperature'] == "F": # else here?
1144 units = 'imperial'
1145
1146 if self.__weather_datasource == WeatherDataSource.YAHOO:
1147 log.debug("Weather: get_wind_chill: weather_datasource is "
1148 "Yahoo, checking temp and humidity")
1149 T_a = float(self.__report['condition']['temp'])
1150 rh = float(self.__report['atmosphere']['humidity'])
1151 ws = float(self.__report['wind']['speed'])
1152 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
1153 log.debug("Weather: get_wind_chill: weather_datasource is "
1154 "Weather.com, checking temp and humidity")
1155 T_a = float(self.__report['current_conditions']['temperature'])
1156 rh = float(self.__report['current_conditions']['humidity'])
1157 ws = float(self.__report['current_conditions']['wind']['speed'])
1158
1159 # calculated in °C, so need to convert from °F
1160 if units == 'imperial':
1161 T_a = ((T_a - 32.0) * 5.0/9.0)
1162
1163 e = (rh / 100) * 6.105 * exp((17.27 * T_a) / (237.7 + T_a))
1164 _value = T_a + 0.33 * e - 0.70 * ws - 4.00
1165
1166 # Now convert back to °F
1167 if units == 'imperial':
1168 _value = (_value*9.0/5.0) + 32.0
1169 return (int(round(_value)), u"°F")
1170 if units == 'metric':
1171 return (int(round(_value)), u"°C")
1172
1173 if _value is None:
1174 return (_value, "")
1175
855 def get_wind_label(self):1176 def get_wind_label(self):
856 if self.__weather_datasource == WeatherDataSource.GOOGLE:1177 """ Get text string for current wind speed and direction """
857 # Convert units picked up from Google and replace units with currently configured1178 if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
858 if 'wind_condition' in self.__localized_report['current_conditions'].keys():1179 # Create a wind_info structure from Weather.com data
859 localized_wind_info = self.__localized_report['current_conditions']['wind_condition'].split(' ')1180 wind_direction = u"%s (%s°)" % (self.__report['current_conditions']['wind']['text'],
860 wind_direction = localized_wind_info[1]1181 self.__report['current_conditions']['wind']['direction'])
861 wind_info = self.__report['current_conditions']['wind_condition'].split(' ')1182 wind_speed = self.__report['current_conditions']['wind']['speed']
862 wind_speed = wind_info[3]1183 wind_units = self.__report['units']['speed']
863 else:1184 if wind_speed == "calm":
864 return _("Unknown")1185 wind_speed = 0
8651186 ##wind_direction = wind_direction.replace("CALM","Calm")
1187 wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
866 if self.__weather_datasource == WeatherDataSource.YAHOO:1188 if self.__weather_datasource == WeatherDataSource.YAHOO:
867 # Create a similar to Google wind_info structure from Yahoo data1189 # Create a wind_info structure from Yahoo data
868 wind_direction = "%s (%s˚)" % (self.get_wind_direction(self.__localized_report['wind']['direction']), self.__localized_report['wind']['direction'])1190 wind_direction = u"%s (%s°)" % (_(pywapi.get_wind_direction(self.__report['wind']['direction'])), self.__report['wind']['direction'])
869 wind_speed = self.__localized_report['wind']['speed']1191 wind_speed = self.__report['wind']['speed']
870 wind_units = self.__localized_report['units']['speed']1192 wind_units = self.__report['units']['speed']
871 localized_wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]1193 wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
872
8731194
874 try:1195 try:
875 _value = float(wind_speed)1196 _value = float(wind_speed)
876 except ValueError as e:1197 except ValueError as e:
877 log.error("Could not parse '%s' as wind speed." % str(wind_speed))1198 log.error("Could not parse '%s' as wind speed." % str(wind_speed))
878 _value = -1.01199 _value = -1.0
879 1200
880 # Parse Wind_direction - convert to selected scale1201 # Parse Wind_direction - convert to selected scale
881 if (self._wind_unit == WindUnits.MPH):1202 if wind_units == "mph":
882 _unit = __("mph", "mph", _value)1203 if (self._wind_unit == WindUnits.MPH):
883 if (self._wind_unit == WindUnits.MPS):1204 _unit = __("mph", "mph", _value)
884 _value *= 0.447041205 if (self._wind_unit == WindUnits.MPS):
885 _unit = __("m/s", "m/s", _value)1206 _value *= 0.44704
886 if (self._wind_unit == WindUnits.BEAUFORT):1207 _unit = __("m/s", "m/s", _value)
887 if _value >= 0.0:1208 if (self._wind_unit == WindUnits.BEAUFORT):
888 _value = self.get_beaufort_from_mph(_value)1209 if _value >= 0.0:
889 _unit = ""1210 _value = pywapi.wind_beaufort_scale(_value, WindUnits.MPH)
890 if (self._wind_unit == WindUnits.KPH):1211 _unit = ""
891 _value *= 1.6093441212 if (self._wind_unit == WindUnits.KPH):
892 _unit = __("km/h", "km/h", _value)1213 _value *= 1.609344
893 if (self._wind_unit == WindUnits.KNOTS):1214 _unit = __("km/h", "km/h", _value)
894 _value *= 0.8689762419006481215 if (self._wind_unit == WindUnits.KNOTS):
895 _unit = __("knot", "knots", _value)1216 _value *= 0.868976241900648
896 1217 _unit = __("knot", "knots", _value)
1218 elif wind_units == "km/h":
1219 if (self._wind_unit == WindUnits.MPH):
1220 _value *= 0.621371
1221 _unit = __("mph", "mph", _value)
1222 if (self._wind_unit == WindUnits.MPS):
1223 _value *= 0.277778
1224 _unit = __("m/s", "m/s", _value)
1225 if (self._wind_unit == WindUnits.BEAUFORT):
1226 if _value >= 0.0:
1227 _value = pywapi.wind_beaufort_scale(_value, WindUnits.KPH)
1228 _unit = ""
1229 if (self._wind_unit == WindUnits.KPH):
1230 _unit = __("km/h", "km/h", _value)
1231 if (self._wind_unit == WindUnits.KNOTS):
1232 _value *= 0.539957
1233 _unit = __("knot", "knots", _value)
1234 else:
1235 log.error("Could not parse '%s' as wind units." % wind_units)
1236 _value = -1.0
1237
897 # Join wind_info data in a label1238 # Join wind_info data in a label
898 localized_wind_info[len(localized_wind_info)-1] = _unit1239 wind_info[len(wind_info)-1] = _unit
899 localized_wind_info[len(localized_wind_info)-2] = \1240 wind_info[len(wind_info)-2] = \
900 NumberFormatter.format_float(_value, 1)1241 NumberFormatter.format_float(_value, 1)
901 if _value < 0.0:1242 if _value < 0.0:
902 localized_wind_info[1:] = ["", "N\A", ""]1243 wind_info[1:] = ["", "N\A", ""]
903 return "%s %s %s %s" % (localized_wind_info[0], localized_wind_info[1], \1244 if _value == 0.0:
904 localized_wind_info[2], localized_wind_info[3])1245 wind_info[1:] = [_("Calm"), "", ""]
1246 return "%s %s %s %s" % (wind_info[0], wind_info[1], \
1247 wind_info[2], wind_info[3])
9051248
906 # Get sunrise label
907 def get_sunrise_label(self):1249 def get_sunrise_label(self):
1250 """ Get text string for sunrise time """
908 return "%s: %s" % (_("Sunrise"), TimeFormatter.format_time(self.__sunrise_t))1251 return "%s: %s" % (_("Sunrise"), TimeFormatter.format_time(self.__sunrise_t))
9091252
910 # Get sunset label
911 def get_sunset_label(self):1253 def get_sunset_label(self):
1254 """ Get text string for sunset time """
912 return "%s: %s" % (_("Sunset"), TimeFormatter.format_time(self.__sunset_t))1255 return "%s: %s" % (_("Sunset"), TimeFormatter.format_time(self.__sunset_t))
9131256
9141257
915 # Additional functions
916 # Convert wind direction from degrees to localized direction
917 def get_wind_direction(self, degrees):
918 try:
919 degrees = int(degrees)
920 except ValueError:
921 return ''
922
923 if degrees < 23 or degrees >= 338:
924 #Short wind direction - north
925 return _('N')
926 elif degrees < 68:
927 return _('NE')
928 elif degrees < 113:
929 return _('E')
930 elif degrees < 158:
931 return _('SE')
932 elif degrees < 203:
933 return _('S')
934 elif degrees < 248:
935 return _('SW')
936 elif degrees < 293:
937 return _('W')
938 elif degrees < 338:
939 return _('NW')
940
941 # Convert mph to Beufort scale
942 def get_beaufort_from_mph(self, value):
943 if value < 1:
944 return 0
945 elif value < 4:
946 return 1
947 elif value < 8:
948 return 2
949 elif value < 13:
950 return 3
951 elif value < 18:
952 return 4
953 elif value < 25:
954 return 5
955 elif value < 27:
956 return 6
957 elif value < 39:
958 return 7
959 elif value < 47:
960 return 8
961 elif value < 89:
962 return 9
963 elif value < 64:
964 return 10
965 elif value < 73:
966 return 11
967 elif value >= 73:
968 return 12
969
970class indicator_weather(threading.Thread):1258class indicator_weather(threading.Thread):
971 """ Indicator class """1259 """ Indicator class """
972 last_update_time = None1260 last_update_time = None
9731261
974 # Settings values1262 # Settings values
975 # Formats: setting value, object name (for preferences dialog), value assigned (optional)1263 # Formats: setting value, object name (for preferences dialog), value assigned (optional)
976 metric_systems = { 'S': ('si', MetricSystem.SI),1264 metric_systems = { 'S': ('si', UnitSystem.SI),
977 'I': ('imperial', MetricSystem.IMPERIAL)}1265 'I': ('imperial', UnitSystem.IMPERIAL)}
9781266
979 weather_sources = { 'G': ('google', WeatherDataSource.GOOGLE),1267 weather_sources = { 'Y': ('yahoo', WeatherDataSource.YAHOO),
980 'Y': ('yahoo', WeatherDataSource.YAHOO)}1268 'W': ('weather-com', WeatherDataSource.WEATHER_COM)}
9811269
982 notifications = {'N': 'nonotif',1270 notifications = {'N': 'nonotif',
983 'O': 'notifsevere',1271 'O': 'notifsevere',
@@ -989,17 +1277,31 @@
989 'beaufort': ("beaufort", WindUnits.BEAUFORT),1277 'beaufort': ("beaufort", WindUnits.BEAUFORT),
990 'knots': ("knots", WindUnits.KNOTS)}1278 'knots': ("knots", WindUnits.KNOTS)}
9911279
992 # Initializing and reading settings1280 heat_estimates = {'heatindex': ("heatindex", RelativeFormula.HEATINDEX),
1281 'humidex': ("humidex", RelativeFormula.HUMIDEX)}
1282
1283 chill_estimates = {'windchill': ("wctindex", RelativeFormula.WINDCHILL),
1284 'apparent': ("aatindex", RelativeFormula.APPARENT)}
1285
993 def __init__(self):1286 def __init__(self):
1287 """ Initializing and reading previously-saved settings """
994 log.debug("Indicator: creating")1288 log.debug("Indicator: creating")
995 threading.Thread.__init__(self)1289 threading.Thread.__init__(self)
996 self.main_icon = os.path.join1290 self.main_icon = os.path.join
997 self.winder = appindicator.Indicator ("indicator-weather", "weather-indicator", appindicator.CATEGORY_OTHER)1291 self.winder = AppIndicator.Indicator.new("indicator-weather", "weather-indicator", AppIndicator.IndicatorCategory.OTHER)
998 self.winder.set_status (appindicator.STATUS_ACTIVE)1292
999 self.winder.set_attention_icon ("weather-indicator-error")1293 self.queue = Queue.Queue()
10001294
1001 self.menu_update_lock = threading.Lock()1295 self.menu_update_lock = threading.Lock()
1296 self.status_update_lock = threading.Lock()
1297
1298 self.status_update_lock.acquire(True)
1299 self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
1300 self.winder.set_attention_icon("weather-indicator-error")
1301 self.status_update_lock.release()
1302
1002 self.refreshed_minutes_ago = -11303 self.refreshed_minutes_ago = -1
1304 self.places_changed = False
1003 monitor_upower(self.on_system_sleep, self.on_system_resume, log)1305 monitor_upower(self.on_system_sleep, self.on_system_resume, log)
10041306
1005 log.debug("Indicator: reading settings")1307 log.debug("Indicator: reading settings")
@@ -1010,27 +1312,37 @@
1010 self.unit = self.settings.get_value("unit")1312 self.unit = self.settings.get_value("unit")
1011 self.notif = self.settings.get_value("notif")1313 self.notif = self.settings.get_value("notif")
1012 self.wind = self.settings.get_value("wind")1314 self.wind = self.settings.get_value("wind")
1315 self.heat = self.settings.get_value("heat")
1316 self.chill = self.settings.get_value("chill")
1013 self.source = self.settings.get_value("data_source")1317 self.source = self.settings.get_value("data_source")
1014 self.placechosen = self.settings.get_value("placechosen")1318 self.placechosen = self.settings.get_value("placechosen")
1015 self.places = str(self.settings.get_value("places"))1319 self.places = str(self.settings.get_value("places"))
1016 self.show_label = self.settings.get_value("show_label")1320 self.show_label = self.settings.get_value("show_label")
10171321 self.show_relative = self.settings.get_value("show_relative")
1018 log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, wind=%s, placechosen=%s, places=%s" %1322 self.show_wind = self.settings.get_value("show_wind")
1019 (self.rate, self.unit, self.notif, self.wind, self.placechosen, self.places))1323 self.show_suntimes = self.settings.get_value("show_suntimes")
1324
1325 log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, "
1326 "wind=%s, placechosen=%s, places=%s, heat=%s, chill=%s" %
1327 (self.rate, self.unit, self.notif, self.wind,
1328 self.placechosen, self.places, self.heat, self.chill))
10201329
1021 #Setting default values1330 #Setting default values
1022 self.metric_system = MetricSystem.SI1331 self.metric_system = UnitSystem.SI
1023 self.wind_unit = WindUnits.MPH1332 self.wind_unit = WindUnits.MPH
1024 self.place = None1333 self.place = None
1025 self.menu = None1334 self.menu = None
1026 self.condition = None1335 self.condition = None
1027 self.icon = None1336 self.icon = None
10281337 self.heat_index = RelativeFormula.HUMIDEX
1338 self.chill_index = RelativeFormula.WINDCHILL
1339
1029 #Parsing settings1340 #Parsing settings
1030 # Metric system1341 # Metric system
1031 if self.unit in (False, None):1342 if self.unit in (False, None):
1032 default_value = 'S'1343 default_value = 'S'
1033 log.debug("Indicator: could not parse unit, setting to %s" % default_value)1344 log.debug("Indicator: could not parse unit, "
1345 "setting to %s" % default_value)
1034 self.settings.set_value("unit", default_value)1346 self.settings.set_value("unit", default_value)
1035 self.unit = default_value1347 self.unit = default_value
1036 self.metric_system = self.metric_systems[self.unit][1]1348 self.metric_system = self.metric_systems[self.unit][1]
@@ -1038,25 +1350,70 @@
1038 # Notification1350 # Notification
1039 if self.notif in (False, None):1351 if self.notif in (False, None):
1040 default_value = 'N'1352 default_value = 'N'
1041 log.debug("Indicator: could not parse notif, setting to %s" % default_value)1353 log.debug("Indicator: could not parse notif, "
1354 "setting to %s" % default_value)
1042 self.settings.set_value("notif", default_value)1355 self.settings.set_value("notif", default_value)
1043 self.notif = default_value1356 self.notif = default_value
1357 Notify.init("weather-indicator")
10441358
1045 # Wind units1359 # Wind units
1046 if self.wind in (False, None):1360 if self.wind in (False, None):
1047 default_value = 'mph'1361 default_value = 'mph'
1048 log.debug("Indicator: could not parse wind, setting to %s" % default_value)1362 log.debug("Indicator: could not parse wind, "
1363 "setting to %s" % default_value)
1049 self.settings.set_value("wind", default_value)1364 self.settings.set_value("wind", default_value)
1050 self.wind = default_value1365 self.wind = default_value
1051 self.wind_unit = self.wind_systems[self.wind][1]1366 self.wind_unit = self.wind_systems[self.wind][1]
10521367
1368 # Heat estimate formula
1369 if self.heat in (False, None):
1370 default_value = 'humidex'
1371 log.debug("Indicator: could not parse heat, "
1372 "setting to %s" % default_value)
1373 self.settings.set_value("heat", default_value)
1374 self.heat = default_value
1375 self.heat_index = self.heat_estimates[self.heat][1]
1376
1377 # Chill estimate formula
1378 if self.chill in (False, None):
1379 default_value = 'windchill'
1380 log.debug("Indicator: could not parse chill, "
1381 "setting to %s" % default_value)
1382 self.settings.set_value("chill", default_value)
1383 self.chill = default_value
1384 self.chill_index = self.chill_estimates[self.chill][1]
1385
1053 # Show label in indicator?1386 # Show label in indicator?
1054 self.show_label = True if self.show_label == 1 else False1387 if self.show_label == 1:
1388 self.show_label = True
1389 self.label_guide = "100 ËšC" # Guide for width of label
1390 else:
1391 self.show_label = False
1392 self.label_guide = " "
10551393
1394 # Show relative temperature in dropdown?
1395 if self.show_relative == 1:
1396 self.show_relative = True
1397 else:
1398 self.show_relative = False
1399
1400 # Show windspeed & direction in dropdown?
1401 if self.show_wind == 1:
1402 self.show_wind = True
1403 else:
1404 self.show_wind = False
1405
1406 # Show sunrise & sunset times in dropdown?
1407 if self.show_suntimes == 1:
1408 self.show_suntimes = True
1409 else:
1410 self.show_suntimes = False
1411
1056 # Weather source1412 # Weather source
1057 if self.source in (False, None):1413 if self.source in (False, None, 'G'): # If set to Google, reset it
1058 default_value = 'Y'1414 default_value = 'Y'
1059 log.debug("Indicator: could not parse data source, setting to %s" % default_value)1415 log.debug("Indicator: could not parse data source, "
1416 "setting to %s" % default_value)
1060 self.settings.set_value("data_source", default_value)1417 self.settings.set_value("data_source", default_value)
1061 self.source = default_value1418 self.source = default_value
1062 self.weather_source = self.weather_sources[self.source][1]1419 self.weather_source = self.weather_sources[self.source][1]
@@ -1064,7 +1421,8 @@
1064 # Rate1421 # Rate
1065 if self.rate in (False, None):1422 if self.rate in (False, None):
1066 default_value = 151423 default_value = 15
1067 log.debug("Indicator: could not parse rate, setting to %s" % str(default_value))1424 log.debug("Indicator: could not parse rate, "
1425 "setting to %s" % str(default_value))
1068 self.settings.set_value("refresh_rate", default_value)1426 self.settings.set_value("refresh_rate", default_value)
1069 self.rate = default_value1427 self.rate = default_value
10701428
@@ -1077,9 +1435,11 @@
1077 self.placechosen = int(self.placechosen)1435 self.placechosen = int(self.placechosen)
10781436
1079 # Places list1437 # Places list
1438 self.menu_update_lock.acquire(True)
1080 if self.places in (False, None, '', '[]', "['']"):1439 if self.places in (False, None, '', '[]', "['']"):
1081 log.debug("Indicator: could not parse places")1440 log.debug("Indicator: could not parse places")
1082 self.menu_noplace()1441 self.menu_noplace()
1442 self.menu_update_lock.release()
1083 else:1443 else:
1084 self.places = eval(self.places)1444 self.places = eval(self.places)
1085 if self.placechosen >= len(self.places):1445 if self.placechosen >= len(self.places):
@@ -1089,89 +1449,96 @@
1089 if self.location_details in (False, None, '', '[]', "['']"):1449 if self.location_details in (False, None, '', '[]', "['']"):
1090 log.debug("Indicator: could not parse current location details")1450 log.debug("Indicator: could not parse current location details")
1091 self.menu_noplace()1451 self.menu_noplace()
1452 self.menu_update_lock.release()
1092 else:1453 else:
1093 self.location_details = eval(self.location_details)1454 self.location_details = eval(self.location_details)
1094 self.menu_normal()1455## self.menu_normal()
1456 self.menu_update_lock.release()
1095 self.update_weather()1457 self.update_weather()
10961458
1097 # Set a label of indicator
1098 def update_label(self, label):1459 def update_label(self, label):
1460 """ Set the label of the indicator """
1099 if (hasattr(self.winder, 'set_label')):1461 if (hasattr(self.winder, 'set_label')):
1100 log.debug("Indicator: update_label: setting label to '%s'" % label)1462 log.debug("Indicator: update_label: setting label to '%s'" % label)
1101 self.previous_label_value = label1463 self.previous_label_value = label
1102 self.winder.set_label(label) if self.show_label else self.winder.set_label(" ")1464 self.status_update_lock.acquire(True)
1103 self.winder.set_status(appindicator.STATUS_ATTENTION)1465 self.winder.set_label(label, self.label_guide) if self.show_label else self.winder.set_label(" ", " ")
1104 self.winder.set_status(appindicator.STATUS_ACTIVE)1466 self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION)
1467 self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
1468 self.status_update_lock.release()
11051469
1106 # Show a menu if no places specified
1107 def menu_noplace(self):1470 def menu_noplace(self):
1471 """ Show a menu if no places specified """
1108 log.debug("Indicator: making a menu for no places")1472 log.debug("Indicator: making a menu for no places")
1109 menu_noplace = gtk.Menu()1473 menu_noplace = Gtk.Menu()
11101474
1111 setup = gtk.MenuItem(_("Set Up Weather..."))1475 setup = Gtk.MenuItem(_("Set Up Weather..."))
1112 setup.connect("activate", self.prefs)1476 setup.connect("activate", self.prefs)
1113 setup.show()1477 setup.show()
1114 menu_noplace.append(setup)1478 menu_noplace.append(setup)
11151479
1116 quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)1480 quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
1117 quit.connect("activate", self.quit)1481 quit.connect("activate", self.quit)
1118 quit.show()1482 quit.show()
1119 menu_noplace.append(quit)1483 menu_noplace.append(quit)
11201484
1485 self.status_update_lock.acquire(True)
1121 self.winder.set_menu(menu_noplace)1486 self.winder.set_menu(menu_noplace)
1122 self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png"))1487 self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png"))
1123 self.winder.set_status(appindicator.STATUS_ATTENTION)1488 self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION)
1124 self.winder.set_status(appindicator.STATUS_ACTIVE)1489 self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
11251490 self.status_update_lock.release()
1126 # Show menu with data1491
1127 def menu_normal(self):1492 def menu_normal(self):
1493 """ Show a menu with weather and location data """
1128 log.debug("Indicator: menu_normal: filling in a menu for found places")1494 log.debug("Indicator: menu_normal: filling in a menu for found places")
1129 self.menu = gtk.Menu()1495
1496 self.menu = Gtk.Menu()
11301497
1131 ##City1498 ##City
1132 self.city_show = gtk.MenuItem()1499 self.city_show = Gtk.MenuItem()
1133 self.city_show.set_sensitive(True)1500 self.city_show.set_sensitive(True)
1134 self.menu.append(self.city_show)1501 self.menu.append(self.city_show)
1135 self.city_show.show()1502 self.city_show.show()
11361503
1137 ##Condition1504 ##Condition
1138 self.cond_show = gtk.MenuItem()1505 self.cond_show = Gtk.MenuItem()
1139 self.cond_show.set_sensitive(True)1506 self.cond_show.set_sensitive(True)
1140 self.cond_show.show()1507 self.cond_show.show()
1141 self.menu.append(self.cond_show)1508 self.menu.append(self.cond_show)
11421509
1143 ##Temperature1510 ##Temperature
1144 self.temp_show = gtk.MenuItem()1511 self.temp_show = Gtk.MenuItem()
1145 self.temp_show.set_sensitive(True)1512 self.temp_show.set_sensitive(True)
1146 self.temp_show.show()1513 self.temp_show.show()
1147 self.menu.append(self.temp_show)1514 self.menu.append(self.temp_show)
11481515
1149 ##Humidex1516 ##Relative Temperature
1150 self.humidex_show = gtk.MenuItem()1517 self.relative_show = Gtk.MenuItem()
1151 self.humidex_show.set_sensitive(True)1518 self.relative_show.set_sensitive(True)
1152 self.humidex_show.show()1519 self.relative_show.show()
1153 self.menu.append(self.humidex_show)1520 self.menu.append(self.relative_show)
11541521
1155 ##Humidity1522 ##Humidity
1156 self.humid_show = gtk.MenuItem()1523 self.humid_show = Gtk.MenuItem()
1157 self.humid_show.set_sensitive(True)1524 self.humid_show.set_sensitive(True)
1158 self.humid_show.show()1525 self.humid_show.show()
1159 self.menu.append(self.humid_show)1526 self.menu.append(self.humid_show)
11601527
1161 ##Wind1528 ##Wind
1162 self.wind_show = gtk.MenuItem()1529 self.wind_show = Gtk.MenuItem()
1163 self.wind_show.set_sensitive(True)1530 self.wind_show.set_sensitive(True)
1164 self.wind_show.show()1531 self.wind_show.show()
1165 self.menu.append(self.wind_show)1532 self.menu.append(self.wind_show)
11661533
1167 ##Sunrise1534 ##Sunrise
1168 self.sunrise_show = gtk.MenuItem()1535 self.sunrise_show = Gtk.MenuItem()
1169 self.sunrise_show.set_sensitive(True)1536 self.sunrise_show.set_sensitive(True)
1170 self.sunrise_show.show()1537 self.sunrise_show.show()
1171 self.menu.append(self.sunrise_show)1538 self.menu.append(self.sunrise_show)
11721539
1173 ##Sunset1540 ##Sunset
1174 self.sunset_show = gtk.MenuItem()1541 self.sunset_show = Gtk.MenuItem()
1175 self.sunset_show.set_sensitive(True)1542 self.sunset_show.set_sensitive(True)
1176 self.sunset_show.show()1543 self.sunset_show.show()
1177 self.menu.append(self.sunset_show)1544 self.menu.append(self.sunset_show)
@@ -1179,65 +1546,68 @@
1179 ##Cities1546 ##Cities
1180 if len(self.places) != 1:1547 if len(self.places) != 1:
1181 ##Breaker1548 ##Breaker
1182 breaker = gtk.SeparatorMenuItem()1549 breaker = Gtk.SeparatorMenuItem()
1183 breaker.show()1550 breaker.show()
1184 self.menu.append(breaker)1551 self.menu.append(breaker)
11851552
1186 log.debug("Indicator: menu_normal: adding first location menu item '%s'" % self.places[0][1])1553 log.debug("Indicator: menu_normal: adding first location menu item '%s'" % self.places[0][1])
1187 loco1 = gtk.RadioMenuItem(None, self.places[0][1])1554 loco1 = Gtk.RadioMenuItem.new_with_label([], self.places[0][1])
1188 if self.placechosen == 0:1555 if self.placechosen == 0:
1189 loco1.set_active(True)1556 loco1.set_active(True)
1190 loco1.connect("toggled", self.on_city_changed)1557 loco1.connect("toggled", self.on_city_changed)
1191 loco1.show()1558 loco1.show()
1192 self.menu.append(loco1)1559 self.menu.append(loco1)
1560 group = loco1.get_group()
1193 for place in self.places[1:]:1561 for place in self.places[1:]:
1194 log.debug("Indicator: menu_normal: adding location menu item '%s'" % place[1])1562 log.debug("Indicator: menu_normal: adding location menu item '%s'" % place[1])
1195 loco = gtk.RadioMenuItem(loco1, place[1])1563 loco = Gtk.RadioMenuItem.new_with_label(group, place[1])
1196 if self.places.index(place) == self.placechosen:1564 if self.places.index(place) == self.placechosen:
1197 loco.set_active(True)1565 loco.set_active(True)
1198 loco.connect("toggled", self.on_city_changed)1566 loco.connect("toggled", self.on_city_changed)
1199 loco.show()1567 loco.show()
1200 self.menu.append(loco)1568 self.menu.append(loco)
1569 group = loco.get_group()
12011570
1202 ##Breaker1571 ##Breaker
1203 breaker = gtk.SeparatorMenuItem()1572 breaker = Gtk.SeparatorMenuItem()
1204 breaker.show()1573 breaker.show()
1205 self.menu.append(breaker)1574 self.menu.append(breaker)
12061575
1207 self.refresh_show = gtk.MenuItem()1576 self.refresh_show = Gtk.MenuItem()
1208 #label will be set later1577 #label will be set later
1209 self.refresh_show.connect("activate", self.update_weather)1578 self.refresh_show.connect("activate", self.update_weather)
1210 self.refresh_show.show()1579 self.refresh_show.show()
1211 self.menu.append(self.refresh_show)1580 self.menu.append(self.refresh_show)
12121581
1213 ext_show = gtk.MenuItem(_("Forecast"))1582 ext_show = Gtk.MenuItem(_("Forecast"))
1214 ext_show.connect("activate", self.extforecast)1583 ext_show.connect("activate", self.extforecast)
1215 ext_show.show()1584 ext_show.show()
1216 self.menu.append(ext_show)1585 self.menu.append(ext_show)
12171586
1218 ##Preferences1587 ##Preferences
1219 prefs_show = gtk.MenuItem(_("Preferences..."))1588 prefs_show = Gtk.MenuItem(_("Preferences..."))
1220 prefs_show.connect("activate", self.prefs)1589 prefs_show.connect("activate", self.prefs)
1221 prefs_show.show()1590 prefs_show.show()
1222 self.menu.append(prefs_show)1591 self.menu.append(prefs_show)
12231592
1224 ##About1593 ##About
1225 about_show = gtk.MenuItem(_("About..."))1594 about_show = Gtk.MenuItem(_("About..."))
1226 about_show.connect("activate", self.about)1595 about_show.connect("activate", self.about)
1227 about_show.show()1596 about_show.show()
1228 self.menu.append(about_show)1597 self.menu.append(about_show)
12291598
1230 ##Quit1599 ##Quit
1231 quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)1600 quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
1232 quit.connect("activate", self.quit)1601 quit.connect("activate", self.quit)
1233 quit.show()1602 quit.show()
1234 self.menu.append(quit)1603 self.menu.append(quit)
12351604
1236 self.winder.set_menu(self.menu)1605 self.winder.set_menu(self.menu)
1237 self.update_label(" ")1606 self.update_label(" ")
1607
12381608
1239 # Another city has been selected from radiobutton
1240 def on_city_changed(self,widget):1609 def on_city_changed(self,widget):
1610 """ Another city has been selected from radiobutton """
1241 if widget.get_active():1611 if widget.get_active():
1242 for place in self.places:1612 for place in self.places:
1243 if (place[1] == widget.get_label()):1613 if (place[1] == widget.get_label()):
@@ -1251,28 +1621,30 @@
1251 self.location_details = self.settings.get_location_details(self.place[0])1621 self.location_details = self.settings.get_location_details(self.place[0])
1252 if self.location_details in (False, None, '', '[]', "['']"):1622 if self.location_details in (False, None, '', '[]', "['']"):
1253 log.debug("Indicator: could not parse location details for placechosen='%s'" % self.placechosen)1623 log.debug("Indicator: could not parse location details for placechosen='%s'" % self.placechosen)
1624 self.menu_update_lock.acquire(True)
1254 self.menu_noplace()1625 self.menu_noplace()
1626 self.menu_update_lock.release()
1255 else:1627 else:
1256 self.location_details = eval(self.location_details)1628 self.location_details = eval(self.location_details)
1257 self.settings.set_value("placechosen", self.placechosen)1629 self.settings.set_value("placechosen", self.placechosen)
1258 self.update_weather(False)1630 self.update_weather(False)
12591631
1260 def on_system_sleep(self):1632 def on_system_sleep(self):
1261 """1633 """Callback from UPower that system suspends/hibernates
1262 Callback from UPower that system suspends/hibernates1634
1263 """1635 """
1264 # store time1636 # store time
1265 self.sleep_time = datetime.datetime.now()1637 self.sleep_time = datetime.datetime.now()
1266 log.debug("Indicator: system goes to sleep at %s" % self.sleep_time)1638 log.debug("Indicator: system goes to sleep at %s" % self.sleep_time)
1267 # remove gobject timeouts1639 # remove gobject timeouts
1268 if hasattr(self, "refresh_id"):1640 if hasattr(self, "refresh_id"):
1269 gobject.source_remove(self.refresh_id)1641 GObject.source_remove(self.refresh_id)
1270 if hasattr(self, "rate_id"):1642 if hasattr(self, "rate_id"):
1271 gobject.source_remove(self.rate_id)1643 GObject.source_remove(self.rate_id)
12721644
1273 def on_system_resume(self):1645 def on_system_resume(self):
1274 """1646 """Callback from UPower that system resumes
1275 Callback from UPower that system resumes1647
1276 """1648 """
1277 now = datetime.datetime.now()1649 now = datetime.datetime.now()
1278 log.debug("Indicator: system resumes at %s" % now)1650 log.debug("Indicator: system resumes at %s" % now)
@@ -1291,25 +1663,26 @@
1291 else:1663 else:
1292 self.update_weather()1664 self.update_weather()
12931665
1294 # Schedule weather update
1295 def schedule_weather_update(self, rate_override = None):1666 def schedule_weather_update(self, rate_override = None):
1667 """ Schedule the next weather update """
1296 if hasattr(self, "rate_id"):1668 if hasattr(self, "rate_id"):
1297 gobject.source_remove(self.rate_id)1669 GObject.source_remove(self.rate_id)
1298 if rate_override:1670 if rate_override:
1299 self.rate_id = gobject.timeout_add(1671 self.rate_id = GObject.timeout_add(
1300 int(rate_override) * 60000, self.update_weather)1672## int(rate_override) * 60000, self.update_weather)
1673 int(rate_override * 60000), self.update_weather)
1301 else:1674 else:
1302 self.rate_id = gobject.timeout_add(1675 self.rate_id = GObject.timeout_add(
1303 int(self.rate) * 60000, self.update_weather)1676 int(self.rate) * 60000, self.update_weather)
13041677
1305 # Schedule weather update
1306 def schedule_refresh_label_update(self):1678 def schedule_refresh_label_update(self):
1679 """ Schedule the next 'Refresh' label update """
1307 if hasattr(self, "refresh_id"):1680 if hasattr(self, "refresh_id"):
1308 gobject.source_remove(self.refresh_id)1681 GObject.source_remove(self.refresh_id)
1309 self.refresh_id = gobject.timeout_add(60000, self.update_refresh_label)1682 self.refresh_id = GObject.timeout_add(60000, self.update_refresh_label)
13101683
1311 # Update 'Refresh' label with time since last successful data refresh
1312 def update_refresh_label(self, reset_minutes = None):1684 def update_refresh_label(self, reset_minutes = None):
1685 """ Update 'Refresh' label with time since last successful data refresh """
1313 if reset_minutes is not None:1686 if reset_minutes is not None:
1314 self.refreshed_minutes_ago = reset_minutes1687 self.refreshed_minutes_ago = reset_minutes
1315 else:1688 else:
@@ -1319,6 +1692,7 @@
1319 return False1692 return False
13201693
1321 def set_refresh_label(self, refreshing=False):1694 def set_refresh_label(self, refreshing=False):
1695 """ Update the 'Refresh' label text """
1322 if refreshing:1696 if refreshing:
1323 refresh_label=_("Refreshing, please wait")1697 refresh_label=_("Refreshing, please wait")
1324 elif self.refreshed_minutes_ago < 0:1698 elif self.refreshed_minutes_ago < 0:
@@ -1329,68 +1703,86 @@
1329 refresh_label = "%s (%s)" % (_("Refresh"), _("%d min. ago") % self.refreshed_minutes_ago)1703 refresh_label = "%s (%s)" % (_("Refresh"), _("%d min. ago") % self.refreshed_minutes_ago)
1330 self.refresh_show.set_label(refresh_label)1704 self.refresh_show.set_label(refresh_label)
13311705
1332 # Load weather data from cache and display its values1706 def get_cached_weather(self, queue):
1333 def show_cached_weather(self):1707 """ Load weather data from cache and put it onto the queue """
1334 try:1708 try:
1335 self.menu_update_lock.acquire(True)1709 log.debug("Indicator: show_cached_weather: setting "
1710 "previous_condition to None")
1336 self.previous_condition = None1711 self.previous_condition = None
1337 cached_weather = self.settings.get_weather(self.places[self.placechosen][0])1712 cached_weather = self.settings.get_weather(self.places[self.placechosen][0])
1338 if cached_weather is not None:1713 if cached_weather is not None:
1339 cached_weather = eval(cached_weather)1714 cached_weather = eval(cached_weather)
1340 log.debug("Indicator: loading weather from cache for %s" % self.places[self.placechosen])1715 log.debug("Indicator: loading weather from cache "
1341 self.menu_normal()1716 "for %s" % self.places[self.placechosen])
1342 self.set_refresh_label(True)1717 # Put the cached weather onto to the Queue
1343 self.icon = cached_weather['icon']1718 queue.put({'cached_weather': cached_weather})
1344 if (self.icon == False):1719 except Exception, e:
1345 self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))1720 log.error(e)
1346 else:1721 log.debug(traceback.format_exc(e))
1347 self.winder.set_icon(self.icon)
13481722
1349 self.city_show.set_label(self.places[self.placechosen][1])1723 def show_cached_weather(self, cached_weather):
1350 self.previous_condition = cached_weather['condition']1724 """ Update the indicator icon, label and menu with cached weather data """
1351 self.cond_show.set_label(cached_weather['condition'])1725 try:
1352 self.temp_show.set_label(cached_weather['temper'])1726 self.menu_normal()
1353 if cached_weather['humidex'] != None:1727 self.set_refresh_label(True)
1354 self.humidex_show.set_label(cached_weather['humidex'])1728 self.icon = cached_weather['icon']
1355 else:1729 if (self.icon == False):
1356 self.humidex_show.destroy()1730 self.winder.set_icon(
1357 self.humid_show.set_label(cached_weather['humidity'])1731 os.path.join(PROJECT_ROOT_DIRECTORY,
1732 "share/indicator-weather/media/icon_unknown_condition.png")
1733 )
1734 else:
1735 self.winder.set_icon(self.icon)
1736
1737 self.city_show.set_label(self.places[self.placechosen][1])
1738 self.previous_condition = cached_weather['condition']
1739 self.cond_show.set_label(cached_weather['condition'])
1740 self.temp_show.set_label(cached_weather['temper'])
1741 if (self.show_relative and cached_weather['feelslike'] != None):
1742 self.relative_show.set_visible(True)
1743 self.relative_show.set_label(cached_weather['feelslike'])
1744 else:
1745 self.relative_show.set_visible(False)
1746 self.humid_show.set_label(cached_weather['humidity'])
1747 if self.show_wind:
1748 self.wind_show.set_visible(True)
1358 self.wind_show.set_label(cached_weather['wind'])1749 self.wind_show.set_label(cached_weather['wind'])
1750 else:
1751 self.wind_show.set_visible(False)
1752 if self.show_suntimes:
1753 self.sunrise_show.set_visible(True)
1359 self.sunrise_show.set_label(cached_weather['sunrise'])1754 self.sunrise_show.set_label(cached_weather['sunrise'])
1755 self.sunset_show.set_visible(True)
1360 self.sunset_show.set_label(cached_weather['sunset'])1756 self.sunset_show.set_label(cached_weather['sunset'])
1361 self.update_label(cached_weather['label'])1757 else:
1362 self.winder.set_status(appindicator.STATUS_ATTENTION)1758 self.sunrise_show.set_visible(False)
1363 self.winder.set_status(appindicator.STATUS_ACTIVE)1759 self.sunset_show.set_visible(False)
13641760
1761 self.update_label(cached_weather['label'])
1762
1365 except Exception, e:1763 except Exception, e:
1366 log.error(e)1764 log.error(e)
1367 log.debug(traceback.format_exc(e))1765 log.debug(traceback.format_exc(e))
13681766
1369 self.menu_update_lock.release()1767 def get_new_weather_data(self, notif, queue):
13701768 """ Get fresh weather data from source and put it onto the queue """
1371 # Get fresh weather data
1372 def get_new_weather_data(self, notif = True):
1373
1374 # get weather and catch any exception1769 # get weather and catch any exception
1375 weather = None1770 weather = None
1376 try:1771 try:
1377 weather = self.get_weather()1772 weather = self.get_weather()
1378
1379 except urllib2.URLError, e:1773 except urllib2.URLError, e:
1380 weather = None1774 weather = None
1381 log.error("Indicator: networking error: %s" % e)1775 log.error("Indicator: networking error: %s" % e)
1382
1383 except Exception, e:1776 except Exception, e:
1384 weather = None1777 weather = None
1385 log.error(e)1778 log.error(e)
1386 log.debug(traceback.format_exc(e))1779 log.debug(traceback.format_exc(e))
1780 # Put the new weather data onto the Queue
1781 queue.put({'weather': weather})
13871782
1783 def show_new_weather_data(self, weather):
1784 """ Update the indicator icon, label and menu with new weather data """
1388 try:1785 try:
1389 # wait until cacher finishes
1390 log.debug("Indicator: updateWeather: waiting for 'Cacher' thread to terminate")
1391 self.menu_update_lock.acquire(True)
1392 self.menu_update_lock.release()
1393
1394 if weather is None:1786 if weather is None:
1395 # remove the "Refreshing" status1787 # remove the "Refreshing" status
1396 self.set_refresh_label()1788 self.set_refresh_label()
@@ -1399,35 +1791,59 @@
1399 # Repeat an attempt in one minute1791 # Repeat an attempt in one minute
1400 self.schedule_weather_update(1)1792 self.schedule_weather_update(1)
1401 return1793 return
14021794
1403 # Fill in menu with data1795 # Fill in menu with data
1404 log.debug("Indicator: updateWeather: got condition '%s', icon '%s'" % (self.condition, self.icon))1796 log.debug("Indicator: updateWeather: got condition '%s', icon '%s'" % (self.condition, self.icon))
1405 self.condition = weather.get_condition_label()1797 self.condition = weather.get_condition_label()
1406 self.icon = weather.get_icon_name()1798 self.icon = weather.get_icon_name()
1407 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()))1799 log.debug("Indicator: fill in menu with params: " \
14081800 "city='%s', temp='%s', humid='%s', " \
1801 "wind='%s', sunrise='%s', sunset='%s', " \
1802 "puretemp=%s" % (self.places[self.placechosen][1],
1803 weather.get_temperature_label(),
1804 weather.get_humidity_label(),
1805 weather.get_wind_label(),
1806 weather.get_sunrise_label(),
1807 weather.get_sunset_label(),
1808 weather.get_temperature_string()))
1409 self.menu_normal()1809 self.menu_normal()
1410 self.update_refresh_label(0)1810 self.update_refresh_label(0)
1411 self.city_show.set_label(self.places[self.placechosen][1])1811 self.city_show.set_label(self.places[self.placechosen][1])
1412 self.cond_show.set_label(self.condition)1812 self.cond_show.set_label(self.condition)
1413 self.temp_show.set_label(weather.get_temperature_label())1813 self.temp_show.set_label(weather.get_temperature_label())
1414 if (weather.get_humidex_label() != None):1814 _relative_label = weather.get_relative_label()
1415 self.humidex_show.set_label(weather.get_humidex_label())1815 if (self.show_relative and "---" not in _relative_label):
1816 self.relative_show.set_visible(True)
1817 self.relative_show.set_label(_relative_label)
1416 else:1818 else:
1417 self.humidex_show.destroy()1819 self.relative_show.set_visible(False)
1418 self.humid_show.set_label(weather.get_humidity_label())1820 self.humid_show.set_label(weather.get_humidity_label())
1419 self.wind_show.set_label(weather.get_wind_label())1821 if self.show_wind:
1420 self.sunrise_show.set_label(weather.get_sunrise_label())1822 self.wind_show.set_visible(True)
1421 self.sunset_show.set_label(weather.get_sunset_label())1823 self.wind_show.set_label(weather.get_wind_label())
14221824 else:
1423 # Saving cached data, unless correct icon is supplied1825 self.wind_show.set_visible(False)
1826 if self.show_suntimes:
1827 self.sunrise_show.set_visible(True)
1828 self.sunrise_show.set_label(weather.get_sunrise_label())
1829 self.sunset_show.set_visible(True)
1830 self.sunset_show.set_label(weather.get_sunset_label())
1831 else:
1832 self.sunrise_show.set_visible(False)
1833 self.sunset_show.set_visible(False)
1834
1835 # Save cached data if correct icon is supplied
1424 if (self.icon == False):1836 if (self.icon == False):
1425 self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))1837 self.winder.set_icon(
1838 os.path.join(PROJECT_ROOT_DIRECTORY,
1839 "share/indicator-weather/media/icon_unknown_condition.png")
1840 )
1426 else:1841 else:
1427 self.winder.set_icon(self.icon)1842 self.winder.set_icon(self.icon)
1428 self.settings.save_weather(weather, self.places[self.placechosen][0])1843 self.settings.save_weather(weather, self.places[self.placechosen][0])
1429 self.update_label(weather.get_temperature(needs_rounding=True))1844 # Update the indicator label
14301845 self.update_label(weather.get_temperature_string())
1846
1431 # Notify user, if notifications are enabled1847 # Notify user, if notifications are enabled
1432 if self.condition != self.previous_condition and self.notif == 'U':1848 if self.condition != self.previous_condition and self.notif == 'U':
1433 # Weather condition has changed1849 # Weather condition has changed
@@ -1437,106 +1853,135 @@
1437 # Severe weather condition notification1853 # Severe weather condition notification
1438 log.debug("Indicator: updateWeather: severe condition notification")1854 log.debug("Indicator: updateWeather: severe condition notification")
1439 self.notify(self.condition, self.icon, severe=True)1855 self.notify(self.condition, self.icon, severe=True)
14401856 # Save the current condition to track changes for notifications
1441 self.previous_condition = self.condition1857 self.previous_condition = self.condition
1442
1443 except Exception, e:1858 except Exception, e:
1444 log.error(e)1859 log.error(e)
1445 log.debug(traceback.format_exc(e))1860 log.debug(traceback.format_exc(e))
14461861
1862 # Schedule the next weather fetch
1447 self.schedule_weather_update()1863 self.schedule_weather_update()
14481864
1449 # Update weather
1450 def update_weather(self, notif=True, widget=None):1865 def update_weather(self, notif=True, widget=None):
1451 log.debug("Indicator: updateWeather: updating weather for %s" % self.places[self.placechosen])1866 """ Update the displayed weather data from cache and fetch new data """
1867 log.debug("Indicator: updateWeather: updating weather for %s" %
1868 self.places[self.placechosen])
1452 # First, display cached data1869 # First, display cached data
1453 threading.Thread(target=self.show_cached_weather, name='Cache').start()1870 cache_thread = threading.Thread(target=self.get_cached_weather,
1871 name='Cache', args=(self.queue,))
1872 cache_thread.start()
1873
1454 # Then, start a new thread with real data pickup1874 # Then, start a new thread with real data pickup
1455 threading.Thread(target=self.get_new_weather_data, name='Fetcher').start()1875 fetcher_thread = threading.Thread(target=self.get_new_weather_data,
14561876 name='Fetcher',
1457 # Get current weather for selected location1877 args=(self.notif, self.queue))
1878 fetcher_thread.start()
1879
1880 # Update the menu with the cached weather
1881 cache_thread.join()
1882 cached_weather = self.queue.get()
1883 if cached_weather is not None and cached_weather.has_key('cached_weather'):
1884 self.show_cached_weather(cached_weather['cached_weather'])
1885
1886 # Update the menu with the new weather
1887 fetcher_thread.join()
1888 weather = self.queue.get()
1889 if weather is not None and weather.has_key('weather'):
1890 self.show_new_weather_data(weather['weather'])
1891
1458 def get_weather(self):1892 def get_weather(self):
1459 log.debug("Indicator: getWeather for location '%s'" % self.location_details['full name'])1893 """ Get current weather for selected location """
1460 self.current_location = Location(self.metric_system, self.wind_unit, self.location_details)1894 log.debug("Indicator: getWeather for location '%s'" %
1895 self.location_details['full name'])
1896 self.current_location = Location(self.metric_system, self.wind_unit,
1897 self.heat_index, self.chill_index,
1898 self.location_details)
1461 log.debug("Indicator: getWeather: updating weather report")1899 log.debug("Indicator: getWeather: updating weather report")
1462 self.current_location.update_weather_data(self.weather_source)1900 self.current_location.update_weather_data(self.weather_source)
1463 return self.current_location.weather1901 return self.current_location.weather
14641902
1465 # Show notification to user1903 def notify(self,condition,icon,severe=False):
1466 def notify(self,conditon,icon,severe=False):1904 """ Show notification to user according to preferences """
1467 log.debug("Indicator: Notify on weather condition, severe=%s, condition=%s, icon=%s" % (severe, self.condition, icon))1905 log.debug("Indicator: Notify on weather condition, severe=%s, "
1906 "condition=%s, icon=%s" % (severe, condition, icon))
1468 if severe:1907 if severe:
1469 n = pynotify.Notification (_("Severe weather alert"),1908 n = Notify.Notification (_("Severe weather alert"),
1470 self.condition,1909 condition,
1471 icon)1910 icon)
1472 else:1911 else:
1473 n = pynotify.Notification (self.condition, "", icon)1912 n = Notify.Notification (condition, "", icon)
1474 n.show ()1913 n.show ()
14751914
1476 # Menu callbacks
1477 # Open Preferences dialog
1478 def prefs(self, widget):1915 def prefs(self, widget):
1916 """ Menu callback to open Preferences dialog """
1479 log.debug("Indicator: open Preferences")1917 log.debug("Indicator: open Preferences")
1480 if ((not hasattr(self, 'prefswindow')) or (not self.prefswindow.get_visible())):1918 if ((not hasattr(self, 'prefswindow')) or (not self.prefswindow.get_visible())):
1481 self.prefswindow = PreferencesDialog()1919 self.prefswindow = PreferencesDialog()
1482 self.prefswindow.show()1920 self.prefswindow.show()
14831921
1484 def about(self, widget):1922 def about(self, widget):
1923 """ Menu callback to open About dialog """
1485 log.debug("Indicator: open About dialog")1924 log.debug("Indicator: open About dialog")
1486 self.aboutdialog = gtk.AboutDialog()1925 self.aboutdialog = Gtk.AboutDialog()
1487 self.aboutdialog.set_name(_("Weather Indicator"))1926 self.aboutdialog.set_name(_("Weather Indicator"))
1488 self.aboutdialog.set_version(VERSION)1927 self.aboutdialog.set_version(VERSION)
14891928
1490 ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/doc/indicator-weather/AUTHORS"), "r")1929 ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY,
1930 "share/doc/indicator-weather/AUTHORS"), "r")
1491 self.aboutdialog.set_copyright(ifile.read().replace('\x0c', ''))1931 self.aboutdialog.set_copyright(ifile.read().replace('\x0c', ''))
1492 ifile.close()1932 ifile.close()
14931933
1494 ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/common-licenses/GPL-3"), "r")1934 ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY,
1935 "share/common-licenses/GPL-3"), "r")
1495 self.aboutdialog.set_license(ifile.read().replace('\x0c', ''))1936 self.aboutdialog.set_license(ifile.read().replace('\x0c', ''))
1496 ifile.close()1937 ifile.close()
14971938
1498 self.aboutdialog.set_website("https://launchpad.net/weather-indicator")1939 self.aboutdialog.set_website("https://launchpad.net/weather-indicator")
1499 self.aboutdialog.set_translator_credits(_("translator-credits"))1940 self.aboutdialog.set_translator_credits(_("translator-credits"))
1500 logo_path = os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png")1941 logo_path = os.path.join(PROJECT_ROOT_DIRECTORY,
1501 self.aboutdialog.set_logo(gtk.gdk.pixbuf_new_from_file(logo_path))1942 "share/indicator-weather/media/icon.png")
15021943 self.aboutdialog.set_logo(GdkPixbuf.Pixbuf.new_from_file(logo_path))
15031944
1504 self.aboutdialog.connect("response", self.about_close)1945 self.aboutdialog.connect("response", self.about_close)
1505 self.aboutdialog.show()1946 self.aboutdialog.show()
15061947
1507 def about_close(self, widget, event=None):1948 def about_close(self, widget, event=None):
1949 """ Menu callback to close About dialog """
1508 log.debug("Indicator: closing About dialog")1950 log.debug("Indicator: closing About dialog")
1509 self.aboutdialog.destroy()1951 self.aboutdialog.destroy()
15101952
1511 # Open Extended forecast window
1512 def extforecast(self, widget):1953 def extforecast(self, widget):
1954 """ Menu callback to open Extended forecast window """
1513 log.debug("Indicator: open Forecast")1955 log.debug("Indicator: open Forecast")
1514 if ((not hasattr(self, 'forecastwd')) or (not self.forecastwd.get_visible())):1956 if ((not hasattr(self, 'forecastwd')) or \
1515 self.forecastwd = ExtendedForecast()1957 (not self.forecastwd.get_visible())):
1516 self.forecastwd.show()1958 self.forecastwd = ExtendedForecast()
1959 self.forecastwd.show()
15171960
1518 # Quit the applet
1519 def quit(self, widget, data=None):1961 def quit(self, widget, data=None):
1962 """ Menu callback to quit the indicator applet """
1520 log.debug("Indicator: Quitting")1963 log.debug("Indicator: Quitting")
1521 gtk.main_quit()1964 Gtk.main_quit()
15221965
1523class PreferencesDialog(gtk.Dialog):1966class PreferencesDialog(Gtk.Dialog):
1524 """ Class for preferences dialog """1967 """ Class for preferences dialog """
1525 __gtype_name__ = "PreferencesDialog"1968 __gtype_name__ = "PreferencesDialog"
15261969
1527 # Creating a new preferences dialog
1528 def __new__(cls):1970 def __new__(cls):
1971 """ Creating a new preferences dialog """
1529 log.debug("Preferences: creating")1972 log.debug("Preferences: creating")
1530 builder = get_builder('PreferencesDialog')1973 builder = get_builder('PreferencesDialog')
1531 new_object = builder.get_object("preferences_dialog")1974 new_object = builder.get_object("preferences_dialog")
1532 new_object.finish_initializing(builder)1975 new_object.finish_initializing(builder)
1533 return new_object1976 return new_object
15341977
1535 # Fill in preferences dialog with currect data
1536 def finish_initializing(self, builder):1978 def finish_initializing(self, builder):
1979 """ Fill in preferences dialog with correct data """
1537 log.debug("Preferences: finishing initialization")1980 log.debug("Preferences: finishing initialization")
1538 log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, rate=%s, source=%s" %1981 log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, "
1539 (wi.unit, wi.notif, wi.wind, wi.rate, wi.source))1982 "rate=%s, source=%s, heat=%s, chill=%s" %
1983 (wi.unit, wi.notif, wi.wind, wi.rate, wi.source,
1984 wi.heat, wi.chill))
1540 self.builder = builder1985 self.builder = builder
15411986
1542 # Set correct wind_unit using dictionary of wind value and object name1987 # Set correct wind_unit using dictionary of wind value and object name
@@ -1547,11 +1992,16 @@
1547 self.builder.get_object('show_label').set_active(wi.show_label)1992 self.builder.get_object('show_label').set_active(wi.show_label)
1548 self.builder.get_object('show_label').set_visible(hasattr(wi.winder, 'set_label'))1993 self.builder.get_object('show_label').set_visible(hasattr(wi.winder, 'set_label'))
1549 self.builder.get_object('rate').set_value(float(wi.rate))1994 self.builder.get_object('rate').set_value(float(wi.rate))
1995 self.builder.get_object(wi.heat_estimates[wi.heat][0]).set_active(True)
1996 self.builder.get_object(wi.chill_estimates[wi.chill][0]).set_active(True)
1997 self.builder.get_object('show_relative').set_active(wi.show_relative)
1998 self.builder.get_object('show_wind').set_active(wi.show_wind)
1999 self.builder.get_object('show_suntimes').set_active(wi.show_suntimes)
15502000
1551 log.debug("Preferences: Loading places")2001 log.debug("Preferences: Loading places")
1552 if wi.places != None:2002 if wi.places != None:
1553 for place in wi.places:2003 for place in wi.places:
1554 if len(place)>1:2004 if len(place)>1 and place[0] is not None:
1555 log.debug("Preferences: Places: got (%s, %s)" % (place[1], place[0]))2005 log.debug("Preferences: Places: got (%s, %s)" % (place[1], place[0]))
1556 newplace = list()2006 newplace = list()
1557 newplace.append(place[1])2007 newplace.append(place[1])
@@ -1563,32 +2013,45 @@
15632013
1564 self.builder.connect_signals(self)2014 self.builder.connect_signals(self)
15652015
1566 # 'Remove' clicked - remove location from list
1567 #TODO: Update settings object
1568 def on_remove_location(self, widget):2016 def on_remove_location(self, widget):
2017 """ 'Remove' clicked - remove location from list """
1569 selection = self.builder.get_object('location_list').get_selection()2018 selection = self.builder.get_object('location_list').get_selection()
1570 model, iter = selection.get_selected()2019 model, iter = selection.get_selected()
1571 if iter != None:2020 if iter != None:
1572 log.debug("Preferences: Removing location %s (code %s)" % (model[iter][0], model[iter][1]))2021 log.debug("Preferences: Removing location %s (code %s)" %
2022 (model[iter][0], model[iter][1]))
2023 newplaces = []
2024 for place in wi.places:
2025 if place[1] != model[iter][0] and \
2026 place[0] != model[iter][1]:
2027 newplaces.append(place)
2028 if newplaces != wi.places:
2029 wi.places = newplaces
2030 log.debug("Preferences: update settings object")
2031 wi.settings.set_value("places", str(wi.places))
2032
1573 model.remove(iter)2033 model.remove(iter)
15742034 wi.places_changed = True
2035
1575 if (self.builder.get_object('citieslist').get_iter_first() == None):2036 if (self.builder.get_object('citieslist').get_iter_first() == None):
1576 self.builder.get_object('ok_button').set_sensitive(False)2037 self.builder.get_object('ok_button').set_sensitive(False)
15772038
1578 # 'Add' clicked - create a new Assistant
1579 def on_add_location(self, widget):2039 def on_add_location(self, widget):
2040 """ 'Add' clicked - create a new Assistant """
1580 log.debug("Preferences: Add location clicked")2041 log.debug("Preferences: Add location clicked")
1581 if ((not hasattr(self, 'assistant')) or (not self.assistant.get_visible())):2042 if ((not hasattr(self, 'assistant')) or (not self.assistant.get_visible())):
1582 self.assistant = Assistant()2043 self.assistant = Assistant()
1583 self.assistant.show()2044 self.assistant.show()
15842045
1585 # 'OK' clicked - save settings
1586 def ok(self, widget, data=None):2046 def ok(self, widget, data=None):
2047 """ 'OK' clicked - save settings """
2048 #TODO: rewrite this - for better response to user clicks,
2049 # it should first copy values to a queue, then destroy the window
1587 log.debug("Preferences: Saving settings")2050 log.debug("Preferences: Saving settings")
1588 need_to_update_weather = False2051 need_to_update_weather = False
1589 need_to_update_indicator = False2052 need_to_update_indicator = False
15902053
1591 #Show label near icon2054 # Show label near icon
1592 new_show_label = self.builder.get_object('show_label').get_active()2055 new_show_label = self.builder.get_object('show_label').get_active()
1593 if (wi.show_label != new_show_label):2056 if (wi.show_label != new_show_label):
1594 wi.show_label = new_show_label2057 wi.show_label = new_show_label
@@ -1597,6 +2060,30 @@
1597 need_to_update_indicator = True2060 need_to_update_indicator = True
1598 log.debug("Preferences: Show Label changed to '%s'" % wi.show_label)2061 log.debug("Preferences: Show Label changed to '%s'" % wi.show_label)
15992062
2063 # Show relative temperature
2064 new_show_relative = self.builder.get_object('show_relative').get_active()
2065 if (wi.show_relative != new_show_relative):
2066 wi.show_relative = new_show_relative
2067 wi.settings.set_value("show_relative", new_show_relative)
2068 need_to_update_weather = True
2069 log.debug("PreferencesDialog: Show Relative Temp changed to %s" % wi.show_relative)
2070
2071 # Show wind speed & direction
2072 new_show_wind = self.builder.get_object('show_wind').get_active()
2073 if (wi.show_wind != new_show_wind):
2074 wi.show_wind = new_show_wind
2075 wi.settings.set_value("show_relative", new_show_wind)
2076 need_to_update_weather = True
2077 log.debug("PreferencesDialog: Show Wind Data changed to %s" % wi.show_wind)
2078
2079 # Show sunrise & sunset times
2080 new_show_suntimes = self.builder.get_object('show_suntimes').get_active()
2081 if (wi.show_suntimes != new_show_suntimes):
2082 wi.show_suntimes = new_show_suntimes
2083 wi.settings.set_value("show_suntimes", new_show_suntimes)
2084 need_to_update_weather = True
2085 log.debug("PreferencesDialog: Show Sunrise/Sunset Times changed to %s" % wi.show_suntimes)
2086
1600 # Metric systems2087 # Metric systems
1601 for k in wi.metric_systems.keys():2088 for k in wi.metric_systems.keys():
1602 if self.builder.get_object(wi.metric_systems[k][0]).get_active():2089 if self.builder.get_object(wi.metric_systems[k][0]).get_active():
@@ -1634,6 +2121,32 @@
1634 need_to_update_weather = True2121 need_to_update_weather = True
1635 log.debug("Preferences: Wind Unit changed to '%s'" % wi.wind)2122 log.debug("Preferences: Wind Unit changed to '%s'" % wi.wind)
16362123
2124 # Heat estimate formula
2125 for k in wi.heat_estimates.keys():
2126 if self.builder.get_object(wi.heat_estimates[k][0]).get_active():
2127 new_heat_index = k
2128 new_heat_estimate = wi.heat_estimates[k][1]
2129
2130 if (wi.heat != new_heat_index):
2131 wi.heat = new_heat_index
2132 wi.heat_index = new_heat_estimate
2133 wi.settings.set_value("heat", wi.heat)
2134 need_to_update_weather = True
2135 log.debug("Preferences: Heat Estimate changed to '%s'" % wi.heat)
2136
2137 # Chill estimate formula
2138 for k in wi.chill_estimates.keys():
2139 if self.builder.get_object(wi.chill_estimates[k][0]).get_active():
2140 new_chill_index = k
2141 new_chill_estimate = wi.chill_estimates[k][1]
2142
2143 if (wi.chill != new_chill_index):
2144 wi.chill = new_chill_index
2145 wi.chill_index = new_chill_estimate
2146 wi.settings.set_value("chill", wi.chill)
2147 need_to_update_weather = True
2148 log.debug("Preferences: Chill Estimate changed to '%s'" % wi.chill)
2149
1637 # Weather source2150 # Weather source
1638 for k in wi.weather_sources.keys():2151 for k in wi.weather_sources.keys():
1639 if self.builder.get_object(wi.weather_sources[k][0]).get_active():2152 if self.builder.get_object(wi.weather_sources[k][0]).get_active():
@@ -1655,18 +2168,19 @@
1655 wi.schedule_weather_update()2168 wi.schedule_weather_update()
16562169
1657 # Get places from location list2170 # Get places from location list
1658 newplaces = list()2171## newplaces = list()
1659 item = self.builder.get_object('citieslist').get_iter_first()2172## item = self.builder.get_object('citieslist').get_iter_first()
1660 while (item != None):2173## while (item != None):
1661 newplace = list()2174## newplace = list()
1662 newplace.append(self.builder.get_object('citieslist').get_value (item, 1))2175## newplace.append(self.builder.get_object('citieslist').get_value (item, 1))
1663 newplace.append(self.builder.get_object('citieslist').get_value (item, 0))2176## newplace.append(self.builder.get_object('citieslist').get_value (item, 0))
1664 newplaces.append(newplace)2177## newplaces.append(newplace)
1665 item = self.builder.get_object('citieslist').iter_next(item)2178## item = self.builder.get_object('citieslist').iter_next(item)
16662179
1667 # If places have changed - update weather data2180 # If places have changed - update weather data
1668 if newplaces != wi.places:2181## if newplaces != wi.places:
1669 wi.places = newplaces2182## wi.places = newplaces
2183 if wi.places_changed:
1670 log.debug("Preferences: Places changed to '%s'" % str(wi.places))2184 log.debug("Preferences: Places changed to '%s'" % str(wi.places))
1671 wi.settings.set_value("places", str(wi.places))2185 wi.settings.set_value("places", str(wi.places))
1672 if (type(wi.place) != None) and (wi.place in wi.places):2186 if (type(wi.place) != None) and (wi.place in wi.places):
@@ -1677,60 +2191,68 @@
1677 log.debug("Preferences: Place Chosen changed to '%s'" % wi.placechosen)2191 log.debug("Preferences: Place Chosen changed to '%s'" % wi.placechosen)
1678 wi.settings.set_value("placechosen", wi.placechosen)2192 wi.settings.set_value("placechosen", wi.placechosen)
1679 wi.location_details = eval(wi.settings.get_location_details(wi.place[0]))2193 wi.location_details = eval(wi.settings.get_location_details(wi.place[0]))
1680 wi.menu_normal()2194## wi.menu_normal()
1681 wi.set_refresh_label()2195## wi.set_refresh_label()
1682 need_to_update_weather = True2196 need_to_update_weather = True
2197 wi.places_changed = False
16832198
1684 if need_to_update_weather:2199 if need_to_update_weather:
1685 wi.update_weather(False)2200#### wi.update_weather(False)
2201 wi.schedule_weather_update(0.003)
16862202
1687 if need_to_update_indicator:2203 if need_to_update_indicator:
1688 wi.update_label(wi.previous_label_value)2204 wi.update_label(wi.previous_label_value)
16892205
1690 self.destroy()2206 self.destroy()
16912207
1692 # 'Cancel' click - forget all changes
1693 def cancel(self, widget, data=None):2208 def cancel(self, widget, data=None):
2209 """ 'Cancel' clicked - forget all changes """
1694 log.debug("Preferences: Cancelling")2210 log.debug("Preferences: Cancelling")
1695 self.destroy()2211 self.destroy()
16962212
1697class ExtendedForecast(gtk.Window):2213class ExtendedForecast(Gtk.Window):
1698 """ Class for forecast window """2214 """ Class for forecast window """
1699 __gtype_name__ = "ExtendedForecast"2215 __gtype_name__ = "ExtendedForecast"
17002216
1701 # Create forecast
1702 def __new__(cls):2217 def __new__(cls):
2218 """ Create forecast """
1703 log.debug("ExtendedForecast: creating")2219 log.debug("ExtendedForecast: creating")
1704 builder = get_builder('ExtendedForecast')2220 builder = get_builder('ExtendedForecast')
1705 new_object = builder.get_object("extended_forecast")2221 new_object = builder.get_object("extended_forecast")
1706 new_object.finish_initializing(builder)2222 new_object.finish_initializing(builder)
1707 return new_object2223 return new_object
17082224
1709 # Fill in forecast parameters
1710 def finish_initializing(self, builder):2225 def finish_initializing(self, builder):
2226 """ Fill in forecast parameters """
1711 log.debug("ExtendedForecast: finishing initialization")2227 log.debug("ExtendedForecast: finishing initialization")
1712 self.builder = builder2228 self.builder = builder
1713 self.builder.connect_signals(self)2229 self.builder.connect_signals(self)
17142230
1715 # Get forecast data using Forecast object2231 # Get forecast data using Forecast object
1716 log.debug("ExtendedForecast: chosen place: %s (code %s)" % (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0]))2232 log.debug("ExtendedForecast: chosen place: %s (code %s)" %
1717 self.builder.get_object('extended_forecast').set_title("%s %s" % (_('Weather Forecast for '), wi.places[wi.placechosen][1]))2233 (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0]))
2234 self.builder.get_object('extended_forecast').set_title("%s %s" %
2235 (_('Weather Forecast for'), wi.places[wi.placechosen][1]))
1718 log.debug("ExtendedForecast: getting forecast data")2236 log.debug("ExtendedForecast: getting forecast data")
1719 forecast = Forecast(wi.metric_system, wi.current_location.location_details['latitude'], wi.current_location.location_details['longitude'], locale_name)2237 forecast = Forecast(wi.metric_system,
2238 wi.current_location.location_details['yahoo id'],
2239 locale_name)
1720 forecast.prepare_forecast_data()2240 forecast.prepare_forecast_data()
1721 if forecast.error_message != None:2241 if forecast.error_message != None:
1722 #Error occurred while getting forecast data2242 #Error occurred while getting forecast data
1723 self.builder.get_object('connection_error').set_text("%s" % forecast.error_message)2243 self.builder.get_object('connection_error').set_text("%s" %
2244 forecast.error_message)
1724 self.builder.get_object('connection_error').set_visible(True)2245 self.builder.get_object('connection_error').set_visible(True)
1725 self.builder.get_object('hbox1').set_visible(False)2246 self.builder.get_object('hbox1').set_visible(False)
1726 else:2247 else:
1727 daysofweek = forecast.get_forecast_daysofweek()2248 daysofweek = forecast.get_forecast_daysofweek()
1728 forecast_data = forecast.get_forecast_data()2249 forecast_data = forecast.get_forecast_data()
1729 if forecast_data == None:2250 if forecast_data == None:
1730 # Forecast data unavailable - hide elements and show 'connection_error' label2251 # Forecast data unavailable - hide elements
1731 self.builder.get_object('connection_error').set_visible(True);2252 # and show 'connection_error' label
1732 self.builder.get_object('hbox1').set_visible(False);2253 self.builder.get_object('connection_error').set_visible(True)
1733 self.builder.get_object('hseparator1').set_visible(False);2254 self.builder.get_object('hbox1').set_visible(False)
2255 self.builder.get_object('hseparator1').set_visible(False)
1734 return2256 return
1735 (highdata, lowdata) = forecast_data2257 (highdata, lowdata) = forecast_data
1736 icons = forecast.get_forecast_icons()2258 icons = forecast.get_forecast_icons()
@@ -1738,68 +2260,81 @@
17382260
1739 log.debug("ExtendedForecast: parsing forecast data")2261 log.debug("ExtendedForecast: parsing forecast data")
1740 # Create labels for each weekday2262 # Create labels for each weekday
1741 self.builder.get_object('day1lbl').set_label('<big>%s</big>' % daysofweek[0].capitalize())2263 for i in xrange(1,5):
1742 self.builder.get_object('day2lbl').set_label('<big>%s</big>' % daysofweek[1].capitalize())2264 try:
1743 self.builder.get_object('day3lbl').set_label('<big>%s</big>' % daysofweek[2].capitalize())2265 lbl_name = 'day%slbl' % (i)
1744 self.builder.get_object('day4lbl').set_label('<big>%s</big>' % daysofweek[3].capitalize())2266 self.builder.get_object(lbl_name).set_label('<big>%s</big>' %
17452267 daysofweek[i].capitalize())
2268 except IndexError:
2269 log.error("ExtendedForecast: Yahoo didn't return "
2270 "forecast for %s days" % i)
2271 log.error(forecast.forecast)
2272
1746 # Fill in icons2273 # Fill in icons
1747 for i in xrange(1,5):2274 for i in xrange(1,5):
1748 # Get icon name from dictionary in Weather object for Google icons2275 # Get icon name from dictionary in Weather object
2276 # for Yahoo condition codes
1749 try:2277 try:
1750 conds = Weather._GoogleConditions.get(icons[i-1])2278 conds = Weather._YahooConditions.get(icons[i])
1751 if conds != None:2279 if conds != None:
1752 google_icon = conds[0]2280 yahoo_icon = conds[0]
1753 else:2281 else:
1754 log.error("ExtendedForecast: unknown Google weather condition '%s'" % icons[i-1])2282 log.error("ExtendedForecast: unknown Yahoo weather "
2283 "condition code '%s'" % icons[i])
1755 log.error(forecast.forecast)2284 log.error(forecast.forecast)
1756 google_icon = 'weather-indicator-unknown'2285 yahoo_icon = 'weather-indicator-unknown'
1757 self.builder.get_object('day%simage' % str(i)).set_from_icon_name(google_icon,gtk.ICON_SIZE_BUTTON)2286 self.builder.get_object('day%simage' %
2287 str(i)).set_from_icon_name(yahoo_icon,
2288 Gtk.IconSize.BUTTON)
1758 except IndexError:2289 except IndexError:
1759 log.error("ExtendedForecast: Google didn't return condition for %s days" % i-1)2290 log.error("ExtendedForecast: Yahoo didn't return "
1760 log.error(forecast.forecast)2291 "condition for %s days" % i)
2292 log.error(forecast.forecast)
17612293
1762 # Fill in condition labels2294 # Fill in condition labels
1763 for i in xrange(1,5):2295 for i in xrange(1,5):
1764 if conditions[i-1] != '':2296 if conditions[i] != '':
1765 condition = conditions[i-1]2297 condition = conditions[i]
1766 else:2298 else:
1767 condition = _("Unknown condition")2299 condition = _("Unknown condition")
1768 self.builder.get_object('day%scond' % str(i)).set_label(condition)2300 self.builder.get_object('day%scond' %
2301 str(i)).set_label(condition)
17692302
1770 # Fill in High and Low temperatures2303 # Fill in High and Low temperatures
1771 if wi.metric_system == MetricSystem.SI:2304 if wi.metric_system == UnitSystem.SI:
1772 tempunit = '°C'2305 tempunit = u"°C"
1773 else:2306 else:
1774 tempunit = '°F'2307 tempunit = u"°F"
1775 for i in xrange(1,5):2308 for i in xrange(1,5):
1776 label = "%s: %s%s" % (_('High'), highdata[i-1],tempunit)2309 label = "%s: %s%s" % (_('High'), highdata[i],tempunit)
1777 self.builder.get_object('day%stemphigh' % str(i)).set_label(label)2310 self.builder.get_object('day%stemphigh' %
1778 label = "%s: %s%s" % (_('Low'), lowdata[i-1],tempunit)2311 str(i)).set_label(label)
1779 self.builder.get_object('day%stemplow' % str(i)).set_label(label)2312 label = "%s: %s%s" % (_('Low'), lowdata[i],tempunit)
2313 self.builder.get_object('day%stemplow' %
2314 str(i)).set_label(label)
17802315
1781 # Closing forecast window
1782 def close(self, widget, data=None):2316 def close(self, widget, data=None):
2317 """ 'Close' clicked - close forecast window """
1783 log.debug("ExtendedForecast: closing window")2318 log.debug("ExtendedForecast: closing window")
1784 self.destroy()2319 self.destroy()
17852320
1786 def on_destroy(self, widget):2321 def on_destroy(self, widget):
1787 pass2322 pass
17882323
1789class Assistant(gtk.Assistant):2324class Assistant(Gtk.Assistant):
1790 """ Class for a wizard, which helps to add a new location in location list """2325 """ Class for a wizard, which helps to add a new location in location list """
1791 __gtype_name__ = "Assistant"2326 __gtype_name__ = "Assistant"
17922327
1793 # Create new object
1794 def __new__(cls):2328 def __new__(cls):
2329 """ Create new object """
1795 log.debug("Assistant: creating new Assistance instance")2330 log.debug("Assistant: creating new Assistance instance")
1796 builder = get_builder('Assistant')2331 builder = get_builder('Assistant')
1797 new_object = builder.get_object("assistant")2332 new_object = builder.get_object("assistant")
1798 new_object.finish_initializing(builder)2333 new_object.finish_initializing(builder)
1799 return new_object2334 return new_object
18002335
1801 # Finish UI initialization - prepare combobox
1802 def finish_initializing(self, builder):2336 def finish_initializing(self, builder):
2337 """ Finish UI initialization - prepare combobox """
1803 log.debug("Assistant: finishing initialization")2338 log.debug("Assistant: finishing initialization")
1804 self.builder = builder2339 self.builder = builder
1805 self.builder.connect_signals(self)2340 self.builder.connect_signals(self)
@@ -1809,18 +2344,18 @@
18092344
1810 # Set up combobox2345 # Set up combobox
1811 log.debug("Assistant: setting up location combobox")2346 log.debug("Assistant: setting up location combobox")
1812 self.store = gtk.ListStore(str, str, str, str, str)2347 self.store = Gtk.ListStore(str, str, str, str, str)
1813 self.location_input_combo = self.builder.get_object("combolocations")2348 self.location_input_combo = self.builder.get_object("combolocations")
1814 self.location_input_combo.set_model(self.store)2349 self.location_input_combo.set_model(self.store)
1815 self.location_input_combo.set_text_column(0)2350 self.location_input_combo.set_entry_text_column(0)
1816 self.location_entry = self.builder.get_object("entrylocation")2351 self.location_entry = self.builder.get_object("entrylocation")
1817 self.place_selected = None2352 self.place_selected = None
1818 self.location = None2353 self.location = None
18192354
1820 self.assistant.set_forward_page_func(self.next_page)2355 self.assistant.set_forward_page_func(self.next_page, None)
18212356
1822 # 'Get cities' button clicked - get suggested cities list
1823 def on_get_city_names(self, widget):2357 def on_get_city_names(self, widget):
2358 """ 'Get cities' button clicked - get suggested cities list """
1824 new_text = self.location_entry.get_text()2359 new_text = self.location_entry.get_text()
1825 log.debug("Assistant: looking for location '%s'" % new_text)2360 log.debug("Assistant: looking for location '%s'" % new_text)
1826 try:2361 try:
@@ -1833,18 +2368,26 @@
1833 for city in cities['geonames']:2368 for city in cities['geonames']:
1834 # Create a full city name, consisting of city name, administrative areas names and country name2369 # Create a full city name, consisting of city name, administrative areas names and country name
1835 if 'adminName2' in city:2370 if 'adminName2' in city:
1836 displayed_city_name = "%s, %s, %s, %s" % (city['name'], city['adminName1'], city['adminName1'], city['countryName'])2371 displayed_city_name = "%s, %s, %s, %s" % (
2372 city['name'], city['adminName1'],
2373 city['adminName1'], city['countryName']
2374 )
1837 elif 'adminName1' in city:2375 elif 'adminName1' in city:
1838 displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])2376 displayed_city_name = "%s, %s, %s" % (
2377 city['name'], city['adminName1'], city['countryName']
2378 )
1839 else:2379 else:
1840 displayed_city_name = "%s, %s" % (city['name'], city['countryName'])2380 displayed_city_name = "%s, %s" % (city['name'],
1841 self.store.append([displayed_city_name, str(city['geonameId']), str(city['lat']), str(city['lng']), str(city['name'])])2381 city['countryName'])
2382 self.store.append([displayed_city_name,
2383 str(city['geonameId']), str(city['lat']),
2384 str(city['lng']), str(city['name'])])
1842 self.location_input_combo.popup()2385 self.location_input_combo.popup()
1843 except urllib2.URLError:2386 except urllib2.URLError:
1844 log.error("Assistant: error reaching url '%s'" % url)2387 log.error("Assistant: error reaching url '%s'" % url)
18452388
1846 # A city is selected from suggested list
1847 def on_select_city(self, entry):2389 def on_select_city(self, entry):
2390 """ A city is selected from suggested list """
1848 if self.location_input_combo.get_active() != -1:2391 if self.location_input_combo.get_active() != -1:
1849 self.place_selected = self.store[self.location_input_combo.get_active()]2392 self.place_selected = self.store[self.location_input_combo.get_active()]
1850 self.assistant.set_page_complete(self.builder.get_object("placeinput"),True)2393 self.assistant.set_page_complete(self.builder.get_object("placeinput"),True)
@@ -1853,50 +2396,74 @@
1853 self.location = None2396 self.location = None
1854 self.assistant.set_page_complete(self.builder.get_object("placeinput"), False)2397 self.assistant.set_page_complete(self.builder.get_object("placeinput"), False)
18552398
1856 # Create a location object out of a selected location2399 def next_page(self, current_page, data):
1857 def next_page(self, current_page):2400 """ Create a location object out of a selected location """
1858 log.debug("Assistant: moved to page %s" % current_page)2401 log.debug("Assistant: moved to page %s" % current_page)
1859 if (self.assistant.get_current_page() == 0) and not self.location and self.place_selected:2402 if (self.assistant.get_current_page() == 0) and \
2403 not self.location and self.place_selected:
1860 # Label input page2404 # Label input page
1861 log.debug("Assistant: Page %s: got location with code %s" % (current_page, self.place_selected[1]))2405 log.debug("Assistant: Page %s: got location with code %s" %
1862 self.location = Location(wi.metric_system, wi.wind_unit)2406 (current_page, self.place_selected[1]))
2407 self.location = Location(wi.metric_system, wi.wind_unit,
2408 wi.heat_index, wi.chill_index)
1863 if self.location.prepare_location(self.place_selected):2409 if self.location.prepare_location(self.place_selected):
1864 log.debug("Assistant: Page %s: City %s found" % (current_page, self.place_selected[0]))2410 log.debug("Assistant: Page %s: City %s found" %
2411 (current_page, self.place_selected[0]))
1865 # Set a short city name as default label2412 # Set a short city name as default label
1866 self.builder.get_object("entrylbl").set_text(self.place_selected[4])2413 self.builder.get_object("entrylbl").set_text(self.place_selected[4])
1867 else:2414 else:
1868 log.error("Assistant: Page %s: City with code %s was NOT found" % (current_page, self.place_selected[0]))2415 log.error("Assistant: Page %s: City with code %s was NOT "
2416 "found" % (current_page, self.place_selected[0]))
1869 return 32417 return 3
1870 elif self.assistant.get_current_page() == 1:2418 elif self.assistant.get_current_page() == 1:
1871 # Confirmation page2419 # Confirmation page
1872 lbl = self.builder.get_object("entrylbl").get_text()2420 lbl = self.builder.get_object("entrylbl").get_text()
1873 log.debug("Assistant: Page %s: City label is %s" % (current_page, lbl))2421 log.debug("Assistant: Page %s: City label is %s" %
2422 (current_page, lbl))
1874 # If empty label was input, set label to short city name2423 # If empty label was input, set label to short city name
1875 if lbl == '':2424 if lbl == '':
1876 log.debug("Assistant: Page %s: Empty label found, setting lbl to short name - %s" % (current_page, self.place_selected[4]))2425 log.debug("Assistant: Page %s: Empty label found, setting "
2426 "lbl to short name - %s" %
2427 (current_page, self.place_selected[4]))
1877 lbl = self.place_selected[4]2428 lbl = self.place_selected[4]
1878 self.location.location_details['label'] = lbl2429 self.location.location_details['label'] = lbl
1879 self.builder.get_object("lbl3").set_label(_('Label:'))2430 self.builder.get_object("lbl3").set_label(_('Label:'))
1880 self.builder.get_object("labellbl").set_label('<b>%s</b>' % lbl)2431 self.builder.get_object("labellbl").set_label('<b>%s</b>' % lbl)
1881 self.builder.get_object("placelbl").set_label('<b>%s</b>' % self.place_selected[0])2432 self.builder.get_object("placelbl").set_label(
2433 '<b>%s</b>' % self.place_selected[0])
18822434
1883 return self.assistant.get_current_page() + 12435 return self.assistant.get_current_page() + 1
18842436
1885 # 'Cancel' clicked
1886 def on_cancel(self,widget):2437 def on_cancel(self,widget):
2438 """ 'Cancel' clicked """
1887 log.debug("Assistant: Cancelled")2439 log.debug("Assistant: Cancelled")
1888 self.destroy()2440 self.destroy()
18892441
1890 # 'Apply' clicked - save location details, add an entry in a location list
1891 def on_apply(self,widget):2442 def on_apply(self,widget):
2443 """ 'Apply' clicked - save location details, add an entry in a location list """
1892 (location_code, location_details) = self.location.export_location_details()2444 (location_code, location_details) = self.location.export_location_details()
1893 log.debug("Assistant: Apply: adding location ('%s', '%s')" % (self.location.location_details['label'], location_code))2445 log.debug("Assistant: Apply: adding location ('%s', '%s')" %
2446 (self.location.location_details['label'], location_code))
1894 newplace = list()2447 newplace = list()
1895 newplace.append(self.location.location_details['label'])2448 newplace.append(self.location.location_details['label'])
1896 newplace.append(str(location_code))2449 newplace.append(str(location_code))
1897 newplace.append(str(location_details))2450 newplace.append(str(location_details))
1898 wi.settings.save_location_details(eval(str(location_details)), str(location_code))2451 wi.settings.save_location_details(
2452 eval(str(location_details)), str(location_code)
2453 )
1899 wi.prefswindow.builder.get_object('citieslist').append(newplace)2454 wi.prefswindow.builder.get_object('citieslist').append(newplace)
2455
2456 newplaces = list()
2457 item = wi.prefswindow.builder.get_object('citieslist').get_iter_first()
2458 while (item != None):
2459 newplace = list()
2460 newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 1))
2461 newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 0))
2462 newplaces.append(newplace)
2463 item = wi.prefswindow.builder.get_object('citieslist').iter_next(item)
2464 wi.places = newplaces
2465 wi.places_changed = True
2466
1900 # Enable 'OK' button in Preferences2467 # Enable 'OK' button in Preferences
1901 wi.prefswindow.builder.get_object('ok_button').set_sensitive(True)2468 wi.prefswindow.builder.get_object('ok_button').set_sensitive(True)
1902 self.hide()2469 self.hide()
@@ -1904,8 +2471,8 @@
1904class SingleInstance(object):2471class SingleInstance(object):
1905 """ Class to ensure, that single instance of the applet is run for each user """2472 """ Class to ensure, that single instance of the applet is run for each user """
19062473
1907 # Initialize, specifying a path to store pids
1908 def __init__(self, pidPath):2474 def __init__(self, pidPath):
2475 """ Initialize, specifying a path to store pids """
1909 self.pidPath=pidPath2476 self.pidPath=pidPath
1910 # See if pidFile exists2477 # See if pidFile exists
1911 if os.path.exists(pidPath):2478 if os.path.exists(pidPath):
@@ -1928,7 +2495,8 @@
1928 shutil.copy(temp_path, pidPath)2495 shutil.copy(temp_path, pidPath)
1929 os.unlink(temp_path)2496 os.unlink(temp_path)
1930 except Exception as e:2497 except Exception as e:
1931 log.error("SingleInstance: exception while renaming '%s' to '%s':\n %s" % (temp_path, pidPath, str(e)))2498 log.error("SingleInstance: exception while renaming '%s' "
2499 "to '%s':\n %s" % (temp_path, pidPath, str(e)))
19322500
1933 def is_already_running(self):2501 def is_already_running(self):
1934 return self.lasterror2502 return self.lasterror
@@ -1939,7 +2507,8 @@
1939 os.unlink(self.pidPath)2507 os.unlink(self.pidPath)
19402508
1941def main():2509def main():
1942 gtk.main()2510 Gtk.main()
2511
1943 return 02512 return 0
19442513
1945if __name__ == "__main__":2514if __name__ == "__main__":
@@ -1952,8 +2521,10 @@
1952 log = logging.getLogger('IndicatorWeather')2521 log = logging.getLogger('IndicatorWeather')
1953 log.propagate = False2522 log.propagate = False
1954 log.setLevel(logging.DEBUG)2523 log.setLevel(logging.DEBUG)
1955 log_handler = logging.handlers.RotatingFileHandler(log_filename, maxBytes=1024*1024, backupCount=5)2524 log_handler = logging.handlers.RotatingFileHandler(
1956 log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - %(levelname)s - %(message)s")2525 log_filename, maxBytes=1024*1024, backupCount=5)
2526 log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - "
2527 "%(levelname)s - %(message)s")
1957 log_handler.setFormatter(log_formatter)2528 log_handler.setFormatter(log_formatter)
1958 log.addHandler(log_handler)2529 log.addHandler(log_handler)
19592530
@@ -1974,8 +2545,7 @@
1974 TimeFormatter.monitor_indicator_datetime(log)2545 TimeFormatter.monitor_indicator_datetime(log)
19752546
1976 # not running, safe to continue...2547 # not running, safe to continue...
1977 gtk.gdk.threads_init()2548
1978 gtk.gdk.threads_enter()
1979 # Remember locale name2549 # Remember locale name
1980 global locale_name2550 global locale_name
1981 locale_name = locale.getlocale()[0]2551 locale_name = locale.getlocale()[0]
@@ -1984,6 +2554,20 @@
1984 else:2554 else:
1985 locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale2555 locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale
1986 locale_name = "en"2556 locale_name = "en"
2557
2558 # init GLib/GObject
2559 GObject.threads_init()
2560
2561 # init Gdk threads and get Gdk lock
2562 Gdk.threads_init()
2563 Gdk.threads_enter()
2564
2565 # init Gtk
2566 Gtk.init(None)
2567
2568 # create main thread and enter main loop
1987 wi = indicator_weather()2569 wi = indicator_weather()
1988 main()2570 main()
1989 gtk.gdk.threads_leave()2571
2572 # release Gdk lock
2573 Gdk.threads_leave()
1990\ No newline at end of file2574\ No newline at end of file
19912575
=== modified file 'data/indicator-weather.gschema.xml'
--- data/indicator-weather.gschema.xml 2011-05-31 09:49:23 +0000
+++ data/indicator-weather.gschema.xml 2013-05-22 05:08:27 +0000
@@ -6,6 +6,21 @@
6 <summary>Display icon/label in the indicator</summary>6 <summary>Display icon/label in the indicator</summary>
7 <description>0 - display icon only, 1 - display label only, 2 - display both icon and text</description>7 <description>0 - display icon only, 1 - display label only, 2 - display both icon and text</description>
8 </key>8 </key>
9 <key type="b" name="relative-display">
10 <default>false</default>
11 <summary>Display relative temperature</summary>
12 <description>Display relative temperature</description>
13 </key>
14 <key type="b" name="wind-display">
15 <default>true</default>
16 <summary>Display wind speed and direction</summary>
17 <description>Display wind speed and direction</description>
18 </key>
19 <key type="b" name="suntimes-display">
20 <default>true</default>
21 <summary>Display sunrise and sunset times</summary>
22 <description>Display sunrise and sunset times</description>
23 </key>
9 <key type="s" name="notifications">24 <key type="s" name="notifications">
10 <default>'N'</default>25 <default>'N'</default>
11 <summary>Notifications</summary>26 <summary>Notifications</summary>
@@ -36,6 +51,16 @@
36 <summary>wind unit</summary>51 <summary>wind unit</summary>
37 <description>wind unit</description>52 <description>wind unit</description>
38 </key>53 </key>
54 <key type="s" name="heat-estimate">
55 <default>'humidex'</default>
56 <summary>heat estimate formula</summary>
57 <description>heat estimate formula</description>
58 </key>
59 <key type="s" name="chill-estimate">
60 <default>'windchill'</default>
61 <summary>chill estimate formula</summary>
62 <description>chill estimate formula</description>
63 </key>
39 <key type="b" name="show-forecast">64 <key type="b" name="show-forecast">
40 <default>true</default>65 <default>true</default>
41 <summary>show forecast</summary>66 <summary>show forecast</summary>
4267
=== modified file 'data/ui/Assistant.ui'
--- data/ui/Assistant.ui 2011-04-03 05:00:19 +0000
+++ data/ui/Assistant.ui 2013-05-22 05:08:27 +0000
@@ -1,29 +1,28 @@
1<?xml version="1.0" encoding="UTF-8"?>1<?xml version="1.0" encoding="UTF-8"?>
2<interface>2<interface>
3 <requires lib="gtk+" version="2.16"/>3 <!-- interface-requires gtk+ 3.0 -->
4 <!-- interface-requires assistant 1.0 -->4 <!-- interface-requires assistant 1.0 -->
5 <!-- interface-naming-policy project-wide -->
6 <!-- interface-local-resource-path ../media -->5 <!-- interface-local-resource-path ../media -->
7 <object class="Assistant" id="assistant">6 <object class="Assistant" id="assistant">
7 <property name="can_focus">False</property>
8 <property name="border_width">12</property>8 <property name="border_width">12</property>
9 <property name="title" translatable="yes">Add a location</property>9 <property name="title" translatable="yes">Add a location</property>
10 <property name="resizable">False</property>
10 <property name="window_position">mouse</property>11 <property name="window_position">mouse</property>
11 <property name="default_width">400</property>12 <property name="default_width">400</property>
12 <property name="default_height">200</property>13 <property name="default_height">200</property>
13 <property name="resizable">False</property>14 <signal name="apply" handler="on_apply" swapped="no"/>
14 <signal name="apply" handler="on_apply"/>15 <signal name="cancel" handler="on_cancel" swapped="no"/>
15 <signal name="cancel" handler="on_cancel"/>
16 <!--<child>
17 <placeholder/>
18 </child> -->
19 <child>16 <child>
20 <object class="GtkVBox" id="placeinput">17 <object class="GtkVBox" id="placeinput">
21 <property name="visible">True</property>18 <property name="visible">True</property>
19 <property name="can_focus">False</property>
22 <property name="border_width">12</property>20 <property name="border_width">12</property>
23 <property name="spacing">6</property>21 <property name="spacing">6</property>
24 <child>22 <child>
25 <object class="GtkLabel" id="lblplaceinput">23 <object class="GtkLabel" id="lblplaceinput">
26 <property name="visible">True</property>24 <property name="visible">True</property>
25 <property name="can_focus">False</property>
27 <property name="xalign">0</property>26 <property name="xalign">0</property>
28 <property name="yalign">0</property>27 <property name="yalign">0</property>
29 <property name="ypad">6</property>28 <property name="ypad">6</property>
@@ -32,43 +31,56 @@
32 <property name="wrap_mode">word-char</property>31 <property name="wrap_mode">word-char</property>
33 </object>32 </object>
34 <packing>33 <packing>
34 <property name="expand">True</property>
35 <property name="fill">True</property>
35 <property name="position">0</property>36 <property name="position">0</property>
36 </packing>37 </packing>
37 </child>38 </child>
38 <child>39 <child>
39 <object class="GtkHBox" id="city_input_vbox">40 <object class="GtkHBox" id="city_input_vbox">
40 <property name="visible">True</property>41 <property name="visible">True</property>
41 <property name="border_width">12</property>42 <property name="can_focus">False</property>
42 <property name="spacing">6</property>43 <property name="border_width">12</property>
43 <child>44 <property name="spacing">6</property>
44 <object class="GtkComboBoxEntry" id="combolocations">45 <child>
45 <property name="visible">True</property>46 <object class="GtkComboBox" id="combolocations">
46 <signal name="changed" handler="on_select_city"/>47 <property name="visible">True</property>
47 <child internal-child="entry"> 48 <property name="can_focus">False</property>
48 <object class="GtkEntry" id="entrylocation"> 49 <property name="has_entry">True</property>
49 <property name="visible">True</property> 50 <signal name="changed" handler="on_select_city" swapped="no"/>
50 <property name="can_focus">True</property>51 <child internal-child="entry">
51 </object> 52 <object class="GtkEntry" id="entrylocation">
52 </child> 53 <property name="visible">True</property>
53 </object>54 <property name="can_focus">True</property>
54 <packing>55 </object>
55 <property name="position">0</property>56 </child>
56 </packing>57 </object>
57 </child>58 <packing>
58 <child>59 <property name="expand">True</property>
59 <object class="GtkButton" id="getcities">60 <property name="fill">True</property>
60 <property name="visible">True</property>61 <property name="position">0</property>
61 <property name="label" translatable="yes">Search</property>62 </packing>
62 <signal name="clicked" handler="on_get_city_names"/>63 </child>
63 </object>64 <child>
64 <packing>65 <object class="GtkButton" id="getcities">
65 <property name="position">1</property>66 <property name="label" translatable="yes">Search</property>
66 </packing>67 <property name="visible">True</property>
67 </child>68 <property name="can_focus">False</property>
68 </object>69 <property name="receives_default">False</property>
69 <packing>70 <signal name="clicked" handler="on_get_city_names" swapped="no"/>
71 </object>
72 <packing>
73 <property name="expand">True</property>
74 <property name="fill">True</property>
70 <property name="position">1</property>75 <property name="position">1</property>
71 </packing>76 </packing>
77 </child>
78 </object>
79 <packing>
80 <property name="expand">True</property>
81 <property name="fill">True</property>
82 <property name="position">1</property>
83 </packing>
72 </child>84 </child>
73 </object>85 </object>
74 <packing>86 <packing>
@@ -78,16 +90,19 @@
78 <child>90 <child>
79 <object class="GtkVBox" id="label">91 <object class="GtkVBox" id="label">
80 <property name="visible">True</property>92 <property name="visible">True</property>
93 <property name="can_focus">False</property>
81 <property name="border_width">12</property>94 <property name="border_width">12</property>
82 <property name="spacing">6</property>95 <property name="spacing">6</property>
83 <child>96 <child>
84 <object class="GtkLabel" id="lblwould">97 <object class="GtkLabel" id="lblwould">
85 <property name="visible">True</property>98 <property name="visible">True</property>
99 <property name="can_focus">False</property>
86 <property name="xalign">0</property>100 <property name="xalign">0</property>
87 <property name="label" translatable="yes">Please enter a name for this location:</property>101 <property name="label" translatable="yes">Please enter a name for this location:</property>
88 </object>102 </object>
89 <packing>103 <packing>
90 <property name="expand">False</property>104 <property name="expand">False</property>
105 <property name="fill">True</property>
91 <property name="position">0</property>106 <property name="position">0</property>
92 </packing>107 </packing>
93 </child>108 </child>
@@ -99,6 +114,7 @@
99 </object>114 </object>
100 <packing>115 <packing>
101 <property name="expand">False</property>116 <property name="expand">False</property>
117 <property name="fill">True</property>
102 <property name="position">1</property>118 <property name="position">1</property>
103 </packing>119 </packing>
104 </child>120 </child>
@@ -110,10 +126,12 @@
110 <child>126 <child>
111 <object class="GtkVBox" id="review">127 <object class="GtkVBox" id="review">
112 <property name="visible">True</property>128 <property name="visible">True</property>
129 <property name="can_focus">False</property>
113 <property name="border_width">12</property>130 <property name="border_width">12</property>
114 <child>131 <child>
115 <object class="GtkLabel" id="lblreview">132 <object class="GtkLabel" id="lblreview">
116 <property name="visible">True</property>133 <property name="visible">True</property>
134 <property name="can_focus">False</property>
117 <property name="xalign">0</property>135 <property name="xalign">0</property>
118 <property name="ypad">6</property>136 <property name="ypad">6</property>
119 <property name="label" translatable="yes">Please review the choices below. If anything is not correct, please go back and select the correct options.</property>137 <property name="label" translatable="yes">Please review the choices below. If anything is not correct, please go back and select the correct options.</property>
@@ -122,99 +140,120 @@
122 </object>140 </object>
123 <packing>141 <packing>
124 <property name="expand">False</property>142 <property name="expand">False</property>
143 <property name="fill">True</property>
125 <property name="position">0</property>144 <property name="position">0</property>
126 </packing>145 </packing>
127 </child>146 </child>
128 <child>147 <child>
129 <object class="GtkAlignment" id="alignment1">148 <object class="GtkAlignment" id="alignment1">
130 <property name="visible">True</property>149 <property name="visible">True</property>
150 <property name="can_focus">False</property>
131 <property name="left_padding">12</property>151 <property name="left_padding">12</property>
132 <child>152 <child>
133 <object class="GtkVBox" id="vbox1">153 <object class="GtkVBox" id="vbox1">
134 <property name="visible">True</property>154 <property name="visible">True</property>
155 <property name="can_focus">False</property>
135 <child>156 <child>
136 <object class="GtkHBox" id="revlabel">157 <object class="GtkHBox" id="revlabel">
137 <property name="visible">True</property>158 <property name="visible">True</property>
159 <property name="can_focus">False</property>
138 <property name="spacing">5</property>160 <property name="spacing">5</property>
139 <child>161 <child>
140 <object class="GtkArrow" id="arrow3">162 <object class="GtkArrow" id="arrow3">
141 <property name="visible">True</property>163 <property name="visible">True</property>
142 <property name="sensitive">False</property>164 <property name="sensitive">False</property>
165 <property name="can_focus">False</property>
143 </object>166 </object>
144 <packing>167 <packing>
145 <property name="expand">False</property>168 <property name="expand">False</property>
169 <property name="fill">True</property>
146 <property name="position">0</property>170 <property name="position">0</property>
147 </packing>171 </packing>
148 </child>172 </child>
149 <child>173 <child>
150 <object class="GtkLabel" id="lbl3">174 <object class="GtkLabel" id="lbl3">
151 <property name="visible">True</property>175 <property name="visible">True</property>
176 <property name="can_focus">False</property>
152 <property name="xalign">0</property>177 <property name="xalign">0</property>
153 <property name="label" translatable="yes">Label:</property>178 <property name="label" translatable="yes">Label:</property>
154 <property name="use_markup">True</property>179 <property name="use_markup">True</property>
155 </object>180 </object>
156 <packing>181 <packing>
157 <property name="expand">False</property>182 <property name="expand">False</property>
183 <property name="fill">True</property>
158 <property name="position">1</property>184 <property name="position">1</property>
159 </packing>185 </packing>
160 </child>186 </child>
161 <child>187 <child>
162 <object class="GtkLabel" id="labellbl">188 <object class="GtkLabel" id="labellbl">
163 <property name="visible">True</property>189 <property name="visible">True</property>
190 <property name="can_focus">False</property>
164 <property name="xalign">0</property>191 <property name="xalign">0</property>
165 <property name="label" translatable="yes">&lt;b&gt;Home&lt;/b&gt;</property>192 <property name="label" translatable="yes">&lt;b&gt;Home&lt;/b&gt;</property>
166 <property name="use_markup">True</property>193 <property name="use_markup">True</property>
167 </object>194 </object>
168 <packing>195 <packing>
196 <property name="expand">True</property>
197 <property name="fill">True</property>
169 <property name="position">2</property>198 <property name="position">2</property>
170 </packing>199 </packing>
171 </child>200 </child>
172 </object>201 </object>
173 <packing>202 <packing>
174 <property name="expand">False</property>203 <property name="expand">False</property>
204 <property name="fill">True</property>
175 <property name="position">0</property>205 <property name="position">0</property>
176 </packing>206 </packing>
177 </child>207 </child>
178 <child>208 <child>
179 <object class="GtkHBox" id="revplace">209 <object class="GtkHBox" id="revplace">
180 <property name="visible">True</property>210 <property name="visible">True</property>
211 <property name="can_focus">False</property>
181 <property name="spacing">5</property>212 <property name="spacing">5</property>
182 <child>213 <child>
183 <object class="GtkArrow" id="arrow2">214 <object class="GtkArrow" id="arrow2">
184 <property name="visible">True</property>215 <property name="visible">True</property>
185 <property name="sensitive">False</property>216 <property name="sensitive">False</property>
217 <property name="can_focus">False</property>
186 </object>218 </object>
187 <packing>219 <packing>
188 <property name="expand">False</property>220 <property name="expand">False</property>
221 <property name="fill">True</property>
189 <property name="position">0</property>222 <property name="position">0</property>
190 </packing>223 </packing>
191 </child>224 </child>
192 <child>225 <child>
193 <object class="GtkLabel" id="lbl2">226 <object class="GtkLabel" id="lbl2">
194 <property name="visible">True</property>227 <property name="visible">True</property>
228 <property name="can_focus">False</property>
195 <property name="xalign">0</property>229 <property name="xalign">0</property>
196 <property name="label" translatable="yes">Location:</property>230 <property name="label" translatable="yes">Location:</property>
197 <property name="use_markup">True</property>231 <property name="use_markup">True</property>
198 </object>232 </object>
199 <packing>233 <packing>
200 <property name="expand">False</property>234 <property name="expand">False</property>
235 <property name="fill">True</property>
201 <property name="position">1</property>236 <property name="position">1</property>
202 </packing>237 </packing>
203 </child>238 </child>
204 <child>239 <child>
205 <object class="GtkLabel" id="placelbl">240 <object class="GtkLabel" id="placelbl">
206 <property name="visible">True</property>241 <property name="visible">True</property>
242 <property name="can_focus">False</property>
207 <property name="xalign">0</property>243 <property name="xalign">0</property>
208 <property name="label" translatable="yes">&lt;b&gt;Orange, Texas&lt;/b&gt;</property>244 <property name="label" translatable="yes">&lt;b&gt;Orange, Texas&lt;/b&gt;</property>
209 <property name="use_markup">True</property>245 <property name="use_markup">True</property>
210 </object>246 </object>
211 <packing>247 <packing>
248 <property name="expand">True</property>
249 <property name="fill">True</property>
212 <property name="position">2</property>250 <property name="position">2</property>
213 </packing>251 </packing>
214 </child>252 </child>
215 </object>253 </object>
216 <packing>254 <packing>
217 <property name="expand">False</property>255 <property name="expand">False</property>
256 <property name="fill">True</property>
218 <property name="position">1</property>257 <property name="position">1</property>
219 </packing>258 </packing>
220 </child>259 </child>
@@ -223,6 +262,7 @@
223 </object>262 </object>
224 <packing>263 <packing>
225 <property name="expand">False</property>264 <property name="expand">False</property>
265 <property name="fill">True</property>
226 <property name="position">1</property>266 <property name="position">1</property>
227 </packing>267 </packing>
228 </child>268 </child>
@@ -235,5 +275,10 @@
235 <property name="title" translatable="yes">Review choices</property>275 <property name="title" translatable="yes">Review choices</property>
236 </packing>276 </packing>
237 </child>277 </child>
278 <child internal-child="action_area">
279 <object class="GtkBox" id="Assistant-action_area1">
280 <property name="can_focus">False</property>
281 </object>
282 </child>
238 </object>283 </object>
239</interface>284</interface>
240285
=== modified file 'data/ui/ExtendedForecast.ui'
--- data/ui/ExtendedForecast.ui 2011-11-27 07:38:08 +0000
+++ data/ui/ExtendedForecast.ui 2013-05-22 05:08:27 +0000
@@ -1,64 +1,76 @@
1<?xml version="1.0" encoding="UTF-8"?>1<?xml version="1.0" encoding="UTF-8"?>
2<interface>2<interface>
3 <requires lib="gtk+" version="2.16"/>3 <!-- interface-requires gtk+ 3.0 -->
4 <!-- interface-requires extended_forecast 1.0 -->4 <!-- interface-requires extended_forecast 1.0 -->
5 <!-- interface-naming-policy project-wide -->
6 <!-- interface-local-resource-path ../media -->5 <!-- interface-local-resource-path ../media -->
7 <object class="ExtendedForecast" id="extended_forecast">6 <object class="ExtendedForecast" id="extended_forecast">
7 <property name="can_focus">False</property>
8 <property name="border_width">12</property>8 <property name="border_width">12</property>
9 <property name="title" translatable="yes">Extended Forecast</property>9 <property name="title" translatable="yes">Extended Forecast</property>
10 <property name="resizable">False</property>10 <property name="resizable">False</property>
11 <property name="window_position">mouse</property>11 <property name="window_position">mouse</property>
12 <property name="icon">../media/icon.png</property>12 <property name="icon">../media/icon.png</property>
13 <signal name="destroy" handler="on_destroy"/>13 <signal name="destroy" handler="on_destroy" swapped="no"/>
14 <child>14 <child>
15 <object class="GtkVBox" id="mainvbox">15 <object class="GtkVBox" id="mainvbox">
16 <property name="visible">True</property>16 <property name="visible">True</property>
17 <property name="can_focus">False</property>
17 <child>18 <child>
18 <object class="GtkLabel" id="connection_error">19 <object class="GtkLabel" id="connection_error">
19 <property name="visible">False</property>20 <property name="can_focus">False</property>
20 <property name="ypad">10</property>21 <property name="ypad">10</property>
21 <property name="label">&lt;big&gt;Forecast information cannot be fetched. Connection cannot be established.&lt;/big&gt;</property>22 <property name="label">&lt;big&gt;Forecast information cannot be fetched. Connection cannot be established.&lt;/big&gt;</property>
22 <property name="use_markup">True</property>23 <property name="use_markup">True</property>
23 <property name="wrap">False</property>24 </object>
24 </object>25 <packing>
25 <packing>26 <property name="expand">True</property>
26 <property name="position">0</property>27 <property name="fill">True</property>
27 </packing>28 <property name="position">0</property>
29 </packing>
28 </child>30 </child>
29 <child>31 <child>
30 <object class="GtkHBox" id="hbox1">32 <object class="GtkHBox" id="hbox1">
31 <property name="visible">True</property>33 <property name="visible">True</property>
34 <property name="can_focus">False</property>
32 <property name="spacing">5</property>35 <property name="spacing">5</property>
33 <property name="homogeneous">True</property>36 <property name="homogeneous">True</property>
34 <child>37 <child>
35 <object class="GtkHBox" id="hbox2">38 <object class="GtkHBox" id="hbox2">
36 <property name="visible">True</property>39 <property name="visible">True</property>
40 <property name="can_focus">False</property>
37 <child>41 <child>
38 <object class="GtkAlignment" id="alignment3">42 <object class="GtkAlignment" id="alignment3">
39 <property name="visible">True</property>43 <property name="visible">True</property>
44 <property name="can_focus">False</property>
40 <property name="right_padding">6</property>45 <property name="right_padding">6</property>
41 <child>46 <child>
42 <object class="GtkVBox" id="day1vbox">47 <object class="GtkVBox" id="day1vbox">
43 <property name="visible">True</property>48 <property name="visible">True</property>
49 <property name="can_focus">False</property>
44 <child>50 <child>
45 <object class="GtkLabel" id="day1lbl">51 <object class="GtkLabel" id="day1lbl">
46 <property name="visible">True</property>52 <property name="visible">True</property>
53 <property name="can_focus">False</property>
47 <property name="ypad">10</property>54 <property name="ypad">10</property>
48 <property name="label">&lt;big&gt;Today (fri.)&lt;/big&gt;</property>55 <property name="label">&lt;big&gt;Today (fri.)&lt;/big&gt;</property>
49 <property name="use_markup">True</property>56 <property name="use_markup">True</property>
50 </object>57 </object>
51 <packing>58 <packing>
59 <property name="expand">True</property>
60 <property name="fill">True</property>
52 <property name="position">0</property>61 <property name="position">0</property>
53 </packing>62 </packing>
54 </child>63 </child>
55 <child>64 <child>
56 <object class="GtkImage" id="day1image">65 <object class="GtkImage" id="day1image">
57 <property name="visible">True</property>66 <property name="visible">True</property>
67 <property name="can_focus">False</property>
58 <property name="pixel_size">50</property>68 <property name="pixel_size">50</property>
59 <property name="icon_name">weather-showers</property>69 <property name="icon_name">weather-showers</property>
60 </object>70 </object>
61 <packing>71 <packing>
72 <property name="expand">True</property>
73 <property name="fill">True</property>
62 <property name="position">1</property>74 <property name="position">1</property>
63 </packing>75 </packing>
64 </child>76 </child>
@@ -66,6 +78,7 @@
66 <object class="GtkLabel" id="day1cond">78 <object class="GtkLabel" id="day1cond">
67 <property name="height_request">80</property>79 <property name="height_request">80</property>
68 <property name="visible">True</property>80 <property name="visible">True</property>
81 <property name="can_focus">False</property>
69 <property name="label">Partially cloudy</property>82 <property name="label">Partially cloudy</property>
70 <property name="justify">center</property>83 <property name="justify">center</property>
71 <property name="wrap">True</property>84 <property name="wrap">True</property>
@@ -73,33 +86,44 @@
73 <property name="width_chars">15</property>86 <property name="width_chars">15</property>
74 </object>87 </object>
75 <packing>88 <packing>
89 <property name="expand">True</property>
90 <property name="fill">True</property>
76 <property name="position">2</property>91 <property name="position">2</property>
77 </packing>92 </packing>
78 </child>93 </child>
79 <child>94 <child>
80 <object class="GtkVBox" id="vbox1">95 <object class="GtkVBox" id="vbox1">
81 <property name="visible">True</property>96 <property name="visible">True</property>
97 <property name="can_focus">False</property>
82 <property name="homogeneous">True</property>98 <property name="homogeneous">True</property>
83 <child>99 <child>
84 <object class="GtkLabel" id="day1temphigh">100 <object class="GtkLabel" id="day1temphigh">
85 <property name="visible">True</property>101 <property name="visible">True</property>
102 <property name="can_focus">False</property>
86 <property name="label">High : 23°C</property>103 <property name="label">High : 23°C</property>
87 </object>104 </object>
88 <packing>105 <packing>
106 <property name="expand">True</property>
107 <property name="fill">True</property>
89 <property name="position">0</property>108 <property name="position">0</property>
90 </packing>109 </packing>
91 </child>110 </child>
92 <child>111 <child>
93 <object class="GtkLabel" id="day1templow">112 <object class="GtkLabel" id="day1templow">
94 <property name="visible">True</property>113 <property name="visible">True</property>
114 <property name="can_focus">False</property>
95 <property name="label">Low : 19°C</property>115 <property name="label">Low : 19°C</property>
96 </object>116 </object>
97 <packing>117 <packing>
118 <property name="expand">True</property>
119 <property name="fill">True</property>
98 <property name="position">1</property>120 <property name="position">1</property>
99 </packing>121 </packing>
100 </child>122 </child>
101 </object>123 </object>
102 <packing>124 <packing>
125 <property name="expand">True</property>
126 <property name="fill">True</property>
103 <property name="position">3</property>127 <property name="position">3</property>
104 </packing>128 </packing>
105 </child>129 </child>
@@ -107,6 +131,8 @@
107 </child>131 </child>
108 </object>132 </object>
109 <packing>133 <packing>
134 <property name="expand">True</property>
135 <property name="fill">True</property>
110 <property name="position">0</property>136 <property name="position">0</property>
111 </packing>137 </packing>
112 </child>138 </child>
@@ -114,45 +140,58 @@
114 <object class="GtkVSeparator" id="vseparator1">140 <object class="GtkVSeparator" id="vseparator1">
115 <property name="width_request">2</property>141 <property name="width_request">2</property>
116 <property name="visible">True</property>142 <property name="visible">True</property>
143 <property name="can_focus">False</property>
117 </object>144 </object>
118 <packing>145 <packing>
119 <property name="expand">False</property>146 <property name="expand">False</property>
147 <property name="fill">True</property>
120 <property name="position">1</property>148 <property name="position">1</property>
121 </packing>149 </packing>
122 </child>150 </child>
123 </object>151 </object>
124 <packing>152 <packing>
153 <property name="expand">True</property>
154 <property name="fill">True</property>
125 <property name="position">0</property>155 <property name="position">0</property>
126 </packing>156 </packing>
127 </child>157 </child>
128 <child>158 <child>
129 <object class="GtkHBox" id="hbox3">159 <object class="GtkHBox" id="hbox3">
130 <property name="visible">True</property>160 <property name="visible">True</property>
161 <property name="can_focus">False</property>
131 <child>162 <child>
132 <object class="GtkAlignment" id="alignment2">163 <object class="GtkAlignment" id="alignment2">
133 <property name="visible">True</property>164 <property name="visible">True</property>
165 <property name="can_focus">False</property>
134 <property name="right_padding">6</property>166 <property name="right_padding">6</property>
135 <child>167 <child>
136 <object class="GtkVBox" id="day2vbox">168 <object class="GtkVBox" id="day2vbox">
137 <property name="visible">True</property>169 <property name="visible">True</property>
170 <property name="can_focus">False</property>
138 <child>171 <child>
139 <object class="GtkLabel" id="day2lbl">172 <object class="GtkLabel" id="day2lbl">
140 <property name="visible">True</property>173 <property name="visible">True</property>
174 <property name="can_focus">False</property>
141 <property name="ypad">10</property>175 <property name="ypad">10</property>
142 <property name="label">&lt;big&gt;Tomorrow (sat.)&lt;/big&gt;</property>176 <property name="label">&lt;big&gt;Tomorrow (sat.)&lt;/big&gt;</property>
143 <property name="use_markup">True</property>177 <property name="use_markup">True</property>
144 </object>178 </object>
145 <packing>179 <packing>
180 <property name="expand">True</property>
181 <property name="fill">True</property>
146 <property name="position">0</property>182 <property name="position">0</property>
147 </packing>183 </packing>
148 </child>184 </child>
149 <child>185 <child>
150 <object class="GtkImage" id="day2image">186 <object class="GtkImage" id="day2image">
151 <property name="visible">True</property>187 <property name="visible">True</property>
188 <property name="can_focus">False</property>
152 <property name="pixel_size">50</property>189 <property name="pixel_size">50</property>
153 <property name="icon_name">weather-storm</property>190 <property name="icon_name">weather-storm</property>
154 </object>191 </object>
155 <packing>192 <packing>
193 <property name="expand">True</property>
194 <property name="fill">True</property>
156 <property name="position">1</property>195 <property name="position">1</property>
157 </packing>196 </packing>
158 </child>197 </child>
@@ -160,6 +199,7 @@
160 <object class="GtkLabel" id="day2cond">199 <object class="GtkLabel" id="day2cond">
161 <property name="height_request">80</property>200 <property name="height_request">80</property>
162 <property name="visible">True</property>201 <property name="visible">True</property>
202 <property name="can_focus">False</property>
163 <property name="label">Couverture nuageuse partielle</property>203 <property name="label">Couverture nuageuse partielle</property>
164 <property name="justify">center</property>204 <property name="justify">center</property>
165 <property name="wrap">True</property>205 <property name="wrap">True</property>
@@ -167,33 +207,44 @@
167 <property name="width_chars">15</property>207 <property name="width_chars">15</property>
168 </object>208 </object>
169 <packing>209 <packing>
210 <property name="expand">True</property>
211 <property name="fill">True</property>
170 <property name="position">2</property>212 <property name="position">2</property>
171 </packing>213 </packing>
172 </child>214 </child>
173 <child>215 <child>
174 <object class="GtkVBox" id="vbox2">216 <object class="GtkVBox" id="vbox2">
175 <property name="visible">True</property>217 <property name="visible">True</property>
218 <property name="can_focus">False</property>
176 <property name="homogeneous">True</property>219 <property name="homogeneous">True</property>
177 <child>220 <child>
178 <object class="GtkLabel" id="day2temphigh">221 <object class="GtkLabel" id="day2temphigh">
179 <property name="visible">True</property>222 <property name="visible">True</property>
223 <property name="can_focus">False</property>
180 <property name="label">High : 26°C</property>224 <property name="label">High : 26°C</property>
181 </object>225 </object>
182 <packing>226 <packing>
227 <property name="expand">True</property>
228 <property name="fill">True</property>
183 <property name="position">0</property>229 <property name="position">0</property>
184 </packing>230 </packing>
185 </child>231 </child>
186 <child>232 <child>
187 <object class="GtkLabel" id="day2templow">233 <object class="GtkLabel" id="day2templow">
188 <property name="visible">True</property>234 <property name="visible">True</property>
235 <property name="can_focus">False</property>
189 <property name="label">Low : 16°C</property>236 <property name="label">Low : 16°C</property>
190 </object>237 </object>
191 <packing>238 <packing>
239 <property name="expand">True</property>
240 <property name="fill">True</property>
192 <property name="position">1</property>241 <property name="position">1</property>
193 </packing>242 </packing>
194 </child>243 </child>
195 </object>244 </object>
196 <packing>245 <packing>
246 <property name="expand">True</property>
247 <property name="fill">True</property>
197 <property name="position">3</property>248 <property name="position">3</property>
198 </packing>249 </packing>
199 </child>250 </child>
@@ -201,6 +252,8 @@
201 </child>252 </child>
202 </object>253 </object>
203 <packing>254 <packing>
255 <property name="expand">True</property>
256 <property name="fill">True</property>
204 <property name="position">0</property>257 <property name="position">0</property>
205 </packing>258 </packing>
206 </child>259 </child>
@@ -208,47 +261,58 @@
208 <object class="GtkVSeparator" id="vseparator2">261 <object class="GtkVSeparator" id="vseparator2">
209 <property name="width_request">2</property>262 <property name="width_request">2</property>
210 <property name="visible">True</property>263 <property name="visible">True</property>
264 <property name="can_focus">False</property>
211 </object>265 </object>
212 <packing>266 <packing>
213 <property name="expand">False</property>267 <property name="expand">False</property>
268 <property name="fill">True</property>
214 <property name="position">1</property>269 <property name="position">1</property>
215 </packing>270 </packing>
216 </child>271 </child>
217 </object>272 </object>
218 <packing>273 <packing>
274 <property name="expand">True</property>
275 <property name="fill">True</property>
219 <property name="position">1</property>276 <property name="position">1</property>
220 </packing>277 </packing>
221 </child>278 </child>
222 <child>279 <child>
223 <object class="GtkHBox" id="hbox4">280 <object class="GtkHBox" id="hbox4">
224 <property name="visible">True</property>281 <property name="visible">True</property>
282 <property name="can_focus">False</property>
225 <child>283 <child>
226 <object class="GtkAlignment" id="alignment4">284 <object class="GtkAlignment" id="alignment4">
227 <property name="visible">True</property>285 <property name="visible">True</property>
286 <property name="can_focus">False</property>
228 <property name="right_padding">6</property>287 <property name="right_padding">6</property>
229 <child>288 <child>
230 <object class="GtkVBox" id="day3vbox">289 <object class="GtkVBox" id="day3vbox">
231 <property name="visible">True</property>290 <property name="visible">True</property>
291 <property name="can_focus">False</property>
232 <child>292 <child>
233 <object class="GtkLabel" id="day3lbl">293 <object class="GtkLabel" id="day3lbl">
234 <property name="visible">True</property>294 <property name="visible">True</property>
295 <property name="can_focus">False</property>
235 <property name="ypad">10</property>296 <property name="ypad">10</property>
236 <property name="label">&lt;big&gt;Sunday&lt;/big&gt;</property>297 <property name="label">&lt;big&gt;Sunday&lt;/big&gt;</property>
237 <property name="use_markup">True</property>298 <property name="use_markup">True</property>
238 </object>299 </object>
239 <packing>300 <packing>
240 <property name="expand">False</property>301 <property name="expand">False</property>
302 <property name="fill">True</property>
241 <property name="position">0</property>303 <property name="position">0</property>
242 </packing>304 </packing>
243 </child>305 </child>
244 <child>306 <child>
245 <object class="GtkImage" id="day3image">307 <object class="GtkImage" id="day3image">
246 <property name="visible">True</property>308 <property name="visible">True</property>
309 <property name="can_focus">False</property>
247 <property name="pixel_size">50</property>310 <property name="pixel_size">50</property>
248 <property name="icon_name">weather-fog</property>311 <property name="icon_name">weather-fog</property>
249 </object>312 </object>
250 <packing>313 <packing>
251 <property name="expand">False</property>314 <property name="expand">False</property>
315 <property name="fill">True</property>
252 <property name="position">1</property>316 <property name="position">1</property>
253 </packing>317 </packing>
254 </child>318 </child>
@@ -256,6 +320,7 @@
256 <object class="GtkLabel" id="day3cond">320 <object class="GtkLabel" id="day3cond">
257 <property name="height_request">80</property>321 <property name="height_request">80</property>
258 <property name="visible">True</property>322 <property name="visible">True</property>
323 <property name="can_focus">False</property>
259 <property name="label">Fogs</property>324 <property name="label">Fogs</property>
260 <property name="justify">center</property>325 <property name="justify">center</property>
261 <property name="wrap">True</property>326 <property name="wrap">True</property>
@@ -263,32 +328,43 @@
263 <property name="width_chars">15</property>328 <property name="width_chars">15</property>
264 </object>329 </object>
265 <packing>330 <packing>
331 <property name="expand">True</property>
332 <property name="fill">True</property>
266 <property name="position">2</property>333 <property name="position">2</property>
267 </packing>334 </packing>
268 </child>335 </child>
269 <child>336 <child>
270 <object class="GtkVBox" id="vbox3">337 <object class="GtkVBox" id="vbox3">
271 <property name="visible">True</property>338 <property name="visible">True</property>
339 <property name="can_focus">False</property>
272 <child>340 <child>
273 <object class="GtkLabel" id="day3temphigh">341 <object class="GtkLabel" id="day3temphigh">
274 <property name="visible">True</property>342 <property name="visible">True</property>
343 <property name="can_focus">False</property>
275 <property name="label">High : 28°C</property>344 <property name="label">High : 28°C</property>
276 </object>345 </object>
277 <packing>346 <packing>
347 <property name="expand">True</property>
348 <property name="fill">True</property>
278 <property name="position">0</property>349 <property name="position">0</property>
279 </packing>350 </packing>
280 </child>351 </child>
281 <child>352 <child>
282 <object class="GtkLabel" id="day3templow">353 <object class="GtkLabel" id="day3templow">
283 <property name="visible">True</property>354 <property name="visible">True</property>
355 <property name="can_focus">False</property>
284 <property name="label">Low : 16°C</property>356 <property name="label">Low : 16°C</property>
285 </object>357 </object>
286 <packing>358 <packing>
359 <property name="expand">True</property>
360 <property name="fill">True</property>
287 <property name="position">1</property>361 <property name="position">1</property>
288 </packing>362 </packing>
289 </child>363 </child>
290 </object>364 </object>
291 <packing>365 <packing>
366 <property name="expand">True</property>
367 <property name="fill">True</property>
292 <property name="position">3</property>368 <property name="position">3</property>
293 </packing>369 </packing>
294 </child>370 </child>
@@ -296,50 +372,65 @@
296 </child>372 </child>
297 </object>373 </object>
298 <packing>374 <packing>
375 <property name="expand">True</property>
376 <property name="fill">True</property>
299 <property name="position">0</property>377 <property name="position">0</property>
300 </packing>378 </packing>
301 </child>379 </child>
302 <child>380 <child>
303 <object class="GtkVSeparator" id="vseparator3">381 <object class="GtkVSeparator" id="vseparator3">
304 <property name="visible">True</property>382 <property name="visible">True</property>
383 <property name="can_focus">False</property>
305 </object>384 </object>
306 <packing>385 <packing>
307 <property name="expand">False</property>386 <property name="expand">False</property>
387 <property name="fill">True</property>
308 <property name="position">1</property>388 <property name="position">1</property>
309 </packing>389 </packing>
310 </child>390 </child>
311 </object>391 </object>
312 <packing>392 <packing>
393 <property name="expand">True</property>
394 <property name="fill">True</property>
313 <property name="position">2</property>395 <property name="position">2</property>
314 </packing>396 </packing>
315 </child>397 </child>
316 <child>398 <child>
317 <object class="GtkHBox" id="hbox5">399 <object class="GtkHBox" id="hbox5">
318 <property name="visible">True</property>400 <property name="visible">True</property>
401 <property name="can_focus">False</property>
319 <child>402 <child>
320 <object class="GtkAlignment" id="alignment5">403 <object class="GtkAlignment" id="alignment5">
321 <property name="visible">True</property>404 <property name="visible">True</property>
405 <property name="can_focus">False</property>
322 <child>406 <child>
323 <object class="GtkVBox" id="day4vbox">407 <object class="GtkVBox" id="day4vbox">
324 <property name="visible">True</property>408 <property name="visible">True</property>
409 <property name="can_focus">False</property>
325 <child>410 <child>
326 <object class="GtkLabel" id="day4lbl">411 <object class="GtkLabel" id="day4lbl">
327 <property name="visible">True</property>412 <property name="visible">True</property>
413 <property name="can_focus">False</property>
328 <property name="ypad">10</property>414 <property name="ypad">10</property>
329 <property name="label">&lt;big&gt;Monday&lt;/big&gt;</property>415 <property name="label">&lt;big&gt;Monday&lt;/big&gt;</property>
330 <property name="use_markup">True</property>416 <property name="use_markup">True</property>
331 </object>417 </object>
332 <packing>418 <packing>
419 <property name="expand">True</property>
420 <property name="fill">True</property>
333 <property name="position">0</property>421 <property name="position">0</property>
334 </packing>422 </packing>
335 </child>423 </child>
336 <child>424 <child>
337 <object class="GtkImage" id="day4image">425 <object class="GtkImage" id="day4image">
338 <property name="visible">True</property>426 <property name="visible">True</property>
427 <property name="can_focus">False</property>
339 <property name="pixel_size">50</property>428 <property name="pixel_size">50</property>
340 <property name="icon_name">weather-clear</property>429 <property name="icon_name">weather-clear</property>
341 </object>430 </object>
342 <packing>431 <packing>
432 <property name="expand">True</property>
433 <property name="fill">True</property>
343 <property name="position">1</property>434 <property name="position">1</property>
344 </packing>435 </packing>
345 </child>436 </child>
@@ -347,6 +438,7 @@
347 <object class="GtkLabel" id="day4cond">438 <object class="GtkLabel" id="day4cond">
348 <property name="height_request">80</property>439 <property name="height_request">80</property>
349 <property name="visible">True</property>440 <property name="visible">True</property>
441 <property name="can_focus">False</property>
350 <property name="label">Clear</property>442 <property name="label">Clear</property>
351 <property name="justify">center</property>443 <property name="justify">center</property>
352 <property name="wrap">True</property>444 <property name="wrap">True</property>
@@ -354,32 +446,43 @@
354 <property name="width_chars">15</property>446 <property name="width_chars">15</property>
355 </object>447 </object>
356 <packing>448 <packing>
449 <property name="expand">True</property>
450 <property name="fill">True</property>
357 <property name="position">2</property>451 <property name="position">2</property>
358 </packing>452 </packing>
359 </child>453 </child>
360 <child>454 <child>
361 <object class="GtkVBox" id="vbox4">455 <object class="GtkVBox" id="vbox4">
362 <property name="visible">True</property>456 <property name="visible">True</property>
457 <property name="can_focus">False</property>
363 <child>458 <child>
364 <object class="GtkLabel" id="day4temphigh">459 <object class="GtkLabel" id="day4temphigh">
365 <property name="visible">True</property>460 <property name="visible">True</property>
461 <property name="can_focus">False</property>
366 <property name="label">High : 26°C</property>462 <property name="label">High : 26°C</property>
367 </object>463 </object>
368 <packing>464 <packing>
465 <property name="expand">True</property>
466 <property name="fill">True</property>
369 <property name="position">0</property>467 <property name="position">0</property>
370 </packing>468 </packing>
371 </child>469 </child>
372 <child>470 <child>
373 <object class="GtkLabel" id="day4templow">471 <object class="GtkLabel" id="day4templow">
374 <property name="visible">True</property>472 <property name="visible">True</property>
473 <property name="can_focus">False</property>
375 <property name="label">Low : 14°C</property>474 <property name="label">Low : 14°C</property>
376 </object>475 </object>
377 <packing>476 <packing>
477 <property name="expand">True</property>
478 <property name="fill">True</property>
378 <property name="position">1</property>479 <property name="position">1</property>
379 </packing>480 </packing>
380 </child>481 </child>
381 </object>482 </object>
382 <packing>483 <packing>
484 <property name="expand">True</property>
485 <property name="fill">True</property>
383 <property name="position">3</property>486 <property name="position">3</property>
384 </packing>487 </packing>
385 </child>488 </child>
@@ -387,16 +490,21 @@
387 </child>490 </child>
388 </object>491 </object>
389 <packing>492 <packing>
493 <property name="expand">True</property>
494 <property name="fill">True</property>
390 <property name="position">0</property>495 <property name="position">0</property>
391 </packing>496 </packing>
392 </child>497 </child>
393 </object>498 </object>
394 <packing>499 <packing>
500 <property name="expand">True</property>
501 <property name="fill">True</property>
395 <property name="position">3</property>502 <property name="position">3</property>
396 </packing>503 </packing>
397 </child>504 </child>
398 </object>505 </object>
399 <packing>506 <packing>
507 <property name="expand">True</property>
400 <property name="fill">False</property>508 <property name="fill">False</property>
401 <property name="position">1</property>509 <property name="position">1</property>
402 </packing>510 </packing>
@@ -404,26 +512,31 @@
404 <child>512 <child>
405 <object class="GtkHSeparator" id="hseparator1">513 <object class="GtkHSeparator" id="hseparator1">
406 <property name="visible">True</property>514 <property name="visible">True</property>
515 <property name="can_focus">False</property>
407 </object>516 </object>
408 <packing>517 <packing>
409 <property name="expand">False</property>518 <property name="expand">False</property>
519 <property name="fill">True</property>
410 <property name="position">2</property>520 <property name="position">2</property>
411 </packing>521 </packing>
412 </child>522 </child>
413 <child>523 <child>
414 <object class="GtkAlignment" id="alignment1">524 <object class="GtkAlignment" id="alignment1">
415 <property name="visible">True</property>525 <property name="visible">True</property>
526 <property name="can_focus">False</property>
416 <property name="xalign">1</property>527 <property name="xalign">1</property>
417 <child>528 <child>
418 <object class="GtkVButtonBox" id="hbuttonbox1">529 <object class="GtkVButtonBox" id="hbuttonbox1">
419 <property name="visible">True</property>530 <property name="visible">True</property>
531 <property name="can_focus">False</property>
420 <child>532 <child>
421 <object class="GtkButton" id="closebtn">533 <object class="GtkButton" id="closebtn">
422 <property name="label">gtk-close</property>534 <property name="label">gtk-close</property>
423 <property name="visible">True</property>535 <property name="visible">True</property>
536 <property name="can_focus">False</property>
424 <property name="receives_default">True</property>537 <property name="receives_default">True</property>
425 <property name="use_stock">True</property>538 <property name="use_stock">True</property>
426 <signal name="clicked" handler="close"/>539 <signal name="clicked" handler="close" swapped="no"/>
427 </object>540 </object>
428 <packing>541 <packing>
429 <property name="expand">False</property>542 <property name="expand">False</property>
@@ -435,6 +548,8 @@
435 </child>548 </child>
436 </object>549 </object>
437 <packing>550 <packing>
551 <property name="expand">True</property>
552 <property name="fill">True</property>
438 <property name="position">3</property>553 <property name="position">3</property>
439 </packing>554 </packing>
440 </child>555 </child>
441556
=== modified file 'data/ui/PreferencesDialog.ui'
--- data/ui/PreferencesDialog.ui 2012-03-12 04:53:24 +0000
+++ data/ui/PreferencesDialog.ui 2013-05-22 05:08:27 +0000
@@ -1,19 +1,25 @@
1<?xml version="1.0" encoding="UTF-8"?>1<?xml version="1.0" encoding="UTF-8"?>
2<interface>2<interface>
3 <requires lib="gtk+" version="2.16"/>3 <!-- interface-requires gtk+ 3.0 -->
4 <!-- interface-requires preferences_dialog 1.0 -->4 <!-- interface-requires preferences_dialog 1.0 -->
5 <!-- interface-naming-policy project-wide -->
6 <object class="GtkListStore" id="citieslist">5 <object class="GtkListStore" id="citieslist">
7 <columns>6 <columns>
8 <!-- column-name Label -->7 <!-- column-name Label -->
9 <column type="gchararray"/>8 <column type="gchararray"/>
10 <!-- column-name City -->9 <!-- column-name City -->
11 <column type="gchararray"/>10 <column type="gchararray"/>
12 <!-- column-name Location Details -->11 <!-- column-name Location -->
13 <column type="gchararray"/>12 <column type="gchararray"/>
14 </columns>13 </columns>
15 </object>14 </object>
15 <object class="GtkAdjustment" id="rate">
16 <property name="lower">1</property>
17 <property name="upper">30</property>
18 <property name="step_increment">1</property>
19 <property name="page_increment">10</property>
20 </object>
16 <object class="PreferencesDialog" id="preferences_dialog">21 <object class="PreferencesDialog" id="preferences_dialog">
22 <property name="can_focus">False</property>
17 <property name="border_width">12</property>23 <property name="border_width">12</property>
18 <property name="title" translatable="yes">Weather Indicator Preferences</property>24 <property name="title" translatable="yes">Weather Indicator Preferences</property>
19 <property name="window_position">mouse</property>25 <property name="window_position">mouse</property>
@@ -22,62 +28,118 @@
22 <property name="icon">../media/icon.png</property>28 <property name="icon">../media/icon.png</property>
23 <property name="type_hint">normal</property>29 <property name="type_hint">normal</property>
24 <child internal-child="vbox">30 <child internal-child="vbox">
25 <object class="GtkVBox" id="dialog-vbox1">31 <object class="GtkBox" id="dialog-vbox1">
26 <property name="visible">True</property>32 <property name="visible">True</property>
33 <property name="can_focus">False</property>
34 <property name="orientation">vertical</property>
27 <property name="spacing">6</property>35 <property name="spacing">6</property>
36 <child internal-child="action_area">
37 <object class="GtkButtonBox" id="dialog-action_area1">
38 <property name="visible">True</property>
39 <property name="can_focus">False</property>
40 <property name="layout_style">end</property>
41 <child>
42 <object class="GtkButton" id="cancel_button">
43 <property name="label">gtk-cancel</property>
44 <property name="visible">True</property>
45 <property name="can_focus">False</property>
46 <property name="receives_default">True</property>
47 <property name="use_stock">True</property>
48 <signal name="clicked" handler="cancel" swapped="no"/>
49 </object>
50 <packing>
51 <property name="expand">False</property>
52 <property name="fill">False</property>
53 <property name="position">0</property>
54 </packing>
55 </child>
56 <child>
57 <object class="GtkButton" id="ok_button">
58 <property name="label">gtk-ok</property>
59 <property name="visible">True</property>
60 <property name="sensitive">False</property>
61 <property name="can_focus">False</property>
62 <property name="receives_default">True</property>
63 <property name="use_stock">True</property>
64 <signal name="clicked" handler="ok" swapped="no"/>
65 </object>
66 <packing>
67 <property name="expand">False</property>
68 <property name="fill">False</property>
69 <property name="position">1</property>
70 </packing>
71 </child>
72 </object>
73 <packing>
74 <property name="expand">False</property>
75 <property name="fill">False</property>
76 <property name="pack_type">end</property>
77 <property name="position">0</property>
78 </packing>
79 </child>
28 <child>80 <child>
29 <object class="GtkNotebook" id="notebook1">81 <object class="GtkNotebook" id="notebook1">
30 <property name="visible">True</property>82 <property name="visible">True</property>
31 <property name="can_focus">True</property>83 <property name="can_focus">True</property>
32 <child>84 <child>
33 <object class="GtkVBox" id="vboxpreferences">85 <object class="GtkBox" id="vboxpreferences">
34 <property name="visible">True</property>86 <property name="visible">True</property>
87 <property name="can_focus">False</property>
35 <property name="border_width">12</property>88 <property name="border_width">12</property>
89 <property name="orientation">vertical</property>
36 <property name="spacing">12</property>90 <property name="spacing">12</property>
37 <child>91 <child>
38 <object class="GtkVBox" id="vbox10">92 <object class="GtkBox" id="vbox10">
39 <property name="visible">True</property>93 <property name="visible">True</property>
94 <property name="can_focus">False</property>
95 <property name="orientation">vertical</property>
40 <property name="homogeneous">True</property>96 <property name="homogeneous">True</property>
41 <child>97 <child>
42 <object class="GtkCheckButton" id="enableindicator">98 <object class="GtkCheckButton" id="enableindicator">
43 <property name="label" translatable="yes">Enable the Weather Indicator Applet</property>99 <property name="label" translatable="yes">Enable the Weather Indicator Applet</property>
44 <property name="can_focus">True</property>100 <property name="can_focus">True</property>
45 <property name="receives_default">False</property>101 <property name="receives_default">False</property>
46 <property name="xalign">0.02</property>102 <property name="xalign">0.019999999552965164</property>
47 <property name="active">True</property>103 <property name="active">True</property>
48 <property name="draw_indicator">True</property>104 <property name="draw_indicator">True</property>
49 </object>105 </object>
50 <packing>106 <packing>
51 <property name="expand">False</property>107 <property name="expand">False</property>
108 <property name="fill">True</property>
52 <property name="position">0</property>109 <property name="position">0</property>
53 </packing>110 </packing>
54 </child>111 </child>
55 <child>112 <child>
56 <object class="GtkCheckButton" id="show_label">113 <object class="GtkCheckButton" id="show_label">
57 <property name="visible">True</property>
58 <property name="label" translatable="yes">Show temperature near indicator</property>114 <property name="label" translatable="yes">Show temperature near indicator</property>
115 <property name="visible">True</property>
59 <property name="can_focus">True</property>116 <property name="can_focus">True</property>
60 <property name="receives_default">False</property>117 <property name="receives_default">False</property>
118 <property name="xalign">0</property>
61 <property name="draw_indicator">True</property>119 <property name="draw_indicator">True</property>
62 </object>120 </object>
63 <packing>121 <packing>
64 <property name="expand">False</property>122 <property name="expand">False</property>
123 <property name="fill">True</property>
65 <property name="position">1</property>124 <property name="position">1</property>
66 </packing>125 </packing>
67 </child>126 </child>
68 <child>127 <child>
69 <object class="GtkHBox" id="hbox_updateevery">128 <object class="GtkBox" id="hbox_updateevery">
70 <property name="visible">True</property>129 <property name="visible">True</property>
130 <property name="can_focus">False</property>
71 <property name="spacing">6</property>131 <property name="spacing">6</property>
72 <child>132 <child>
73 <object class="GtkLabel" id="updateevery">133 <object class="GtkLabel" id="updateevery">
74 <property name="visible">True</property>134 <property name="visible">True</property>
135 <property name="can_focus">False</property>
75 <property name="xalign">1</property>136 <property name="xalign">1</property>
76 <property name="label" translatable="yes">Update every</property>137 <property name="label" translatable="yes">Update every</property>
77 <property name="use_markup">True</property>138 <property name="use_markup">True</property>
78 </object>139 </object>
79 <packing>140 <packing>
80 <property name="expand">False</property>141 <property name="expand">False</property>
142 <property name="fill">True</property>
81 <property name="position">0</property>143 <property name="position">0</property>
82 </packing>144 </packing>
83 </child>145 </child>
@@ -86,6 +148,7 @@
86 <property name="visible">True</property>148 <property name="visible">True</property>
87 <property name="can_focus">True</property>149 <property name="can_focus">True</property>
88 <property name="invisible_char">•</property>150 <property name="invisible_char">•</property>
151 <property name="invisible_char_set">True</property>
89 <property name="adjustment">rate</property>152 <property name="adjustment">rate</property>
90 </object>153 </object>
91 <packing>154 <packing>
@@ -97,31 +160,39 @@
97 <child>160 <child>
98 <object class="GtkLabel" id="label4">161 <object class="GtkLabel" id="label4">
99 <property name="visible">True</property>162 <property name="visible">True</property>
163 <property name="can_focus">False</property>
100 <property name="xalign">0</property>164 <property name="xalign">0</property>
101 <property name="label" translatable="yes">minutes</property>165 <property name="label" translatable="yes">minutes</property>
102 </object>166 </object>
103 <packing>167 <packing>
104 <property name="expand">False</property>168 <property name="expand">False</property>
169 <property name="fill">True</property>
105 <property name="position">2</property>170 <property name="position">2</property>
106 </packing>171 </packing>
107 </child>172 </child>
108 </object>173 </object>
109 <packing>174 <packing>
175 <property name="expand">True</property>
176 <property name="fill">True</property>
110 <property name="position">2</property>177 <property name="position">2</property>
111 </packing>178 </packing>
112 </child>179 </child>
113 </object>180 </object>
114 <packing>181 <packing>
115 <property name="expand">False</property>182 <property name="expand">False</property>
183 <property name="fill">True</property>
116 <property name="position">0</property>184 <property name="position">0</property>
117 </packing>185 </packing>
118 </child>186 </child>
119 <child>187 <child>
120 <object class="GtkVBox" id="vbox1">188 <object class="GtkBox" id="vbox1">
121 <property name="visible">True</property>189 <property name="visible">True</property>
190 <property name="can_focus">False</property>
191 <property name="orientation">vertical</property>
122 <child>192 <child>
123 <object class="GtkLabel" id="notificationslabel">193 <object class="GtkLabel" id="notificationslabel">
124 <property name="visible">True</property>194 <property name="visible">True</property>
195 <property name="can_focus">False</property>
125 <property name="xalign">0</property>196 <property name="xalign">0</property>
126 <property name="ypad">3</property>197 <property name="ypad">3</property>
127 <property name="label" translatable="yes">&lt;b&gt;Notifications&lt;/b&gt;</property>198 <property name="label" translatable="yes">&lt;b&gt;Notifications&lt;/b&gt;</property>
@@ -129,16 +200,20 @@
129 </object>200 </object>
130 <packing>201 <packing>
131 <property name="expand">False</property>202 <property name="expand">False</property>
203 <property name="fill">True</property>
132 <property name="position">0</property>204 <property name="position">0</property>
133 </packing>205 </packing>
134 </child>206 </child>
135 <child>207 <child>
136 <object class="GtkAlignment" id="alignment1">208 <object class="GtkAlignment" id="alignment1">
137 <property name="visible">True</property>209 <property name="visible">True</property>
210 <property name="can_focus">False</property>
138 <property name="left_padding">12</property>211 <property name="left_padding">12</property>
139 <child>212 <child>
140 <object class="GtkVBox" id="vbox4">213 <object class="GtkBox" id="vbox4">
141 <property name="visible">True</property>214 <property name="visible">True</property>
215 <property name="can_focus">False</property>
216 <property name="orientation">vertical</property>
142 <property name="homogeneous">True</property>217 <property name="homogeneous">True</property>
143 <child>218 <child>
144 <object class="GtkRadioButton" id="nonotif">219 <object class="GtkRadioButton" id="nonotif">
@@ -146,6 +221,7 @@
146 <property name="visible">True</property>221 <property name="visible">True</property>
147 <property name="can_focus">True</property>222 <property name="can_focus">True</property>
148 <property name="receives_default">False</property>223 <property name="receives_default">False</property>
224 <property name="xalign">0.5</property>
149 <property name="active">True</property>225 <property name="active">True</property>
150 <property name="draw_indicator">True</property>226 <property name="draw_indicator">True</property>
151 </object>227 </object>
@@ -161,6 +237,7 @@
161 <property name="visible">True</property>237 <property name="visible">True</property>
162 <property name="can_focus">True</property>238 <property name="can_focus">True</property>
163 <property name="receives_default">False</property>239 <property name="receives_default">False</property>
240 <property name="xalign">0.5</property>
164 <property name="draw_indicator">True</property>241 <property name="draw_indicator">True</property>
165 <property name="group">nonotif</property>242 <property name="group">nonotif</property>
166 </object>243 </object>
@@ -176,6 +253,7 @@
176 <property name="visible">True</property>253 <property name="visible">True</property>
177 <property name="can_focus">True</property>254 <property name="can_focus">True</property>
178 <property name="receives_default">False</property>255 <property name="receives_default">False</property>
256 <property name="xalign">0.5</property>
179 <property name="draw_indicator">True</property>257 <property name="draw_indicator">True</property>
180 <property name="group">nonotif</property>258 <property name="group">nonotif</property>
181 </object>259 </object>
@@ -189,12 +267,15 @@
189 </child>267 </child>
190 </object>268 </object>
191 <packing>269 <packing>
270 <property name="expand">True</property>
271 <property name="fill">True</property>
192 <property name="position">1</property>272 <property name="position">1</property>
193 </packing>273 </packing>
194 </child>274 </child>
195 <child>275 <child>
196 <object class="GtkLabel" id="weathersourcelabel">276 <object class="GtkLabel" id="weathersourcelabel">
197 <property name="visible">True</property>277 <property name="visible">True</property>
278 <property name="can_focus">False</property>
198 <property name="xalign">0</property>279 <property name="xalign">0</property>
199 <property name="ypad">3</property>280 <property name="ypad">3</property>
200 <property name="label" translatable="yes">&lt;b&gt;Weather Data Source&lt;/b&gt;</property>281 <property name="label" translatable="yes">&lt;b&gt;Weather Data Source&lt;/b&gt;</property>
@@ -202,40 +283,46 @@
202 </object>283 </object>
203 <packing>284 <packing>
204 <property name="expand">False</property>285 <property name="expand">False</property>
286 <property name="fill">True</property>
205 <property name="position">2</property>287 <property name="position">2</property>
206 </packing>288 </packing>
207 </child>289 </child>
208 <child>290 <child>
209 <object class="GtkAlignment" id="alignment2">291 <object class="GtkAlignment" id="alignment2">
210 <property name="visible">True</property>292 <property name="visible">True</property>
293 <property name="can_focus">False</property>
211 <property name="left_padding">12</property>294 <property name="left_padding">12</property>
212 <child>295 <child>
213 <object class="GtkVBox" id="vbox5">296 <object class="GtkBox" id="vbox5">
214 <property name="visible">True</property>297 <property name="visible">True</property>
298 <property name="can_focus">False</property>
299 <property name="orientation">vertical</property>
215 <property name="homogeneous">True</property>300 <property name="homogeneous">True</property>
216 <child>301 <child>
217 <object class="GtkRadioButton" id="google">
218 <property name="label" translatable="yes">Google</property>
219 <property name="visible">True</property>
220 <property name="can_focus">True</property>
221 <property name="receives_default">False</property>
222 <property name="active">True</property>
223 <property name="draw_indicator">True</property>
224 </object>
225 <packing>
226 <property name="expand">False</property>
227 <property name="fill">False</property>
228 <property name="position">0</property>
229 </packing>
230 </child>
231 <child>
232 <object class="GtkRadioButton" id="yahoo">302 <object class="GtkRadioButton" id="yahoo">
233 <property name="label" translatable="yes">Yahoo!</property>303 <property name="label" translatable="yes">Yahoo!</property>
234 <property name="visible">True</property>304 <property name="visible">True</property>
235 <property name="can_focus">True</property>305 <property name="can_focus">True</property>
236 <property name="receives_default">False</property>306 <property name="receives_default">False</property>
237 <property name="draw_indicator">True</property>307 <property name="xalign">0.5</property>
238 <property name="group">google</property>308 <property name="draw_indicator">True</property>
309 </object>
310 <packing>
311 <property name="expand">False</property>
312 <property name="fill">False</property>
313 <property name="position">0</property>
314 </packing>
315 </child>
316 <child>
317 <object class="GtkRadioButton" id="weather-com">
318 <property name="label" translatable="yes">Weather.com</property>
319 <property name="visible">True</property>
320 <property name="can_focus">True</property>
321 <property name="receives_default">False</property>
322 <property name="xalign">0.5</property>
323 <property name="active">True</property>
324 <property name="draw_indicator">True</property>
325 <property name="group">yahoo</property>
239 </object>326 </object>
240 <packing>327 <packing>
241 <property name="expand">False</property>328 <property name="expand">False</property>
@@ -247,12 +334,15 @@
247 </child>334 </child>
248 </object>335 </object>
249 <packing>336 <packing>
337 <property name="expand">True</property>
338 <property name="fill">True</property>
250 <property name="position">3</property>339 <property name="position">3</property>
251 </packing>340 </packing>
252 </child>341 </child>
253 </object>342 </object>
254 <packing>343 <packing>
255 <property name="expand">False</property>344 <property name="expand">False</property>
345 <property name="fill">True</property>
256 <property name="position">1</property>346 <property name="position">1</property>
257 </packing>347 </packing>
258 </child>348 </child>
@@ -261,6 +351,7 @@
261 <child type="tab">351 <child type="tab">
262 <object class="GtkLabel" id="label1">352 <object class="GtkLabel" id="label1">
263 <property name="visible">True</property>353 <property name="visible">True</property>
354 <property name="can_focus">False</property>
264 <property name="label" translatable="yes">General</property>355 <property name="label" translatable="yes">General</property>
265 </object>356 </object>
266 <packing>357 <packing>
@@ -268,16 +359,296 @@
268 </packing>359 </packing>
269 </child>360 </child>
270 <child>361 <child>
271 <object class="GtkVBox" id="vbox9">362 <object class="GtkBox" id="boxdisplay">
272 <property name="visible">True</property>363 <property name="visible">True</property>
273 <property name="border_width">12</property>364 <property name="can_focus">False</property>
274 <property name="spacing">12</property>365 <property name="border_width">12</property>
275 <child>366 <property name="orientation">vertical</property>
276 <object class="GtkVBox" id="vbox2">367 <property name="spacing">12</property>
277 <property name="visible">True</property>368 <child>
369 <object class="GtkBox" id="box1">
370 <property name="visible">True</property>
371 <property name="can_focus">False</property>
372 <property name="orientation">vertical</property>
373 <child>
374 <object class="GtkLabel" id="conditionslabel">
375 <property name="visible">True</property>
376 <property name="can_focus">False</property>
377 <property name="xalign">0</property>
378 <property name="ypad">3</property>
379 <property name="label" translatable="yes">&lt;b&gt;Weather Conditions&lt;/b&gt;</property>
380 <property name="use_markup">True</property>
381 </object>
382 <packing>
383 <property name="expand">False</property>
384 <property name="fill">True</property>
385 <property name="position">0</property>
386 </packing>
387 </child>
388 <child>
389 <object class="GtkAlignment" id="alignment5">
390 <property name="visible">True</property>
391 <property name="can_focus">False</property>
392 <property name="left_padding">12</property>
393 <child>
394 <object class="GtkBox" id="box3">
395 <property name="visible">True</property>
396 <property name="can_focus">False</property>
397 <property name="orientation">vertical</property>
398 <property name="homogeneous">True</property>
399 <child>
400 <object class="GtkCheckButton" id="show_relative">
401 <property name="label" translatable="yes">Relative temperature ("Feels like")</property>
402 <property name="visible">True</property>
403 <property name="can_focus">True</property>
404 <property name="receives_default">False</property>
405 <property name="xalign">0</property>
406 <property name="draw_indicator">True</property>
407 </object>
408 <packing>
409 <property name="expand">False</property>
410 <property name="fill">False</property>
411 <property name="position">0</property>
412 </packing>
413 </child>
414 <child>
415 <object class="GtkCheckButton" id="show_wind">
416 <property name="label" translatable="yes">Wind speed and direction</property>
417 <property name="visible">True</property>
418 <property name="can_focus">True</property>
419 <property name="receives_default">False</property>
420 <property name="xalign">0</property>
421 <property name="active">True</property>
422 <property name="draw_indicator">True</property>
423 </object>
424 <packing>
425 <property name="expand">False</property>
426 <property name="fill">False</property>
427 <property name="position">1</property>
428 </packing>
429 </child>
430 <child>
431 <object class="GtkCheckButton" id="show_suntimes">
432 <property name="label" translatable="yes">Sunrise and sunset times</property>
433 <property name="visible">True</property>
434 <property name="can_focus">True</property>
435 <property name="receives_default">False</property>
436 <property name="xalign">0</property>
437 <property name="active">True</property>
438 <property name="draw_indicator">True</property>
439 </object>
440 <packing>
441 <property name="expand">False</property>
442 <property name="fill">False</property>
443 <property name="position">2</property>
444 </packing>
445 </child>
446 </object>
447 </child>
448 </object>
449 <packing>
450 <property name="expand">True</property>
451 <property name="fill">True</property>
452 <property name="position">1</property>
453 </packing>
454 </child>
455 </object>
456 <packing>
457 <property name="expand">False</property>
458 <property name="fill">True</property>
459 <property name="position">0</property>
460 </packing>
461 </child>
462 <child>
463 <object class="GtkBox" id="box6">
464 <property name="visible">True</property>
465 <property name="can_focus">False</property>
466 <property name="orientation">vertical</property>
467 <child>
468 <object class="GtkLabel" id="heatlabel">
469 <property name="visible">True</property>
470 <property name="can_focus">False</property>
471 <property name="xalign">0</property>
472 <property name="ypad">3</property>
473 <property name="label" translatable="yes">&lt;b&gt;Relative Heat Formula&lt;/b&gt;</property>
474 <property name="use_markup">True</property>
475 </object>
476 <packing>
477 <property name="expand">False</property>
478 <property name="fill">True</property>
479 <property name="position">0</property>
480 </packing>
481 </child>
482 <child>
483 <object class="GtkAlignment" id="alignment6">
484 <property name="visible">True</property>
485 <property name="can_focus">False</property>
486 <property name="left_padding">12</property>
487 <child>
488 <object class="GtkBox" id="box4">
489 <property name="visible">True</property>
490 <property name="can_focus">False</property>
491 <property name="orientation">vertical</property>
492 <property name="homogeneous">True</property>
493 <child>
494 <object class="GtkRadioButton" id="heatindex">
495 <property name="label" translatable="yes">Heat Index (US)</property>
496 <property name="visible">True</property>
497 <property name="can_focus">True</property>
498 <property name="receives_default">False</property>
499 <property name="xalign">0</property>
500 <property name="active">True</property>
501 <property name="draw_indicator">True</property>
502 </object>
503 <packing>
504 <property name="expand">False</property>
505 <property name="fill">False</property>
506 <property name="position">0</property>
507 </packing>
508 </child>
509 <child>
510 <object class="GtkRadioButton" id="humidex">
511 <property name="label" translatable="yes">Humidex (Canada)</property>
512 <property name="visible">True</property>
513 <property name="can_focus">True</property>
514 <property name="receives_default">False</property>
515 <property name="xalign">0</property>
516 <property name="draw_indicator">True</property>
517 <property name="group">heatindex</property>
518 </object>
519 <packing>
520 <property name="expand">False</property>
521 <property name="fill">False</property>
522 <property name="position">1</property>
523 </packing>
524 </child>
525 </object>
526 </child>
527 </object>
528 <packing>
529 <property name="expand">False</property>
530 <property name="fill">True</property>
531 <property name="position">1</property>
532 </packing>
533 </child>
534 </object>
535 <packing>
536 <property name="expand">False</property>
537 <property name="fill">True</property>
538 <property name="position">1</property>
539 </packing>
540 </child>
541 <child>
542 <object class="GtkBox" id="box2">
543 <property name="visible">True</property>
544 <property name="can_focus">False</property>
545 <property name="orientation">vertical</property>
546 <child>
547 <object class="GtkLabel" id="chilllabel">
548 <property name="visible">True</property>
549 <property name="can_focus">False</property>
550 <property name="xalign">0</property>
551 <property name="ypad">3</property>
552 <property name="label" translatable="yes">&lt;b&gt;Wind Chill Formula&lt;/b&gt;</property>
553 <property name="use_markup">True</property>
554 </object>
555 <packing>
556 <property name="expand">False</property>
557 <property name="fill">True</property>
558 <property name="position">0</property>
559 </packing>
560 </child>
561 <child>
562 <object class="GtkAlignment" id="alignment7">
563 <property name="visible">True</property>
564 <property name="can_focus">False</property>
565 <property name="left_padding">12</property>
566 <child>
567 <object class="GtkBox" id="box7">
568 <property name="visible">True</property>
569 <property name="can_focus">False</property>
570 <property name="orientation">vertical</property>
571 <property name="homogeneous">True</property>
572 <child>
573 <object class="GtkRadioButton" id="wctindex">
574 <property name="label" translatable="yes">JAG/TI Wind Chill Index (US/UK/Canada)</property>
575 <property name="visible">True</property>
576 <property name="can_focus">True</property>
577 <property name="receives_default">False</property>
578 <property name="xalign">0</property>
579 <property name="active">True</property>
580 <property name="draw_indicator">True</property>
581 </object>
582 <packing>
583 <property name="expand">False</property>
584 <property name="fill">False</property>
585 <property name="position">0</property>
586 </packing>
587 </child>
588 <child>
589 <object class="GtkRadioButton" id="aatindex">
590 <property name="label" translatable="yes">Apparent Temperature (Australia)</property>
591 <property name="visible">True</property>
592 <property name="can_focus">True</property>
593 <property name="receives_default">False</property>
594 <property name="xalign">0</property>
595 <property name="draw_indicator">True</property>
596 <property name="group">wctindex</property>
597 </object>
598 <packing>
599 <property name="expand">False</property>
600 <property name="fill">False</property>
601 <property name="position">1</property>
602 </packing>
603 </child>
604 </object>
605 </child>
606 </object>
607 <packing>
608 <property name="expand">False</property>
609 <property name="fill">True</property>
610 <property name="position">1</property>
611 </packing>
612 </child>
613 </object>
614 <packing>
615 <property name="expand">False</property>
616 <property name="fill">True</property>
617 <property name="position">2</property>
618 </packing>
619 </child>
620 </object>
621 <packing>
622 <property name="position">1</property>
623 </packing>
624 </child>
625 <child type="tab">
626 <object class="GtkLabel" id="label5">
627 <property name="visible">True</property>
628 <property name="can_focus">False</property>
629 <property name="label" translatable="yes">Conditions</property>
630 </object>
631 <packing>
632 <property name="position">1</property>
633 <property name="tab_fill">False</property>
634 </packing>
635 </child>
636 <child>
637 <object class="GtkBox" id="vbox9">
638 <property name="visible">True</property>
639 <property name="can_focus">False</property>
640 <property name="border_width">12</property>
641 <property name="orientation">vertical</property>
642 <property name="spacing">12</property>
643 <child>
644 <object class="GtkBox" id="vbox2">
645 <property name="visible">True</property>
646 <property name="can_focus">False</property>
647 <property name="orientation">vertical</property>
278 <child>648 <child>
279 <object class="GtkLabel" id="Metric System Unit">649 <object class="GtkLabel" id="Metric System Unit">
280 <property name="visible">True</property>650 <property name="visible">True</property>
651 <property name="can_focus">False</property>
281 <property name="xalign">0</property>652 <property name="xalign">0</property>
282 <property name="ypad">3</property>653 <property name="ypad">3</property>
283 <property name="label" translatable="yes">&lt;b&gt;Temperature Scale&lt;/b&gt;</property>654 <property name="label" translatable="yes">&lt;b&gt;Temperature Scale&lt;/b&gt;</property>
@@ -285,16 +656,20 @@
285 </object>656 </object>
286 <packing>657 <packing>
287 <property name="expand">False</property>658 <property name="expand">False</property>
659 <property name="fill">True</property>
288 <property name="position">0</property>660 <property name="position">0</property>
289 </packing>661 </packing>
290 </child>662 </child>
291 <child>663 <child>
292 <object class="GtkAlignment" id="alignment3">664 <object class="GtkAlignment" id="alignment3">
293 <property name="visible">True</property>665 <property name="visible">True</property>
666 <property name="can_focus">False</property>
294 <property name="left_padding">12</property>667 <property name="left_padding">12</property>
295 <child>668 <child>
296 <object class="GtkVBox" id="vbox6">669 <object class="GtkBox" id="box5">
297 <property name="visible">True</property>670 <property name="visible">True</property>
671 <property name="can_focus">False</property>
672 <property name="orientation">vertical</property>
298 <property name="homogeneous">True</property>673 <property name="homogeneous">True</property>
299 <child>674 <child>
300 <object class="GtkRadioButton" id="imperial">675 <object class="GtkRadioButton" id="imperial">
@@ -302,6 +677,7 @@
302 <property name="visible">True</property>677 <property name="visible">True</property>
303 <property name="can_focus">True</property>678 <property name="can_focus">True</property>
304 <property name="receives_default">False</property>679 <property name="receives_default">False</property>
680 <property name="xalign">0</property>
305 <property name="active">True</property>681 <property name="active">True</property>
306 <property name="draw_indicator">True</property>682 <property name="draw_indicator">True</property>
307 </object>683 </object>
@@ -317,6 +693,7 @@
317 <property name="visible">True</property>693 <property name="visible">True</property>
318 <property name="can_focus">True</property>694 <property name="can_focus">True</property>
319 <property name="receives_default">False</property>695 <property name="receives_default">False</property>
696 <property name="xalign">0</property>
320 <property name="draw_indicator">True</property>697 <property name="draw_indicator">True</property>
321 <property name="group">imperial</property>698 <property name="group">imperial</property>
322 </object>699 </object>
@@ -331,21 +708,26 @@
331 </object>708 </object>
332 <packing>709 <packing>
333 <property name="expand">False</property>710 <property name="expand">False</property>
711 <property name="fill">True</property>
334 <property name="position">1</property>712 <property name="position">1</property>
335 </packing>713 </packing>
336 </child>714 </child>
337 </object>715 </object>
338 <packing>716 <packing>
339 <property name="expand">False</property>717 <property name="expand">False</property>
718 <property name="fill">True</property>
340 <property name="position">0</property>719 <property name="position">0</property>
341 </packing>720 </packing>
342 </child>721 </child>
343 <child>722 <child>
344 <object class="GtkVBox" id="vbox3">723 <object class="GtkBox" id="vbox3">
345 <property name="visible">True</property>724 <property name="visible">True</property>
725 <property name="can_focus">False</property>
726 <property name="orientation">vertical</property>
346 <child>727 <child>
347 <object class="GtkLabel" id="Wind Speed Unit">728 <object class="GtkLabel" id="Wind Speed Unit">
348 <property name="visible">True</property>729 <property name="visible">True</property>
730 <property name="can_focus">False</property>
349 <property name="xalign">0</property>731 <property name="xalign">0</property>
350 <property name="ypad">3</property>732 <property name="ypad">3</property>
351 <property name="label" translatable="yes">&lt;b&gt;Wind Speed Unit&lt;/b&gt;</property>733 <property name="label" translatable="yes">&lt;b&gt;Wind Speed Unit&lt;/b&gt;</property>
@@ -353,23 +735,28 @@
353 </object>735 </object>
354 <packing>736 <packing>
355 <property name="expand">False</property>737 <property name="expand">False</property>
738 <property name="fill">True</property>
356 <property name="position">0</property>739 <property name="position">0</property>
357 </packing>740 </packing>
358 </child>741 </child>
359 <child>742 <child>
360 <object class="GtkAlignment" id="alignment4">743 <object class="GtkAlignment" id="alignment4">
361 <property name="visible">True</property>744 <property name="visible">True</property>
745 <property name="can_focus">False</property>
362 <property name="left_padding">12</property>746 <property name="left_padding">12</property>
363 <child>747 <child>
364 <object class="GtkVBox" id="vbox7">748 <object class="GtkBox" id="vbox7">
365 <property name="visible">True</property>749 <property name="visible">True</property>
750 <property name="can_focus">False</property>
751 <property name="orientation">vertical</property>
366 <property name="homogeneous">True</property>752 <property name="homogeneous">True</property>
367 <child>753 <child>
368 <object class="GtkRadioButton" id="mps">754 <object class="GtkRadioButton" id="mps">
369 <property name="label" translatable="yes">Meter per second (m/s)</property>755 <property name="label" translatable="yes">Meters per second (m/s)</property>
370 <property name="visible">True</property>756 <property name="visible">True</property>
371 <property name="can_focus">True</property>757 <property name="can_focus">True</property>
372 <property name="receives_default">False</property>758 <property name="receives_default">False</property>
759 <property name="xalign">0.5</property>
373 <property name="active">True</property>760 <property name="active">True</property>
374 <property name="draw_indicator">True</property>761 <property name="draw_indicator">True</property>
375 </object>762 </object>
@@ -385,6 +772,7 @@
385 <property name="visible">True</property>772 <property name="visible">True</property>
386 <property name="can_focus">True</property>773 <property name="can_focus">True</property>
387 <property name="receives_default">False</property>774 <property name="receives_default">False</property>
775 <property name="xalign">0.5</property>
388 <property name="draw_indicator">True</property>776 <property name="draw_indicator">True</property>
389 <property name="group">mps</property>777 <property name="group">mps</property>
390 </object>778 </object>
@@ -400,6 +788,7 @@
400 <property name="visible">True</property>788 <property name="visible">True</property>
401 <property name="can_focus">True</property>789 <property name="can_focus">True</property>
402 <property name="receives_default">False</property>790 <property name="receives_default">False</property>
791 <property name="xalign">0.5</property>
403 <property name="draw_indicator">True</property>792 <property name="draw_indicator">True</property>
404 <property name="group">mps</property>793 <property name="group">mps</property>
405 </object>794 </object>
@@ -411,10 +800,11 @@
411 </child>800 </child>
412 <child>801 <child>
413 <object class="GtkRadioButton" id="beaufort">802 <object class="GtkRadioButton" id="beaufort">
414 <property name="label" translatable="yes">Beaufort</property>803 <property name="label" translatable="yes">Beaufort number</property>
415 <property name="visible">True</property>804 <property name="visible">True</property>
416 <property name="can_focus">True</property>805 <property name="can_focus">True</property>
417 <property name="receives_default">False</property>806 <property name="receives_default">False</property>
807 <property name="xalign">0.5</property>
418 <property name="draw_indicator">True</property>808 <property name="draw_indicator">True</property>
419 <property name="group">mps</property>809 <property name="group">mps</property>
420 </object>810 </object>
@@ -430,6 +820,7 @@
430 <property name="visible">True</property>820 <property name="visible">True</property>
431 <property name="can_focus">True</property>821 <property name="can_focus">True</property>
432 <property name="receives_default">False</property>822 <property name="receives_default">False</property>
823 <property name="xalign">0.5</property>
433 <property name="draw_indicator">True</property>824 <property name="draw_indicator">True</property>
434 <property name="group">mps</property>825 <property name="group">mps</property>
435 </object>826 </object>
@@ -444,41 +835,44 @@
444 </object>835 </object>
445 <packing>836 <packing>
446 <property name="expand">False</property>837 <property name="expand">False</property>
838 <property name="fill">True</property>
447 <property name="position">1</property>839 <property name="position">1</property>
448 </packing>840 </packing>
449 </child>841 </child>
450 </object>842 </object>
451 <packing>843 <packing>
452 <property name="expand">False</property>844 <property name="expand">False</property>
845 <property name="fill">True</property>
453 <property name="position">1</property>846 <property name="position">1</property>
454 </packing>847 </packing>
455 </child>848 </child>
456 </object>849 </object>
457 <packing>850 <packing>
458 <property name="position">1</property>851 <property name="position">2</property>
459 </packing>852 </packing>
460 </child>853 </child>
461 <child type="tab">854 <child type="tab">
462 <object class="GtkLabel" id="label2">855 <object class="GtkLabel" id="label2">
463 <property name="visible">True</property>856 <property name="visible">True</property>
857 <property name="can_focus">False</property>
464 <property name="label" translatable="yes">Units</property>858 <property name="label" translatable="yes">Units</property>
465 </object>859 </object>
466 <packing>860 <packing>
467 <property name="position">1</property>861 <property name="position">2</property>
468 <property name="tab_fill">False</property>862 <property name="tab_fill">False</property>
469 </packing>863 </packing>
470 </child>864 </child>
471 <child>865 <child>
472 <object class="GtkVBox" id="vbox8">866 <object class="GtkBox" id="vbox8">
473 <property name="visible">True</property>867 <property name="visible">True</property>
868 <property name="can_focus">False</property>
474 <property name="border_width">12</property>869 <property name="border_width">12</property>
870 <property name="orientation">vertical</property>
475 <property name="spacing">6</property>871 <property name="spacing">6</property>
476 <child>872 <child>
477 <object class="GtkScrolledWindow" id="scrolledwindow1">873 <object class="GtkScrolledWindow" id="scrolledwindow1">
478 <property name="visible">True</property>874 <property name="visible">True</property>
479 <property name="can_focus">True</property>875 <property name="can_focus">True</property>
480 <property name="hscrollbar_policy">automatic</property>
481 <property name="vscrollbar_policy">automatic</property>
482 <property name="window_placement_set">True</property>876 <property name="window_placement_set">True</property>
483 <property name="shadow_type">in</property>877 <property name="shadow_type">in</property>
484 <child>878 <child>
@@ -490,6 +884,9 @@
490 <property name="headers_clickable">False</property>884 <property name="headers_clickable">False</property>
491 <property name="reorderable">True</property>885 <property name="reorderable">True</property>
492 <property name="search_column">0</property>886 <property name="search_column">0</property>
887 <child internal-child="selection">
888 <object class="GtkTreeSelection" id="treeview-selection1"/>
889 </child>
493 <child>890 <child>
494 <object class="GtkTreeViewColumn" id="City">891 <object class="GtkTreeViewColumn" id="City">
495 <property name="title">City</property>892 <property name="title">City</property>
@@ -506,12 +903,15 @@
506 </child>903 </child>
507 </object>904 </object>
508 <packing>905 <packing>
906 <property name="expand">True</property>
907 <property name="fill">True</property>
509 <property name="position">0</property>908 <property name="position">0</property>
510 </packing>909 </packing>
511 </child>910 </child>
512 <child>911 <child>
513 <object class="GtkHButtonBox" id="hbuttonbox1">912 <object class="GtkHButtonBox" id="hbuttonbox1">
514 <property name="visible">True</property>913 <property name="visible">True</property>
914 <property name="can_focus">False</property>
515 <property name="spacing">6</property>915 <property name="spacing">6</property>
516 <property name="layout_style">start</property>916 <property name="layout_style">start</property>
517 <child>917 <child>
@@ -521,7 +921,7 @@
521 <property name="can_focus">True</property>921 <property name="can_focus">True</property>
522 <property name="receives_default">True</property>922 <property name="receives_default">True</property>
523 <property name="use_stock">True</property>923 <property name="use_stock">True</property>
524 <signal name="clicked" handler="on_add_location"/>924 <signal name="clicked" handler="on_add_location" swapped="no"/>
525 </object>925 </object>
526 <packing>926 <packing>
527 <property name="expand">False</property>927 <property name="expand">False</property>
@@ -536,7 +936,7 @@
536 <property name="can_focus">True</property>936 <property name="can_focus">True</property>
537 <property name="receives_default">True</property>937 <property name="receives_default">True</property>
538 <property name="use_stock">True</property>938 <property name="use_stock">True</property>
539 <signal name="clicked" handler="on_remove_location"/>939 <signal name="clicked" handler="on_remove_location" swapped="no"/>
540 </object>940 </object>
541 <packing>941 <packing>
542 <property name="expand">False</property>942 <property name="expand">False</property>
@@ -547,70 +947,33 @@
547 </object>947 </object>
548 <packing>948 <packing>
549 <property name="expand">False</property>949 <property name="expand">False</property>
950 <property name="fill">True</property>
550 <property name="position">1</property>951 <property name="position">1</property>
551 </packing>952 </packing>
552 </child>953 </child>
553 </object>954 </object>
554 <packing>955 <packing>
555 <property name="position">2</property>956 <property name="position">3</property>
556 </packing>957 </packing>
557 </child>958 </child>
558 <child type="tab">959 <child type="tab">
559 <object class="GtkLabel" id="label3">960 <object class="GtkLabel" id="label3">
560 <property name="visible">True</property>961 <property name="visible">True</property>
962 <property name="can_focus">False</property>
561 <property name="label" translatable="yes">Locations</property>963 <property name="label" translatable="yes">Locations</property>
562 </object>964 </object>
563 <packing>965 <packing>
564 <property name="position">2</property>966 <property name="position">3</property>
565 <property name="tab_fill">False</property>967 <property name="tab_fill">False</property>
566 </packing>968 </packing>
567 </child>969 </child>
568 </object>970 </object>
569 <packing>971 <packing>
972 <property name="expand">False</property>
973 <property name="fill">True</property>
570 <property name="position">1</property>974 <property name="position">1</property>
571 </packing>975 </packing>
572 </child>976 </child>
573 <child internal-child="action_area">
574 <object class="GtkHButtonBox" id="dialog-action_area1">
575 <property name="visible">True</property>
576 <property name="layout_style">end</property>
577 <child>
578 <object class="GtkButton" id="cancel_button">
579 <property name="label">gtk-cancel</property>
580 <property name="visible">True</property>
581 <property name="receives_default">True</property>
582 <property name="use_stock">True</property>
583 <signal name="clicked" handler="cancel"/>
584 </object>
585 <packing>
586 <property name="expand">False</property>
587 <property name="fill">False</property>
588 <property name="position">0</property>
589 </packing>
590 </child>
591 <child>
592 <object class="GtkButton" id="ok_button">
593 <property name="label">gtk-ok</property>
594 <property name="visible">True</property>
595 <property name="sensitive">False</property>
596 <property name="receives_default">True</property>
597 <property name="use_stock">True</property>
598 <signal name="clicked" handler="ok"/>
599 </object>
600 <packing>
601 <property name="expand">False</property>
602 <property name="fill">False</property>
603 <property name="position">1</property>
604 </packing>
605 </child>
606 </object>
607 <packing>
608 <property name="expand">False</property>
609 <property name="fill">False</property>
610 <property name="pack_type">end</property>
611 <property name="position">0</property>
612 </packing>
613 </child>
614 </object>977 </object>
615 </child>978 </child>
616 <action-widgets>979 <action-widgets>
@@ -618,10 +981,4 @@
618 <action-widget response="-5">ok_button</action-widget>981 <action-widget response="-5">ok_button</action-widget>
619 </action-widgets>982 </action-widgets>
620 </object>983 </object>
621 <object class="GtkAdjustment" id="rate">
622 <property name="lower">1</property>
623 <property name="upper">30</property>
624 <property name="step_increment">1</property>
625 <property name="page_increment">10</property>
626 </object>
627</interface>984</interface>
628985
=== modified file 'debian/changelog'
--- debian/changelog 2012-07-30 04:02:24 +0000
+++ debian/changelog 2013-05-22 05:08:27 +0000
@@ -1,3 +1,38 @@
1indicator-weather (13.05.17-quantal2) quantal; urgency=low
2
3 * Fix for 4-day Forecast display (LP: #1182324)
4 * 'OK' button in Preferences Dialog is now more responsive
5
6 -- Joshua Tasker <jtasker@gmail.com> Wed, 22 May 2013 00:32:54 +0500
7
8indicator-weather (13.05.17~quantal1) quantal; urgency=low
9
10 * Ported to GTK3 and GObject from PyGTK
11 * Rewrite threading code to avoid dbus-related crashes (LP: #743541)
12 * Added "feels like" temperature (humidex/heat index/wind chill)
13 * New "Conditions" tab in Preferences dialog, choose temperature formulas, toggle display of conditions
14 * Bumped version number to reflect massive changes
15
16 -- Joshua Tasker <jtasker@gmail.com> Sat, 18 May 2013 12:59:03 +0500
17
18indicator-weather (12.07.30~quantal2) quantal; urgency=low
19
20 * Don't crash if Yahoo doesn't return conditions
21 * Fixed a crash when reading saved Places with no location IDs
22 * Fix units in Forecast when metric is selected
23 * One-line fix for outdated data sources
24
25 -- Joshua Tasker <jtasker@gmail.com> Sat, 04 May 2013 01:20:38 +0500
26
27indicator-weather (12.07.30~quantal1) quantal; urgency=low
28
29 * Fix adding location, now uses Yahoo's YQL service
30 * Fix for "Forecast", now uses Yahoo instead of Google
31 * Bump dependency for python-pywapi to 0.3
32 * Hide Google radio button on Preferences UI
33
34 -- Joshua Tasker <jtasker@gmail.com> Tue, 09 Apr 2013 02:27:04 +0500
35
1indicator-weather (12.07.30~precise1) precise; urgency=low36indicator-weather (12.07.30~precise1) precise; urgency=low
2 * Skip sunset and sunrise check as Earthtools.org is down (LP: #964365)37 * Skip sunset and sunrise check as Earthtools.org is down (LP: #964365)
338
439
=== modified file 'debian/control'
--- debian/control 2011-06-22 09:52:46 +0000
+++ debian/control 2013-05-22 05:08:27 +0000
@@ -1,26 +1,33 @@
1Source: indicator-weather1Source: indicator-weather
2Section: python2Section: python
3Priority: extra3Priority: extra
4Build-Depends: cdbs (>= 0.4.90-1~),4Build-Depends: debhelper (>= 7.0.50~),
5 debhelper (>= 6),5 gobject-introspection,
6 python (>= 2.6.6-3~),6 python (>= 2.6.6-3~),
7 gobject-introspection,7 python-distutils-extra (>= 2.10)
8 python-distutils-extra (>= 2.10)8X-Python-Version: >= 2.6
9Maintainer: Vadim Rutkovsky <roignac@gmail.com>9Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
10Standards-Version: 3.8.310Standards-Version: 3.9.3
11Homepage: https://launchpad.net/weather-indicator
1112
12Package: indicator-weather13Package: indicator-weather
13Architecture: all14Architecture: all
14Depends: ${misc:Depends},15Depends: gir1.2-glib-2.0,
15 ${python:Depends},16 gir1.2-gtk-3.0,
16 libglib2.0-bin,17 gir1.2-appindicator3-0.1,
17 python-appindicator,18 gnome-icon-theme,
18 python-notify,19 libglib2.0-bin,
19 python-gobject,20 libgtk-3-bin,
20 python-gtk2,21 python-gconf,
21 python-gconf,22 python-gi,
22 python-pywapi23 python-pywapi (>= 0.3.2
24 xdg-utils,
25 ${misc:Depends},
26 ${python:Depends}
23Recommends: python-apport27Recommends: python-apport
24Description: A weather indicator for Ubuntu's Indicator Applet28Description: indicator that displays weather information
25 A weather indicator that displays information for one or multiple places29 Indicator-Weather displays information for one or multiple places
26 in the world30 in the world. Current weather status is displayed directly on your
31 panel and detailed forecasts are no more than a click away.
32 .
33 It is implemented using the Indicator Applet API.
2734
=== modified file 'debian/copyright'
--- debian/copyright 2011-06-22 09:52:46 +0000
+++ debian/copyright 2013-05-22 05:08:27 +0000
@@ -1,12 +1,31 @@
1Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat1Format: http://dep.debian.net/deps/dep5
2Upstream-Name: indicator-weather2Upstream-Name: Indicator-Weather
3Upstream-Maintainer: Vadim Rutkovsky <roignac@gmail.com>3Upstream-Contact: Vadim Rutkovsky <roignac@gmail.com>
4Upstream-Source: https://launchpad.net/weather-indicator4Source: https://launchpad.net/weather-indicator/+download
55
6Files: *6Files: *
7Copyright: (C) 2010 Mehdi Rejraji mehd36@gmail.com7Copyright: 2010, Mehdi Rejraji <mehd36@gmail.com>
8Copyright: (C) 2010 Sebastian MacDonald Sebas310@gmail.com8 2010, Sebastian MacDonald <Sebas310@gmail.com>
9Copyright: (C) 2011 Vadim Rutkovsky <roignac@gmail.com>9 2011, Vadim Rutkovsky <roignac@gmail.com>
10License: GPL-310 2013, Joshua Tasker <jtasker@gmail.com>
11 The full text of the GPL is distributed in11License: GPL-3
12 /usr/share/common-licenses/GPL-3 on Debian systems.12
13Files: debian/*
14Copyright: 2011, Andrew Starr-Bochicchio <a.starr.b@gmail.com>
15License: GPL-3
16
17License: GPL-3
18 This package is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; version 3 of the License.
21 .
22 This package is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26 .
27 You should have received a copy of the GNU General Public License
28 along with this package; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 .
31 On Debian systems, see `/usr/share/common-licenses/GPL-3'
1332
=== modified file 'debian/indicator-weather.install'
--- debian/indicator-weather.install 2011-01-23 14:08:21 +0000
+++ debian/indicator-weather.install 2013-05-22 05:08:27 +0000
@@ -1,1 +1,2 @@
1AUTHORS /usr/share/doc/indicator-weather
1debian/source_indicator-weather.py usr/share/apport/package-hooks2debian/source_indicator-weather.py usr/share/apport/package-hooks
23
=== modified file 'debian/postinst'
--- debian/postinst 2011-11-02 15:34:54 +0000
+++ debian/postinst 2013-05-22 05:08:27 +0000
@@ -1,19 +1,18 @@
1#!/bin/sh1#!/bin/sh
2 2
3set -e
4
3#DEBHELPER#5#DEBHELPER#
6
4echo "Installing indicator-specific icons..."7echo "Installing indicator-specific icons..."
5xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon.png weather-indicator8xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon.png weather-indicator
6xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_unknown_condition.png weather-indicator-unknown9xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_unknown_condition.png weather-indicator-unknown
7xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_connection_error.png weather-indicator-error10xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_connection_error.png weather-indicator-error
811
9#installing dconf schema
10echo "Installing indicator dconf schema..."
11cp /usr/share/indicator-weather/indicator-weather.gschema.xml /usr/share/glib-2.0/schemas
12glib-compile-schemas /usr/share/glib-2.0/schemas
13
14#quick fix for incomplete icon themes12#quick fix for incomplete icon themes
15echo "Fixing incomplete weather icons..."13echo "Fixing incomplete weather icons..."
16if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then14if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ] && \
15 [ ! -L "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then
17 ln -s /usr/share/icons/gnome/22x22/status/weather-few-clouds.png /usr/share/icons/gnome/22x22/status/weather-clouds.png16 ln -s /usr/share/icons/gnome/22x22/status/weather-few-clouds.png /usr/share/icons/gnome/22x22/status/weather-clouds.png
18 ln -s /usr/share/icons/gnome/22x22/status/weather-clouds-night.png /usr/share/icons/gnome/22x22/status/weather-clouds-night.png17 ln -s /usr/share/icons/gnome/22x22/status/weather-clouds-night.png /usr/share/icons/gnome/22x22/status/weather-clouds-night.png
19 18
2019
=== modified file 'debian/rules'
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: