2
# -*- coding: utf-8 -*-
4
# Copyright (C) 2010 Sebastian MacDonald Sebas310@gmail.com
5
# Copyright (C) 2010 Mehdi Rejraji mehd36@gmail.com
6
# Copyright (C) 2011 Vadim Rutkovsky roignac@gmail.com
7
# This program is free software: you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License version 3, as published
9
# by the Free Software Foundation.
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranties of
13
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
# PURPOSE. See the GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License along
17
# with this program. If not, see <http://www.gnu.org/licenses/>.
21
from gi.repository import Gio
24
import sys, os, shutil, tempfile
25
import gtk, pygtk, gobject, pynotify
28
import urllib2, urllib
29
from urllib import urlencode
32
from xml.dom.minidom import parseString
38
# Will be used for humidex
40
import commands, threading
41
import logging, logging.handlers
45
from gettext import gettext as _
46
from gettext import ngettext as __
47
gettext.textdomain('indicator-weather')
49
# Add project root directory (enable symlink, and trunk execution).
50
PROJECT_ROOT_DIRECTORY = os.path.abspath(
51
os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
53
if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'indicator_weather'))
54
and PROJECT_ROOT_DIRECTORY not in sys.path):
55
sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
56
os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses
58
VERSION = "11.11.28 'Cloudy 9'"
60
from indicator_weather.helpers import *
63
INFO_SETTING = 'setting'
66
""" Class to read/write settings """
68
BASE_KEY = 'apps.indicators.weather'
69
WEATHER_KEY = 'weather'
70
LOCATIONS_KEY = 'locations'
71
INDICATOR_DISPLAY = 'show_label'
72
NOTIFICATIONS = 'notif'
73
WEATHER_SOURCE = 'data_source'
74
REFRESH_RATE = 'refresh_rate'
75
METRIC_SYSTEM = 'unit'
77
PLACECHOSEN = 'placechosen'
81
# INFO_TYPE : Python type
82
# INFO_SETTING : dconf setting name
85
INFO_TYPE : types.IntType,
86
INFO_SETTING : 'indicator-display'
89
INFO_TYPE : types.StringType,
90
INFO_SETTING : 'notifications'
93
INFO_TYPE : types.StringType,
94
INFO_SETTING : 'weather-source'
97
INFO_TYPE : types.IntType,
98
INFO_SETTING : 'refresh-rate'
101
INFO_TYPE : types.StringType,
102
INFO_SETTING : 'metric-system'
105
INFO_TYPE : types.StringType,
106
INFO_SETTING : 'wind-unit'
109
INFO_TYPE : types.IntType,
110
INFO_SETTING: 'placechosen'
113
INFO_TYPE : types.ListType,
114
INFO_SETTING: 'places'
119
def prepare_settings_store(self):
120
log.debug("Settings: preparing settings store")
122
self.db = Gio.Settings.new(self.BASE_KEY)
123
except Exception as e:
124
log.debug("Settings: exception occurred while opening settings:\n %s" % str(e))
126
# Make sure autostart file is installed. Inspired by GTG.
127
def check_autostart(self):
128
autostart_dir = os.path.join(os.path.expanduser("~"),".config/autostart/")
129
autostart_file = "indicator-weather.desktop"
130
autostart_path = os.path.join(autostart_dir, autostart_file)
131
if not os.path.isfile(autostart_path):
132
#Look for the desktop file
133
desktop_file_path = None
134
desktop_file_directories = ["/usr/share/applications",
135
"/usr/local/share/applications"]
136
this_directory = os.path.dirname(os.path.abspath(__file__))
137
for path in desktop_file_directories:
138
fullpath = os.path.normpath(os.path.join(this_directory, path, \
140
if os.path.isfile(fullpath):
141
desktop_file_path = fullpath
143
#If we have found the desktop file, we make a link to in in
145
if desktop_file_path:
146
if not os.path.exists(autostart_dir):
147
os.mkdir(autostart_dir)
148
if os.path.isdir(autostart_dir):
149
log.debug("Installing autostart file.")
150
os.symlink(desktop_file_path, autostart_path)
152
# Get a value of the setting
153
def get_value(self, setting, return_id = False):
154
log.debug("Settings: getting value for %s" % setting)
155
setting_name = Settings.INFO[setting][INFO_SETTING]
157
setting_type = Settings.INFO[setting][INFO_TYPE]
159
types.IntType: self.db.get_int,
160
types.StringType: self.db.get_string,
161
types.BooleanType: self.db.get_boolean,
162
types.ListType: self.db.get_string,
163
types.DictType: self.db.get_string,
164
types.NoneType: self.db.get_value,
166
return get_func(setting_name)
168
self.log.debug("Settings: can't find value for %s" % setting)
171
# Set a setting value
172
def set_value(self, setting, value):
174
value = '' if value is None else value
175
value = str(value) if type(value) is types.ListType else value
176
log.debug("Settings: setting '%s'='%s'" % (setting, value))
178
setting_name = Settings.INFO[setting][INFO_SETTING]
180
setting_type = Settings.INFO[setting][INFO_TYPE]
182
types.IntType: self.db.set_int,
183
types.StringType: self.db.set_string,
184
types.BooleanType: self.db.set_boolean,
185
types.ListType: self.db.set_string,
186
types.DictType: self.db.set_string,
187
types.NoneType: self.db.set_value,
189
set_func(setting_name, value)
192
"Settings: schema for '%s' not found, aborting" % setting)
194
# Get cached weather by location code.
195
# If return_id is True, only document id is returned, otherwise - full weather data
196
def get_weather(self, location_code, return_id = False):
197
log.debug("Settings: getting cached weather for %s" % \
200
cached_weather_string = self.db.get_string(self.WEATHER_KEY)
201
cached_weather = {} if cached_weather_string == ''\
202
else eval(cached_weather_string)
203
if location_code in cached_weather.keys():
204
return str(cached_weather[location_code])
207
"Settings: can't find value for %s" % location_code)
210
log.debug("Settings: can't find %s setting" % WEATHER_KEY)
213
# Save weather info in cache for specific location
214
def save_weather(self, weather, location_code):
217
"label" : weather.get_temperature(needs_rounding=True),
218
"condition": weather.get_condition_label(),
219
"icon" : weather.get_icon_name(),
220
"temper" : weather.get_temperature_label(),
221
"humidex" : weather.get_humidex_label(),
222
"humidity" : weather.get_humidity_label(),
223
"wind" : weather.get_wind_label(),
224
"sunrise" : weather.get_sunrise_label(),
225
"sunset" : weather.get_sunset_label()
227
log.debug("Settings: setting '%s'='%s'" % (location_code, record))
230
cached_weather_string = self.db.get_string(self.WEATHER_KEY)
231
cached_weather = {} if cached_weather_string == ''\
232
else eval(cached_weather_string)
233
cached_weather[location_code] = record
234
cached_weather_string = str(cached_weather)
235
self.db.set_string(self.WEATHER_KEY, cached_weather_string)
238
"Settings: schema for '%s' not found, aborting" % setting)
240
# Get location details by location code
241
# If return_id is True, only document id is returned, otherwise - full location data
242
def get_location_details(self, location_code, return_id = False):
244
locations_string = self.db.get_string(self.LOCATIONS_KEY)
245
locations = {} if locations_string == ''\
246
else eval(locations_string)
247
if location_code in locations.keys():
248
return str(locations[location_code])
251
"Settings: can't find value for %s" % location_code)
254
log.debug("Settings: can't find location details for %s" % \
258
# Save location details
259
def save_location_details(self, location_details, location_code):
260
log.debug("Settings: setting '%s'='%s'" %\
261
(location_code, location_details))
264
locations_string = self.db.get_string(self.LOCATIONS_KEY)
265
locations = {} if locations_string == ''\
266
else eval(locations_string)
267
locations[location_code] = location_details
268
locations_string = str(locations)
269
self.db.set_string(self.LOCATIONS_KEY, locations_string)
274
""" Class with available metric systems units """
279
""" Class for available wind unit systems """
286
class WeatherDataSource:
287
""" Class for available weather data sources """
292
""" Data object to store location details """
294
# Initialize an object with a label
295
def __init__(self, metric_system, wind_unit, location_details = None):
296
self.metric_system = metric_system
297
self.wind_unit = wind_unit
298
self.location_details = location_details
300
# Convert coordinate for google
301
def convert_coordinate_for_google(self, value):
302
value = float(value) * 1e6
303
return int(round(value))
305
# Get necessary location details by its GeoNames details
306
def prepare_location(self, geonames_details):
307
self.location_details = {}
308
self.location_details['full name'] = geonames_details[0]
309
self.location_details['latitude'] = geonames_details[2]
310
self.location_details['longitude'] = geonames_details[3]
311
self.prepare_location_for_google(geonames_details)
312
self.prepare_location_for_yahoo(geonames_details)
313
#TODO: Get noaa id from geonames service
314
self.location_details['noaa id'] = "woot"
316
# check mandatory attributes
317
if not hasattr(self, 'location_code') or \
318
'latitude' not in self.location_details or \
319
'longitude' not in self.location_details:
322
# check that we have at least one supported data source
323
if 'google id' not in self.location_details and \
324
'yahoo id' not in self.location_details:
325
log.error(("Location '%s'" %
326
self.location_details['full name'])) + \
327
"is not supported by current data sources"
332
def prepare_location_for_google(self, geonames_details):
333
# Format latitude and longitude for Google needs
335
lat = self.convert_coordinate_for_google(geonames_details[2])
336
lon = self.convert_coordinate_for_google(geonames_details[3])
337
self.location_details['google id'] = ",,,%s,%s" % (lat, lon)
342
def prepare_location_for_yahoo(self, geonames_details):
343
# Get location details in english for Yahoo
344
baseurl = 'http://api.geonames.org/getJSON'
345
params = {'geonameId': geonames_details[1], 'username': 'indicatorweather'}
346
url = '?'.join((baseurl, urlencode(params)))
347
log.debug("Location: Get GeoNames location details, url %s" % url)
349
city = eval(urllib2.urlopen(url).read())
350
if 'adminName1' in city:
351
displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])
353
displayed_city_name = "%s, %s" % (city['name'], city['countryName'])
355
log.error("Location: Cannot find GeoNames info for code %s Full Response:\n %s" % (geonames_details[1], str(city)))
358
# Get YAHOO WOEID by english name of location
359
baseurl = 'http://where.yahooapis.com/geocode'
360
params = {'location': displayed_city_name, 'appid': 'mOawLd4s', 'flags': 'J'}
361
url = '?'.join((baseurl, urlencode(params)))
362
log.debug("Location: Get Yahoo WOEID, url %s" % url)
363
f = urllib2.urlopen(url)
366
yahoo_woeid_result = eval(s)
367
if (yahoo_woeid_result['ResultSet']['Error'] != 0) and (yahoo_woeid_result['ResultSet']['Results'] != None):
368
log.error("Location: Yahoo woeid return error. Full response:\n %s" % str(yahoo_woeid_result))
371
woeid = yahoo_woeid_result['ResultSet']['Results'][0]['woeid']
372
self.location_code = woeid
373
log.debug("Location: woeid is %s" % woeid)
375
# Get old Yahoo id by woeid
376
url = 'http://weather.yahooapis.com/forecastrss?w=%s' % woeid
377
log.debug("Location: Get Yahoo RSS ID, url %s" % url)
378
f = urllib2.urlopen(url)
380
parsed = parseString(s)
381
#TODO: Add a try-catch for empty guid_value
382
guid = parsed.getElementsByTagName("guid")
384
guid_value = guid[0].firstChild.nodeValue
385
p = re.compile('([^_]*)_')
386
m = p.match(guid_value)
388
self.location_details['yahoo id'] = m.group(1)
389
log.debug("Location: yahoo id is %s" % self.location_details['yahoo id'])
391
log.error("Location: Can't find yahoo id via woeid. Full response:\n %s" % guid_value)
394
log.error("Location: Can't guid in yahoo RSS response. Full response:\n %s" % s)
397
except urllib2.URLError:
398
log.error("Location: error reaching url '%s'" % url)
403
# Return lcoation code and location details
404
def export_location_details(self):
405
return (self.location_code, self.location_details)
407
# Get fresh weather data and store it to weather object
408
def update_weather_data(self, source):
409
# gather existing source keys
414
WeatherDataSource.GOOGLE : ("google id", "Google"),
415
WeatherDataSource.YAHOO : ("yahoo id", "Yahoo"),
418
for source_id in SOURCES.keys():
419
if SOURCES[source_id][0] in self.location_details:
420
loc_ids[source_id] = SOURCES[source_id][0]
422
# try with the default source
423
if source in loc_ids:
424
valid_source = source
425
log.debug(("Location: default weather source '%s' " +
426
"chosen for '%s'") % (SOURCES[valid_source][1],
427
self.location_details['label']))
429
# try with the first alternative
430
elif len(loc_ids.keys()):
431
valid_source = loc_ids.keys()[0]
432
log.debug(("Location: non default weather source '%s' " +
433
"chosen for '%s'") % (SOURCES[valid_source][1],
434
self.location_details['label']))
436
if valid_source is None:
437
log.error(("Location: no valid weather source can be " +
438
"chosen for '%s'") % (
439
self.location_details['label']))
442
self.weather = Weather(
443
self.location_details[loc_ids[valid_source]],
444
valid_source, self.metric_system, self.wind_unit,
445
self.location_details['latitude'],
446
self.location_details['longitude'])
449
""" Class to get forecast information """
451
# Initialize a class with metric system, wind units, location and current user locale
452
def __init__ (self, units, lat, lon, locale):
453
self.metric_system = units
454
self.lat = self.convert_coordinate_for_google(lat)
455
self.lon = self.convert_coordinate_for_google(lon)
458
# Convert coordinate for google
459
def convert_coordinate_for_google(self, value):
460
value = float(value) * 1e6
461
return int(round(value))
463
# Get and store forecast data. For now using Google only
464
def prepare_forecast_data(self):
468
self.error_message = None
470
# Generate a fake location by current coordinates
471
location_name = ",,,%s,%s" % (self.lat, self.lon)
472
self.forecast = pywapi.get_weather_from_google (location_name, hl = self.locale)
473
self.unitsystem = self.forecast['forecast_information']['unit_system']
475
for forecast in self.forecast['forecasts']:
476
self.daysofweek.append(forecast["day_of_week"])
477
forecast_icon = str(forecast["icon"])
478
if "/ig/images/weather/" in forecast_icon:
479
self.icons.append(forecast_icon.split("/ig/images/weather/")[-1].split(".gif")[0])
480
elif "http://g0.gstatic.com/images/icons/onebox" in forecast_icon:
481
self.icons.append(forecast_icon.split("http://g0.gstatic.com/images/icons/onebox/weather_")[-1].split("-40.gif")[0])
482
self.conditions.append(forecast["condition"])
483
self.error_message = None
485
except urllib2.URLError:
486
log.error("Forecast: error reading forecast for %s" % location_name)
488
log.error("Forecast: returned empty forecast %s" % location_name)
489
self.error_message = _('Unknown error occurred while picking up weather data')
491
# Parse high values for forecast data
492
def get_forecast_data(self):
496
if not hasattr(self, 'unitsystem'):
499
if ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.SI)) or ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.IMPERIAL)):
500
#correct scale selected
501
for forecast in self.forecast['forecasts']:
502
self.highdata.append(forecast["high"])
503
self.lowdata.append(forecast["low"])
505
elif ((self.unitsystem == 'SI') and (self.metric_system == MetricSystem.IMPERIAL)):
506
#convert from SI to imperial
507
for forecast in self.forecast['forecasts']:
508
self.highdata.append(int(((int(forecast["high"])*9)/5)+32))
509
self.lowdata.append(int(((int(forecast["low"])*9)/5)+32))
511
elif ((self.unitsystem == 'US') and (self.metric_system == MetricSystem.SI)):
512
#convert from imperial to SI
513
for forecast in self.forecast['forecasts']:
514
self.highdata.append(int((((int(forecast["high"]))-32)*5)/9))
515
self.lowdata.append(int((((int(forecast["low"]))-32)*5)/9))
517
return (self.highdata, self.lowdata)
519
# Parse a list of days of week with forecast data
520
def get_forecast_daysofweek(self):
521
return self.daysofweek
523
# Parse icons for forecast data
524
def get_forecast_icons(self):
527
# Parse conditions for forecast data
528
def get_forecast_conditions(self):
529
return self.conditions
533
Data object to parse weather data with unit convertion
536
#Available conditions by google icon
537
#Format: Google icon name: (day icon, night icon, is a severe weather condition)
538
#Reference: http://www.blindmotion.com/2009/03/google-weather-api-images/
539
_GoogleConditions = {
540
"sunny" : ( "weather-clear", "weather-clear-night", False),
541
"mostly_sunny" : ( "weather-clear", "weather-clear-night", False),
542
"partlycloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
543
"partly_cloudy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
544
"windy" : ( "weather-few-clouds", "weather-few-clouds-night", False),
545
"cloudy" : ( "weather-clouds", "weather-clouds-night", False),
546
"mostlycloudy" : ( "weather-overcast", "weather-overcast", False),
547
"mostly_cloudy" : ( "weather-overcast", "weather-overcast", False),
548
"overcast" : ( "weather-overcast", "weather-overcast", False),
549
"rain" : ( "weather-showers", "weather-showers", False),
550
"chanceofrain" : ( "weather-showers", "weather-showers", False),
551
"chance_of_rain" : ( "weather-showers", "weather-showers", False),
552
"heavyrain" : ( "weather-showers", "weather-showers", False),
553
"drizzle" : ( "weather-showers", "weather-showers", False),
554
"sleet" : ( "weather-snow", "weather-snow", False),
555
"rain_snow" : ( "weather-snow", "weather-snow", False),
556
"rainsnow" : ( "weather-snow", "weather-snow", False),
557
"snow" : ( "weather-snow", "weather-snow", False),
558
"chanceofsnow" : ( "weather-snow", "weather-snow", False),
559
"chance_of_snow" : ( "weather-snow", "weather-snow", False),
560
"heavysnow" : ( "weather-snow", "weather-snow", False),
561
"icy" : ( "weather-snow", "weather-snow", False),
562
"snowflurries" : ( "weather-snow", "weather-snow", False),
563
"dust" : ( "weather-fog", "weather-fog", False),
564
"fog" : ( "weather-fog", "weather-fog", False),
565
"smoke" : ( "weather-fog", "weather-fog", False),
566
"haze" : ( "weather-fog", "weather-fog", False),
567
"mist" : ( "weather-fog", "weather-fog", False),
568
"thunderstorm" : ( "weather-storm", "weather-storm", True),
569
"chance_of_storm" : ( "weather-storm", "weather-storm", True),
570
"thunderstorms" : ( "weather-storm", "weather-storm", True),
571
"scatteredshowers" : ( "weather-showers-scattered", "weather-showers-scattered", True),
572
"scatteredthunderstorms" : ( "weather-storm", "weather-storm", True),
575
#Available conditions by yahoo condition code
576
#Format: condition code: (day icon, night icon, is a severe weather condition, localized condition name)
578
'0' : ("weather-storm", "weather-storm", True, _("Tornado")),
579
'1' : ("weather-storm", "weather-storm", True, _("Tropical storm")),
580
'2' : ("weather-storm", "weather-storm", True, _("Hurricane")),
581
'3' : ("weather-storm", "weather-storm", True, _("Severe thunderstorms")),
582
'4' : ("weather-storm", "weather-storm", True, _("Thunderstorms")),
583
'5' : ("weather-snow", "weather-snow", False, _("Mixed rain and snow")),
584
# Use American meaning of sleet - see http://en.wikipedia.org/wiki/Sleet
585
'6' : ("weather-showers", "weather-showers", False, _("Mixed rain and sleet")),
586
'7' : ("weather-snow", "weather-snow", False, _("Mixed snow and sleet")),
587
'8' : ("weather-showers", "weather-showers", False, _("Freezing drizzle")),
588
'9' : ("weather-showers", "weather-showers", False, _("Drizzle")),
589
'10': ("weather-snow", "weather-snow", False, _("Freezing rain")),
590
'11': ("weather-showers", "weather-showers", False, _("Showers")),
591
'12': ("weather-showers", "weather-showers", False, _("Showers")),
592
'13': ("weather-snow", "weather-snow", False, _("Snow flurries")),
593
'14': ("weather-snow", "weather-snow", False, _("Light snow showers")),
594
'15': ("weather-snow", "weather-snow", False, _("Blowing snow")),
595
'16': ("weather-snow", "weather-snow", False, _("Snow")),
596
'17': ("weather-showers", "weather-showers", False, _("Hail")),
597
'18': ("weather-snow", "weather-snow", False, _("Sleet")),
598
'19': ("weather-fog", "weather-fog", False, _("Dust")),
599
'20': ("weather-fog", "weather-fog", False, _("Foggy")),
600
'21': ("weather-fog", "weather-fog", False, _("Haze")),
601
'22': ("weather-fog", "weather-fog", False, _("Smoky")),
602
'23': ("weather-clear", "weather-clear-night", False, _("Blustery")),
603
'24': ("weather-clear", "weather-clear-night", False, _("Windy")),
604
'25': ("weather-clear", "weather-clear-night", False, _("Cold")),
605
'26': ("weather-clouds", "weather-clouds-night", False, _("Cloudy")),
606
'27': ("weather-clouds", "weather-clouds-night", False, _("Mostly cloudy")),
607
'28': ("weather-clouds", "weather-clouds-night", False, _("Mostly cloudy")),
608
'29': ("weather-few-clouds", "weather-few-clouds-night", False, _("Partly cloudy")),
609
'30': ("weather-few-clouds", "weather-few-clouds-night", False, _("Partly cloudy")),
610
'31': ("weather-clear", "weather-clear-night", False, _("Clear")),
611
'32': ("weather-clear", "weather-clear-night", False, _("Sunny")),
612
'33': ("weather-clear", "weather-clear-night", False, _("Fair")),
613
'34': ("weather-clear", "weather-clear-night", False, _("Fair")),
614
'35': ("weather-showers-scattered", "weather-showers-scattered",False, _("Mixed rain and hail")),
615
'36': ("weather-clear", "weather-clear-night", False, _("Hot")),
616
'37': ("weather-storm", "weather-storm", True, _("Isolated thunderstorms")),
617
'38': ("weather-storm", "weather-storm", True, _("Scattered thunderstorms")),
618
'39': ("weather-storm", "weather-storm", True, _("Scattered thunderstorms")),
619
'40': ("weather-showers-scattered", "weather-showers-scattered",False, _("Scattered showers")),
620
'41': ("weather-snow", "weather-snow", False, _("Heavy snow")),
621
'42': ("weather-snow", "weather-snow", False, _("Scattered snow showers")),
622
'43': ("weather-snow", "weather-snow", False, _("Heavy snow")),
623
'44': ("weather-few-clouds", "weather-few-clouds-night", False, _("Partly cloudy")),
624
'45': ("weather-storm", "weather-storm", True, _("Thundershowers")),
625
'46': ("weather-snow", "weather-snow", False, _("Snow showers")),
626
'47': ("weather-storm", "weather-storm", True, _("Isolated thundershowers")),
627
'3200': (False, False, False, _("Unknown condition"))
630
# Initialize and get fresh data
631
def __init__(self, location_id, weather_datasource, metric_system, wind_unit, lat, lon):
632
self.__weather_datasource = weather_datasource
633
self.__metric_system = metric_system
634
self._wind_unit = wind_unit
635
self.__current_condition = None
639
# Get data from Google
640
if self.__weather_datasource == WeatherDataSource.GOOGLE:
641
# Get data in english locale, then - switch back
642
self.__report = pywapi.get_weather_from_google (location_id, hl = 'en')
643
# Get data in original locale for condition name
644
self.__localized_report = pywapi.get_weather_from_google (location_id, hl = locale_name)
646
if 'current_conditions' not in self.__report.keys():
647
log.error("Weather: could not get Google weather condition from report")
648
log.error("Weather: got data '%s'" % str(self.__report))
649
self.__current_condition = (False, False, False, _("Unknown condition"))
651
if 'current_conditions' not in self.__localized_report.keys():
652
log.error("Weather: could not get Google weather condition from localized report")
653
log.error("Weather: got data '%s'" % str(self.__localized_report))
654
self.__current_condition = (False, False, False, _("Unknown condition"))
656
if 'icon' in self.__report['current_conditions'].keys():
657
icon_path = self.__report['current_conditions']['icon']
658
if '/ig/images/weather/' in icon_path:
659
icon_name = icon_path.replace('/ig/images/weather/', '').replace('.gif', '')
660
elif 'http://g0.gstatic.com/images/icons/onebox' in icon_path:
661
icon_name = icon_path.replace('http://g0.gstatic.com/images/icons/onebox/weather_', '').replace('-40.gif', '')
663
icon_name = icon_path
665
log.error("Weather: could not get weather icon from report")
666
log.error("Weather: got data '%s'" % str(self.__report['current_conditions']))
669
self.__current_condition = self._GoogleConditions.get(icon_name)
670
if self.__current_condition == None:
671
log.error("ExtendedForecast: unknown Google weather condition '%s'" % icon_name)
672
self.__current_condition = (False, False, False, _("Unknown condition"))
674
# Get data from Yahoo
675
if self.__weather_datasource == WeatherDataSource.YAHOO:
676
self.__report = pywapi.get_weather_from_yahoo (location_id, 'imperial')
677
self.__localized_report = self.__report
678
if 'condition' not in self.__report.keys():
679
log.error("Weather: could not get Yahoo weather condition from report")
680
log.error("Weather: got data '%s'" % str(self.__report))
681
self.__current_condition = (False, False, False, _("Unknown condition"))
683
elif 'code' in self.__report['condition'].keys():
684
icon_name = self.__report['condition']['code']
685
self.__current_condition = self._YahooConditions.get(icon_name)
689
log.error("Weather: could not get icon name from Yahoo report")
690
log.error("Weather: got data '%s'" % str(self.__report['condition']))
691
self.__current_condition = (False, False, False, _("Unknown condition"))
693
log.debug("Weather: current condition: '%s', '%s'" % (icon_name, str(self.__current_condition)))
694
#Prepare sunrise/sunset data
697
#Get sunrise/sunset times, calculate whether it is night already
698
def get_sun_data(self):
700
self.__sunrise_t = None
701
self.__sunset_t = None
702
# Grab local datetime and the daylight saving status (1/0)
703
# from earthtools.org
704
url = 'http://www.earthtools.org/timezone-1.1/%s/%s' % \
705
(self.__lat, self.__lon)
707
f = urllib2.urlopen(url)
709
parsed = parseString(s)
710
localtime = parsed.getElementsByTagName(
711
"isotime")[0].firstChild.nodeValue
712
dst = parsed.getElementsByTagName(
713
"dst")[0].firstChild.nodeValue
714
# strip timezone info
715
localtime = datetime.datetime.strptime(localtime.rsplit(' ',1)[0],
717
dst = 1 if dst == "True" else 0
719
except urllib2.URLError:
720
log.error("Weather: error reaching url '%s'" % url)
723
# Grab sunrise/sunset from earthtools.org
724
url = 'http://www.earthtools.org/sun/%s/%s/%s/%s/99/%s' % \
725
(self.__lat, self.__lon, localtime.day, localtime.month, dst)
727
f = urllib2.urlopen(url)
729
parsed = parseString(s)
730
sunrise = parsed.getElementsByTagName("morning")[0].getElementsByTagName("sunrise")[0].firstChild.nodeValue
731
sunset = parsed.getElementsByTagName("evening")[0].getElementsByTagName("sunset")[0].firstChild.nodeValue
732
self.__sunrise_t = datetime.datetime.strptime(sunrise, '%H:%M:%S').time()
733
self.__sunset_t = datetime.datetime.strptime(sunset, '%H:%M:%S').time()
734
except urllib2.URLError:
735
log.error("Weather: error reaching url '%s'" % url)
738
# Calculate, whether it is night or day
739
if localtime.time()<self.__sunrise_t or localtime.time()>self.__sunset_t:
743
log.debug("Weather: got localtime " +
744
"%s, dst %s, sunrise '%s', sunset '%s', night = %s" % (
745
localtime, dst, self.__sunrise_t, self.__sunset_t, self.__night))
747
# Return True, if weather condition is severe
748
def condition_is_severe(self):
749
if self.__current_condition != None:
750
log.debug("Weather: got severe condition '%s'" % self.__current_condition[2])
751
return self.__current_condition[2]
753
log.error("Weather: condition is not set while condition severity check")
756
# Get associated icon name
757
def get_icon_name(self):
758
if self.__current_condition != None:
760
log.debug("Weather: night, show '%s' icon" % self.__current_condition[1])
761
return self.__current_condition[1]
763
log.debug("Weather: day, show '%s' icon" % self.__current_condition[0])
764
return self.__current_condition[0]
766
log.error("Weather: return 'offline' icon due to empty condition")
770
def get_condition_label(self):
771
if self.__weather_datasource == WeatherDataSource.GOOGLE:
772
if 'condition' in self.__localized_report['current_conditions'].keys():
773
condition = self.__localized_report['current_conditions']['condition']
775
condition = _("Unknown condition")
776
if self.__weather_datasource == WeatherDataSource.YAHOO:
777
condition = self.__current_condition[3]
781
def get_humidity_label(self):
782
humidity = "%s: ---%%" % (_("Humidity"))
783
if self.__weather_datasource == WeatherDataSource.GOOGLE \
784
and 'humidity' in self.__localized_report['current_conditions']:
785
humidity = self.__localized_report['current_conditions']['humidity']
786
if self.__weather_datasource == WeatherDataSource.YAHOO \
787
and 'humidity' in self.__localized_report['atmosphere']:
788
humidity = "%s: %s%%" % (_("Humidity"), self.__localized_report['atmosphere']['humidity'])
791
# Get dew point - using in humidex calculation
792
#TODO: Update with NOAA
793
def get_dew_point_label(self):
794
if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:
795
# Not returned by Google and Yahoo
799
def get_pressure_label(self):
800
if self.__weather_datasource == WeatherDataSource.GOOGLE:
801
# TODO: Empty for Google, use NOAA data?
804
if self.__weather_datasource == WeatherDataSource.YAHOO \
805
and 'pressure' in self.__localized_report['atmosphere'].keys() \
806
and 'pressure' in self.__localized_report['units'].keys():
807
value = self.__localized_report['atmosphere']['pressure']
808
unit = self.__localized_report['units']['pressure']
809
return "%s: %s %s" % (_("Pressure"), value, units)
811
# Get temperature with units value - doesn't include 'Temperature' label
812
def get_temperature(self, needs_rounding = False):
815
if self.__weather_datasource == WeatherDataSource.GOOGLE:
816
if (self.__metric_system == MetricSystem.SI) \
817
and 'temp_c' in self.__report['current_conditions'].keys():
818
_value = self.__report['current_conditions']['temp_c']
820
elif 'temp_f' in self.__report['current_conditions'].keys():
821
_value = self.__report['current_conditions']['temp_f']
823
if self.__weather_datasource == WeatherDataSource.YAHOO:
824
if (self.__metric_system == MetricSystem.SI) \
825
and 'temp' in self.__report['condition'].keys():
826
_value = NumberFormatter.format_float(
827
((float(self.__report['condition']['temp']) - 32) * 5/9), 1)
830
_value = self.__report['condition']['temp']
832
# round the value if required
833
if needs_rounding and _value != "---":
834
_value = NumberFormatter.format_float(locale.atof(_value), 0)
835
return ("%s %s" % (_value, _unit))
837
# Get temperature label
838
def get_temperature_label(self):
839
return "%s: %s" % (_("Temperature"), self.get_temperature())
841
# Get humidex parameter
842
def get_humidex_label(self):
843
if self.__weather_datasource == WeatherDataSource.GOOGLE or self.__weather_datasource == WeatherDataSource.YAHOO:
844
#Empty for Yahoo and Google
846
#TODO: Update with NOAA data
849
#self.vapour_pressure = 6.11 * math.exp(5417.7530 * ( (1/273.16) - (1/(dewPoint+273.16))))
850
#self.humidex = temp_c + (0.5555)*(self.vapour_pressure - 10.0);
851
#return ("%s: %.1f" % (_("Humidex"), self.humidex)).replace(".0", "")
854
def get_wind_label(self):
855
if self.__weather_datasource == WeatherDataSource.GOOGLE:
856
# Convert units picked up from Google and replace units with currently configured
857
if 'wind_condition' in self.__localized_report['current_conditions'].keys():
858
localized_wind_info = self.__localized_report['current_conditions']['wind_condition'].split(' ')
859
wind_direction = localized_wind_info[1]
860
wind_info = self.__report['current_conditions']['wind_condition'].split(' ')
861
wind_speed = wind_info[3]
865
if self.__weather_datasource == WeatherDataSource.YAHOO:
866
# Create a similar to Google wind_info structure from Yahoo data
867
wind_direction = "%s (%s˚)" % (self.get_wind_direction(self.__localized_report['wind']['direction']), self.__localized_report['wind']['direction'])
868
wind_speed = self.__localized_report['wind']['speed']
869
wind_units = self.__localized_report['units']['speed']
870
localized_wind_info = [_("Wind") + ":", wind_direction, wind_speed, wind_units]
872
# Parse Wind_direction - convert to selected scale
873
if (self._wind_unit == WindUnits.MPH):
874
_value = float(wind_speed)
875
_unit = __("mph", "mph", _value)
876
if (self._wind_unit == WindUnits.MPS):
877
_value = float(wind_speed) * 0.44704
878
_unit = __("m/s", "m/s", _value)
879
if (self._wind_unit == WindUnits.BEAUFORT):
880
_value = self.get_beaufort_from_mph(float(wind_speed))
882
if (self._wind_unit == WindUnits.KPH):
883
_value = float(wind_speed) * 1.609344
884
_unit = __("km/h", "km/h", _value)
885
if (self._wind_unit == WindUnits.KNOTS):
886
_value = float(wind_speed) * 0.868976241900648
887
_unit = __("knot", "knots", _value)
889
# Join wind_info data in a label
890
localized_wind_info[len(localized_wind_info)-1] = _unit
891
localized_wind_info[len(localized_wind_info)-2] = \
892
NumberFormatter.format_float(_value, 1)
893
return "%s %s %s %s" % (localized_wind_info[0], localized_wind_info[1], \
894
localized_wind_info[2], localized_wind_info[3])
897
def get_sunrise_label(self):
898
return "%s: %s" % (_("Sunrise"), TimeFormatter.format_time(self.__sunrise_t))
901
def get_sunset_label(self):
902
return "%s: %s" % (_("Sunset"), TimeFormatter.format_time(self.__sunset_t))
905
# Additional functions
906
# Convert wind direction from degrees to localized direction
907
def get_wind_direction(self, degrees):
909
degrees = int(degrees)
913
if degrees < 23 or degrees >= 338:
914
#Short wind direction - north
931
# Convert mph to Beufort scale
932
def get_beaufort_from_mph(self, value):
960
class indicator_weather(threading.Thread):
961
""" Indicator class """
962
last_update_time = None
965
# Formats: setting value, object name (for preferences dialog), value assigned (optional)
966
metric_systems = { 'S': ('si', MetricSystem.SI),
967
'I': ('imperial', MetricSystem.IMPERIAL)}
969
weather_sources = { 'G': ('google', WeatherDataSource.GOOGLE),
970
'Y': ('yahoo', WeatherDataSource.YAHOO)}
972
notifications = {'N': 'nonotif',
976
wind_systems = {'mps': ("mps", WindUnits.MPS),
977
'mph': ("mph", WindUnits.MPH),
978
'kph': ("kph", WindUnits.KPH),
979
'beaufort': ("beaufort", WindUnits.BEAUFORT),
980
'knots': ("knots", WindUnits.KNOTS)}
982
# Initializing and reading settings
984
log.debug("Indicator: creating")
985
threading.Thread.__init__(self)
986
self.main_icon = os.path.join
987
self.winder = appindicator.Indicator ("indicator-weather", "weather-indicator", appindicator.CATEGORY_OTHER)
988
self.winder.set_status (appindicator.STATUS_ACTIVE)
989
self.winder.set_attention_icon ("weather-indicator-error")
991
self.menu_update_lock = threading.Lock()
992
self.refreshed_minutes_ago = -1
993
monitor_upower(self.on_system_sleep, self.on_system_resume, log)
995
log.debug("Indicator: reading settings")
996
self.settings = Settings()
997
self.settings.check_autostart()
998
self.settings.prepare_settings_store()
999
self.rate = self.settings.get_value("refresh_rate")
1000
self.unit = self.settings.get_value("unit")
1001
self.notif = self.settings.get_value("notif")
1002
self.wind = self.settings.get_value("wind")
1003
self.source = self.settings.get_value("data_source")
1004
self.placechosen = self.settings.get_value("placechosen")
1005
self.places = str(self.settings.get_value("places"))
1006
self.show_label = self.settings.get_value("show_label")
1008
log.debug("Preferences: got settings: rate=%s, unit=%s, notif=%s, wind=%s, placechosen=%s, places=%s" %
1009
(self.rate, self.unit, self.notif, self.wind, self.placechosen, self.places))
1011
#Setting default values
1012
self.metric_system = MetricSystem.SI
1013
self.wind_unit = WindUnits.MPH
1016
self.condition = None
1021
if self.unit in (False, None):
1023
log.debug("Indicator: could not parse unit, setting to %s" % default_value)
1024
self.settings.set_value("unit", default_value)
1025
self.unit = default_value
1026
self.metric_system = self.metric_systems[self.unit][1]
1029
if self.notif in (False, None):
1031
log.debug("Indicator: could not parse notif, setting to %s" % default_value)
1032
self.settings.set_value("notif", default_value)
1033
self.notif = default_value
1036
if self.wind in (False, None):
1037
default_value = 'mph'
1038
log.debug("Indicator: could not parse notif, setting to %s" % default_value)
1039
self.settings.set_value("wind", default_value)
1040
self.wind = default_value
1041
self.wind_unit = self.wind_systems[self.wind][1]
1043
# Show label in indicator?
1044
self.show_label = True if self.show_label == 1 else False
1047
if self.source in (False, None):
1049
log.debug("Indicator: could not parse data source, setting to %s" % default_value)
1050
self.settings.set_value("data_source", default_value)
1051
self.source = default_value
1052
self.weather_source = self.weather_sources[self.source][1]
1055
if self.rate in (False, None):
1057
log.debug("Indicator: could not parse rate, setting to %s" % str(default_value))
1058
self.settings.set_value("refresh_rate", default_value)
1059
self.rate = default_value
1062
if self.placechosen == None:
1063
log.debug("Indicator: could not parse placechosen, setting to 0")
1064
self.settings.set_value("placechosen", 0)
1065
self.placechosen = 0
1067
self.placechosen = int(self.placechosen)
1070
if self.places in (False, None, '', '[]', "['']"):
1071
log.debug("Indicator: could not parse places")
1074
self.places = eval(self.places)
1075
if self.placechosen >= len(self.places):
1076
self.placechosen = 0
1077
self.place = self.places[self.placechosen]
1078
self.location_details = self.settings.get_location_details(self.place[0])
1079
if self.location_details in (False, None, '', '[]', "['']"):
1080
log.debug("Indicator: could not parse current location details")
1083
self.location_details = eval(self.location_details)
1085
self.update_weather()
1087
# Set a label of indicator
1088
def update_label(self, label):
1089
if (hasattr(self.winder, 'set_label')):
1090
log.debug("Indicator: update_label: setting label to '%s'" % label)
1091
self.previous_label_value = label
1092
self.winder.set_label(label) if self.show_label else self.winder.set_label(" ")
1093
self.winder.set_status(appindicator.STATUS_ATTENTION)
1094
self.winder.set_status(appindicator.STATUS_ACTIVE)
1096
# Show a menu if no places specified
1097
def menu_noplace(self):
1098
log.debug("Indicator: making a menu for no places")
1099
menu_noplace = gtk.Menu()
1101
setup = gtk.MenuItem(_("Set Up Weather..."))
1102
setup.connect("activate", self.prefs)
1104
menu_noplace.append(setup)
1106
quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
1107
quit.connect("activate", self.quit)
1109
menu_noplace.append(quit)
1111
self.winder.set_menu(menu_noplace)
1112
self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png"))
1113
self.winder.set_status(appindicator.STATUS_ATTENTION)
1114
self.winder.set_status(appindicator.STATUS_ACTIVE)
1116
# Show menu with data
1117
def menu_normal(self):
1118
log.debug("Indicator: menu_normal: filling in a menu for found places")
1119
self.menu = gtk.Menu()
1122
self.city_show = gtk.MenuItem()
1123
self.city_show.set_sensitive(True)
1124
self.menu.append(self.city_show)
1125
self.city_show.show()
1128
self.cond_show = gtk.MenuItem()
1129
self.cond_show.set_sensitive(True)
1130
self.cond_show.show()
1131
self.menu.append(self.cond_show)
1134
self.temp_show = gtk.MenuItem()
1135
self.temp_show.set_sensitive(True)
1136
self.temp_show.show()
1137
self.menu.append(self.temp_show)
1140
self.humidex_show = gtk.MenuItem()
1141
self.humidex_show.set_sensitive(True)
1142
self.humidex_show.show()
1143
self.menu.append(self.humidex_show)
1146
self.humid_show = gtk.MenuItem()
1147
self.humid_show.set_sensitive(True)
1148
self.humid_show.show()
1149
self.menu.append(self.humid_show)
1152
self.wind_show = gtk.MenuItem()
1153
self.wind_show.set_sensitive(True)
1154
self.wind_show.show()
1155
self.menu.append(self.wind_show)
1158
self.sunrise_show = gtk.MenuItem()
1159
self.sunrise_show.set_sensitive(True)
1160
self.sunrise_show.show()
1161
self.menu.append(self.sunrise_show)
1164
self.sunset_show = gtk.MenuItem()
1165
self.sunset_show.set_sensitive(True)
1166
self.sunset_show.show()
1167
self.menu.append(self.sunset_show)
1170
if len(self.places) != 1:
1172
breaker = gtk.SeparatorMenuItem()
1174
self.menu.append(breaker)
1176
log.debug("Indicator: menu_normal: adding first location menu item '%s'" % self.places[0][1])
1177
loco1 = gtk.RadioMenuItem(None, self.places[0][1])
1178
if self.placechosen == 0:
1179
loco1.set_active(True)
1180
loco1.connect("toggled", self.on_city_changed)
1182
self.menu.append(loco1)
1183
for place in self.places[1:]:
1184
log.debug("Indicator: menu_normal: adding location menu item '%s'" % place[1])
1185
loco = gtk.RadioMenuItem(loco1, place[1])
1186
if self.places.index(place) == self.placechosen:
1187
loco.set_active(True)
1188
loco.connect("toggled", self.on_city_changed)
1190
self.menu.append(loco)
1193
breaker = gtk.SeparatorMenuItem()
1195
self.menu.append(breaker)
1197
self.refresh_show = gtk.MenuItem()
1198
#label will be set later
1199
self.refresh_show.connect("activate", self.update_weather)
1200
self.refresh_show.show()
1201
self.menu.append(self.refresh_show)
1203
ext_show = gtk.MenuItem(_("Forecast"))
1204
ext_show.connect("activate", self.extforecast)
1206
self.menu.append(ext_show)
1209
prefs_show = gtk.MenuItem(_("Preferences..."))
1210
prefs_show.connect("activate", self.prefs)
1212
self.menu.append(prefs_show)
1215
about_show = gtk.MenuItem(_("About..."))
1216
about_show.connect("activate", self.about)
1218
self.menu.append(about_show)
1221
quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
1222
quit.connect("activate", self.quit)
1224
self.menu.append(quit)
1226
self.winder.set_menu(self.menu)
1227
self.update_label(" ")
1229
# Another city has been selected from radiobutton
1230
def on_city_changed(self,widget):
1231
if widget.get_active():
1232
for place in self.places:
1233
if (place[1] == widget.get_label()):
1234
log.debug("Indicator: new location selected: %s" % self.places.index(place))
1235
self.placechosen = self.places.index(place)
1238
if self.placechosen >= len(self.places):
1239
self.placechosen = 0
1240
self.place = self.places[self.placechosen]
1241
self.location_details = self.settings.get_location_details(self.place[0])
1242
if self.location_details in (False, None, '', '[]', "['']"):
1243
log.debug("Indicator: could not parse location details for placechosen='%s'" % self.placechosen)
1246
self.location_details = eval(self.location_details)
1247
self.settings.set_value("placechosen", self.placechosen)
1248
self.update_weather(False)
1250
def on_system_sleep(self):
1252
Callback from UPower that system suspends/hibernates
1255
self.sleep_time = datetime.datetime.now()
1256
log.debug("Indicator: system goes to sleep at %s" % self.sleep_time)
1257
# remove gobject timeouts
1258
if hasattr(self, "refresh_id"):
1259
gobject.source_remove(self.refresh_id)
1260
if hasattr(self, "rate_id"):
1261
gobject.source_remove(self.rate_id)
1263
def on_system_resume(self):
1265
Callback from UPower that system resumes
1267
now = datetime.datetime.now()
1268
log.debug("Indicator: system resumes at %s" % now)
1269
# if we have places set
1270
if isinstance(self.places, types.ListType) and len(self.places)>0:
1271
td = now - self.sleep_time
1272
mins_elapsed = td.days/24*60 + td.seconds/60
1273
# update refresh label
1274
if self.refreshed_minutes_ago >= 0:
1275
mins_elapsed += self.refreshed_minutes_ago
1276
self.update_refresh_label(mins_elapsed)
1277
# check if we need to update the weather now or to reschedule the update
1278
min_diff = int(self.rate) - mins_elapsed
1280
self.schedule_weather_update(min_diff)
1282
self.update_weather()
1284
# Schedule weather update
1285
def schedule_weather_update(self, rate_override = None):
1286
if hasattr(self, "rate_id"):
1287
gobject.source_remove(self.rate_id)
1289
self.rate_id = gobject.timeout_add(
1290
int(rate_override) * 60000, self.update_weather)
1292
self.rate_id = gobject.timeout_add(
1293
int(self.rate) * 60000, self.update_weather)
1295
# Schedule weather update
1296
def schedule_refresh_label_update(self):
1297
if hasattr(self, "refresh_id"):
1298
gobject.source_remove(self.refresh_id)
1299
self.refresh_id = gobject.timeout_add(60000, self.update_refresh_label)
1301
# Update 'Refresh' label with time since last successful data refresh
1302
def update_refresh_label(self, reset_minutes = None):
1303
if reset_minutes is not None:
1304
self.refreshed_minutes_ago = reset_minutes
1306
self.refreshed_minutes_ago += 1
1307
self.set_refresh_label()
1308
self.schedule_refresh_label_update()
1311
def set_refresh_label(self, refreshing=False):
1313
refresh_label=_("Refreshing, please wait")
1314
elif self.refreshed_minutes_ago < 0:
1315
refresh_label=_("Refresh")
1316
elif self.refreshed_minutes_ago == 0:
1317
refresh_label="%s (%s)" % (_("Refresh"), _("just now"))
1319
refresh_label = "%s (%s)" % (_("Refresh"), _("%d min. ago") % self.refreshed_minutes_ago)
1320
self.refresh_show.set_label(refresh_label)
1322
# Load weather data from cache and display its values
1323
def show_cached_weather(self):
1325
self.menu_update_lock.acquire(True)
1326
self.previous_condition = None
1327
cached_weather = self.settings.get_weather(self.places[self.placechosen][0])
1328
if cached_weather is not None:
1329
cached_weather = eval(cached_weather)
1330
log.debug("Indicator: loading weather from cache for %s" % self.places[self.placechosen])
1332
self.set_refresh_label(True)
1333
self.icon = cached_weather['icon']
1334
if (self.icon == False):
1335
self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))
1337
self.winder.set_icon(self.icon)
1339
self.city_show.set_label(self.places[self.placechosen][1])
1340
self.previous_condition = cached_weather['condition']
1341
self.cond_show.set_label(cached_weather['condition'])
1342
self.temp_show.set_label(cached_weather['temper'])
1343
if cached_weather['humidex'] != None:
1344
self.humidex_show.set_label(cached_weather['humidex'])
1346
self.humidex_show.destroy()
1347
self.humid_show.set_label(cached_weather['humidity'])
1348
self.wind_show.set_label(cached_weather['wind'])
1349
self.sunrise_show.set_label(cached_weather['sunrise'])
1350
self.sunset_show.set_label(cached_weather['sunset'])
1351
self.update_label(cached_weather['label'])
1352
self.winder.set_status(appindicator.STATUS_ATTENTION)
1353
self.winder.set_status(appindicator.STATUS_ACTIVE)
1355
except Exception, e:
1357
log.debug(traceback.format_exc(e))
1359
self.menu_update_lock.release()
1361
# Get fresh weather data
1362
def get_new_weather_data(self, notif = True):
1364
# get weather and catch any exception
1367
weather = self.get_weather()
1369
except urllib2.URLError, e:
1371
log.error("Indicator: networking error: %s" % e)
1373
except Exception, e:
1376
log.debug(traceback.format_exc(e))
1379
# wait until cacher finishes
1380
log.debug("Indicator: updateWeather: waiting for 'Cacher' thread to terminate")
1381
self.menu_update_lock.acquire(True)
1382
self.menu_update_lock.release()
1385
# remove the "Refreshing" status
1386
self.set_refresh_label()
1387
# No data returned - leave cached data to be displayed
1388
log.error("Indicator: updateWeather: could not get weather, leaving cached data")
1389
# Repeat an attempt in one minute
1390
self.schedule_weather_update(1)
1393
# Fill in menu with data
1394
log.debug("Indicator: updateWeather: got condition '%s', icon '%s'" % (self.condition, self.icon))
1395
self.condition = weather.get_condition_label()
1396
self.icon = weather.get_icon_name()
1397
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()))
1400
self.update_refresh_label(0)
1401
self.city_show.set_label(self.places[self.placechosen][1])
1402
self.cond_show.set_label(self.condition)
1403
self.temp_show.set_label(weather.get_temperature_label())
1404
if (weather.get_humidex_label() != None):
1405
self.humidex_show.set_label(weather.get_humidex_label())
1407
self.humidex_show.destroy()
1408
self.humid_show.set_label(weather.get_humidity_label())
1409
self.wind_show.set_label(weather.get_wind_label())
1410
self.sunrise_show.set_label(weather.get_sunrise_label())
1411
self.sunset_show.set_label(weather.get_sunset_label())
1413
# Saving cached data, unless correct icon is supplied
1414
if (self.icon == False):
1415
self.winder.set_icon(os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon_unknown_condition.png"))
1417
self.winder.set_icon(self.icon)
1418
self.settings.save_weather(weather, self.places[self.placechosen][0])
1419
self.update_label(weather.get_temperature(needs_rounding=True))
1421
# Notify user, if notifications are enabled
1422
if self.condition != self.previous_condition and self.notif == 'U':
1423
# Weather condition has changed
1424
log.debug("Indicator: updateWeather: weather has changed, notify")
1425
self.notify(self.condition, self.icon)
1426
if self.notif == 'S' and weather.condition_is_severe():
1427
# Severe weather condition notification
1428
log.debug("Indicator: updateWeather: severe condition notification")
1429
self.notify(self.condition, self.icon, severe=True)
1431
self.previous_condition = self.condition
1433
except Exception, e:
1435
log.debug(traceback.format_exc(e))
1437
self.schedule_weather_update()
1440
def update_weather(self, notif=True, widget=None):
1441
log.debug("Indicator: updateWeather: updating weather for %s" % self.places[self.placechosen])
1442
# First, display cached data
1443
threading.Thread(target=self.show_cached_weather, name='Cache').start()
1444
# Then, start a new thread with real data pickup
1445
threading.Thread(target=self.get_new_weather_data, name='Fetcher').start()
1447
# Get current weather for selected location
1448
def get_weather(self):
1449
log.debug("Indicator: getWeather for location '%s'" % self.location_details['full name'])
1450
self.current_location = Location(self.metric_system, self.wind_unit, self.location_details)
1451
log.debug("Indicator: getWeather: updating weather report")
1452
self.current_location.update_weather_data(self.weather_source)
1453
return self.current_location.weather
1455
# Show notification to user
1456
def notify(self,conditon,icon,severe=False):
1457
log.debug("Indicator: Notify on weather condition, severe=%s, condition=%s, icon=%s" % (severe, self.condition, icon))
1459
n = pynotify.Notification (_("Severe weather alert"),
1463
n = pynotify.Notification (self.condition, "", icon)
1467
# Open Preferences dialog
1468
def prefs(self, widget):
1469
log.debug("Indicator: open Preferences")
1470
if ((not hasattr(self, 'prefswindow')) or (not self.prefswindow.get_visible())):
1471
self.prefswindow = PreferencesDialog()
1472
self.prefswindow.show()
1474
def about(self, widget):
1475
log.debug("Indicator: open About dialog")
1476
self.aboutdialog = gtk.AboutDialog()
1477
self.aboutdialog.set_name(_("Weather Indicator"))
1478
self.aboutdialog.set_version(VERSION)
1480
ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/doc/indicator-weather/AUTHORS"), "r")
1481
self.aboutdialog.set_copyright(ifile.read().replace('\x0c', ''))
1484
ifile = open(os.path.join(PROJECT_ROOT_DIRECTORY, "share/common-licenses/GPL-3"), "r")
1485
self.aboutdialog.set_license(ifile.read().replace('\x0c', ''))
1488
self.aboutdialog.set_website("https://launchpad.net/weather-indicator")
1489
self.aboutdialog.set_translator_credits(_("translator-credits"))
1490
logo_path = os.path.join(PROJECT_ROOT_DIRECTORY, "share/indicator-weather/media/icon.png")
1491
self.aboutdialog.set_logo(gtk.gdk.pixbuf_new_from_file(logo_path))
1494
self.aboutdialog.connect("response", self.about_close)
1495
self.aboutdialog.show()
1497
def about_close(self, widget, event=None):
1498
log.debug("Indicator: closing About dialog")
1499
self.aboutdialog.destroy()
1501
# Open Extended forecast window
1502
def extforecast(self, widget):
1503
log.debug("Indicator: open Forecast")
1504
if ((not hasattr(self, 'forecastwd')) or (not self.forecastwd.get_visible())):
1505
self.forecastwd = ExtendedForecast()
1506
self.forecastwd.show()
1509
def quit(self, widget, data=None):
1510
log.debug("Indicator: Quitting")
1513
class PreferencesDialog(gtk.Dialog):
1514
""" Class for preferences dialog """
1515
__gtype_name__ = "PreferencesDialog"
1517
# Creating a new preferences dialog
1519
log.debug("Preferences: creating")
1520
builder = get_builder('PreferencesDialog')
1521
new_object = builder.get_object("preferences_dialog")
1522
new_object.finish_initializing(builder)
1525
# Fill in preferences dialog with currect data
1526
def finish_initializing(self, builder):
1527
log.debug("Preferences: finishing initialization")
1528
log.debug("Preferences: got settings: unit=%s, notif=%s, wind=%s, rate=%s, source=%s" %
1529
(wi.unit, wi.notif, wi.wind, wi.rate, wi.source))
1530
self.builder = builder
1532
# Set correct wind_unit using dictionary of wind value and object name
1533
self.builder.get_object(wi.metric_systems[wi.unit][0]).set_active(True)
1534
self.builder.get_object(wi.notifications[wi.notif]).set_active(True)
1535
self.builder.get_object(wi.wind_systems[wi.wind][0]).set_active(True)
1536
self.builder.get_object(wi.weather_sources[wi.source][0]).set_active(True)
1537
self.builder.get_object('show_label').set_active(wi.show_label)
1538
self.builder.get_object('show_label').set_visible(hasattr(wi.winder, 'set_label'))
1539
self.builder.get_object('rate').set_value(float(wi.rate))
1541
log.debug("Preferences: Loading places")
1542
if wi.places != None:
1543
for place in wi.places:
1545
log.debug("Preferences: Places: got (%s, %s)" % (place[1], place[0]))
1547
newplace.append(place[1])
1548
newplace.append(place[0])
1549
newplace.append(wi.settings.get_location_details(place[0]))
1551
self.builder.get_object('citieslist').append(newplace)
1552
self.builder.get_object('ok_button').set_sensitive(True)
1554
self.builder.connect_signals(self)
1556
# 'Remove' clicked - remove location from list
1557
#TODO: Update settings object
1558
def on_remove_location(self, widget):
1559
selection = self.builder.get_object('location_list').get_selection()
1560
model, iter = selection.get_selected()
1562
log.debug("Preferences: Removing location %s (code %s)" % (model[iter][0], model[iter][1]))
1565
if (self.builder.get_object('citieslist').get_iter_first() == None):
1566
self.builder.get_object('ok_button').set_sensitive(False)
1568
# 'Add' clicked - create a new Assistant
1569
def on_add_location(self, widget):
1570
log.debug("Preferences: Add location clicked")
1571
if ((not hasattr(self, 'assistant')) or (not self.assistant.get_visible())):
1572
self.assistant = Assistant()
1573
self.assistant.show()
1575
# 'OK' clicked - save settings
1576
def ok(self, widget, data=None):
1577
log.debug("Preferences: Saving settings")
1578
need_to_update_weather = False
1579
need_to_update_indicator = False
1581
#Show label near icon
1582
new_show_label = self.builder.get_object('show_label').get_active()
1583
if (wi.show_label != new_show_label):
1584
wi.show_label = new_show_label
1585
wi.settings.set_value("show_label", new_show_label)
1586
need_to_update_weather = False
1587
need_to_update_indicator = True
1588
log.debug("Preferences: Show Label changed to '%s'" % wi.show_label)
1591
for k in wi.metric_systems.keys():
1592
if self.builder.get_object(wi.metric_systems[k][0]).get_active():
1594
new_metric_system = wi.metric_systems[k][1]
1596
if (wi.unit != new_unit):
1598
wi.metric_system = new_metric_system
1599
wi.settings.set_value("unit", wi.unit)
1600
need_to_update_weather = True
1601
log.debug("Preferences: Unit changed to '%s'" % wi.unit)
1604
for k in wi.notifications.keys():
1605
if self.builder.get_object(wi.notifications[k]).get_active():
1606
new_notification = k
1608
if (wi.notif != new_notification):
1609
wi.notif = new_notification
1610
wi.settings.set_value("notif", wi.notif)
1611
need_to_update_weather = True
1612
log.debug("Preferences: Notifications changed to '%s'" % wi.notif)
1615
for k in wi.wind_systems.keys():
1616
if self.builder.get_object(wi.wind_systems[k][0]).get_active():
1618
new_wind_system = wi.wind_systems[k][1]
1620
if (wi.wind != new_wind_unit):
1621
wi.wind = new_wind_unit
1622
wi.wind_unit = new_wind_system
1623
wi.settings.set_value("wind", wi.wind)
1624
need_to_update_weather = True
1625
log.debug("Preferences: Wind Unit changed to '%s'" % wi.wind)
1628
for k in wi.weather_sources.keys():
1629
if self.builder.get_object(wi.weather_sources[k][0]).get_active():
1631
new_weather_source = wi.weather_sources[k][1]
1633
if (wi.source != new_source):
1634
wi.source = new_source
1635
wi.weather_source = new_weather_source
1636
wi.settings.set_value("data_source", wi.source)
1637
need_to_update_weather = True
1638
log.debug("Preferences: Weather Source changed to '%s'" % wi.source)
1641
if int(self.builder.get_object('rate').get_value()) != wi.rate:
1642
wi.settings.set_value("refresh_rate", int(self.builder.get_object('rate').get_value()))
1643
wi.rate = int(self.builder.get_object('rate').get_value())
1644
log.debug("Preferences: Rate changed to '%s'" % wi.rate)
1645
wi.schedule_weather_update()
1647
# Get places from location list
1649
item = self.builder.get_object('citieslist').get_iter_first()
1650
while (item != None):
1652
newplace.append(self.builder.get_object('citieslist').get_value (item, 1))
1653
newplace.append(self.builder.get_object('citieslist').get_value (item, 0))
1654
newplaces.append(newplace)
1655
item = self.builder.get_object('citieslist').iter_next(item)
1657
# If places have changed - update weather data
1658
if newplaces != wi.places:
1659
wi.places = newplaces
1660
log.debug("Preferences: Places changed to '%s'" % str(wi.places))
1661
wi.settings.set_value("places", str(wi.places))
1662
if (type(wi.place) != None) and (wi.place in wi.places):
1663
wi.placechosen = wi.places.index(wi.place)
1666
wi.place = wi.places[0]
1667
log.debug("Preferences: Place Chosen changed to '%s'" % wi.placechosen)
1668
wi.settings.set_value("placechosen", wi.placechosen)
1669
wi.location_details = eval(wi.settings.get_location_details(wi.place[0]))
1671
wi.set_refresh_label()
1672
need_to_update_weather = True
1674
if need_to_update_weather:
1675
wi.update_weather(False)
1677
if need_to_update_indicator:
1678
wi.update_label(wi.previous_label_value)
1682
# 'Cancel' click - forget all changes
1683
def cancel(self, widget, data=None):
1684
log.debug("Preferences: Cancelling")
1687
class ExtendedForecast(gtk.Window):
1688
""" Class for forecast window """
1689
__gtype_name__ = "ExtendedForecast"
1693
log.debug("ExtendedForecast: creating")
1694
builder = get_builder('ExtendedForecast')
1695
new_object = builder.get_object("extended_forecast")
1696
new_object.finish_initializing(builder)
1699
# Fill in forecast parameters
1700
def finish_initializing(self, builder):
1701
log.debug("ExtendedForecast: finishing initialization")
1702
self.builder = builder
1703
self.builder.connect_signals(self)
1705
# Get forecast data using Forecast object
1706
log.debug("ExtendedForecast: chosen place: %s (code %s)" % (wi.places[wi.placechosen][1], wi.places[wi.placechosen][0]))
1707
self.builder.get_object('extended_forecast').set_title("%s %s" % (_('Weather Forecast for '), wi.places[wi.placechosen][1]))
1708
log.debug("ExtendedForecast: getting forecast data")
1709
forecast = Forecast(wi.metric_system, wi.current_location.location_details['latitude'], wi.current_location.location_details['longitude'], locale_name)
1710
forecast.prepare_forecast_data()
1711
if forecast.error_message != None:
1712
#Error occurred while getting forecast data
1713
self.builder.get_object('connection_error').set_text("%s" % forecast.error_message)
1714
self.builder.get_object('connection_error').set_visible(True)
1715
self.builder.get_object('hbox1').set_visible(False)
1717
daysofweek = forecast.get_forecast_daysofweek()
1718
forecast_data = forecast.get_forecast_data()
1719
if forecast_data == None:
1720
# Forecast data unavailable - hide elements and show 'connection_error' label
1721
self.builder.get_object('connection_error').set_visible(True);
1722
self.builder.get_object('hbox1').set_visible(False);
1723
self.builder.get_object('hseparator1').set_visible(False);
1725
(highdata, lowdata) = forecast_data
1726
icons = forecast.get_forecast_icons()
1727
conditions = forecast.get_forecast_conditions()
1729
log.debug("ExtendedForecast: parsing forecast data")
1730
# Create labels for each weekday
1731
self.builder.get_object('day1lbl').set_label('<big>%s</big>' % daysofweek[0].capitalize())
1732
self.builder.get_object('day2lbl').set_label('<big>%s</big>' % daysofweek[1].capitalize())
1733
self.builder.get_object('day3lbl').set_label('<big>%s</big>' % daysofweek[2].capitalize())
1734
self.builder.get_object('day4lbl').set_label('<big>%s</big>' % daysofweek[3].capitalize())
1737
for i in xrange(1,5):
1738
# Get icon name from dictionary in Weather object for Google icons
1740
conds = Weather._GoogleConditions.get(icons[i-1])
1742
google_icon = conds[0]
1744
log.error("ExtendedForecast: unknown Google weather condition '%s'" % icons[i-1])
1745
log.error(forecast.forecast)
1746
google_icon = 'weather-indicator-unknown'
1747
self.builder.get_object('day%simage' % str(i)).set_from_icon_name(google_icon,gtk.ICON_SIZE_BUTTON)
1749
log.error("ExtendedForecast: Google didn't return condition for %s days" % i-1)
1750
log.error(forecast.forecast)
1752
# Fill in condition labels
1753
for i in xrange(1,5):
1754
if conditions[i-1] != '':
1755
condition = conditions[i-1]
1757
condition = _("Unknown condition")
1758
self.builder.get_object('day%scond' % str(i)).set_label(condition)
1760
# Fill in High and Low temperatures
1761
if wi.metric_system == MetricSystem.SI:
1765
for i in xrange(1,5):
1766
label = "%s: %s%s" % (_('High'), highdata[i-1],tempunit)
1767
self.builder.get_object('day%stemphigh' % str(i)).set_label(label)
1768
label = "%s: %s%s" % (_('Low'), lowdata[i-1],tempunit)
1769
self.builder.get_object('day%stemplow' % str(i)).set_label(label)
1771
# Closing forecast window
1772
def close(self, widget, data=None):
1773
log.debug("ExtendedForecast: closing window")
1776
def on_destroy(self, widget):
1779
class Assistant(gtk.Assistant):
1780
""" Class for a wizard, which helps to add a new location in location list """
1781
__gtype_name__ = "Assistant"
1785
log.debug("Assistant: creating new Assistance instance")
1786
builder = get_builder('Assistant')
1787
new_object = builder.get_object("assistant")
1788
new_object.finish_initializing(builder)
1791
# Finish UI initialization - prepare combobox
1792
def finish_initializing(self, builder):
1793
log.debug("Assistant: finishing initialization")
1794
self.builder = builder
1795
self.builder.connect_signals(self)
1796
self.assistant = self.builder.get_object("assistant")
1797
self.assistant.set_page_complete(self.builder.get_object("label"),True)
1798
self.assistant.set_page_complete(self.builder.get_object("review"),True)
1801
log.debug("Assistant: setting up location combobox")
1802
self.store = gtk.ListStore(str, str, str, str, str)
1803
self.location_input_combo = self.builder.get_object("combolocations")
1804
self.location_input_combo.set_model(self.store)
1805
self.location_input_combo.set_text_column(0)
1806
self.location_entry = self.builder.get_object("entrylocation")
1807
self.place_selected = None
1808
self.location = None
1810
self.assistant.set_forward_page_func(self.next_page)
1812
# 'Get cities' button clicked - get suggested cities list
1813
def on_get_city_names(self, widget):
1814
new_text = self.location_entry.get_text()
1815
log.debug("Assistant: looking for location '%s'" % new_text)
1817
# Clear up exising suggestions
1819
# Get suggested city names from GeoNames DB in native locale
1820
new_text = urllib.quote(new_text)
1821
url = 'http://api.geonames.org/searchJSON?q=%s&featureClass=P&maxRows=10&lang=%s&username=indicatorweather' % (new_text, locale_name)
1822
cities = eval(urllib2.urlopen(url).read())
1823
for city in cities['geonames']:
1824
# Create a full city name, consisting of city name, administrative areas names and country name
1825
if 'adminName2' in city:
1826
displayed_city_name = "%s, %s, %s, %s" % (city['name'], city['adminName1'], city['adminName1'], city['countryName'])
1827
elif 'adminName1' in city:
1828
displayed_city_name = "%s, %s, %s" % (city['name'], city['adminName1'], city['countryName'])
1830
displayed_city_name = "%s, %s" % (city['name'], city['countryName'])
1831
self.store.append([displayed_city_name, str(city['geonameId']), str(city['lat']), str(city['lng']), str(city['name'])])
1832
self.location_input_combo.popup()
1833
except urllib2.URLError:
1834
log.error("Assistant: error reaching url '%s'" % url)
1836
# A city is selected from suggested list
1837
def on_select_city(self, entry):
1838
if self.location_input_combo.get_active() != -1:
1839
self.place_selected = self.store[self.location_input_combo.get_active()]
1840
self.assistant.set_page_complete(self.builder.get_object("placeinput"),True)
1842
self.place_selected = None
1843
self.location = None
1844
self.assistant.set_page_complete(self.builder.get_object("placeinput"), False)
1846
# Create a location object out of a selected location
1847
def next_page(self, current_page):
1848
log.debug("Assistant: moved to page %s" % current_page)
1849
if (self.assistant.get_current_page() == 0) and not self.location and self.place_selected:
1851
log.debug("Assistant: Page %s: got location with code %s" % (current_page, self.place_selected[1]))
1852
self.location = Location(wi.metric_system, wi.wind_unit)
1853
if self.location.prepare_location(self.place_selected):
1854
log.debug("Assistant: Page %s: City %s found" % (current_page, self.place_selected[0]))
1855
# Set a short city name as default label
1856
self.builder.get_object("entrylbl").set_text(self.place_selected[4])
1858
log.error("Assistant: Page %s: City with code %s was NOT found" % (current_page, self.place_selected[0]))
1860
elif self.assistant.get_current_page() == 1:
1862
lbl = self.builder.get_object("entrylbl").get_text()
1863
log.debug("Assistant: Page %s: City label is %s" % (current_page, lbl))
1864
# If empty label was input, set label to short city name
1866
log.debug("Assistant: Page %s: Empty label found, setting lbl to short name - %s" % (current_page, self.place_selected[4]))
1867
lbl = self.place_selected[4]
1868
self.location.location_details['label'] = lbl
1869
self.builder.get_object("lbl3").set_label(_('Label:'))
1870
self.builder.get_object("labellbl").set_label('<b>%s</b>' % lbl)
1871
self.builder.get_object("placelbl").set_label('<b>%s</b>' % self.place_selected[0])
1873
return self.assistant.get_current_page() + 1
1876
def on_cancel(self,widget):
1877
log.debug("Assistant: Cancelled")
1880
# 'Apply' clicked - save location details, add an entry in a location list
1881
def on_apply(self,widget):
1882
(location_code, location_details) = self.location.export_location_details()
1883
log.debug("Assistant: Apply: adding location ('%s', '%s')" % (self.location.location_details['label'], location_code))
1885
newplace.append(self.location.location_details['label'])
1886
newplace.append(str(location_code))
1887
newplace.append(str(location_details))
1888
wi.settings.save_location_details(eval(str(location_details)), str(location_code))
1889
wi.prefswindow.builder.get_object('citieslist').append(newplace)
1890
# Enable 'OK' button in Preferences
1891
wi.prefswindow.builder.get_object('ok_button').set_sensitive(True)
1894
class SingleInstance(object):
1895
""" Class to ensure, that single instance of the applet is run for each user """
1897
# Initialize, specifying a path to store pids
1898
def __init__(self, pidPath):
1899
self.pidPath=pidPath
1900
# See if pidFile exists
1901
if os.path.exists(pidPath):
1902
log.debug("SingleInstance: pid file %s exists" % pidPath)
1903
# Make sure it is not a "stale" pidFile
1904
pid=open(pidPath, 'r').read().strip()
1905
# Check list of running pids, if not running it is stale so overwrite
1906
pidRunning = commands.getoutput('ls /proc | grep %s' % pid)
1907
log.debug("SingleInstance: pid running %s" % pidRunning)
1908
self.lasterror = True if pidRunning else False
1910
self.lasterror = False
1912
if not self.lasterror:
1913
log.debug("SingleInstance: writing new pid %s" % str(os.getpid()))
1914
# Create a temp file, copy it to pidPath and remove temporary file
1915
(fp, temp_path)=tempfile.mkstemp()
1917
os.fdopen(fp, "w+b").write(str(os.getpid()))
1918
shutil.copy(temp_path, pidPath)
1919
os.unlink(temp_path)
1920
except Exception as e:
1921
log.error("SingleInstance: exception while renaming '%s' to '%s':\n %s" % (temp_path, pidPath, str(e)))
1923
def is_already_running(self):
1924
return self.lasterror
1927
if not self.lasterror:
1928
log.debug("SingleInstance: deleting %s" % self.pidPath)
1929
os.unlink(self.pidPath)
1935
if __name__ == "__main__":
1936
#Enable and configure logs
1938
cachedir = os.environ.get('XDG_CACHE_HOME','').strip()
1940
cachedir = os.path.expanduser("~/.cache")
1941
log_filename = os.path.join(cachedir, "indicator-weather.log")
1942
log = logging.getLogger('IndicatorWeather')
1943
log.propagate = False
1944
log.setLevel(logging.DEBUG)
1945
log_handler = logging.handlers.RotatingFileHandler(log_filename, maxBytes=1024*1024, backupCount=5)
1946
log_formatter = logging.Formatter("[%(threadName)s] %(asctime)s - %(levelname)s - %(message)s")
1947
log_handler.setFormatter(log_formatter)
1948
log.addHandler(log_handler)
1950
log.info("------------------------------")
1951
log.info("Started Weather Indicator from %s" % PROJECT_ROOT_DIRECTORY)
1952
log.info("Weather Indicator version %s" % VERSION)
1954
# Single instance stuff for weather indicator
1955
myapp = SingleInstance("/tmp/indicator-weather-%d.pid" % os.getuid())
1956
# check is another instance of same program running
1957
if myapp.is_already_running():
1958
log.info("Another instance of this program is already running")
1959
sys.exit(_("Another instance of this program is already running"))
1961
# Set http proxy support
1962
ProxyMonitor.monitor_proxy(log)
1963
# Use date-time format as in indicator-datetime
1964
TimeFormatter.monitor_indicator_datetime(log)
1966
# not running, safe to continue...
1967
gtk.gdk.threads_init()
1968
gtk.gdk.threads_enter()
1969
# Remember locale name
1971
locale_name = locale.getlocale()[0]
1972
if locale_name is not None:
1973
locale_name = locale_name.split('_')[0]
1975
locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale
1977
wi = indicator_weather()
1979
gtk.gdk.threads_leave()