=== modified file 'AUTHORS'
--- AUTHORS 2011-03-19 09:27:37 +0000
+++ AUTHORS 2013-05-22 05:08:27 +0000
@@ -1,3 +1,4 @@
Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
Copyright (C) 2010 Vadim Rutkovsky roignac@gmail.com
+Copyright (C) 2013 Joshua Tasker jtasker@gmail.com
=== 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 @@
# Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
# Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
# Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com
+# Copyright (C) 2013 Joshua Tasker jtasker@gmail.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
@@ -17,29 +18,26 @@
# with this program. If not, see .
### END LICENSE
-try:
- from gi.repository import Gio
-except ImportError:
- pass
+from gi.repository import Gio, GLib, Gtk, Gdk, Notify, GObject, GdkPixbuf
+from gi.repository import AppIndicator3 as AppIndicator
+
import sys, os, shutil, tempfile
-import gtk, pygtk, gobject, pynotify
-pygtk.require('2.0')
-import appindicator
+
import urllib2, urllib
from urllib import urlencode
import re
import locale
from xml.dom.minidom import parseString
import datetime
-import dbus
+#import dbus
import time
import traceback
import types
-# Will be used for humidex
-#import math
+from math import exp
import commands, threading
import logging, logging.handlers
import pywapi
+import Queue
import gettext
from gettext import gettext as _
@@ -55,7 +53,7 @@
sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses
-VERSION = "12.07.30 'Cloudy 10'"
+VERSION = "13.05.26 'Rainy'"
from indicator_weather.helpers import *
@@ -69,11 +67,16 @@
WEATHER_KEY = 'weather'
LOCATIONS_KEY = 'locations'
INDICATOR_DISPLAY = 'show_label'
+ RELATIVE_DISPLAY = 'show_relative'
+ WIND_DISPLAY = 'show_wind'
+ SUNTIMES_DISPLAY = 'show_suntimes'
NOTIFICATIONS = 'notif'
WEATHER_SOURCE = 'data_source'
REFRESH_RATE = 'refresh_rate'
METRIC_SYSTEM = 'unit'
WIND_UNIT = 'wind'
+ HEAT_ESTIMATE = 'heat'
+ CHILL_ESTIMATE = 'chill'
PLACECHOSEN = 'placechosen'
PLACES = 'places'
@@ -84,48 +87,70 @@
INDICATOR_DISPLAY : {
INFO_TYPE : types.IntType,
INFO_SETTING : 'indicator-display'
- },
+ },
+ RELATIVE_DISPLAY : {
+ INFO_TYPE : types.BooleanType,
+ INFO_SETTING : 'relative-display'
+ },
+ WIND_DISPLAY : {
+ INFO_TYPE : types.BooleanType,
+ INFO_SETTING : 'wind-display'
+ },
+ SUNTIMES_DISPLAY : {
+ INFO_TYPE : types.BooleanType,
+ INFO_SETTING : 'suntimes-display'
+ },
NOTIFICATIONS : {
INFO_TYPE : types.StringType,
INFO_SETTING : 'notifications'
- },
+ },
WEATHER_SOURCE : {
INFO_TYPE : types.StringType,
INFO_SETTING : 'weather-source'
- },
+ },
REFRESH_RATE : {
INFO_TYPE : types.IntType,
INFO_SETTING : 'refresh-rate'
- },
+ },
METRIC_SYSTEM : {
INFO_TYPE : types.StringType,
INFO_SETTING : 'metric-system'
- },
+ },
WIND_UNIT : {
INFO_TYPE : types.StringType,
INFO_SETTING : 'wind-unit'
- },
+ },
+ HEAT_ESTIMATE: {
+ INFO_TYPE : types.StringType,
+ INFO_SETTING : 'heat-estimate'
+ },
+ CHILL_ESTIMATE: {
+ INFO_TYPE : types.StringType,
+ INFO_SETTING : 'chill-estimate'
+ },
PLACECHOSEN : {
INFO_TYPE : types.IntType,
INFO_SETTING: 'placechosen'
- },
+ },
PLACES : {
INFO_TYPE : types.ListType,
INFO_SETTING: 'places'
- },
+ },
}
- # Open the DB
def prepare_settings_store(self):
+ """ Open the DB """
log.debug("Settings: preparing settings store")
try:
self.db = Gio.Settings.new(self.BASE_KEY)
except Exception as e:
- log.debug("Settings: exception occurred while opening settings:\n %s" % str(e))
+ log.debug("Settings: exception occurred while "
+ "opening settings:\n %s" % str(e))
- # Make sure autostart file is installed. Inspired by GTG.
def check_autostart(self):
- autostart_dir = os.path.join(os.path.expanduser("~"),".config/autostart/")
+ """ Make sure autostart file is installed. Inspired by GTG. """
+ autostart_dir = os.path.join(os.path.expanduser("~"),
+ ".config/autostart/")
autostart_file = "indicator-weather.desktop"
autostart_path = os.path.join(autostart_dir, autostart_file)
if not os.path.isfile(autostart_path):
@@ -135,12 +160,12 @@
"/usr/local/share/applications"]
this_directory = os.path.dirname(os.path.abspath(__file__))
for path in desktop_file_directories:
- fullpath = os.path.normpath(os.path.join(this_directory, path, \
- autostart_file))
+ fullpath = os.path.normpath(os.path.join(this_directory, path,
+ autostart_file))
if os.path.isfile(fullpath):
desktop_file_path = fullpath
break
- #If we have found the desktop file, we make a link to in in
+ # If we have found the desktop file, we make a link to in in
# autostart_path.
if desktop_file_path:
if not os.path.exists(autostart_dir):
@@ -149,8 +174,8 @@
log.debug("Installing autostart file.")
os.symlink(desktop_file_path, autostart_path)
- # Get a value of the setting
def get_value(self, setting, return_id = False):
+ """ Get a value of the specified setting """
log.debug("Settings: getting value for %s" % setting)
setting_name = Settings.INFO[setting][INFO_SETTING]
try:
@@ -162,15 +187,15 @@
types.ListType: self.db.get_string,
types.DictType: self.db.get_string,
types.NoneType: self.db.get_value,
- }[setting_type]
+ }[setting_type]
return get_func(setting_name)
except:
- self.log.debug("Settings: can't find value for %s" % setting)
+ log.debug("Settings: can't find value for %s" % setting)
return None
- # Set a setting value
+
def set_value(self, setting, value):
-
+ """ Set a value for the specified setting """
value = '' if value is None else value
value = str(value) if type(value) is types.ListType else value
log.debug("Settings: setting '%s'='%s'" % (setting, value))
@@ -185,17 +210,20 @@
types.ListType: self.db.set_string,
types.DictType: self.db.set_string,
types.NoneType: self.db.set_value,
- }[setting_type]
+ }[setting_type]
set_func(setting_name, value)
except:
log.debug( \
"Settings: schema for '%s' not found, aborting" % setting)
- # Get cached weather by location code.
- # If return_id is True, only document id is returned, otherwise - full weather data
def get_weather(self, location_code, return_id = False):
- log.debug("Settings: getting cached weather for %s" % \
- location_code)
+ """Get cached weather by location code.
+ If return_id is True, only document id is returned,
+ otherwise full weather data is returned.
+
+ """
+ log.debug("Settings: getting cached weather for %s" %
+ location_code)
try:
cached_weather_string = self.db.get_string(self.WEATHER_KEY)
cached_weather = {} if cached_weather_string == ''\
@@ -207,22 +235,22 @@
"Settings: can't find value for %s" % location_code)
return None
except:
- log.debug("Settings: can't find %s setting" % WEATHER_KEY)
+ log.debug("Settings: can't find %s setting" % self.WEATHER_KEY)
return None
- # Save weather info in cache for specific location
def save_weather(self, weather, location_code):
-
+ """ Save weather info in cache for specific location """
record = {
- "label" : weather.get_temperature(needs_rounding=True),
- "condition": weather.get_condition_label(),
- "icon" : weather.get_icon_name(),
- "temper" : weather.get_temperature_label(),
- "humidex" : weather.get_humidex_label(),
- "humidity" : weather.get_humidity_label(),
- "wind" : weather.get_wind_label(),
- "sunrise" : weather.get_sunrise_label(),
- "sunset" : weather.get_sunset_label()
+## "label" : weather.get_temperature(needs_rounding=True),
+ "label" : weather.get_temperature_string(),
+ "condition" : weather.get_condition_label(),
+ "icon" : weather.get_icon_name(),
+ "temper" : weather.get_temperature_label(),
+ "feelslike" : weather.get_relative_label(),
+ "humidity" : weather.get_humidity_label(),
+ "wind" : weather.get_wind_label(),
+ "sunrise" : weather.get_sunrise_label(),
+ "sunset" : weather.get_sunset_label()
}
log.debug("Settings: setting '%s'='%s'" % (location_code, record))
@@ -234,12 +262,17 @@
cached_weather_string = str(cached_weather)
self.db.set_string(self.WEATHER_KEY, cached_weather_string)
except:
- log.debug(\
- "Settings: schema for '%s' not found, aborting" % setting)
+ log.debug(
+ "Settings: schema for '%s' not found, aborting" %
+ self.WEATHER_KEY
+ )
- # Get location details by location code
- # If return_id is True, only document id is returned, otherwise - full location data
def get_location_details(self, location_code, return_id = False):
+ """ Get location details by location code
+ If return_id is True, only document id is returned,
+ otherwise - full location data is returned
+
+ """
try:
locations_string = self.db.get_string(self.LOCATIONS_KEY)
locations = {} if locations_string == ''\
@@ -247,19 +280,19 @@
if location_code in locations.keys():
return str(locations[location_code])
else:
- log.debug(\
- "Settings: can't find value for %s" % location_code)
+ log.debug(
+ "Settings: can't find value for %s" % location_code
+ )
return None
except:
- log.debug("Settings: can't find location details for %s" % \
- location_code)
+ log.debug("Settings: can't find location details for %s" %
+ location_code)
return None
- # Save location details
def save_location_details(self, location_details, location_code):
- log.debug("Settings: setting '%s'='%s'" %\
- (location_code, location_details))
-
+ """ Save location details """
+ log.debug("Settings: setting '%s'='%s'" %
+ (location_code, location_details))
try:
locations_string = self.db.get_string(self.LOCATIONS_KEY)
locations = {} if locations_string == ''\
@@ -270,8 +303,8 @@
except:
pass
-class MetricSystem:
- """ Class with available metric systems units """
+class UnitSystem:
+ """ Class with available measurement unit systems """
SI = 1
IMPERIAL = 2
@@ -285,52 +318,64 @@
class WeatherDataSource:
""" Class for available weather data sources """
- GOOGLE = 1
- YAHOO = 2
+ YAHOO = 1
+ WEATHER_COM = 2
+class RelativeFormula:
+ """ Class for relative temperature formulas """
+ HEATINDEX = 1
+ HUMIDEX = 2
+ WINDCHILL = 3
+ APPARENT = 4
+
class Location:
""" Data object to store location details """
- # Initialize an object with a label
- def __init__(self, metric_system, wind_unit, location_details = None):
+ def __init__(self, metric_system, wind_unit, heat_index,
+ chill_index, location_details = None):
+ """ Initialize an object with a label """
self.metric_system = metric_system
self.wind_unit = wind_unit
+ self.heat_index = heat_index
+ self.chill_index = chill_index
self.location_details = location_details
- # Convert coordinate for google
def convert_coordinate_for_google(self, value):
- value = float(value) * 1e6
- return int(round(value))
+ """ Convert coordinate for google """
+ value = float(value) * 1e6
+ return int(round(value))
- # Get necessary location details by its GeoNames details
def prepare_location(self, geonames_details):
+ """ Get necessary location details by its GeoNames details """
self.location_details = {}
self.location_details['full name'] = geonames_details[0]
self.location_details['latitude'] = geonames_details[2]
self.location_details['longitude'] = geonames_details[3]
self.prepare_location_for_google(geonames_details)
self.prepare_location_for_yahoo(geonames_details)
+ self.prepare_location_for_weather_com(geonames_details)
#TODO: Get noaa id from geonames service
self.location_details['noaa id'] = "woot"
# check mandatory attributes
if not hasattr(self, 'location_code') or \
- 'latitude' not in self.location_details or \
- 'longitude' not in self.location_details:
+ 'latitude' not in self.location_details or \
+ 'longitude' not in self.location_details:
return False
# check that we have at least one supported data source
if 'google id' not in self.location_details and \
- 'yahoo id' not in self.location_details:
+ 'yahoo id' not in self.location_details and \
+ 'weather-com id' not in self.location_details:
log.error(("Location '%s'" %
- self.location_details['full name'])) + \
+ self.location_details['full name'])) + \
"is not supported by current data sources"
return False
return True
def prepare_location_for_google(self, geonames_details):
- # Format latitude and longitude for Google needs
+ """ Format latitude and longitude for Google needs """
try:
lat = self.convert_coordinate_for_google(geonames_details[2])
lon = self.convert_coordinate_for_google(geonames_details[3])
@@ -340,7 +385,7 @@
log.error(e)
def prepare_location_for_yahoo(self, geonames_details):
- # Get location details in english for Yahoo
+ """ Get location details in English for Yahoo """
baseurl = 'http://api.geonames.org/getJSON'
params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}
url = '?'.join((baseurl, urlencode(params)))
@@ -356,19 +401,13 @@
return
# Get YAHOO WOEID by english name of location
- baseurl = 'http://where.yahooapis.com/geocode'
- params = {'location': displayed_city_name, 'appid': 'mOawLd4s', 'flags': 'J'}
- url = '?'.join((baseurl, urlencode(params)))
- log.debug("Location: Get Yahoo WOEID, url %s" % url)
- f = urllib2.urlopen(url)
- s=f.read()
- null = None
- yahoo_woeid_result = eval(s)
- if (yahoo_woeid_result['ResultSet']['Error'] != 0) and (yahoo_woeid_result['ResultSet']['Results'] != None):
- log.error("Location: Yahoo woeid return error. Full response:\n %s" % str(yahoo_woeid_result))
+ woeid_result = pywapi.get_woeid_from_yahoo(displayed_city_name)
+ if woeid_result.has_key('error'):
+ log.error("Location: Yahoo woeid return error. Full response:\n %s" % woeid_result['error'])
return
else:
- woeid = yahoo_woeid_result['ResultSet']['Results'][0]['woeid']
+ # only look at the the first woeid result
+ woeid = woeid_result[0][0]
self.location_code = woeid
log.debug("Location: woeid is %s" % woeid)
@@ -400,21 +439,54 @@
except Exception, e:
log.error(e)
- # Return lcoation code and location details
+ def prepare_location_for_weather_com(self, geonames_details):
+ """ Get location details in English for Weather.com """
+ baseurl = 'http://api.geonames.org/getJSON'
+ params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}
+ url = '?'.join((baseurl, urlencode(params)))
+ log.debug("Location: Get GeoNames location details, url %s" % url)
+ try:
+ city = eval(urllib2.urlopen(url).read())
+ if 'adminName1' in city:
+ displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])
+ elif 'name' in city:
+ displayed_city_name = "%s, %s" % (city['name'], city['countryName'])
+ else:
+ log.error("Location: Cannot find GeoNames info for code %s Full Response:\n %s" % (geonames_details[1], str(city)))
+ return
+
+ # Get Weather.com Location ID by English name of location
+ locid_result = pywapi.get_location_ids(displayed_city_name)
+ if locid_result.has_key('error'):
+ log.error("Location: Weather.com locid return error. Full response:\n %s" % locid_result['error'])
+ return
+ else:
+ # only look at the the first locid result
+ locid = locid_result[0][0]
+ self.location_details['weather-com id'] = locid
+ log.debug("Location: locid is %s" % locid)
+
+ except urllib2.URLError:
+ log.error("Location: error reaching url '%s'" % url)
+
+ except Exception, e:
+ log.error(e)
+
def export_location_details(self):
+ """ Return location code and location details """
return (self.location_code, self.location_details)
- # Get fresh weather data and store it to weather object
def update_weather_data(self, source):
- # gather existing source keys
+ """ Get fresh weather data and store it to weather object """
valid_source = None
loc_ids = {}
SOURCES = {
- WeatherDataSource.GOOGLE : ("google id", "Google"),
- WeatherDataSource.YAHOO : ("yahoo id", "Yahoo"),
+ WeatherDataSource.WEATHER_COM : ("weather-com id", "Weather.com"),
+ WeatherDataSource.YAHOO : ("yahoo id", "Yahoo")
}
+ # gather existing source keys
for source_id in SOURCES.keys():
if SOURCES[source_id][0] in self.location_details:
loc_ids[source_id] = SOURCES[source_id][0]
@@ -423,158 +495,145 @@
if source in loc_ids:
valid_source = source
log.debug(("Location: default weather source '%s' " +
- "chosen for '%s'") % (SOURCES[valid_source][1],
- self.location_details['label']))
+ "chosen for '%s'") % (SOURCES[valid_source][1],
+ self.location_details['label']))
# try with the first alternative
elif len(loc_ids.keys()):
valid_source = loc_ids.keys()[0]
- log.debug(("Location: non default weather source '%s' " +
- "chosen for '%s'") % (SOURCES[valid_source][1],
- self.location_details['label']))
if valid_source is None:
log.error(("Location: no valid weather source can be " +
- "chosen for '%s'") % (
- self.location_details['label']))
+ "chosen for '%s'") % (
+ self.location_details['label']))
self.weather = None
else:
+ log.debug(("Location: non default weather source '%s' " +
+ "chosen for '%s'") % (SOURCES[valid_source][1],
+ self.location_details['label']))
self.weather = Weather(
self.location_details[loc_ids[valid_source]],
valid_source, self.metric_system, self.wind_unit,
+ self.heat_index, self.chill_index,
self.location_details['latitude'],
self.location_details['longitude'])
+
class Forecast:
""" Class to get forecast information """
- # Initialize a class with metric system, wind units, location and current user locale
- def __init__ (self, units, lat, lon, locale):
+ def __init__ (self, units, location_id, locale):
+ """Initialize a class with metric system, wind units,
+ location code and current user locale
+
+ """
self.metric_system = units
- self.lat = self.convert_coordinate_for_google(lat)
- self.lon = self.convert_coordinate_for_google(lon)
+ self.location_id = location_id
self.locale = locale
- # Convert coordinate for google
- def convert_coordinate_for_google(self, value):
- value = float(value) * 1e6
- return int(round(value))
-
- # Get and store forecast data. For now using Google only
def prepare_forecast_data(self):
+ """ Get and store forecast data. For now using Weather.com only. """
+ # TODO: Implement for NOAA
self.daysofweek = []
self.icons = []
self.conditions = []
self.error_message = None
try:
- # Generate a fake location by current coordinates
- location_name = ",,,%s,%s" % (self.lat, self.lon)
- self.forecast = pywapi.get_weather_from_google (location_name, hl = self.locale)
- self.unitsystem = self.forecast['forecast_information']['unit_system']
+ log.debug("Forecast: units set to %s" % self.metric_system)
+ # Check units, default to imperial
+ if self.metric_system == UnitSystem.SI:
+ self.unitsystem = 'metric'
+ elif self.metric_system == UnitSystem.IMPERIAL:
+ self.unitsystem = 'imperial'
+ else:
+ self.unitsystem = 'imperial'
+
+ self.forecast = pywapi.get_weather_from_weather_com(self.location_id, self.unitsystem)
for forecast in self.forecast['forecasts']:
self.daysofweek.append(forecast["day_of_week"])
- forecast_icon = str(forecast["icon"])
- if "/ig/images/weather/" in forecast_icon:
- self.icons.append(forecast_icon.split("/ig/images/weather/")[-1].split(".gif")[0])
- elif "http://g0.gstatic.com/images/icons/onebox" in forecast_icon:
- self.icons.append(forecast_icon.split("http://g0.gstatic.com/images/icons/onebox/weather_")[-1].split("-40.gif")[0])
- self.conditions.append(forecast["condition"])
+
+ # Yahoo forecast icon URL is "http://l.yimg.com/a/i/us/we/52/.gif"
+ self.icons.append(forecast["day"]["icon"])
+ self.conditions.append(forecast["day"]["brief_text"])
+
self.error_message = None
except urllib2.URLError:
- log.error("Forecast: error reading forecast for %s" % location_name)
+ log.error("Forecast: error reading forecast for %s" % self.location_id)
except KeyError:
- log.error("Forecast: returned empty forecast %s" % location_name)
+ log.error("Forecast: returned empty forecast for %s" % self.location_id)
self.error_message = _('Unknown error occurred while picking up weather data')
- # Parse high values for forecast data
def get_forecast_data(self):
+ """ Parse high and low values for forecast data """
self.highdata = []
self.lowdata = []
- if not hasattr(self, 'unitsystem'):
- return None
-
- if ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.SI)) or ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.IMPERIAL)):
- #correct scale selected
- for forecast in self.forecast['forecasts']:
- self.highdata.append(forecast["high"])
- self.lowdata.append(forecast["low"])
-
- elif ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.IMPERIAL)):
- #convert from SI to imperial
- for forecast in self.forecast['forecasts']:
- self.highdata.append(int(((int(forecast["high"])*9)/5)+32))
- self.lowdata.append(int(((int(forecast["low"])*9)/5)+32))
-
- elif ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.SI)):
- #convert from imperial to SI
- for forecast in self.forecast['forecasts']:
- self.highdata.append(int((((int(forecast["high"]))-32)*5)/9))
- self.lowdata.append(int((((int(forecast["low"]))-32)*5)/9))
+ # Since we are now using Weather.com, forecast will always be in correct units
+ for forecast in self.forecast['forecasts']:
+ self.highdata.append(forecast["high"])
+ self.lowdata.append(forecast["low"])
return (self.highdata, self.lowdata)
- # Parse a list of days of week with forecast data
def get_forecast_daysofweek(self):
+ """ Parse a list of days of week with forecast data """
return self.daysofweek
- # Parse icons for forecast data
def get_forecast_icons(self):
+ """ Parse icons for forecast data """
return self.icons
- # Parse conditions for forecast data
def get_forecast_conditions(self):
+ """ Parse conditions for forecast data """
return self.conditions
class Weather:
- """
- Data object to parse weather data with unit convertion
- """
-
- #Available conditions by google icon
- #Format: Google icon name: (day icon, night icon, is a severe weather condition)
- #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/
- _GoogleConditions = {
- "sunny" : ( "weather-clear", "weather-clear-night", False),
- "mostly_sunny" : ( "weather-clear", "weather-clear-night", False),
- "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
- "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
- "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
- "cloudy" : ( "weather-clouds", "weather-clouds-night", False),
- "mostlycloudy" : ( "weather-overcast", "weather-overcast", False),
- "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False),
- "overcast" : ( "weather-overcast", "weather-overcast", False),
- "rain" : ( "weather-showers", "weather-showers", False),
- "chanceofrain" : ( "weather-showers", "weather-showers", False),
- "chance_of_rain" : ( "weather-showers", "weather-showers", False),
- "heavyrain" : ( "weather-showers", "weather-showers", False),
- "drizzle" : ( "weather-showers", "weather-showers", False),
- "sleet" : ( "weather-snow", "weather-snow", False),
- "rain_snow" : ( "weather-snow", "weather-snow", False),
- "rainsnow" : ( "weather-snow", "weather-snow", False),
- "snow" : ( "weather-snow", "weather-snow", False),
- "chanceofsnow" : ( "weather-snow", "weather-snow", False),
- "chance_of_snow" : ( "weather-snow", "weather-snow", False),
- "heavysnow" : ( "weather-snow", "weather-snow", False),
- "icy" : ( "weather-snow", "weather-snow", False),
- "snowflurries" : ( "weather-snow", "weather-snow", False),
- "flurries" : ( "weather-snow", "weather-snow", False),
- "dust" : ( "weather-fog", "weather-fog", False),
- "fog" : ( "weather-fog", "weather-fog", False),
- "smoke" : ( "weather-fog", "weather-fog", False),
- "haze" : ( "weather-fog", "weather-fog", False),
- "mist" : ( "weather-fog", "weather-fog", False),
- "thunderstorm" : ( "weather-storm", "weather-storm", True),
- "chance_of_storm" : ( "weather-storm", "weather-storm", True),
- "thunderstorms" : ( "weather-storm", "weather-storm", True),
- "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True),
- "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True),
- }
-
- #Available conditions by yahoo condition code
- #Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name)
+ """Data object to parse weather data with unit conversion """
+
+## #Available conditions by google icon
+## #Format: Google icon name: (day icon, night icon, is a severe weather condition)
+## #Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/
+## _GoogleConditions = {
+## "sunny" : ( "weather-clear", "weather-clear-night", False),
+## "mostly_sunny" : ( "weather-clear", "weather-clear-night", False),
+## "partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
+## "partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
+## "windy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
+## "cloudy" : ( "weather-clouds", "weather-clouds-night", False),
+## "mostlycloudy" : ( "weather-overcast", "weather-overcast", False),
+## "mostly_cloudy" : ( "weather-overcast", "weather-overcast", False),
+## "overcast" : ( "weather-overcast", "weather-overcast", False),
+## "rain" : ( "weather-showers", "weather-showers", False),
+## "chanceofrain" : ( "weather-showers", "weather-showers", False),
+## "chance_of_rain" : ( "weather-showers", "weather-showers", False),
+## "heavyrain" : ( "weather-showers", "weather-showers", False),
+## "drizzle" : ( "weather-showers", "weather-showers", False),
+## "sleet" : ( "weather-snow", "weather-snow", False),
+## "rain_snow" : ( "weather-snow", "weather-snow", False),
+## "rainsnow" : ( "weather-snow", "weather-snow", False),
+## "snow" : ( "weather-snow", "weather-snow", False),
+## "chanceofsnow" : ( "weather-snow", "weather-snow", False),
+## "chance_of_snow" : ( "weather-snow", "weather-snow", False),
+## "heavysnow" : ( "weather-snow", "weather-snow", False),
+## "icy" : ( "weather-snow", "weather-snow", False),
+## "snowflurries" : ( "weather-snow", "weather-snow", False),
+## "flurries" : ( "weather-snow", "weather-snow", False),
+## "dust" : ( "weather-fog", "weather-fog", False),
+## "fog" : ( "weather-fog", "weather-fog", False),
+## "smoke" : ( "weather-fog", "weather-fog", False),
+## "haze" : ( "weather-fog", "weather-fog", False),
+## "mist" : ( "weather-fog", "weather-fog", False),
+## "thunderstorm" : ( "weather-storm", "weather-storm", True),
+## "chance_of_storm" : ( "weather-storm", "weather-storm", True),
+## "thunderstorms" : ( "weather-storm", "weather-storm", True),
+## "scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True),
+## "scatteredthunderstorms" : ( "weather-storm", "weather-storm", True),
+## }
+
+ # Available conditions by yahoo condition code
+ # Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name)
_YahooConditions = {
'0' : ("weather-storm", "weather-storm", True, _("Tornado")),
'1' : ("weather-storm", "weather-storm", True, _("Tropical storm")),
@@ -628,55 +687,60 @@
'3200': (False, False, False, _("Unknown condition"))
}
- # Initialize and get fresh data
- def __init__(self, location_id, weather_datasource, metric_system, wind_unit, lat, lon):
+ # Available conditions by Weather.com condition code; same as Yahoo
+ _WeathercomConditions = _YahooConditions
+
+ def __init__(self, location_id, weather_datasource, metric_system,
+ wind_unit, heat_index, chill_index, lat, lon):
+ """ Initialize and get fresh weather data """
self.__weather_datasource = weather_datasource
self.__metric_system = metric_system
self._wind_unit = wind_unit
self.__current_condition = None
+ self.__heat_index = heat_index
+ self.__chill_index = chill_index
self.__lat = lat
self.__lon = lon
- # Get data from Google
- if self.__weather_datasource == WeatherDataSource.GOOGLE:
- # Get data in english locale, then - switch back
- self.__report = pywapi.get_weather_from_google (location_id, hl = 'en')
- # Get data in original locale for condition name
- self.__localized_report = pywapi.get_weather_from_google (location_id, hl = locale_name)
-
+ if self.__metric_system == UnitSystem.SI:
+ unit_system = 'metric'
+ elif self.__metric_system == UnitSystem.IMPERIAL:
+ unit_system = 'imperial'
+ else:
+ unit_system = 'imperial'
+
+ # Get data from Weather.com
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ self.__report = pywapi.get_weather_from_weather_com(
+ location_id, unit_system)
+ ##self.__localized_report = self.__report
+ log.debug("Weather: checking Weather.com report for "
+ "weather condition and icon name")
if 'current_conditions' not in self.__report.keys():
- log.error("Weather: could not get Google weather condition from report")
+ icon_name = ""
+ log.error("Weather: could not get Weather.com "
+ "weather condition from report")
log.error("Weather: got data '%s'" % str(self.__report))
self.__current_condition = (False, False, False, _("Unknown condition"))
- if 'current_conditions' not in self.__localized_report.keys():
- log.error("Weather: could not get Google weather condition from localized report")
- log.error("Weather: got data '%s'" % str(self.__localized_report))
- self.__current_condition = (False, False, False, _("Unknown condition"))
+ elif 'icon' in self.__report['current_conditions'].keys():
+ icon_name = self.__report['current_conditions']['icon']
+ self.__current_condition = self._WeathercomConditions.get(icon_name)
- if 'icon' in self.__report['current_conditions'].keys():
- icon_path = self.__report['current_conditions']['icon']
- if '/ig/images/weather/' in icon_path:
- icon_name = icon_path.replace('/ig/images/weather/', '').replace('.gif', '')
- elif 'http://g0.gstatic.com/images/icons/onebox' in icon_path:
- icon_name = icon_path.replace('http://g0.gstatic.com/images/icons/onebox/weather_', '').replace('-40.gif', '')
- else:
- icon_name = icon_path
else:
- log.error("Weather: could not get weather icon from report")
+ icon_name = ""
+ log.error("Weather: could not get icon name from Weather.com report")
log.error("Weather: got data '%s'" % str(self.__report['current_conditions']))
- icon_name = ""
-
- self.__current_condition = self._GoogleConditions.get(icon_name)
- if self.__current_condition == None:
- log.error("ExtendedForecast: unknown Google weather condition '%s'" % icon_name)
self.__current_condition = (False, False, False, _("Unknown condition"))
-
+
# Get data from Yahoo
if self.__weather_datasource == WeatherDataSource.YAHOO:
- self.__report = pywapi.get_weather_from_yahoo (location_id, 'imperial')
- self.__localized_report = self.__report
+ self.__report = pywapi.get_weather_from_yahoo(location_id, unit_system)
+ ##self.__localized_report = self.__report
+ log.debug("Weather: checking Yahoo report for "
+ "weather condition and icon name")
if 'condition' not in self.__report.keys():
+ icon_name = ""
log.error("Weather: could not get Yahoo weather condition from report")
log.error("Weather: got data '%s'" % str(self.__report))
self.__current_condition = (False, False, False, _("Unknown condition"))
@@ -695,8 +759,8 @@
#Prepare sunrise/sunset data
self.get_sun_data()
- #Get sunrise/sunset times, calculate whether it is night already
def get_sun_data(self):
+ """ Get sunrise/sunset times and calculate whether it is night already """
self.__night = False
self.__sunrise_t = None
self.__sunset_t = None
@@ -714,7 +778,7 @@
"dst")[0].firstChild.nodeValue
# strip timezone info
localtime = datetime.datetime.strptime(localtime.rsplit(' ',1)[0],
- '%Y-%m-%d %H:%M:%S')
+ '%Y-%m-%d %H:%M:%S')
dst = 1 if dst == "True" else 0
except urllib2.URLError:
@@ -725,6 +789,7 @@
url = 'http://www.earthtools.org/sun/%s/%s/%s/%s/99/%s' % \
(self.__lat, self.__lon, localtime.day, localtime.month, dst)
try:
+ log.debug("Weather: get_sun_data: getting sunrise/sunset data")
f = urllib2.urlopen(url)
s=f.read()
parsed = parseString(s)
@@ -742,20 +807,20 @@
else:
self.__night = False
log.debug("Weather: got localtime " +
- "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % (
- localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night))
+ "%s, dst %s, sunrise '%s', sunset '%s', night = %s" % (
+ localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night))
- # Return True, if weather condition is severe
def condition_is_severe(self):
+ """ Return True if weather condition is severe """
if self.__current_condition != None:
log.debug("Weather: got severe condition '%s'" % self.__current_condition[2])
return self.__current_condition[2]
else:
log.error("Weather: condition is not set while condition severity check")
- return False;
+ return False
- # Get associated icon name
def get_icon_name(self):
+ """ Get icon name associated with current condition """
if self.__current_condition != None:
if self.__night:
log.debug("Weather: night, show '%s' icon" % self.__current_condition[1])
@@ -767,217 +832,440 @@
log.error("Weather: return 'offline' icon due to empty condition")
return False
- # Get condition text
def get_condition_label(self):
- if self.__weather_datasource == WeatherDataSource.GOOGLE:
- if 'condition' in self.__localized_report['current_conditions'].keys():
- condition = self.__localized_report['current_conditions']['condition']
- else:
- condition = _("Unknown condition")
+ """ Get text of current condition """
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ condition = self.__report['current_conditions']['text']
if self.__weather_datasource == WeatherDataSource.YAHOO:
condition = self.__current_condition[3]
return condition
- # Get humidity label
def get_humidity_label(self):
+ """ Get text string for current humidity """
humidity = "%s: ---%%" % (_("Humidity"))
- if self.__weather_datasource == WeatherDataSource.GOOGLE \
- and 'humidity' in self.__localized_report['current_conditions']:
- humidity = self.__localized_report['current_conditions']['humidity']
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM \
+ and 'humidity' in self.__report['current_conditions']:
+ humidity = "%s: %s%%" % (_("Humidity"), self.__report['current_conditions']['humidity'])
if self.__weather_datasource == WeatherDataSource.YAHOO \
- and 'humidity' in self.__localized_report['atmosphere']:
- humidity = "%s: %s%%" % (_("Humidity"), self.__localized_report['atmosphere']['humidity'])
+ and 'humidity' in self.__report['atmosphere']:
+ humidity = "%s: %s%%" % (_("Humidity"), self.__report['atmosphere']['humidity'])
return humidity
- # Get dew point - using in humidex calculation
- #TODO: Update with NOAA
def get_dew_point_label(self):
- if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:
- # Not returned by Google and Yahoo
+ """ Get dew point, which is used in humidex calculation """
+ #TODO: Update with NOAA
+ _value = "---"
+ _unit = ""
+ if self.__weather_datasource == WeatherDataSource.YAHOO:
+ # Not returned by Yahoo
return None
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ _value = self.__report['current_conditions']['dewpoint']
+ _unit = self.__report['units']['temperature']
+ return u"%s: %s °%s" % (_("Dewpoint"), _value, _unit)
- # Get pressure label
def get_pressure_label(self):
- if self.__weather_datasource == WeatherDataSource.GOOGLE:
- # TODO: Empty for Google, use NOAA data?
- value = "---"
- unit = ""
+ """ Get text string for current air pressure """
+ _value = "---"
+ _unit = ""
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM \
+ and 'barometer' in self.__report['current_conditions'].keys() \
+ and 'pressure' in self.__report['units'].keys():
+ _value = self.__report['current_conditions']['barometer']['reading']
+ _unit = self.__report['units']['pressure']
if self.__weather_datasource == WeatherDataSource.YAHOO \
- and 'pressure' in self.__localized_report['atmosphere'].keys() \
- and 'pressure' in self.__localized_report['units'].keys():
- value = self.__localized_report['atmosphere']['pressure']
- unit = self.__localized_report['units']['pressure']
- return "%s: %s %s" % (_("Pressure"), value, units)
+ and 'pressure' in self.__report['atmosphere'].keys() \
+ and 'pressure' in self.__report['units'].keys():
+ _value = self.__report['atmosphere']['pressure']
+ _unit = self.__report['units']['pressure']
+ return "%s: %s %s" % (_("Pressure"), _value, _unit)
- # Get temperature with units value - doesn't include 'Temperature' label
- def get_temperature(self, needs_rounding = False):
- _value = "---"
+## def get_temperature(self, needs_rounding = False):
+ def get_temperature(self):
+ """ Get temperature value and units string """
+ _value = None
_unit = ""
- if self.__weather_datasource == WeatherDataSource.GOOGLE:
- if (self.__metric_system == MetricSystem.SI) \
- and 'temp_c' in self.__report['current_conditions'].keys():
- _value = self.__report['current_conditions']['temp_c']
- _unit = "˚C"
- elif 'temp_f' in self.__report['current_conditions'].keys():
- _value = self.__report['current_conditions']['temp_f']
- _unit = "˚F"
- if self.__weather_datasource == WeatherDataSource.YAHOO:
- if (self.__metric_system == MetricSystem.SI) \
- and 'temp' in self.__report['condition'].keys():
- _value = NumberFormatter.format_float(
- ((float(self.__report['condition']['temp']) - 32) * 5/9), 1)
- _unit = "˚C"
- else:
+ if (self.__weather_datasource == WeatherDataSource.WEATHER_COM and
+ 'temperature' in self.__report['current_conditions'].keys() and
+ 'temperature' in self.__report['units'].keys()):
+ if ((self.__metric_system == UnitSystem.SI and
+ self.__report['units']['temperature'] == u"C") or
+ (self.__metric_system == UnitSystem.IMPERIAL and
+ self.__report['units']['temperature' ] == u"F")):
+ _value = self.__report['current_conditions']['temperature']
+ _unit = u"°%s" % self.__report['units']['temperature']
+ if (self.__weather_datasource == WeatherDataSource.YAHOO and
+ 'temp' in self.__report['condition'].keys() and
+ 'temperature' in self.__report['units'].keys()):
+ if ((self.__metric_system == UnitSystem.SI and
+ self.__report['units']['temperature'] == u"C") or
+ (self.__metric_system == UnitSystem.IMPERIAL and
+ self.__report['units']['temperature'] == u"F")):
_value = self.__report['condition']['temp']
- _unit = "˚F"
- # round the value if required
- if needs_rounding and _value != "---":
- _value = NumberFormatter.format_float(locale.atof(_value), 0)
- return ("%s %s" % (_value, _unit))
-
- # Get temperature label
+ _unit = u"°%s" % self.__report['units']['temperature']
+## # round the value if required
+## if needs_rounding and _value != "---":
+## _value = NumberFormatter.format_float(locale.atof(_value), 0)
+ return (_value, _unit)
+
+ def get_temperature_string(self):
+ """ Get temperature with units value - doesn't include 'Temperature' string """
+ (_value, _unit) = self.get_temperature()
+ if _value is None:
+ _value = "---"
+ _unit = ""
+ return (u"%s %s" % (_value, _unit))
+
def get_temperature_label(self):
- return "%s: %s" % (_("Temperature"), self.get_temperature())
-
- # Get humidex parameter
- def get_humidex_label(self):
- if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:
- #Empty for Yahoo and Google
- return None
- #TODO: Update with NOAA data
- #dewPoint=2
- #temp_c = 1
- #self.vapour_pressure = 6.11 * math.exp(5417.7530 * ( (1/273.16) - (1/(dewPoint+273.16))))
- #self.humidex = temp_c + (0.5555)*(self.vapour_pressure - 10.0);
- #return ("%s: %.1f" % (_("Humidex"), self.humidex)).replace(".0", "")
-
- # Get wind label
+ """ Get text string for current temperature label """
+ return "%s: %s" % (_("Temperature"), self.get_temperature_string())
+
+ def get_relative_string(self):
+ """ Get relative temperature with units value - doesn't include 'Feels Like' string """
+ # try relative heat
+ if self.__heat_index == RelativeFormula.HUMIDEX:
+ (_value, _unit) = self.get_humidex()
+ if self.__heat_index == RelativeFormula.HEATINDEX:
+ (_value, _unit) = self.get_heat_index()
+ if _value is not None:
+ return (u"%s %s" % (_value, _unit))
+ # try relative chill
+ if self.__chill_index == RelativeFormula.WINDCHILL:
+ (_value, _unit) = self.get_wind_chill()
+ if self.__chill_index == RelativeFormula.APPARENT:
+ (_value, _unit) = self.get_apparent_temp()
+ if _value is not None:
+ return (u"%s %s" % (_value, _unit))
+ # use current temperature
+ return self.get_temperature_string()
+
+ def get_relative_label(self):
+ """ Get text string for relative temperature ("feels like") label """
+ return "%s: %s" % (_("Feels Like"), self.get_relative_string())
+
+ def get_humidex(self):
+ """ Calculate humidex and get value and units
+
+ The standard Humidex formula used by Environment Canada is:
+
+ humidex = (air temperature) + h
+
+ h = (0.5555)*(e - 10.0);
+ e = vapour pressure in hPa (mbar), given by:
+ e = 6.11 * exp [5417.7530 * ( (1/273.16) - (1/dewpoint) ) ]
+ where dewpoint is expressed in Kelvins
+ (temperature in K = temperature in °C + 273.1)
+ and 5417.7530 is a rounded constant based on the molecular weight
+ of water, latent heat of evaporation, and the universal gas constant.
+
+ """
+ #TODO: Update with NOAA data
+ if self.__weather_datasource == WeatherDataSource.YAHOO:
+ # Empty for Yahoo
+ return (None, "")
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ if self.__report['units']['temperature'] == "F":
+ # Humidex is calculated in C
+ dew_point_f = float(self.__report['current_conditions']['dewpoint'])
+ temp_f = float(self.__report['current_conditions']['temperature'])
+ dew_point_c = ((dew_point_f - 32.0) * 5.0/9.0)
+ temp_c = ((temp_f - 32.0) * 5.0/9.0)
+ else:
+ dew_point_c = float(self.__report['current_conditions']['dewpoint'])
+ temp_c = float(self.__report['current_conditions']['temperature'])
+ vapour_pressure = 6.11 * exp(5417.7530 * ( (1/273.16) - (1/(dew_point_c+273.16))))
+ humidex = temp_c + (0.5555)*(vapour_pressure - 10.0)
+ # Humidex is meaningless if it is lower than temperature
+ if humidex < temp_c:
+ #return (u"%s: N/A" % _("Humidex"))
+ return (None, "")
+ if self.__report['units']['temperature'] == "F":
+ humidex_f = (humidex*9.0/5.0) + 32.0
+ # Humidex is unitless (represents Celsius) so in F show units
+ #return (u"%s: %s°F" % (_("Humidex"), NumberFormatter.format_float(humidex_f, 1))).replace(".0", "")
+ #return (NumberFormatter.format_float(humidex_f, 1).replace(".0", ""), u"°F")
+ return (int(round(humidex_f)), u"°F")
+ else:
+ #return ("%s: %.1f" % (_("Humidex"), humidex)).replace(".0", "")
+ #return (u"%s: %s" % (_("Humidex"), NumberFormatter.format_float(humidex, 1))).replace(".0", "")
+ #return (NumberFormatter.format_float(humidex, 1).replace(".0", ""), u"°C")
+ return (int(round(humidex)), u"°C")
+
+ def get_heat_index(self):
+ """ Calculate heat index and get value and units
+
+ The formula below approximates the heat index in degrees
+ Fahrenheit, to within ±1.3 °F. It is the result of a
+ multivariate fit (temperature equal to or greater than
+ 80°F and relative humidity equal to or greater than 40%)
+ to a model of the human body.
+
+ Heat Index = c_1 + (c_2 * T) + (c_3 * R) + (c_4 * T * R) +
+ (c_5 * T^2) + (c_6 * R^2) + (c_7 * T^2 * R) +
+ (c_8 * T * R^2) + (c_9 * T^2 * R^2)
+ where:
+ T = ambient dry-bulb temperature (in degrees Fahrenheit)
+ R = relative humidity (percentage value between 0 and 100)
+
+ """
+ #TODO: Update with NOAA data
+ _value = None
+
+ if self.__weather_datasource == WeatherDataSource.YAHOO:
+ log.debug("Weather: get_heat_index: weather_datasource is Yahoo, "
+ "checking temp and humidity")
+ if self.__report['units']['temperature'] == "C":
+ units = 'metric'
+ elif self.__report['units']['temperature'] == "F": # else here?
+ units = 'imperial'
+ T = float(self.__report['condition']['temp'])
+ R = float(self.__report['atmosphere']['humidity'])
+ _value = pywapi.heat_index(T, R, units)
+
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ log.debug("Weather: get_heat_index: weather_datasource is "
+ "Weather.com, checking temp and humidity")
+ if self.__report['units']['temperature'] == "C":
+ units = 'metric'
+ elif self.__report['units']['temperature'] == "F": # else here?
+ units = 'imperial'
+ T = float(self.__report['current_conditions']['temperature'])
+ R = float(self.__report['current_conditions']['humidity'])
+ _value = pywapi.heat_index(T, R, units)
+
+ # Heat Index is only valid for temp >= 80°F and humidity >= 40%)
+ if _value is None:
+ return (_value, "")
+ if units == 'metric':
+ #return (u"%s: %s°C" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "")
+ #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°C")
+ return (int(round(_value)), u"°C")
+
+ elif units == 'imperial': # else here?
+ #return (u"%s: %s°F" % (_("Heat Index"), NumberFormatter.format_float(heat_index, 1))).replace(".0", "")
+ #return (NumberFormatter.format_float(_value, 1).replace(".0", ""), u"°F")
+ return (int(round(_value)), u"°F")
+
+ def get_wind_chill(self):
+ """ Calculate wind chill index and get text string for label
+
+ The standard Wind Chill formula used by Environment Canada,
+ the 2001 JAG/TI Wind Chill Equivalent Temperature Index, is:
+
+ T_wc = 13.12 + 0.6215 * T_a -
+ 11.37 * V^0.16 +
+ 0.3965 * T_a * V^0.16
+ where:
+ T_wc is the wind chill index based on Celsius
+ T_a is the air temperature in °C
+ V is the wind speed in km/h, at 10 m (standard anemometer height)
+
+ The equivalent formula in US customary units is:
+
+ T_wc = 35.74 + 0.6215 * T_a -
+ 35.75 * V^0.16 +
+ 0.4275 * T_a * V^0.16
+
+ where:
+ T_wc is the wind chill index based on Fahrenheit
+ T_a is the air temperature °F
+ V is the wind speed in mph
+
+ Windchill temperature is defined only for temperatures at or
+ below 10 °C (50 °F) and wind speeds above 4.8 kilometres per
+ hour (3.0 mph).
+
+ """
+ #TODO: Update with NOAA data
+ _value = None
+
+ if self.__report['units']['temperature'] == "C":
+ units = 'metric'
+ elif self.__report['units']['temperature'] == "F": # else here?
+ units = 'imperial'
+
+ if self.__weather_datasource == WeatherDataSource.YAHOO:
+ log.debug("Weather: get_wind_chill: weather_datasource is "
+ "Yahoo, checking temp and humidity")
+ T_a = float(self.__report['condition']['temp'])
+ V = float(self.__report['wind']['speed'])
+
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ log.debug("Weather: get_wind_chill: weather_datasource is "
+ "Weather.com, checking temp and humidity")
+ T_a = float(self.__report['current_conditions']['temperature'])
+ wind_speed = self.__report['current_conditions']['wind']['speed']
+ V = (0.0 if wind_speed == 'calm' else float(wind_speed))
+
+ # move below to pywapi
+ if units == 'metric':
+ _value = 13.12 + 0.6215 * T_a - 11.37 * pow(V, 0.16) + 0.3965 * T_a * pow(V, 0.16)
+ return (int(round(_value)), u"°C")
+ elif units == 'imperial':
+ _value = 35.74 + 0.6215 * T_a - 35.75 * pow(V, 0.16) + 0.4275 * T_a * pow(V, 0.16)
+ return (int(round(_value)), u"°F")
+
+ if _value is None:
+ return (_value, "")
+
+ def get_apparent_temp(self):
+ """ Calculate Australian apparent temperature and get text string for label
+
+ The standard formula for cooler temperatures used by the Australian
+ Bureau of Meteorology, the Australian Apparent Temperature, is:
+
+ AT = T_a + 0.33e - 0.70ws - 4.00
+
+ Where:
+ T_a = Dry bulb temperature (°C)
+ e = Water vapour pressure (hPa)
+ ws = Wind speed (m/s) at an elevation of 10 meters
+
+ The vapour pressure can be calculated from the temperature and
+ relative humidity using the equation:
+
+ e = (rh / 100) * 6.105 * exp^[(17.27 * T_a) / (237.7 + T_a)]
+
+ Where:
+ T_a = Dry bulb temperature (°C)
+ rh = Relative humidity [%]
+ exp^ represents the exponential function
+
+ The Australian chill formula is only defined for temperatures
+ at or below 20°C (68°F) and wind speeds above 4.8 km/h (3.0 mph).
+
+ """
+ #TODO: Update with NOAA data
+ _value = None
+
+ if self.__report['units']['temperature'] == "C":
+ units = 'metric'
+ elif self.__report['units']['temperature'] == "F": # else here?
+ units = 'imperial'
+
+ if self.__weather_datasource == WeatherDataSource.YAHOO:
+ log.debug("Weather: get_wind_chill: weather_datasource is "
+ "Yahoo, checking temp and humidity")
+ T_a = float(self.__report['condition']['temp'])
+ rh = float(self.__report['atmosphere']['humidity'])
+ ws = float(self.__report['wind']['speed'])
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ log.debug("Weather: get_wind_chill: weather_datasource is "
+ "Weather.com, checking temp and humidity")
+ T_a = float(self.__report['current_conditions']['temperature'])
+ rh = float(self.__report['current_conditions']['humidity'])
+ ws = float(self.__report['current_conditions']['wind']['speed'])
+
+ # calculated in °C, so need to convert from °F
+ if units == 'imperial':
+ T_a = ((T_a - 32.0) * 5.0/9.0)
+
+ e = (rh / 100) * 6.105 * exp((17.27 * T_a) / (237.7 + T_a))
+ _value = T_a + 0.33 * e - 0.70 * ws - 4.00
+
+ # Now convert back to °F
+ if units == 'imperial':
+ _value = (_value*9.0/5.0) + 32.0
+ return (int(round(_value)), u"°F")
+ if units == 'metric':
+ return (int(round(_value)), u"°C")
+
+ if _value is None:
+ return (_value, "")
+
def get_wind_label(self):
- if self.__weather_datasource == WeatherDataSource.GOOGLE:
- # Convert units picked up from Google and replace units with currently configured
- if 'wind_condition' in self.__localized_report['current_conditions'].keys():
- localized_wind_info = self.__localized_report['current_conditions']['wind_condition'].split(' ')
- wind_direction = localized_wind_info[1]
- wind_info = self.__report['current_conditions']['wind_condition'].split(' ')
- wind_speed = wind_info[3]
- else:
- return _("Unknown")
-
+ """ Get text string for current wind speed and direction """
+ if self.__weather_datasource == WeatherDataSource.WEATHER_COM:
+ # Create a wind_info structure from Weather.com data
+ wind_direction = u"%s (%s°)" % (self.__report['current_conditions']['wind']['text'],
+ self.__report['current_conditions']['wind']['direction'])
+ wind_speed = self.__report['current_conditions']['wind']['speed']
+ wind_units = self.__report['units']['speed']
+ if wind_speed == "calm":
+ wind_speed = 0
+ ##wind_direction = wind_direction.replace("CALM","Calm")
+ wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
if self.__weather_datasource == WeatherDataSource.YAHOO:
- # Create a similar to Google wind_info structure from Yahoo data
- wind_direction = "%s (%s˚)" % (self.get_wind_direction(self.__localized_report['wind']['direction']), self.__localized_report['wind']['direction'])
- wind_speed = self.__localized_report['wind']['speed']
- wind_units = self.__localized_report['units']['speed']
- localized_wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
-
+ # Create a wind_info structure from Yahoo data
+ wind_direction = u"%s (%s°)" % (_(pywapi.get_wind_direction(self.__report['wind']['direction'])), self.__report['wind']['direction'])
+ wind_speed = self.__report['wind']['speed']
+ wind_units = self.__report['units']['speed']
+ wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
try:
_value = float(wind_speed)
except ValueError as e:
log.error("Could not parse '%s' as wind speed." % str(wind_speed))
_value = -1.0
-
+
# Parse Wind_direction - convert to selected scale
- if (self._wind_unit == WindUnits.MPH):
- _unit = __("mph", "mph", _value)
- if (self._wind_unit == WindUnits.MPS):
- _value *= 0.44704
- _unit = __("m/s", "m/s", _value)
- if (self._wind_unit == WindUnits.BEAUFORT):
- if _value >= 0.0:
- _value = self.get_beaufort_from_mph(_value)
- _unit = ""
- if (self._wind_unit == WindUnits.KPH):
- _value *= 1.609344
- _unit = __("km/h", "km/h", _value)
- if (self._wind_unit == WindUnits.KNOTS):
- _value *= 0.868976241900648
- _unit = __("knot", "knots", _value)
-
+ if wind_units == "mph":
+ if (self._wind_unit == WindUnits.MPH):
+ _unit = __("mph", "mph", _value)
+ if (self._wind_unit == WindUnits.MPS):
+ _value *= 0.44704
+ _unit = __("m/s", "m/s", _value)
+ if (self._wind_unit == WindUnits.BEAUFORT):
+ if _value >= 0.0:
+ _value = pywapi.wind_beaufort_scale(_value, WindUnits.MPH)
+ _unit = ""
+ if (self._wind_unit == WindUnits.KPH):
+ _value *= 1.609344
+ _unit = __("km/h", "km/h", _value)
+ if (self._wind_unit == WindUnits.KNOTS):
+ _value *= 0.868976241900648
+ _unit = __("knot", "knots", _value)
+ elif wind_units == "km/h":
+ if (self._wind_unit == WindUnits.MPH):
+ _value *= 0.621371
+ _unit = __("mph", "mph", _value)
+ if (self._wind_unit == WindUnits.MPS):
+ _value *= 0.277778
+ _unit = __("m/s", "m/s", _value)
+ if (self._wind_unit == WindUnits.BEAUFORT):
+ if _value >= 0.0:
+ _value = pywapi.wind_beaufort_scale(_value, WindUnits.KPH)
+ _unit = ""
+ if (self._wind_unit == WindUnits.KPH):
+ _unit = __("km/h", "km/h", _value)
+ if (self._wind_unit == WindUnits.KNOTS):
+ _value *= 0.539957
+ _unit = __("knot", "knots", _value)
+ else:
+ log.error("Could not parse '%s' as wind units." % wind_units)
+ _value = -1.0
+
# Join wind_info data in a label
- localized_wind_info[len(localized_wind_info)-1] = _unit
- localized_wind_info[len(localized_wind_info)-2] = \
+ wind_info[len(wind_info)-1] = _unit
+ wind_info[len(wind_info)-2] = \
NumberFormatter.format_float(_value, 1)
if _value < 0.0:
- localized_wind_info[1:] = ["", "N\A", ""]
- return "%s %s %s %s" % (localized_wind_info[0], localized_wind_info[1], \
- localized_wind_info[2], localized_wind_info[3])
+ wind_info[1:] = ["", "N\A", ""]
+ if _value == 0.0:
+ wind_info[1:] = [_("Calm"), "", ""]
+ return "%s %s %s %s" % (wind_info[0], wind_info[1], \
+ wind_info[2], wind_info[3])
- # Get sunrise label
def get_sunrise_label(self):
+ """ Get text string for sunrise time """
return "%s: %s" % (_("Sunrise"), TimeFormatter.format_time(self.__sunrise_t))
- # Get sunset label
def get_sunset_label(self):
+ """ Get text string for sunset time """
return "%s: %s" % (_("Sunset"), TimeFormatter.format_time(self.__sunset_t))
- # Additional functions
- # Convert wind direction from degrees to localized direction
- def get_wind_direction(self, degrees):
- try:
- degrees = int(degrees)
- except ValueError:
- return ''
-
- if degrees < 23 or degrees >= 338:
- #Short wind direction - north
- return _('N')
- elif degrees < 68:
- return _('NE')
- elif degrees < 113:
- return _('E')
- elif degrees < 158:
- return _('SE')
- elif degrees < 203:
- return _('S')
- elif degrees < 248:
- return _('SW')
- elif degrees < 293:
- return _('W')
- elif degrees < 338:
- return _('NW')
-
- # Convert mph to Beufort scale
- def get_beaufort_from_mph(self, value):
- if value < 1:
- return 0
- elif value < 4:
- return 1
- elif value < 8:
- return 2
- elif value < 13:
- return 3
- elif value < 18:
- return 4
- elif value < 25:
- return 5
- elif value < 27:
- return 6
- elif value < 39:
- return 7
- elif value < 47:
- return 8
- elif value < 89:
- return 9
- elif value < 64:
- return 10
- elif value < 73:
- return 11
- elif value >= 73:
- return 12
-
class indicator_weather(threading.Thread):
""" Indicator class """
last_update_time = None
# Settings values
# Formats: setting value, object name (for preferences dialog), value assigned (optional)
- metric_systems = { 'S': ('si', MetricSystem.SI),
- 'I': ('imperial', MetricSystem.IMPERIAL)}
+ metric_systems = { 'S': ('si', UnitSystem.SI),
+ 'I': ('imperial', UnitSystem.IMPERIAL)}
- weather_sources = { 'G': ('google', WeatherDataSource.GOOGLE),
- 'Y': ('yahoo', WeatherDataSource.YAHOO)}
+ weather_sources = { 'Y': ('yahoo', WeatherDataSource.YAHOO),
+ 'W': ('weather-com', WeatherDataSource.WEATHER_COM)}
notifications = {'N': 'nonotif',
'O': 'notifsevere',
@@ -989,17 +1277,31 @@
'beaufort': ("beaufort", WindUnits.BEAUFORT),
'knots': ("knots", WindUnits.KNOTS)}
- # Initializing and reading settings
+ heat_estimates = {'heatindex': ("heatindex", RelativeFormula.HEATINDEX),
+ 'humidex': ("humidex", RelativeFormula.HUMIDEX)}
+
+ chill_estimates = {'windchill': ("wctindex", RelativeFormula.WINDCHILL),
+ 'apparent': ("aatindex", RelativeFormula.APPARENT)}
+
def __init__(self):
+ """ Initializing and reading previously-saved settings """
log.debug("Indicator: creating")
threading.Thread.__init__(self)
self.main_icon = os.path.join
- self.winder = appindicator.Indicator ("indicator-weather", "weather-indicator", appindicator.CATEGORY_OTHER)
- self.winder.set_status (appindicator.STATUS_ACTIVE)
- self.winder.set_attention_icon ("weather-indicator-error")
-
+ self.winder = AppIndicator.Indicator.new("indicator-weather", "weather-indicator", AppIndicator.IndicatorCategory.OTHER)
+
+ self.queue = Queue.Queue()
+
self.menu_update_lock = threading.Lock()
+ self.status_update_lock = threading.Lock()
+
+ self.status_update_lock.acquire(True)
+ self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
+ self.winder.set_attention_icon("weather-indicator-error")
+ self.status_update_lock.release()
+
self.refreshed_minutes_ago = -1
+ self.places_changed = False
monitor_upower(self.on_system_sleep, self.on_system_resume, log)
log.debug("Indicator: reading settings")
@@ -1010,27 +1312,37 @@
self.unit = self.settings.get_value("unit")
self.notif = self.settings.get_value("notif")
self.wind = self.settings.get_value("wind")
+ self.heat = self.settings.get_value("heat")
+ self.chill = self.settings.get_value("chill")
self.source = self.settings.get_value("data_source")
self.placechosen = self.settings.get_value("placechosen")
self.places = str(self.settings.get_value("places"))
self.show_label = self.settings.get_value("show_label")
-
- log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, wind=%s, placechosen=%s, places=%s" %
- (self.rate, self.unit, self.notif, self.wind, self.placechosen, self.places))
+ self.show_relative = self.settings.get_value("show_relative")
+ self.show_wind = self.settings.get_value("show_wind")
+ self.show_suntimes = self.settings.get_value("show_suntimes")
+
+ log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, "
+ "wind=%s, placechosen=%s, places=%s, heat=%s, chill=%s" %
+ (self.rate, self.unit, self.notif, self.wind,
+ self.placechosen, self.places, self.heat, self.chill))
#Setting default values
- self.metric_system = MetricSystem.SI
+ self.metric_system = UnitSystem.SI
self.wind_unit = WindUnits.MPH
self.place = None
self.menu = None
self.condition = None
self.icon = None
-
+ self.heat_index = RelativeFormula.HUMIDEX
+ self.chill_index = RelativeFormula.WINDCHILL
+
#Parsing settings
# Metric system
if self.unit in (False, None):
default_value = 'S'
- log.debug("Indicator: could not parse unit, setting to %s" % default_value)
+ log.debug("Indicator: could not parse unit, "
+ "setting to %s" % default_value)
self.settings.set_value("unit", default_value)
self.unit = default_value
self.metric_system = self.metric_systems[self.unit][1]
@@ -1038,25 +1350,70 @@
# Notification
if self.notif in (False, None):
default_value = 'N'
- log.debug("Indicator: could not parse notif, setting to %s" % default_value)
+ log.debug("Indicator: could not parse notif, "
+ "setting to %s" % default_value)
self.settings.set_value("notif", default_value)
self.notif = default_value
+ Notify.init("weather-indicator")
# Wind units
if self.wind in (False, None):
default_value = 'mph'
- log.debug("Indicator: could not parse wind, setting to %s" % default_value)
+ log.debug("Indicator: could not parse wind, "
+ "setting to %s" % default_value)
self.settings.set_value("wind", default_value)
self.wind = default_value
self.wind_unit = self.wind_systems[self.wind][1]
+ # Heat estimate formula
+ if self.heat in (False, None):
+ default_value = 'humidex'
+ log.debug("Indicator: could not parse heat, "
+ "setting to %s" % default_value)
+ self.settings.set_value("heat", default_value)
+ self.heat = default_value
+ self.heat_index = self.heat_estimates[self.heat][1]
+
+ # Chill estimate formula
+ if self.chill in (False, None):
+ default_value = 'windchill'
+ log.debug("Indicator: could not parse chill, "
+ "setting to %s" % default_value)
+ self.settings.set_value("chill", default_value)
+ self.chill = default_value
+ self.chill_index = self.chill_estimates[self.chill][1]
+
# Show label in indicator?
- self.show_label = True if self.show_label == 1 else False
+ if self.show_label == 1:
+ self.show_label = True
+ self.label_guide = "100 ˚C" # Guide for width of label
+ else:
+ self.show_label = False
+ self.label_guide = " "
+ # Show relative temperature in dropdown?
+ if self.show_relative == 1:
+ self.show_relative = True
+ else:
+ self.show_relative = False
+
+ # Show windspeed & direction in dropdown?
+ if self.show_wind == 1:
+ self.show_wind = True
+ else:
+ self.show_wind = False
+
+ # Show sunrise & sunset times in dropdown?
+ if self.show_suntimes == 1:
+ self.show_suntimes = True
+ else:
+ self.show_suntimes = False
+
# Weather source
- if self.source in (False, None):
+ if self.source in (False, None, 'G'): # If set to Google, reset it
default_value = 'Y'
- log.debug("Indicator: could not parse data source, setting to %s" % default_value)
+ log.debug("Indicator: could not parse data source, "
+ "setting to %s" % default_value)
self.settings.set_value("data_source", default_value)
self.source = default_value
self.weather_source = self.weather_sources[self.source][1]
@@ -1064,7 +1421,8 @@
# Rate
if self.rate in (False, None):
default_value = 15
- log.debug("Indicator: could not parse rate, setting to %s" % str(default_value))
+ log.debug("Indicator: could not parse rate, "
+ "setting to %s" % str(default_value))
self.settings.set_value("refresh_rate", default_value)
self.rate = default_value
@@ -1077,9 +1435,11 @@
self.placechosen = int(self.placechosen)
# Places list
+ self.menu_update_lock.acquire(True)
if self.places in (False, None, '', '[]', "['']"):
log.debug("Indicator: could not parse places")
self.menu_noplace()
+ self.menu_update_lock.release()
else:
self.places = eval(self.places)
if self.placechosen >= len(self.places):
@@ -1089,89 +1449,96 @@
if self.location_details in (False, None, '', '[]', "['']"):
log.debug("Indicator: could not parse current location details")
self.menu_noplace()
+ self.menu_update_lock.release()
else:
self.location_details = eval(self.location_details)
- self.menu_normal()
+## self.menu_normal()
+ self.menu_update_lock.release()
self.update_weather()
- # Set a label of indicator
def update_label(self, label):
+ """ Set the label of the indicator """
if (hasattr(self.winder, 'set_label')):
log.debug("Indicator: update_label: setting label to '%s'" % label)
self.previous_label_value = label
- self.winder.set_label(label) if self.show_label else self.winder.set_label(" ")
- self.winder.set_status(appindicator.STATUS_ATTENTION)
- self.winder.set_status(appindicator.STATUS_ACTIVE)
+ self.status_update_lock.acquire(True)
+ self.winder.set_label(label, self.label_guide) if self.show_label else self.winder.set_label(" ", " ")
+ self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION)
+ self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
+ self.status_update_lock.release()
- # Show a menu if no places specified
def menu_noplace(self):
+ """ Show a menu if no places specified """
log.debug("Indicator: making a menu for no places")
- menu_noplace = gtk.Menu()
+ menu_noplace = Gtk.Menu()
- setup = gtk.MenuItem(_("Set Up Weather..."))
+ setup = Gtk.MenuItem(_("Set Up Weather..."))
setup.connect("activate", self.prefs)
setup.show()
menu_noplace.append(setup)
- quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+ quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
quit.connect("activate", self.quit)
quit.show()
menu_noplace.append(quit)
+ self.status_update_lock.acquire(True)
self.winder.set_menu(menu_noplace)
self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png"))
- self.winder.set_status(appindicator.STATUS_ATTENTION)
- self.winder.set_status(appindicator.STATUS_ACTIVE)
-
- # Show menu with data
+ self.winder.set_status(AppIndicator.IndicatorStatus.ATTENTION)
+ self.winder.set_status(AppIndicator.IndicatorStatus.ACTIVE)
+ self.status_update_lock.release()
+
def menu_normal(self):
+ """ Show a menu with weather and location data """
log.debug("Indicator: menu_normal: filling in a menu for found places")
- self.menu = gtk.Menu()
+
+ self.menu = Gtk.Menu()
##City
- self.city_show = gtk.MenuItem()
+ self.city_show = Gtk.MenuItem()
self.city_show.set_sensitive(True)
self.menu.append(self.city_show)
self.city_show.show()
##Condition
- self.cond_show = gtk.MenuItem()
+ self.cond_show = Gtk.MenuItem()
self.cond_show.set_sensitive(True)
self.cond_show.show()
self.menu.append(self.cond_show)
##Temperature
- self.temp_show = gtk.MenuItem()
+ self.temp_show = Gtk.MenuItem()
self.temp_show.set_sensitive(True)
self.temp_show.show()
self.menu.append(self.temp_show)
- ##Humidex
- self.humidex_show = gtk.MenuItem()
- self.humidex_show.set_sensitive(True)
- self.humidex_show.show()
- self.menu.append(self.humidex_show)
+ ##Relative Temperature
+ self.relative_show = Gtk.MenuItem()
+ self.relative_show.set_sensitive(True)
+ self.relative_show.show()
+ self.menu.append(self.relative_show)
##Humidity
- self.humid_show = gtk.MenuItem()
+ self.humid_show = Gtk.MenuItem()
self.humid_show.set_sensitive(True)
self.humid_show.show()
self.menu.append(self.humid_show)
##Wind
- self.wind_show = gtk.MenuItem()
+ self.wind_show = Gtk.MenuItem()
self.wind_show.set_sensitive(True)
self.wind_show.show()
self.menu.append(self.wind_show)
##Sunrise
- self.sunrise_show = gtk.MenuItem()
+ self.sunrise_show = Gtk.MenuItem()
self.sunrise_show.set_sensitive(True)
self.sunrise_show.show()
self.menu.append(self.sunrise_show)
##Sunset
- self.sunset_show = gtk.MenuItem()
+ self.sunset_show = Gtk.MenuItem()
self.sunset_show.set_sensitive(True)
self.sunset_show.show()
self.menu.append(self.sunset_show)
@@ -1179,65 +1546,68 @@
##Cities
if len(self.places) != 1:
##Breaker
- breaker = gtk.SeparatorMenuItem()
+ breaker = Gtk.SeparatorMenuItem()
breaker.show()
self.menu.append(breaker)
log.debug("Indicator: menu_normal: adding first location menu item '%s'" % self.places[0][1])
- loco1 = gtk.RadioMenuItem(None, self.places[0][1])
+ loco1 = Gtk.RadioMenuItem.new_with_label([], self.places[0][1])
if self.placechosen == 0:
loco1.set_active(True)
loco1.connect("toggled", self.on_city_changed)
loco1.show()
self.menu.append(loco1)
+ group = loco1.get_group()
for place in self.places[1:]:
log.debug("Indicator: menu_normal: adding location menu item '%s'" % place[1])
- loco = gtk.RadioMenuItem(loco1, place[1])
+ loco = Gtk.RadioMenuItem.new_with_label(group, place[1])
if self.places.index(place) == self.placechosen:
loco.set_active(True)
loco.connect("toggled", self.on_city_changed)
loco.show()
self.menu.append(loco)
+ group = loco.get_group()
##Breaker
- breaker = gtk.SeparatorMenuItem()
+ breaker = Gtk.SeparatorMenuItem()
breaker.show()
self.menu.append(breaker)
- self.refresh_show = gtk.MenuItem()
+ self.refresh_show = Gtk.MenuItem()
#label will be set later
self.refresh_show.connect("activate", self.update_weather)
self.refresh_show.show()
self.menu.append(self.refresh_show)
- ext_show = gtk.MenuItem(_("Forecast"))
+ ext_show = Gtk.MenuItem(_("Forecast"))
ext_show.connect("activate", self.extforecast)
ext_show.show()
self.menu.append(ext_show)
##Preferences
- prefs_show = gtk.MenuItem(_("Preferences..."))
+ prefs_show = Gtk.MenuItem(_("Preferences..."))
prefs_show.connect("activate", self.prefs)
prefs_show.show()
self.menu.append(prefs_show)
##About
- about_show = gtk.MenuItem(_("About..."))
+ about_show = Gtk.MenuItem(_("About..."))
about_show.connect("activate", self.about)
about_show.show()
self.menu.append(about_show)
##Quit
- quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+ quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
quit.connect("activate", self.quit)
quit.show()
self.menu.append(quit)
self.winder.set_menu(self.menu)
self.update_label(" ")
+
- # Another city has been selected from radiobutton
def on_city_changed(self,widget):
+ """ Another city has been selected from radiobutton """
if widget.get_active():
for place in self.places:
if (place[1] == widget.get_label()):
@@ -1251,28 +1621,30 @@
self.location_details = self.settings.get_location_details(self.place[0])
if self.location_details in (False, None, '', '[]', "['']"):
log.debug("Indicator: could not parse location details for placechosen='%s'" % self.placechosen)
+ self.menu_update_lock.acquire(True)
self.menu_noplace()
+ self.menu_update_lock.release()
else:
self.location_details = eval(self.location_details)
self.settings.set_value("placechosen", self.placechosen)
self.update_weather(False)
def on_system_sleep(self):
- """
- Callback from UPower that system suspends/hibernates
+ """Callback from UPower that system suspends/hibernates
+
"""
# store time
self.sleep_time = datetime.datetime.now()
log.debug("Indicator: system goes to sleep at %s" % self.sleep_time)
# remove gobject timeouts
if hasattr(self, "refresh_id"):
- gobject.source_remove(self.refresh_id)
+ GObject.source_remove(self.refresh_id)
if hasattr(self, "rate_id"):
- gobject.source_remove(self.rate_id)
+ GObject.source_remove(self.rate_id)
def on_system_resume(self):
- """
- Callback from UPower that system resumes
+ """Callback from UPower that system resumes
+
"""
now = datetime.datetime.now()
log.debug("Indicator: system resumes at %s" % now)
@@ -1291,25 +1663,26 @@
else:
self.update_weather()
- # Schedule weather update
def schedule_weather_update(self, rate_override = None):
+ """ Schedule the next weather update """
if hasattr(self, "rate_id"):
- gobject.source_remove(self.rate_id)
+ GObject.source_remove(self.rate_id)
if rate_override:
- self.rate_id = gobject.timeout_add(
- int(rate_override) * 60000, self.update_weather)
+ self.rate_id = GObject.timeout_add(
+## int(rate_override) * 60000, self.update_weather)
+ int(rate_override * 60000), self.update_weather)
else:
- self.rate_id = gobject.timeout_add(
+ self.rate_id = GObject.timeout_add(
int(self.rate) * 60000, self.update_weather)
- # Schedule weather update
def schedule_refresh_label_update(self):
+ """ Schedule the next 'Refresh' label update """
if hasattr(self, "refresh_id"):
- gobject.source_remove(self.refresh_id)
- self.refresh_id = gobject.timeout_add(60000, self.update_refresh_label)
+ GObject.source_remove(self.refresh_id)
+ self.refresh_id = GObject.timeout_add(60000, self.update_refresh_label)
- # Update 'Refresh' label with time since last successful data refresh
def update_refresh_label(self, reset_minutes = None):
+ """ Update 'Refresh' label with time since last successful data refresh """
if reset_minutes is not None:
self.refreshed_minutes_ago = reset_minutes
else:
@@ -1319,6 +1692,7 @@
return False
def set_refresh_label(self, refreshing=False):
+ """ Update the 'Refresh' label text """
if refreshing:
refresh_label=_("Refreshing, please wait")
elif self.refreshed_minutes_ago < 0:
@@ -1329,68 +1703,86 @@
refresh_label = "%s (%s)" % (_("Refresh"), _("%d min. ago") % self.refreshed_minutes_ago)
self.refresh_show.set_label(refresh_label)
- # Load weather data from cache and display its values
- def show_cached_weather(self):
+ def get_cached_weather(self, queue):
+ """ Load weather data from cache and put it onto the queue """
try:
- self.menu_update_lock.acquire(True)
+ log.debug("Indicator: show_cached_weather: setting "
+ "previous_condition to None")
self.previous_condition = None
cached_weather = self.settings.get_weather(self.places[self.placechosen][0])
if cached_weather is not None:
cached_weather = eval(cached_weather)
- log.debug("Indicator: loading weather from cache for %s" % self.places[self.placechosen])
- self.menu_normal()
- self.set_refresh_label(True)
- self.icon = cached_weather['icon']
- if (self.icon == False):
- self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))
- else:
- self.winder.set_icon(self.icon)
+ log.debug("Indicator: loading weather from cache "
+ "for %s" % self.places[self.placechosen])
+ # Put the cached weather onto to the Queue
+ queue.put({'cached_weather': cached_weather})
+ except Exception, e:
+ log.error(e)
+ log.debug(traceback.format_exc(e))
- self.city_show.set_label(self.places[self.placechosen][1])
- self.previous_condition = cached_weather['condition']
- self.cond_show.set_label(cached_weather['condition'])
- self.temp_show.set_label(cached_weather['temper'])
- if cached_weather['humidex'] != None:
- self.humidex_show.set_label(cached_weather['humidex'])
- else:
- self.humidex_show.destroy()
- self.humid_show.set_label(cached_weather['humidity'])
+ def show_cached_weather(self, cached_weather):
+ """ Update the indicator icon, label and menu with cached weather data """
+ try:
+ self.menu_normal()
+ self.set_refresh_label(True)
+ self.icon = cached_weather['icon']
+ if (self.icon == False):
+ self.winder.set_icon(
+ os.path.join(PROJECT_ROOT_DIRECTORY,
+ "share/indicator-weather/media/icon_unknown_condition.png")
+ )
+ else:
+ self.winder.set_icon(self.icon)
+
+ self.city_show.set_label(self.places[self.placechosen][1])
+ self.previous_condition = cached_weather['condition']
+ self.cond_show.set_label(cached_weather['condition'])
+ self.temp_show.set_label(cached_weather['temper'])
+ if (self.show_relative and cached_weather['feelslike'] != None):
+ self.relative_show.set_visible(True)
+ self.relative_show.set_label(cached_weather['feelslike'])
+ else:
+ self.relative_show.set_visible(False)
+ self.humid_show.set_label(cached_weather['humidity'])
+ if self.show_wind:
+ self.wind_show.set_visible(True)
self.wind_show.set_label(cached_weather['wind'])
+ else:
+ self.wind_show.set_visible(False)
+ if self.show_suntimes:
+ self.sunrise_show.set_visible(True)
self.sunrise_show.set_label(cached_weather['sunrise'])
+ self.sunset_show.set_visible(True)
self.sunset_show.set_label(cached_weather['sunset'])
- self.update_label(cached_weather['label'])
- self.winder.set_status(appindicator.STATUS_ATTENTION)
- self.winder.set_status(appindicator.STATUS_ACTIVE)
+ else:
+ self.sunrise_show.set_visible(False)
+ self.sunset_show.set_visible(False)
+ self.update_label(cached_weather['label'])
+
except Exception, e:
log.error(e)
log.debug(traceback.format_exc(e))
- self.menu_update_lock.release()
-
- # Get fresh weather data
- def get_new_weather_data(self, notif = True):
-
+ def get_new_weather_data(self, notif, queue):
+ """ Get fresh weather data from source and put it onto the queue """
# get weather and catch any exception
weather = None
try:
weather = self.get_weather()
-
except urllib2.URLError, e:
weather = None
log.error("Indicator: networking error: %s" % e)
-
except Exception, e:
weather = None
log.error(e)
log.debug(traceback.format_exc(e))
+ # Put the new weather data onto the Queue
+ queue.put({'weather': weather})
+ def show_new_weather_data(self, weather):
+ """ Update the indicator icon, label and menu with new weather data """
try:
- # wait until cacher finishes
- log.debug("Indicator: updateWeather: waiting for 'Cacher' thread to terminate")
- self.menu_update_lock.acquire(True)
- self.menu_update_lock.release()
-
if weather is None:
# remove the "Refreshing" status
self.set_refresh_label()
@@ -1399,35 +1791,59 @@
# Repeat an attempt in one minute
self.schedule_weather_update(1)
return
-
+
# Fill in menu with data
log.debug("Indicator: updateWeather: got condition '%s', icon '%s'" % (self.condition, self.icon))
self.condition = weather.get_condition_label()
self.icon = weather.get_icon_name()
- 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()))
-
+ 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_string()))
self.menu_normal()
self.update_refresh_label(0)
self.city_show.set_label(self.places[self.placechosen][1])
self.cond_show.set_label(self.condition)
self.temp_show.set_label(weather.get_temperature_label())
- if (weather.get_humidex_label() != None):
- self.humidex_show.set_label(weather.get_humidex_label())
+ _relative_label = weather.get_relative_label()
+ if (self.show_relative and "---" not in _relative_label):
+ self.relative_show.set_visible(True)
+ self.relative_show.set_label(_relative_label)
else:
- self.humidex_show.destroy()
+ self.relative_show.set_visible(False)
self.humid_show.set_label(weather.get_humidity_label())
- self.wind_show.set_label(weather.get_wind_label())
- self.sunrise_show.set_label(weather.get_sunrise_label())
- self.sunset_show.set_label(weather.get_sunset_label())
-
- # Saving cached data, unless correct icon is supplied
+ if self.show_wind:
+ self.wind_show.set_visible(True)
+ self.wind_show.set_label(weather.get_wind_label())
+ else:
+ self.wind_show.set_visible(False)
+ if self.show_suntimes:
+ self.sunrise_show.set_visible(True)
+ self.sunrise_show.set_label(weather.get_sunrise_label())
+ self.sunset_show.set_visible(True)
+ self.sunset_show.set_label(weather.get_sunset_label())
+ else:
+ self.sunrise_show.set_visible(False)
+ self.sunset_show.set_visible(False)
+
+ # Save cached data if correct icon is supplied
if (self.icon == False):
- self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))
+ self.winder.set_icon(
+ os.path.join(PROJECT_ROOT_DIRECTORY,
+ "share/indicator-weather/media/icon_unknown_condition.png")
+ )
else:
self.winder.set_icon(self.icon)
self.settings.save_weather(weather, self.places[self.placechosen][0])
- self.update_label(weather.get_temperature(needs_rounding=True))
-
+ # Update the indicator label
+ self.update_label(weather.get_temperature_string())
+
# Notify user, if notifications are enabled
if self.condition != self.previous_condition and self.notif == 'U':
# Weather condition has changed
@@ -1437,106 +1853,135 @@
# Severe weather condition notification
log.debug("Indicator: updateWeather: severe condition notification")
self.notify(self.condition, self.icon, severe=True)
-
+ # Save the current condition to track changes for notifications
self.previous_condition = self.condition
-
except Exception, e:
log.error(e)
log.debug(traceback.format_exc(e))
+ # Schedule the next weather fetch
self.schedule_weather_update()
-
- # Update weather
+
def update_weather(self, notif=True, widget=None):
- log.debug("Indicator: updateWeather: updating weather for %s" % self.places[self.placechosen])
+ """ Update the displayed weather data from cache and fetch new data """
+ log.debug("Indicator: updateWeather: updating weather for %s" %
+ self.places[self.placechosen])
# First, display cached data
- threading.Thread(target=self.show_cached_weather, name='Cache').start()
+ cache_thread = threading.Thread(target=self.get_cached_weather,
+ name='Cache', args=(self.queue,))
+ cache_thread.start()
+
# Then, start a new thread with real data pickup
- threading.Thread(target=self.get_new_weather_data, name='Fetcher').start()
-
- # Get current weather for selected location
+ fetcher_thread = threading.Thread(target=self.get_new_weather_data,
+ name='Fetcher',
+ args=(self.notif, self.queue))
+ fetcher_thread.start()
+
+ # Update the menu with the cached weather
+ cache_thread.join()
+ cached_weather = self.queue.get()
+ if cached_weather is not None and cached_weather.has_key('cached_weather'):
+ self.show_cached_weather(cached_weather['cached_weather'])
+
+ # Update the menu with the new weather
+ fetcher_thread.join()
+ weather = self.queue.get()
+ if weather is not None and weather.has_key('weather'):
+ self.show_new_weather_data(weather['weather'])
+
def get_weather(self):
- log.debug("Indicator: getWeather for location '%s'" % self.location_details['full name'])
- self.current_location = Location(self.metric_system, self.wind_unit, self.location_details)
+ """ Get current weather for selected location """
+ log.debug("Indicator: getWeather for location '%s'" %
+ self.location_details['full name'])
+ self.current_location = Location(self.metric_system, self.wind_unit,
+ self.heat_index, self.chill_index,
+ self.location_details)
log.debug("Indicator: getWeather: updating weather report")
self.current_location.update_weather_data(self.weather_source)
return self.current_location.weather
- # Show notification to user
- def notify(self,conditon,icon,severe=False):
- log.debug("Indicator: Notify on weather condition, severe=%s, condition=%s, icon=%s" % (severe, self.condition, icon))
+ def notify(self,condition,icon,severe=False):
+ """ Show notification to user according to preferences """
+ log.debug("Indicator: Notify on weather condition, severe=%s, "
+ "condition=%s, icon=%s" % (severe, condition, icon))
if severe:
- n = pynotify.Notification (_("Severe weather alert"),
- self.condition,
- icon)
+ n = Notify.Notification (_("Severe weather alert"),
+ condition,
+ icon)
else:
- n = pynotify.Notification (self.condition, "", icon)
+ n = Notify.Notification (condition, "", icon)
n.show ()
- # Menu callbacks
- # Open Preferences dialog
def prefs(self, widget):
+ """ Menu callback to open Preferences dialog """
log.debug("Indicator: open Preferences")
if ((not hasattr(self, 'prefswindow')) or (not self.prefswindow.get_visible())):
self.prefswindow = PreferencesDialog()
self.prefswindow.show()
-
+
def about(self, widget):
+ """ Menu callback to open About dialog """
log.debug("Indicator: open About dialog")
- self.aboutdialog = gtk.AboutDialog()
+ self.aboutdialog = Gtk.AboutDialog()
self.aboutdialog.set_name(_("Weather Indicator"))
self.aboutdialog.set_version(VERSION)
- ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/doc/indicator-weather/AUTHORS"), "r")
+ ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY,
+ "share/doc/indicator-weather/AUTHORS"), "r")
self.aboutdialog.set_copyright(ifile.read().replace('\x0c', ''))
ifile.close()
- ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/common-licenses/GPL-3"), "r")
+ ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY,
+ "share/common-licenses/GPL-3"), "r")
self.aboutdialog.set_license(ifile.read().replace('\x0c', ''))
ifile.close()
self.aboutdialog.set_website("https://launchpad.net/weather-indicator")
self.aboutdialog.set_translator_credits(_("translator-credits"))
- logo_path = os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png")
- self.aboutdialog.set_logo(gtk.gdk.pixbuf_new_from_file(logo_path))
-
+ logo_path = os.path.join(PROJECT_ROOT_DIRECTORY,
+ "share/indicator-weather/media/icon.png")
+ self.aboutdialog.set_logo(GdkPixbuf.Pixbuf.new_from_file(logo_path))
self.aboutdialog.connect("response", self.about_close)
self.aboutdialog.show()
def about_close(self, widget, event=None):
+ """ Menu callback to close About dialog """
log.debug("Indicator: closing About dialog")
self.aboutdialog.destroy()
- # Open Extended forecast window
def extforecast(self, widget):
+ """ Menu callback to open Extended forecast window """
log.debug("Indicator: open Forecast")
- if ((not hasattr(self, 'forecastwd')) or (not self.forecastwd.get_visible())):
- self.forecastwd = ExtendedForecast()
- self.forecastwd.show()
+ if ((not hasattr(self, 'forecastwd')) or \
+ (not self.forecastwd.get_visible())):
+ self.forecastwd = ExtendedForecast()
+ self.forecastwd.show()
- # Quit the applet
def quit(self, widget, data=None):
+ """ Menu callback to quit the indicator applet """
log.debug("Indicator: Quitting")
- gtk.main_quit()
+ Gtk.main_quit()
-class PreferencesDialog(gtk.Dialog):
+class PreferencesDialog(Gtk.Dialog):
""" Class for preferences dialog """
__gtype_name__ = "PreferencesDialog"
- # Creating a new preferences dialog
def __new__(cls):
+ """ Creating a new preferences dialog """
log.debug("Preferences: creating")
builder = get_builder('PreferencesDialog')
new_object = builder.get_object("preferences_dialog")
new_object.finish_initializing(builder)
return new_object
- # Fill in preferences dialog with currect data
def finish_initializing(self, builder):
+ """ Fill in preferences dialog with correct data """
log.debug("Preferences: finishing initialization")
- log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, rate=%s, source=%s" %
- (wi.unit, wi.notif, wi.wind, wi.rate, wi.source))
+ log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, "
+ "rate=%s, source=%s, heat=%s, chill=%s" %
+ (wi.unit, wi.notif, wi.wind, wi.rate, wi.source,
+ wi.heat, wi.chill))
self.builder = builder
# Set correct wind_unit using dictionary of wind value and object name
@@ -1547,11 +1992,16 @@
self.builder.get_object('show_label').set_active(wi.show_label)
self.builder.get_object('show_label').set_visible(hasattr(wi.winder, 'set_label'))
self.builder.get_object('rate').set_value(float(wi.rate))
+ self.builder.get_object(wi.heat_estimates[wi.heat][0]).set_active(True)
+ self.builder.get_object(wi.chill_estimates[wi.chill][0]).set_active(True)
+ self.builder.get_object('show_relative').set_active(wi.show_relative)
+ self.builder.get_object('show_wind').set_active(wi.show_wind)
+ self.builder.get_object('show_suntimes').set_active(wi.show_suntimes)
log.debug("Preferences: Loading places")
if wi.places != None:
for place in wi.places:
- if len(place)>1:
+ if len(place)>1 and place[0] is not None:
log.debug("Preferences: Places: got (%s, %s)" % (place[1], place[0]))
newplace = list()
newplace.append(place[1])
@@ -1563,32 +2013,45 @@
self.builder.connect_signals(self)
- # 'Remove' clicked - remove location from list
- #TODO: Update settings object
def on_remove_location(self, widget):
+ """ 'Remove' clicked - remove location from list """
selection = self.builder.get_object('location_list').get_selection()
model, iter = selection.get_selected()
if iter != None:
- log.debug("Preferences: Removing location %s (code %s)" % (model[iter][0], model[iter][1]))
+ log.debug("Preferences: Removing location %s (code %s)" %
+ (model[iter][0], model[iter][1]))
+ newplaces = []
+ for place in wi.places:
+ if place[1] != model[iter][0] and \
+ place[0] != model[iter][1]:
+ newplaces.append(place)
+ if newplaces != wi.places:
+ wi.places = newplaces
+ log.debug("Preferences: update settings object")
+ wi.settings.set_value("places", str(wi.places))
+
model.remove(iter)
-
+ wi.places_changed = True
+
if (self.builder.get_object('citieslist').get_iter_first() == None):
self.builder.get_object('ok_button').set_sensitive(False)
-
- # 'Add' clicked - create a new Assistant
+
def on_add_location(self, widget):
+ """ 'Add' clicked - create a new Assistant """
log.debug("Preferences: Add location clicked")
if ((not hasattr(self, 'assistant')) or (not self.assistant.get_visible())):
self.assistant = Assistant()
self.assistant.show()
- # 'OK' clicked - save settings
def ok(self, widget, data=None):
+ """ 'OK' clicked - save settings """
+ #TODO: rewrite this - for better response to user clicks,
+ # it should first copy values to a queue, then destroy the window
log.debug("Preferences: Saving settings")
need_to_update_weather = False
need_to_update_indicator = False
- #Show label near icon
+ # Show label near icon
new_show_label = self.builder.get_object('show_label').get_active()
if (wi.show_label != new_show_label):
wi.show_label = new_show_label
@@ -1597,6 +2060,30 @@
need_to_update_indicator = True
log.debug("Preferences: Show Label changed to '%s'" % wi.show_label)
+ # Show relative temperature
+ new_show_relative = self.builder.get_object('show_relative').get_active()
+ if (wi.show_relative != new_show_relative):
+ wi.show_relative = new_show_relative
+ wi.settings.set_value("show_relative", new_show_relative)
+ need_to_update_weather = True
+ log.debug("PreferencesDialog: Show Relative Temp changed to %s" % wi.show_relative)
+
+ # Show wind speed & direction
+ new_show_wind = self.builder.get_object('show_wind').get_active()
+ if (wi.show_wind != new_show_wind):
+ wi.show_wind = new_show_wind
+ wi.settings.set_value("show_relative", new_show_wind)
+ need_to_update_weather = True
+ log.debug("PreferencesDialog: Show Wind Data changed to %s" % wi.show_wind)
+
+ # Show sunrise & sunset times
+ new_show_suntimes = self.builder.get_object('show_suntimes').get_active()
+ if (wi.show_suntimes != new_show_suntimes):
+ wi.show_suntimes = new_show_suntimes
+ wi.settings.set_value("show_suntimes", new_show_suntimes)
+ need_to_update_weather = True
+ log.debug("PreferencesDialog: Show Sunrise/Sunset Times changed to %s" % wi.show_suntimes)
+
# Metric systems
for k in wi.metric_systems.keys():
if self.builder.get_object(wi.metric_systems[k][0]).get_active():
@@ -1634,6 +2121,32 @@
need_to_update_weather = True
log.debug("Preferences: Wind Unit changed to '%s'" % wi.wind)
+ # Heat estimate formula
+ for k in wi.heat_estimates.keys():
+ if self.builder.get_object(wi.heat_estimates[k][0]).get_active():
+ new_heat_index = k
+ new_heat_estimate = wi.heat_estimates[k][1]
+
+ if (wi.heat != new_heat_index):
+ wi.heat = new_heat_index
+ wi.heat_index = new_heat_estimate
+ wi.settings.set_value("heat", wi.heat)
+ need_to_update_weather = True
+ log.debug("Preferences: Heat Estimate changed to '%s'" % wi.heat)
+
+ # Chill estimate formula
+ for k in wi.chill_estimates.keys():
+ if self.builder.get_object(wi.chill_estimates[k][0]).get_active():
+ new_chill_index = k
+ new_chill_estimate = wi.chill_estimates[k][1]
+
+ if (wi.chill != new_chill_index):
+ wi.chill = new_chill_index
+ wi.chill_index = new_chill_estimate
+ wi.settings.set_value("chill", wi.chill)
+ need_to_update_weather = True
+ log.debug("Preferences: Chill Estimate changed to '%s'" % wi.chill)
+
# Weather source
for k in wi.weather_sources.keys():
if self.builder.get_object(wi.weather_sources[k][0]).get_active():
@@ -1655,18 +2168,19 @@
wi.schedule_weather_update()
# Get places from location list
- newplaces = list()
- item = self.builder.get_object('citieslist').get_iter_first()
- while (item != None):
- newplace = list()
- newplace.append(self.builder.get_object('citieslist').get_value (item, 1))
- newplace.append(self.builder.get_object('citieslist').get_value (item, 0))
- newplaces.append(newplace)
- item = self.builder.get_object('citieslist').iter_next(item)
+## newplaces = list()
+## item = self.builder.get_object('citieslist').get_iter_first()
+## while (item != None):
+## newplace = list()
+## newplace.append(self.builder.get_object('citieslist').get_value (item, 1))
+## newplace.append(self.builder.get_object('citieslist').get_value (item, 0))
+## newplaces.append(newplace)
+## item = self.builder.get_object('citieslist').iter_next(item)
# If places have changed - update weather data
- if newplaces != wi.places:
- wi.places = newplaces
+## if newplaces != wi.places:
+## wi.places = newplaces
+ if wi.places_changed:
log.debug("Preferences: Places changed to '%s'" % str(wi.places))
wi.settings.set_value("places", str(wi.places))
if (type(wi.place) != None) and (wi.place in wi.places):
@@ -1677,60 +2191,68 @@
log.debug("Preferences: Place Chosen changed to '%s'" % wi.placechosen)
wi.settings.set_value("placechosen", wi.placechosen)
wi.location_details = eval(wi.settings.get_location_details(wi.place[0]))
- wi.menu_normal()
- wi.set_refresh_label()
+## wi.menu_normal()
+## wi.set_refresh_label()
need_to_update_weather = True
+ wi.places_changed = False
if need_to_update_weather:
- wi.update_weather(False)
+#### wi.update_weather(False)
+ wi.schedule_weather_update(0.003)
if need_to_update_indicator:
wi.update_label(wi.previous_label_value)
self.destroy()
- # 'Cancel' click - forget all changes
def cancel(self, widget, data=None):
+ """ 'Cancel' clicked - forget all changes """
log.debug("Preferences: Cancelling")
self.destroy()
-class ExtendedForecast(gtk.Window):
+class ExtendedForecast(Gtk.Window):
""" Class for forecast window """
__gtype_name__ = "ExtendedForecast"
- # Create forecast
def __new__(cls):
+ """ Create forecast """
log.debug("ExtendedForecast: creating")
builder = get_builder('ExtendedForecast')
new_object = builder.get_object("extended_forecast")
new_object.finish_initializing(builder)
return new_object
- # Fill in forecast parameters
def finish_initializing(self, builder):
+ """ Fill in forecast parameters """
log.debug("ExtendedForecast: finishing initialization")
self.builder = builder
self.builder.connect_signals(self)
# Get forecast data using Forecast object
- log.debug("ExtendedForecast: chosen place: %s (code %s)" % (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0]))
- self.builder.get_object('extended_forecast').set_title("%s %s" % (_('Weather Forecast for '), wi.places[wi.placechosen][1]))
+ log.debug("ExtendedForecast: chosen place: %s (code %s)" %
+ (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0]))
+ self.builder.get_object('extended_forecast').set_title("%s %s" %
+ (_('Weather Forecast for'), wi.places[wi.placechosen][1]))
log.debug("ExtendedForecast: getting forecast data")
- forecast = Forecast(wi.metric_system, wi.current_location.location_details['latitude'], wi.current_location.location_details['longitude'], locale_name)
+ forecast = Forecast(wi.metric_system,
+ wi.current_location.location_details['yahoo id'],
+ locale_name)
forecast.prepare_forecast_data()
if forecast.error_message != None:
#Error occurred while getting forecast data
- self.builder.get_object('connection_error').set_text("%s" % forecast.error_message)
+ self.builder.get_object('connection_error').set_text("%s" %
+ forecast.error_message)
self.builder.get_object('connection_error').set_visible(True)
self.builder.get_object('hbox1').set_visible(False)
else:
daysofweek = forecast.get_forecast_daysofweek()
forecast_data = forecast.get_forecast_data()
if forecast_data == None:
- # Forecast data unavailable - hide elements and show 'connection_error' label
- self.builder.get_object('connection_error').set_visible(True);
- self.builder.get_object('hbox1').set_visible(False);
- self.builder.get_object('hseparator1').set_visible(False);
+ # Forecast data unavailable - hide elements
+ # and show 'connection_error' label
+ self.builder.get_object('connection_error').set_visible(True)
+ self.builder.get_object('hbox1').set_visible(False)
+ self.builder.get_object('hseparator1').set_visible(False)
return
(highdata, lowdata) = forecast_data
icons = forecast.get_forecast_icons()
@@ -1738,68 +2260,81 @@
log.debug("ExtendedForecast: parsing forecast data")
# Create labels for each weekday
- self.builder.get_object('day1lbl').set_label('%s' % daysofweek[0].capitalize())
- self.builder.get_object('day2lbl').set_label('%s' % daysofweek[1].capitalize())
- self.builder.get_object('day3lbl').set_label('%s' % daysofweek[2].capitalize())
- self.builder.get_object('day4lbl').set_label('%s' % daysofweek[3].capitalize())
-
+ for i in xrange(1,5):
+ try:
+ lbl_name = 'day%slbl' % (i)
+ self.builder.get_object(lbl_name).set_label('%s' %
+ daysofweek[i].capitalize())
+ except IndexError:
+ log.error("ExtendedForecast: Yahoo didn't return "
+ "forecast for %s days" % i)
+ log.error(forecast.forecast)
+
# Fill in icons
for i in xrange(1,5):
- # Get icon name from dictionary in Weather object for Google icons
+ # Get icon name from dictionary in Weather object
+ # for Yahoo condition codes
try:
- conds = Weather._GoogleConditions.get(icons[i-1])
+ conds = Weather._YahooConditions.get(icons[i])
if conds != None:
- google_icon = conds[0]
+ yahoo_icon = conds[0]
else:
- log.error("ExtendedForecast: unknown Google weather condition '%s'" % icons[i-1])
+ log.error("ExtendedForecast: unknown Yahoo weather "
+ "condition code '%s'" % icons[i])
log.error(forecast.forecast)
- google_icon = 'weather-indicator-unknown'
- self.builder.get_object('day%simage' % str(i)).set_from_icon_name(google_icon,gtk.ICON_SIZE_BUTTON)
+ yahoo_icon = 'weather-indicator-unknown'
+ self.builder.get_object('day%simage' %
+ str(i)).set_from_icon_name(yahoo_icon,
+ Gtk.IconSize.BUTTON)
except IndexError:
- log.error("ExtendedForecast: Google didn't return condition for %s days" % i-1)
- log.error(forecast.forecast)
+ log.error("ExtendedForecast: Yahoo didn't return "
+ "condition for %s days" % i)
+ log.error(forecast.forecast)
# Fill in condition labels
for i in xrange(1,5):
- if conditions[i-1] != '':
- condition = conditions[i-1]
+ if conditions[i] != '':
+ condition = conditions[i]
else:
condition = _("Unknown condition")
- self.builder.get_object('day%scond' % str(i)).set_label(condition)
+ self.builder.get_object('day%scond' %
+ str(i)).set_label(condition)
# Fill in High and Low temperatures
- if wi.metric_system == MetricSystem.SI:
- tempunit = '°C'
+ if wi.metric_system == UnitSystem.SI:
+ tempunit = u"°C"
else:
- tempunit = '°F'
+ tempunit = u"°F"
for i in xrange(1,5):
- label = "%s: %s%s" % (_('High'), highdata[i-1],tempunit)
- self.builder.get_object('day%stemphigh' % str(i)).set_label(label)
- label = "%s: %s%s" % (_('Low'), lowdata[i-1],tempunit)
- self.builder.get_object('day%stemplow' % str(i)).set_label(label)
+ label = "%s: %s%s" % (_('High'), highdata[i],tempunit)
+ self.builder.get_object('day%stemphigh' %
+ str(i)).set_label(label)
+ label = "%s: %s%s" % (_('Low'), lowdata[i],tempunit)
+ self.builder.get_object('day%stemplow' %
+ str(i)).set_label(label)
- # Closing forecast window
def close(self, widget, data=None):
+ """ 'Close' clicked - close forecast window """
log.debug("ExtendedForecast: closing window")
self.destroy()
def on_destroy(self, widget):
pass
-class Assistant(gtk.Assistant):
+class Assistant(Gtk.Assistant):
""" Class for a wizard, which helps to add a new location in location list """
__gtype_name__ = "Assistant"
- # Create new object
def __new__(cls):
+ """ Create new object """
log.debug("Assistant: creating new Assistance instance")
builder = get_builder('Assistant')
new_object = builder.get_object("assistant")
new_object.finish_initializing(builder)
return new_object
- # Finish UI initialization - prepare combobox
def finish_initializing(self, builder):
+ """ Finish UI initialization - prepare combobox """
log.debug("Assistant: finishing initialization")
self.builder = builder
self.builder.connect_signals(self)
@@ -1809,18 +2344,18 @@
# Set up combobox
log.debug("Assistant: setting up location combobox")
- self.store = gtk.ListStore(str, str, str, str, str)
+ self.store = Gtk.ListStore(str, str, str, str, str)
self.location_input_combo = self.builder.get_object("combolocations")
self.location_input_combo.set_model(self.store)
- self.location_input_combo.set_text_column(0)
+ self.location_input_combo.set_entry_text_column(0)
self.location_entry = self.builder.get_object("entrylocation")
self.place_selected = None
self.location = None
- self.assistant.set_forward_page_func(self.next_page)
+ self.assistant.set_forward_page_func(self.next_page, None)
- # 'Get cities' button clicked - get suggested cities list
def on_get_city_names(self, widget):
+ """ 'Get cities' button clicked - get suggested cities list """
new_text = self.location_entry.get_text()
log.debug("Assistant: looking for location '%s'" % new_text)
try:
@@ -1833,18 +2368,26 @@
for city in cities['geonames']:
# Create a full city name, consisting of city name, administrative areas names and country name
if 'adminName2' in city:
- displayed_city_name = "%s, %s, %s, %s" % (city['name'], city['adminName1'], city['adminName1'], city['countryName'])
+ displayed_city_name = "%s, %s, %s, %s" % (
+ city['name'], city['adminName1'],
+ city['adminName1'], city['countryName']
+ )
elif 'adminName1' in city:
- displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])
+ displayed_city_name = "%s, %s, %s" % (
+ city['name'], city['adminName1'], city['countryName']
+ )
else:
- displayed_city_name = "%s, %s" % (city['name'], city['countryName'])
- self.store.append([displayed_city_name, str(city['geonameId']), str(city['lat']), str(city['lng']), str(city['name'])])
+ displayed_city_name = "%s, %s" % (city['name'],
+ city['countryName'])
+ self.store.append([displayed_city_name,
+ str(city['geonameId']), str(city['lat']),
+ str(city['lng']), str(city['name'])])
self.location_input_combo.popup()
except urllib2.URLError:
log.error("Assistant: error reaching url '%s'" % url)
- # A city is selected from suggested list
def on_select_city(self, entry):
+ """ A city is selected from suggested list """
if self.location_input_combo.get_active() != -1:
self.place_selected = self.store[self.location_input_combo.get_active()]
self.assistant.set_page_complete(self.builder.get_object("placeinput"),True)
@@ -1853,50 +2396,74 @@
self.location = None
self.assistant.set_page_complete(self.builder.get_object("placeinput"), False)
- # Create a location object out of a selected location
- def next_page(self, current_page):
+ def next_page(self, current_page, data):
+ """ Create a location object out of a selected location """
log.debug("Assistant: moved to page %s" % current_page)
- if (self.assistant.get_current_page() == 0) and not self.location and self.place_selected:
+ if (self.assistant.get_current_page() == 0) and \
+ not self.location and self.place_selected:
# Label input page
- log.debug("Assistant: Page %s: got location with code %s" % (current_page, self.place_selected[1]))
- self.location = Location(wi.metric_system, wi.wind_unit)
+ log.debug("Assistant: Page %s: got location with code %s" %
+ (current_page, self.place_selected[1]))
+ self.location = Location(wi.metric_system, wi.wind_unit,
+ wi.heat_index, wi.chill_index)
if self.location.prepare_location(self.place_selected):
- log.debug("Assistant: Page %s: City %s found" % (current_page, self.place_selected[0]))
+ log.debug("Assistant: Page %s: City %s found" %
+ (current_page, self.place_selected[0]))
# Set a short city name as default label
self.builder.get_object("entrylbl").set_text(self.place_selected[4])
else:
- log.error("Assistant: Page %s: City with code %s was NOT found" % (current_page, self.place_selected[0]))
+ log.error("Assistant: Page %s: City with code %s was NOT "
+ "found" % (current_page, self.place_selected[0]))
return 3
elif self.assistant.get_current_page() == 1:
# Confirmation page
lbl = self.builder.get_object("entrylbl").get_text()
- log.debug("Assistant: Page %s: City label is %s" % (current_page, lbl))
+ log.debug("Assistant: Page %s: City label is %s" %
+ (current_page, lbl))
# If empty label was input, set label to short city name
if lbl == '':
- log.debug("Assistant: Page %s: Empty label found, setting lbl to short name - %s" % (current_page, self.place_selected[4]))
+ log.debug("Assistant: Page %s: Empty label found, setting "
+ "lbl to short name - %s" %
+ (current_page, self.place_selected[4]))
lbl = self.place_selected[4]
self.location.location_details['label'] = lbl
self.builder.get_object("lbl3").set_label(_('Label:'))
self.builder.get_object("labellbl").set_label('%s' % lbl)
- self.builder.get_object("placelbl").set_label('%s' % self.place_selected[0])
+ self.builder.get_object("placelbl").set_label(
+ '%s' % self.place_selected[0])
return self.assistant.get_current_page() + 1
- # 'Cancel' clicked
def on_cancel(self,widget):
+ """ 'Cancel' clicked """
log.debug("Assistant: Cancelled")
self.destroy()
- # 'Apply' clicked - save location details, add an entry in a location list
def on_apply(self,widget):
+ """ 'Apply' clicked - save location details, add an entry in a location list """
(location_code, location_details) = self.location.export_location_details()
- log.debug("Assistant: Apply: adding location ('%s', '%s')" % (self.location.location_details['label'], location_code))
+ log.debug("Assistant: Apply: adding location ('%s', '%s')" %
+ (self.location.location_details['label'], location_code))
newplace = list()
newplace.append(self.location.location_details['label'])
newplace.append(str(location_code))
newplace.append(str(location_details))
- wi.settings.save_location_details(eval(str(location_details)), str(location_code))
+ wi.settings.save_location_details(
+ eval(str(location_details)), str(location_code)
+ )
wi.prefswindow.builder.get_object('citieslist').append(newplace)
+
+ newplaces = list()
+ item = wi.prefswindow.builder.get_object('citieslist').get_iter_first()
+ while (item != None):
+ newplace = list()
+ newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 1))
+ newplace.append(wi.prefswindow.builder.get_object('citieslist').get_value(item, 0))
+ newplaces.append(newplace)
+ item = wi.prefswindow.builder.get_object('citieslist').iter_next(item)
+ wi.places = newplaces
+ wi.places_changed = True
+
# Enable 'OK' button in Preferences
wi.prefswindow.builder.get_object('ok_button').set_sensitive(True)
self.hide()
@@ -1904,8 +2471,8 @@
class SingleInstance(object):
""" Class to ensure, that single instance of the applet is run for each user """
- # Initialize, specifying a path to store pids
def __init__(self, pidPath):
+ """ Initialize, specifying a path to store pids """
self.pidPath=pidPath
# See if pidFile exists
if os.path.exists(pidPath):
@@ -1928,7 +2495,8 @@
shutil.copy(temp_path, pidPath)
os.unlink(temp_path)
except Exception as e:
- log.error("SingleInstance: exception while renaming '%s' to '%s':\n %s" % (temp_path, pidPath, str(e)))
+ log.error("SingleInstance: exception while renaming '%s' "
+ "to '%s':\n %s" % (temp_path, pidPath, str(e)))
def is_already_running(self):
return self.lasterror
@@ -1939,7 +2507,8 @@
os.unlink(self.pidPath)
def main():
- gtk.main()
+ Gtk.main()
+
return 0
if __name__ == "__main__":
@@ -1952,8 +2521,10 @@
log = logging.getLogger('IndicatorWeather')
log.propagate = False
log.setLevel(logging.DEBUG)
- log_handler = logging.handlers.RotatingFileHandler(log_filename, maxBytes=1024*1024, backupCount=5)
- log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - %(levelname)s - %(message)s")
+ log_handler = logging.handlers.RotatingFileHandler(
+ log_filename, maxBytes=1024*1024, backupCount=5)
+ log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - "
+ "%(levelname)s - %(message)s")
log_handler.setFormatter(log_formatter)
log.addHandler(log_handler)
@@ -1974,8 +2545,7 @@
TimeFormatter.monitor_indicator_datetime(log)
# not running, safe to continue...
- gtk.gdk.threads_init()
- gtk.gdk.threads_enter()
+
# Remember locale name
global locale_name
locale_name = locale.getlocale()[0]
@@ -1984,6 +2554,20 @@
else:
locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale
locale_name = "en"
+
+ # init GLib/GObject
+ GObject.threads_init()
+
+ # init Gdk threads and get Gdk lock
+ Gdk.threads_init()
+ Gdk.threads_enter()
+
+ # init Gtk
+ Gtk.init(None)
+
+ # create main thread and enter main loop
wi = indicator_weather()
main()
- gtk.gdk.threads_leave()
+
+ # release Gdk lock
+ Gdk.threads_leave()
\ No newline at end of file
=== 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 @@
Display icon/label in the indicator
0 - display icon only, 1 - display label only, 2 - display both icon and text
+
+ false
+ Display relative temperature
+ Display relative temperature
+
+
+ true
+ Display wind speed and direction
+ Display wind speed and direction
+
+
+ true
+ Display sunrise and sunset times
+ Display sunrise and sunset times
+
'N'
Notifications
@@ -36,6 +51,16 @@
wind unit
wind unit
+
+ 'humidex'
+ heat estimate formula
+ heat estimate formula
+
+
+ 'windchill'
+ chill estimate formula
+ chill estimate formula
+
true
show forecast
=== 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 @@
-
+
-
@@ -78,16 +90,19 @@
True
+ False
12
6
True
+ False
0
Please enter a name for this location:
False
+ True
0
@@ -99,6 +114,7 @@
False
+ True
1
@@ -110,10 +126,12 @@
True
+ False
12
True
+ False
0
6
Please review the choices below. If anything is not correct, please go back and select the correct options.
@@ -122,99 +140,120 @@
False
+ True
0
True
+ False
12
True
+ False
True
+ False
5
True
False
+ False
False
+ True
0
True
+ False
0
Label:
True
False
+ True
1
True
+ False
0
<b>Home</b>
True
+ True
+ True
2
False
+ True
0
True
+ False
5
True
False
+ False
False
+ True
0
True
+ False
0
Location:
True
False
+ True
1
True
+ False
0
<b>Orange, Texas</b>
True
+ True
+ True
2
False
+ True
1
@@ -223,6 +262,7 @@
False
+ True
1
@@ -235,5 +275,10 @@
Review choices
+
+
+ False
+
+
=== 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 @@
-
+
-
+ False
12
Extended Forecast
False
mouse
../media/icon.png
-
+
True
+ False
-
- False
- 10
- <big>Forecast information cannot be fetched. Connection cannot be established.</big>
- True
- False
-
-
- 0
-
+
+ False
+ 10
+ <big>Forecast information cannot be fetched. Connection cannot be established.</big>
+ True
+
+
+ True
+ True
+ 0
+
True
+ False
5
True
True
+ False
True
+ False
6
True
+ False
True
+ False
10
<big>Today (fri.)</big>
True
+ True
+ True
0
True
+ False
50
weather-showers
+ True
+ True
1
@@ -66,6 +78,7 @@
80
True
+ False
Partially cloudy
center
True
@@ -73,33 +86,44 @@
15
+ True
+ True
2
True
+ False
True
True
+ False
High : 23°C
+ True
+ True
0
True
+ False
Low : 19°C
+ True
+ True
1
+ True
+ True
3
@@ -107,6 +131,8 @@
+ True
+ True
0
@@ -114,45 +140,58 @@
2
True
+ False
False
+ True
1
+ True
+ True
0
True
+ False
True
+ False
6
True
+ False
True
+ False
10
<big>Tomorrow (sat.)</big>
True
+ True
+ True
0
True
+ False
50
weather-storm
+ True
+ True
1
@@ -160,6 +199,7 @@
80
True
+ False
Couverture nuageuse partielle
center
True
@@ -167,33 +207,44 @@
15
+ True
+ True
2
True
+ False
True
True
+ False
High : 26°C
+ True
+ True
0
True
+ False
Low : 16°C
+ True
+ True
1
+ True
+ True
3
@@ -201,6 +252,8 @@
+ True
+ True
0
@@ -208,47 +261,58 @@
2
True
+ False
False
+ True
1
+ True
+ True
1
True
+ False
True
+ False
6
True
+ False
True
+ False
10
<big>Sunday</big>
True
False
+ True
0
True
+ False
50
weather-fog
False
+ True
1
@@ -256,6 +320,7 @@
80
True
+ False
Fogs
center
True
@@ -263,32 +328,43 @@
15
+ True
+ True
2
True
+ False
True
+ False
High : 28°C
+ True
+ True
0
True
+ False
Low : 16°C
+ True
+ True
1
+ True
+ True
3
@@ -296,50 +372,65 @@
+ True
+ True
0
True
+ False
False
+ True
1
+ True
+ True
2
True
+ False
True
+ False
True
+ False
True
+ False
10
<big>Monday</big>
True
+ True
+ True
0
True
+ False
50
weather-clear
+ True
+ True
1
@@ -347,6 +438,7 @@
80
True
+ False
Clear
center
True
@@ -354,32 +446,43 @@
15
+ True
+ True
2
True
+ False
True
+ False
High : 26°C
+ True
+ True
0
True
+ False
Low : 14°C
+ True
+ True
1
+ True
+ True
3
@@ -387,16 +490,21 @@
+ True
+ True
0
+ True
+ True
3
+ True
False
1
@@ -404,26 +512,31 @@
True
+ False
False
+ True
2
True
+ False
1
True
+ False
gtk-close
True
+ False
True
True
-
+
False
@@ -435,6 +548,8 @@
+ True
+ True
3
=== 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
+ 30
+ 1
+ 10
+
+ False
12
Weather Indicator Preferences
mouse
@@ -22,62 +28,118 @@
../media/icon.png
normal
-
+
True
+ False
+ vertical
6
+
+
+ True
+ False
+ end
+
+
+ gtk-cancel
+ True
+ False
+ True
+ True
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ gtk-ok
+ True
+ False
+ False
+ True
+ True
+
+
+
+ False
+ False
+ 1
+
+
+
+
+ False
+ False
+ end
+ 0
+
+
True
True
-
+
True
+ False
12
+ vertical
12
-
+
True
+ False
+ vertical
True
Enable the Weather Indicator Applet
True
False
- 0.02
+ 0.019999999552965164
True
True
False
+ True
0
- True
Show temperature near indicator
+ True
True
False
+ 0
True
False
+ True
1
-
+
True
+ False
6
True
+ False
1
Update every
True
False
+ True
0
@@ -86,6 +148,7 @@
True
True
•
+ True
rate
@@ -97,31 +160,39 @@
True
+ False
0
minutes
False
+ True
2
+ True
+ True
2
False
+ True
0
-
+
True
+ False
+ vertical
True
+ False
0
3
<b>Notifications</b>
@@ -129,16 +200,20 @@
False
+ True
0
True
+ False
12
-
+
True
+ False
+ vertical
True
@@ -146,6 +221,7 @@
True
True
False
+ 0.5
True
True
@@ -161,6 +237,7 @@
True
True
False
+ 0.5
True
nonotif
@@ -176,6 +253,7 @@
True
True
False
+ 0.5
True
nonotif
@@ -189,12 +267,15 @@
+ True
+ True
1
True
+ False
0
3
<b>Weather Data Source</b>
@@ -202,40 +283,46 @@
False
+ True
2
True
+ False
12
-
+
True
+ False
+ vertical
True
-
- Google
- True
- True
- False
- True
- True
-
-
- False
- False
- 0
-
-
-
Yahoo!
True
True
False
- True
- google
+ 0.5
+ True
+
+
+ False
+ False
+ 0
+
+
+
+
+ Weather.com
+ True
+ True
+ False
+ 0.5
+ True
+ True
+ yahoo
False
@@ -247,12 +334,15 @@
+ True
+ True
3
False
+ True
1
@@ -261,6 +351,7 @@
True
+ False
General
@@ -268,16 +359,296 @@
-
- True
- 12
- 12
-
-
- True
+
+ True
+ False
+ 12
+ vertical
+ 12
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 0
+ 3
+ <b>Weather Conditions</b>
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ 12
+
+
+ True
+ False
+ vertical
+ True
+
+
+ Relative temperature ("Feels like")
+ True
+ True
+ False
+ 0
+ True
+
+
+ False
+ False
+ 0
+
+
+
+
+ Wind speed and direction
+ True
+ True
+ False
+ 0
+ True
+ True
+
+
+ False
+ False
+ 1
+
+
+
+
+ Sunrise and sunset times
+ True
+ True
+ False
+ 0
+ True
+ True
+
+
+ False
+ False
+ 2
+
+
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 0
+ 3
+ <b>Relative Heat Formula</b>
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ 12
+
+
+ True
+ False
+ vertical
+ True
+
+
+ Heat Index (US)
+ True
+ True
+ False
+ 0
+ True
+ True
+
+
+ False
+ False
+ 0
+
+
+
+
+ Humidex (Canada)
+ True
+ True
+ False
+ 0
+ True
+ heatindex
+
+
+ False
+ False
+ 1
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 0
+ 3
+ <b>Wind Chill Formula</b>
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ 12
+
+
+ True
+ False
+ vertical
+ True
+
+
+ JAG/TI Wind Chill Index (US/UK/Canada)
+ True
+ True
+ False
+ 0
+ True
+ True
+
+
+ False
+ False
+ 0
+
+
+
+
+ Apparent Temperature (Australia)
+ True
+ True
+ False
+ 0
+ True
+ wctindex
+
+
+ False
+ False
+ 1
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ 1
+
+
+
+
+ True
+ False
+ Conditions
+
+
+ 1
+ False
+
+
+
+
+ True
+ False
+ 12
+ vertical
+ 12
+
+
+ True
+ False
+ vertical
True
+ False
0
3
<b>Temperature Scale</b>
@@ -285,16 +656,20 @@
False
+ True
0
True
+ False
12
-
+
True
+ False
+ vertical
True
@@ -302,6 +677,7 @@
True
True
False
+ 0
True
True
@@ -317,6 +693,7 @@
True
True
False
+ 0
True
imperial
@@ -331,21 +708,26 @@
False
+ True
1
False
+ True
0
-
+
True
+ False
+ vertical
True
+ False
0
3
<b>Wind Speed Unit</b>
@@ -353,23 +735,28 @@
False
+ True
0
True
+ False
12
-
+
True
+ False
+ vertical
True
- Meter per second (m/s)
+ Meters per second (m/s)
True
True
False
+ 0.5
True
True
@@ -385,6 +772,7 @@
True
True
False
+ 0.5
True
mps
@@ -400,6 +788,7 @@
True
True
False
+ 0.5
True
mps
@@ -411,10 +800,11 @@
- Beaufort
+ Beaufort number
True
True
False
+ 0.5
True
mps
@@ -430,6 +820,7 @@
True
True
False
+ 0.5
True
mps
@@ -444,41 +835,44 @@
False
+ True
1
False
+ True
1
- 1
+ 2
True
+ False
Units
- 1
+ 2
False
-
+
True
+ False
12
+ vertical
6
True
True
- automatic
- automatic
True
in
@@ -490,6 +884,9 @@
False
True
0
+
+
+
City
@@ -506,12 +903,15 @@
+ True
+ True
0
True
+ False
6
start
@@ -521,7 +921,7 @@
True
True
True
-
+
False
@@ -536,7 +936,7 @@
True
True
True
-
+
False
@@ -547,70 +947,33 @@
False
+ True
1
- 2
+ 3
True
+ False
Locations
- 2
+ 3
False
+ False
+ True
1
-
-
- True
- end
-
-
- gtk-cancel
- True
- True
- True
-
-
-
- False
- False
- 0
-
-
-
-
- gtk-ok
- True
- False
- True
- True
-
-
-
- False
- False
- 1
-
-
-
-
- False
- False
- end
- 0
-
-
@@ -618,10 +981,4 @@
ok_button
-
- 1
- 30
- 1
- 10
-
=== 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 @@
+indicator-weather (13.05.17-quantal2) quantal; urgency=low
+
+ * Fix for 4-day Forecast display (LP: #1182324)
+ * 'OK' button in Preferences Dialog is now more responsive
+
+ -- Joshua Tasker Wed, 22 May 2013 00:32:54 +0500
+
+indicator-weather (13.05.17~quantal1) quantal; urgency=low
+
+ * 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, choose temperature formulas, toggle display of conditions
+ * Bumped version number to reflect massive changes
+
+ -- Joshua Tasker Sat, 18 May 2013 12:59:03 +0500
+
+indicator-weather (12.07.30~quantal2) quantal; urgency=low
+
+ * Don't crash if Yahoo doesn't return conditions
+ * Fixed a crash when reading saved Places with no location IDs
+ * Fix units in Forecast when metric is selected
+ * One-line fix for outdated data sources
+
+ -- Joshua Tasker Sat, 04 May 2013 01:20:38 +0500
+
+indicator-weather (12.07.30~quantal1) quantal; urgency=low
+
+ * Fix adding location, now uses Yahoo's YQL service
+ * Fix for "Forecast", now uses Yahoo instead of Google
+ * Bump dependency for python-pywapi to 0.3
+ * Hide Google radio button on Preferences UI
+
+ -- Joshua Tasker Tue, 09 Apr 2013 02:27:04 +0500
+
indicator-weather (12.07.30~precise1) precise; urgency=low
* Skip sunset and sunrise check as Earthtools.org is down (LP: #964365)
=== 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 @@
Source: indicator-weather
Section: python
Priority: extra
-Build-Depends: cdbs (>= 0.4.90-1~),
- debhelper (>= 6),
- python (>= 2.6.6-3~),
- gobject-introspection,
- python-distutils-extra (>= 2.10)
-Maintainer: Vadim Rutkovsky
-Standards-Version: 3.8.3
+Build-Depends: debhelper (>= 7.0.50~),
+ gobject-introspection,
+ python (>= 2.6.6-3~),
+ python-distutils-extra (>= 2.10)
+X-Python-Version: >= 2.6
+Maintainer: Ubuntu Developers
+Standards-Version: 3.9.3
+Homepage: https://launchpad.net/weather-indicator
Package: indicator-weather
Architecture: all
-Depends: ${misc:Depends},
- ${python:Depends},
- libglib2.0-bin,
- python-appindicator,
- python-notify,
- python-gobject,
- python-gtk2,
- python-gconf,
- python-pywapi
+Depends: gir1.2-glib-2.0,
+ gir1.2-gtk-3.0,
+ gir1.2-appindicator3-0.1,
+ gnome-icon-theme,
+ libglib2.0-bin,
+ libgtk-3-bin,
+ python-gconf,
+ python-gi,
+ python-pywapi (>= 0.3.2
+ xdg-utils,
+ ${misc:Depends},
+ ${python:Depends}
Recommends: python-apport
-Description: A weather indicator for Ubuntu's Indicator Applet
- A weather indicator that displays information for one or multiple places
- in the world
+Description: indicator that displays weather information
+ Indicator-Weather displays information for one or multiple places
+ in the world. Current weather status is displayed directly on your
+ panel and detailed forecasts are no more than a click away.
+ .
+ It is implemented using the Indicator Applet API.
=== 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 @@
-Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat
-Upstream-Name: indicator-weather
-Upstream-Maintainer: Vadim Rutkovsky
-Upstream-Source: https://launchpad.net/weather-indicator
+Format: http://dep.debian.net/deps/dep5
+Upstream-Name: Indicator-Weather
+Upstream-Contact: Vadim Rutkovsky
+Source: https://launchpad.net/weather-indicator/+download
Files: *
-Copyright: (C) 2010 Mehdi Rejraji mehd36@gmail.com
-Copyright: (C) 2010 Sebastian MacDonald Sebas310@gmail.com
-Copyright: (C) 2011 Vadim Rutkovsky
-License: GPL-3
- The full text of the GPL is distributed in
- /usr/share/common-licenses/GPL-3 on Debian systems.
+Copyright: 2010, Mehdi Rejraji
+ 2010, Sebastian MacDonald
+ 2011, Vadim Rutkovsky
+ 2013, Joshua Tasker
+License: GPL-3
+
+Files: debian/*
+Copyright: 2011, Andrew Starr-Bochicchio
+License: GPL-3
+
+License: GPL-3
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 3 of the License.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, see `/usr/share/common-licenses/GPL-3'
=== 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 @@
+AUTHORS /usr/share/doc/indicator-weather
debian/source_indicator-weather.py usr/share/apport/package-hooks
=== 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 @@
#!/bin/sh
-
+
+set -e
+
#DEBHELPER#
+
echo "Installing indicator-specific icons..."
xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon.png weather-indicator
xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_unknown_condition.png weather-indicator-unknown
xdg-icon-resource install --theme hicolor --novendor --size 22 /usr/share/indicator-weather/media/icon_connection_error.png weather-indicator-error
-#installing dconf schema
-echo "Installing indicator dconf schema..."
-cp /usr/share/indicator-weather/indicator-weather.gschema.xml /usr/share/glib-2.0/schemas
-glib-compile-schemas /usr/share/glib-2.0/schemas
-
#quick fix for incomplete icon themes
echo "Fixing incomplete weather icons..."
-if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then
+if [ ! -e "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ] && \
+ [ ! -L "/usr/share/icons/gnome/22x22/status/weather-clouds.png" ]; then
ln -s /usr/share/icons/gnome/22x22/status/weather-few-clouds.png /usr/share/icons/gnome/22x22/status/weather-clouds.png
ln -s /usr/share/icons/gnome/22x22/status/weather-clouds-night.png /usr/share/icons/gnome/22x22/status/weather-clouds-night.png
=== modified file 'debian/rules'
--- debian/rules 2011-06-22 09:52:46 +0000
+++ debian/rules 2013-05-22 05:08:27 +0000
@@ -1,5 +1,11 @@
#!/usr/bin/make -f
-DEB_PYTHON2_MODULE_PACKAGES:=indicator-weather
-
-include /usr/share/cdbs/1/rules/debhelper.mk
-include /usr/share/cdbs/1/class/python-distutils.mk
+
+%:
+ dh $@ --with python2
+
+override_dh_auto_install:
+ dh_auto_install
+ find -iname "*.desktop" -exec chmod a-x {} \;
+
+override_dh_compress:
+ dh_compress -XAUTHORS
=== modified file 'indicator_weather/helpers.py'
--- indicator_weather/helpers.py 2012-07-30 03:58:13 +0000
+++ indicator_weather/helpers.py 2013-05-22 05:08:27 +0000
@@ -31,10 +31,10 @@
except ImportError:
DCONF_SCHEMAS = []
-import gconf
+from gi.repository import GConf
import traceback
import os
-import gtk
+from gi.repository import Gtk
import urllib2
import locale
import re
@@ -47,7 +47,7 @@
gettext.textdomain('indicator-weather')
def get_builder(builder_file_name):
- """Return a fully-instantiated gtk.Builder instance from specified ui
+ """Return a fully-instantiated Gtk.Builder instance from specified ui
file
:param builder_file_name: The name of the builder file, without extension.
@@ -58,7 +58,7 @@
if not os.path.exists(ui_filename):
ui_filename = None
- builder = gtk.Builder()
+ builder = Gtk.Builder()
builder.set_translation_domain('indicator-weather')
builder.add_from_file(ui_filename)
return builder
@@ -101,10 +101,10 @@
proxy_settings.connect("changed", ProxyMonitor.dconf_proxy_changed)
else:
# load gconf settings
- client = gconf.client_get_default()
- client.add_dir("/system/http_proxy", gconf.CLIENT_PRELOAD_ONELEVEL)
+ client = GConf.Client.get_default()
+ client.add_dir("/system/http_proxy", GConf.ClientPreloadType.PRELOAD_ONELEVEL)
ProxyMonitor.gconf_proxy_changed(client)
- client.notify_add("/system/http_proxy", ProxyMonitor.gconf_proxy_changed)
+ client.notify_add("/system/http_proxy", ProxyMonitor.gconf_proxy_changed, None)
except Exception, e:
log.error("ProxyMonitor: %s" % e)
@@ -113,7 +113,7 @@
@staticmethod
def dconf_proxy_changed(settings, changed_key=None):
"""
- Loads dconf hhtp proxy settings
+ Loads dconf http proxy settings
"""
try:
ProxyMonitor.log.debug("ProxyMonitor: loading dconf settings")
@@ -135,7 +135,7 @@
@staticmethod
def gconf_proxy_changed(client, cnxn_id=None, entry=None, data=None):
"""
- Loads gconf hhtp proxy settings
+ Loads gconf http proxy settings
"""
try:
ProxyMonitor.log.debug("ProxyMonitor: loading gconf settings")
=== modified file 'indicator_weather/indicator_weatherconfig.py'
--- indicator_weather/indicator_weatherconfig.py 2010-07-06 12:10:37 +0000
+++ indicator_weather/indicator_weatherconfig.py 2013-05-22 05:08:27 +0000
@@ -28,7 +28,7 @@
# Where your project will look for your data (for instance, images and ui
# files). By default, this is ../data, relative your trunk layout
-__indicator_weather_data_directory__ = '../data'
+__indicator_weather_data_directory__ = '/usr/share/indicator-weather'
__license__ = 'GPL-3'
import os
@@ -62,7 +62,7 @@
# Get pathname absolute or relative.
path = os.path.join(
os.path.dirname(__file__), __indicator_weather_data_directory__)
-
+
abs_data_path = os.path.abspath(path)
if not os.path.exists(abs_data_path):
raise project_path_not_found
=== modified file 'po/indicator-weather.pot'
--- po/indicator-weather.pot 2011-06-22 09:52:46 +0000
+++ po/indicator-weather.pot 2013-05-22 05:08:27 +0000
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-06-22 02:48-0700\n"
+"POT-Creation-Date: 2012-07-30 07:11+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -18,422 +18,425 @@
"Content-Transfer-Encoding: 8bit\n"
#: ../data/ui/PreferencesDialog.ui.h:1
-msgid "Notifications"
+msgid "Weather Indicator Preferences"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:2
-msgid "Temperature Scale"
+msgid "Enable the Weather Indicator Applet"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:3
-msgid "Weather Data Source"
+msgid "Show temperature near indicator"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:4
-msgid "Wind Speed Unit"
+msgid "Update every"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:5
-msgid "Beaufort"
+msgid "minutes"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:6
-msgid "Enable the Weather Indicator Applet"
+msgid "Notifications"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:7
-msgid "General"
+msgid "No notifications"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:8
-msgid "Google"
+msgid "Only use notifications to give severe weather alerts"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:9
-msgid "Imperial (°F)"
+msgid "Use notifications to give every weather condition change"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:10
-msgid "Km per hour (km/h)"
+msgid "Weather Data Source"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:11
-msgid "Knots"
+msgid "Google"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:12
-msgid "Locations"
+msgid "Yahoo!"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:13
-msgid "Meter per second (m/s)"
+msgid "General"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:14
-msgid "Miles per hour (mph)"
+msgid "Temperature Scale"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:15
-msgid "No notifications"
+msgid "Imperial (°F)"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:16
-msgid "Only use notifications to give severe weather alerts"
+msgid "SI (metric, °C)"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:17
-msgid "SI (metric, °C)"
+msgid "Wind Speed Unit"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:18
-msgid "Show temperature near indicator"
+msgid "Meter per second (m/s)"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:19
-msgid "Units"
+msgid "Miles per hour (mph)"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:20
-msgid "Update every"
+msgid "Kilometers per hour (km/h)"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:21
-msgid "Use notifications to give every weather condition change"
+msgid "Beaufort"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:22
-msgid "Weather Indicator Preferences"
+msgid "Knots"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:23
-msgid "Yahoo!"
+msgid "Units"
msgstr ""
#: ../data/ui/PreferencesDialog.ui.h:24
-msgid "minutes"
+msgid "Locations"
msgstr ""
-#: ../bin/indicator-weather.py:463
+#: ../bin/indicator-weather.py:489
msgid "Unknown error occurred while picking up weather data"
msgstr ""
-#: ../bin/indicator-weather.py:551
+#: ../bin/indicator-weather.py:579
msgid "Tornado"
msgstr ""
-#: ../bin/indicator-weather.py:552
+#: ../bin/indicator-weather.py:580
msgid "Tropical storm"
msgstr ""
-#: ../bin/indicator-weather.py:553
+#: ../bin/indicator-weather.py:581
msgid "Hurricane"
msgstr ""
-#: ../bin/indicator-weather.py:554
+#: ../bin/indicator-weather.py:582
msgid "Severe thunderstorms"
msgstr ""
-#: ../bin/indicator-weather.py:555
+#: ../bin/indicator-weather.py:583
msgid "Thunderstorms"
msgstr ""
-#: ../bin/indicator-weather.py:556
+#: ../bin/indicator-weather.py:584
msgid "Mixed rain and snow"
msgstr ""
#. Use American meaning of sleet - see http://en.wikipedia.org/wiki/Sleet
-#: ../bin/indicator-weather.py:558
+#: ../bin/indicator-weather.py:586
msgid "Mixed rain and sleet"
msgstr ""
-#: ../bin/indicator-weather.py:559
+#: ../bin/indicator-weather.py:587
msgid "Mixed snow and sleet"
msgstr ""
-#: ../bin/indicator-weather.py:560
+#: ../bin/indicator-weather.py:588
msgid "Freezing drizzle"
msgstr ""
-#: ../bin/indicator-weather.py:561
+#: ../bin/indicator-weather.py:589
msgid "Drizzle"
msgstr ""
-#: ../bin/indicator-weather.py:562
+#: ../bin/indicator-weather.py:590
msgid "Freezing rain"
msgstr ""
-#: ../bin/indicator-weather.py:563 ../bin/indicator-weather.py:564
+#: ../bin/indicator-weather.py:591 ../bin/indicator-weather.py:592
msgid "Showers"
msgstr ""
-#: ../bin/indicator-weather.py:565
+#: ../bin/indicator-weather.py:593
msgid "Snow flurries"
msgstr ""
-#: ../bin/indicator-weather.py:566
+#: ../bin/indicator-weather.py:594
msgid "Light snow showers"
msgstr ""
-#: ../bin/indicator-weather.py:567
+#: ../bin/indicator-weather.py:595
msgid "Blowing snow"
msgstr ""
-#: ../bin/indicator-weather.py:568
+#: ../bin/indicator-weather.py:596
msgid "Snow"
msgstr ""
-#: ../bin/indicator-weather.py:569
+#: ../bin/indicator-weather.py:597
msgid "Hail"
msgstr ""
-#: ../bin/indicator-weather.py:570
+#: ../bin/indicator-weather.py:598
msgid "Sleet"
msgstr ""
-#: ../bin/indicator-weather.py:571
+#: ../bin/indicator-weather.py:599
msgid "Dust"
msgstr ""
-#: ../bin/indicator-weather.py:572
+#: ../bin/indicator-weather.py:600
msgid "Foggy"
msgstr ""
-#: ../bin/indicator-weather.py:573
+#: ../bin/indicator-weather.py:601
msgid "Haze"
msgstr ""
-#: ../bin/indicator-weather.py:574
+#: ../bin/indicator-weather.py:602
msgid "Smoky"
msgstr ""
-#: ../bin/indicator-weather.py:575
+#: ../bin/indicator-weather.py:603
msgid "Blustery"
msgstr ""
-#: ../bin/indicator-weather.py:576
+#: ../bin/indicator-weather.py:604
msgid "Windy"
msgstr ""
-#: ../bin/indicator-weather.py:577
+#: ../bin/indicator-weather.py:605
msgid "Cold"
msgstr ""
-#: ../bin/indicator-weather.py:578
+#: ../bin/indicator-weather.py:606
msgid "Cloudy"
msgstr ""
-#: ../bin/indicator-weather.py:579 ../bin/indicator-weather.py:580
+#: ../bin/indicator-weather.py:607 ../bin/indicator-weather.py:608
msgid "Mostly cloudy"
msgstr ""
-#: ../bin/indicator-weather.py:581 ../bin/indicator-weather.py:582
-#: ../bin/indicator-weather.py:596
+#: ../bin/indicator-weather.py:609 ../bin/indicator-weather.py:610
+#: ../bin/indicator-weather.py:624
msgid "Partly cloudy"
msgstr ""
-#: ../bin/indicator-weather.py:583
+#: ../bin/indicator-weather.py:611
msgid "Clear"
msgstr ""
-#: ../bin/indicator-weather.py:584
+#: ../bin/indicator-weather.py:612
msgid "Sunny"
msgstr ""
-#: ../bin/indicator-weather.py:585 ../bin/indicator-weather.py:586
+#: ../bin/indicator-weather.py:613 ../bin/indicator-weather.py:614
msgid "Fair"
msgstr ""
-#: ../bin/indicator-weather.py:587
+#: ../bin/indicator-weather.py:615
msgid "Mixed rain and hail"
msgstr ""
-#: ../bin/indicator-weather.py:588
+#: ../bin/indicator-weather.py:616
msgid "Hot"
msgstr ""
-#: ../bin/indicator-weather.py:589
+#: ../bin/indicator-weather.py:617
msgid "Isolated thunderstorms"
msgstr ""
-#: ../bin/indicator-weather.py:590 ../bin/indicator-weather.py:591
+#: ../bin/indicator-weather.py:618 ../bin/indicator-weather.py:619
msgid "Scattered thunderstorms"
msgstr ""
-#: ../bin/indicator-weather.py:592
+#: ../bin/indicator-weather.py:620
msgid "Scattered showers"
msgstr ""
-#: ../bin/indicator-weather.py:593 ../bin/indicator-weather.py:595
+#: ../bin/indicator-weather.py:621 ../bin/indicator-weather.py:623
msgid "Heavy snow"
msgstr ""
-#: ../bin/indicator-weather.py:594
+#: ../bin/indicator-weather.py:622
msgid "Scattered snow showers"
msgstr ""
-#: ../bin/indicator-weather.py:597
+#: ../bin/indicator-weather.py:625
msgid "Thundershowers"
msgstr ""
-#: ../bin/indicator-weather.py:598
+#: ../bin/indicator-weather.py:626
msgid "Snow showers"
msgstr ""
-#: ../bin/indicator-weather.py:599
+#: ../bin/indicator-weather.py:627
msgid "Isolated thundershowers"
msgstr ""
-#: ../bin/indicator-weather.py:600 ../bin/indicator-weather.py:622
-#: ../bin/indicator-weather.py:627 ../bin/indicator-weather.py:645
-#: ../bin/indicator-weather.py:654 ../bin/indicator-weather.py:664
-#: ../bin/indicator-weather.py:748 ../bin/indicator-weather.py:1730
+#: ../bin/indicator-weather.py:628 ../bin/indicator-weather.py:650
+#: ../bin/indicator-weather.py:655 ../bin/indicator-weather.py:673
+#: ../bin/indicator-weather.py:682 ../bin/indicator-weather.py:692
+#: ../bin/indicator-weather.py:776 ../bin/indicator-weather.py:1767
msgid "Unknown condition"
msgstr ""
-#: ../bin/indicator-weather.py:755 ../bin/indicator-weather.py:761
+#: ../bin/indicator-weather.py:783 ../bin/indicator-weather.py:789
msgid "Humidity"
msgstr ""
-#: ../bin/indicator-weather.py:782
+#: ../bin/indicator-weather.py:810
msgid "Pressure"
msgstr ""
-#: ../bin/indicator-weather.py:812
+#: ../bin/indicator-weather.py:840
msgid "Temperature"
msgstr ""
-#: ../bin/indicator-weather.py:836 ../bin/indicator-weather.py:837
-#: ../bin/indicator-weather.py:838
+#: ../bin/indicator-weather.py:864
msgid "Unknown"
msgstr ""
-#: ../bin/indicator-weather.py:845
+#: ../bin/indicator-weather.py:871
msgid "Wind"
msgstr ""
-#: ../bin/indicator-weather.py:872
+#: ../bin/indicator-weather.py:908
msgid "Sunrise"
msgstr ""
-#: ../bin/indicator-weather.py:876
+#: ../bin/indicator-weather.py:912
msgid "Sunset"
msgstr ""
#. Short wind direction - north
-#: ../bin/indicator-weather.py:889
+#: ../bin/indicator-weather.py:925
msgid "N"
msgstr ""
-#: ../bin/indicator-weather.py:891
+#: ../bin/indicator-weather.py:927
msgid "NE"
msgstr ""
-#: ../bin/indicator-weather.py:893
+#: ../bin/indicator-weather.py:929
msgid "E"
msgstr ""
-#: ../bin/indicator-weather.py:895
+#: ../bin/indicator-weather.py:931
msgid "SE"
msgstr ""
-#: ../bin/indicator-weather.py:897
+#: ../bin/indicator-weather.py:933
msgid "S"
msgstr ""
-#: ../bin/indicator-weather.py:899
+#: ../bin/indicator-weather.py:935
msgid "SW"
msgstr ""
-#: ../bin/indicator-weather.py:901
+#: ../bin/indicator-weather.py:937
msgid "W"
msgstr ""
-#: ../bin/indicator-weather.py:903
+#: ../bin/indicator-weather.py:939
msgid "NW"
msgstr ""
-#: ../bin/indicator-weather.py:1074
+#: ../bin/indicator-weather.py:1111
msgid "Set Up Weather..."
msgstr ""
-#: ../bin/indicator-weather.py:1176
+#: ../bin/indicator-weather.py:1213
msgid "Forecast"
msgstr ""
#. #Preferences
-#: ../bin/indicator-weather.py:1182
+#: ../bin/indicator-weather.py:1219
msgid "Preferences..."
msgstr ""
#. #About
-#: ../bin/indicator-weather.py:1188
+#: ../bin/indicator-weather.py:1225
msgid "About..."
msgstr ""
-#: ../bin/indicator-weather.py:1289
+#: ../bin/indicator-weather.py:1323
msgid "Refreshing, please wait"
msgstr ""
-#: ../bin/indicator-weather.py:1291 ../bin/indicator-weather.py:1293
-#: ../bin/indicator-weather.py:1295
+#: ../bin/indicator-weather.py:1325 ../bin/indicator-weather.py:1327
+#: ../bin/indicator-weather.py:1329
msgid "Refresh"
msgstr ""
-#: ../bin/indicator-weather.py:1293
+#: ../bin/indicator-weather.py:1327
msgid "just now"
msgstr ""
-#: ../bin/indicator-weather.py:1295
+#: ../bin/indicator-weather.py:1329
#, python-format
msgid "%d min. ago"
msgstr ""
-#: ../bin/indicator-weather.py:1436
+#: ../bin/indicator-weather.py:1469
msgid "Severe weather alert"
msgstr ""
-#: ../bin/indicator-weather.py:1454 ../indicator-weather.desktop.in.h:2
+#: ../bin/indicator-weather.py:1487 ../indicator-weather.desktop.in.h:1
msgid "Weather Indicator"
msgstr ""
-#: ../bin/indicator-weather.py:1466
+#: ../bin/indicator-weather.py:1499
msgid "translator-credits"
msgstr ""
-#: ../bin/indicator-weather.py:1684
+#: ../bin/indicator-weather.py:1717
msgid "Weather Forecast for "
msgstr ""
-#: ../bin/indicator-weather.py:1739
+#: ../bin/indicator-weather.py:1776
msgid "High"
msgstr ""
-#: ../bin/indicator-weather.py:1741
+#: ../bin/indicator-weather.py:1778
msgid "Low"
msgstr ""
-#: ../bin/indicator-weather.py:1842 ../data/ui/Assistant.ui.h:4
+#: ../bin/indicator-weather.py:1879 ../data/ui/Assistant.ui.h:8
msgid "Label:"
msgstr ""
-#: ../bin/indicator-weather.py:1932
+#: ../bin/indicator-weather.py:1969
msgid "Another instance of this program is already running"
msgstr ""
#: ../data/ui/Assistant.ui.h:1
-msgid "Home"
+msgid "Add a location"
msgstr ""
#: ../data/ui/Assistant.ui.h:2
-msgid "Orange, Texas"
+msgid "Please search our database for your location:"
msgstr ""
#: ../data/ui/Assistant.ui.h:3
-msgid "Add a location"
+msgid "Search"
+msgstr ""
+
+#: ../data/ui/Assistant.ui.h:4
+msgid "Select a location"
msgstr ""
#: ../data/ui/Assistant.ui.h:5
-msgid "Location:"
+msgid "Please enter a name for this location:"
msgstr ""
#: ../data/ui/Assistant.ui.h:6
@@ -441,36 +444,32 @@
msgstr ""
#: ../data/ui/Assistant.ui.h:7
-msgid "Please enter a name for this location:"
-msgstr ""
-
-#: ../data/ui/Assistant.ui.h:8
msgid ""
"Please review the choices below. If anything is not correct, please go back "
"and select the correct options."
msgstr ""
#: ../data/ui/Assistant.ui.h:9
-msgid "Please search our database for your location:"
+msgid "Home"
msgstr ""
#: ../data/ui/Assistant.ui.h:10
-msgid "Review choices"
+msgid "Location:"
msgstr ""
#: ../data/ui/Assistant.ui.h:11
-msgid "Search"
+msgid "Orange, Texas"
msgstr ""
#: ../data/ui/Assistant.ui.h:12
-msgid "Select a location"
+msgid "Review choices"
msgstr ""
#: ../data/ui/ExtendedForecast.ui.h:1
msgid "Extended Forecast"
msgstr ""
-#: ../indicator-weather.desktop.in.h:1
+#: ../indicator-weather.desktop.in.h:2
msgid ""
"A weather indicator that displays information for one or multiple places in "
"the world"
=== modified file 'setup.py'
--- setup.py 2011-12-10 10:11:20 +0000
+++ setup.py 2013-05-22 05:08:27 +0000
@@ -4,6 +4,7 @@
# Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
# Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
# Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com
+# Copyright (C) 2013 Joshua Tasker jtasker@gmail.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
@@ -85,10 +86,10 @@
DistUtilsExtra.auto.setup(
name='indicator-weather',
- version='11.05.31',
+ version='13.05.17',
license='GPL-3',
- author='Vadim Rutkovsky | Sebastian MacDonald | Mehdi Rejraji',
- author_email='roignac@gmail.com',
+ author='Joshua Tasker | Vadim Rutkovsky | Sebastian MacDonald | Mehdi Rejraji',
+ author_email='jtasker@gmail.com, roignac@gmail.com',
description="A weather indicator for Ubuntu's Indicator Applet",
long_description='A weather indicator that displays information for one or multiple places in the world',
url='https://launchpad.net/weather-indicator',