Author: duncan
Date: Sat Sep 30 15:22:34 2006
New Revision: 8287

Added:
   branches/rel-1-5/freevo/share/images/weather/
   branches/rel-1-5/freevo/share/images/weather/cloudy.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/fair.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/flurries.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/fog.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/lshowers.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/mcloudy.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/pcloudy.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/rainsnow.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/showers.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/snowshow.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/sunny.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/thunshowers.png   (contents, 
props changed)
   branches/rel-1-5/freevo/share/images/weather/unknown.png   (contents, props 
changed)
   branches/rel-1-5/freevo/share/images/weather/weather_frame.png   (contents, 
props changed)
   branches/rel-1-5/freevo/share/images/weather/weather_watermark.png   
(contents, props changed)
   branches/rel-1-5/freevo/share/images/weather/weathertypes.dat
   branches/rel-1-5/freevo/share/skins/plugins/weather.fxd
   branches/rel-1-5/freevo/src/plugins/weather.py

Log:
Added plugin weather-0.8

Added: branches/rel-1-5/freevo/share/images/weather/cloudy.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/fair.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/flurries.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/fog.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/lshowers.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/mcloudy.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/pcloudy.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/rainsnow.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/showers.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/snowshow.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/sunny.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/thunshowers.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/unknown.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/weather_frame.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/weather_watermark.png
==============================================================================
Binary file. No diff available.

Added: branches/rel-1-5/freevo/share/images/weather/weathertypes.dat
==============================================================================
--- (empty file)
+++ branches/rel-1-5/freevo/share/images/weather/weathertypes.dat       Sat Sep 
30 15:22:34 2006
@@ -0,0 +1,116 @@
+1,Cloudy,cloudy.png
+3,Mostly Cloudy,mcloudy.png
+4,Partly Cloudy,pcloudy.png
+13,Light Rain,lshowers.png
+14,Showers,showers.png
+16,Snow,snowshow.png
+18,Rain,showers.png
+19,AM Showers,showers.png
+20,Fog,fog.png
+21,Few Showers,lshowers.png
+22,Mostly Sunny,sunny.png
+24,Sunny,sunny.png
+25,Scattered Flurries,flurries.png
+26,AM Clouds/PM Sun,pcloudy.png
+27,Isolated T-Storms,thunshowers.png
+28,Scattered Thunderstorms,thunshowers.png
+29,PM Showers,showers.png
+30,PM Showers/Wind,showers.png
+31,Rain/Snow Showers,rainsnow.png
+32,Few Snow Showers,flurries.png
+33,Cloudy/Wind,cloudy.png
+34,Flurries/Wind,flurries.png
+35,Mostly Cloudy/Windy,mcloudy.png
+36,Rain/Thunder,thunshowers.png
+37,Partly Cloudy/Windy,pcloudy.png
+38,AM Rain/Snow Showers,rainsnow.png
+40,Light Rain/Wind,lshowers.png
+41,Showers/Wind,showers.png
+42,Heavy Snow,snowshow.png
+44,Mostly Sunny/Wind,sunny.png
+45,Flurries,flurries.png
+47,Rain/Wind,showers.png
+49,Sct Flurries/Wind,flurries.png
+50,Sct Strong Storms,thunshowers.png
+51,PM T-Storms,thunshowers.png
+53,Thunderstorms,thunshowers.png
+55,Sunny/Windy,sunny.png
+56,AM Thunderstorms,thunshowers.png
+62,AM Rain,showers.png
+64,Iso T-Storms/Wind,thunshowers.png
+65,Rain/Snow,rainsnow.png
+66,Sct T-Storms/Wind,showers.png
+67,AM Showers/Wind,showers.png
+70,Sct Snow Showers,snowshow.png
+71,Snow to Ice/Wind,snowshow.png
+76,AM Ice,rainsnow.png
+77,Snow to Rain,rainsnow.png
+80,AM Light Rain,lshowers.png
+81,PM Light Rain,lshowers.png
+82,PM Rain,showers.png
+84,Snow Showers,snowshow.png
+85,Rain to Snow,rainsnow.png
+86,PM Rain/Snow,snowshow.png
+88,Few Showers/Wind,showers.png
+90,Snow/Wind,snowshow.png
+91,PM Rain/Snow Showers,rainsnow.png
+92,PM Rain/Snow/Wind,rainsnow.png
+93,Rain/Snow Showers/Wind,rainsnow.png
+94,Rain/Snow/Wind,rainsnow.png
+98,Light Snow,flurries.png
+100,PM Snow,snowshow.png
+101,Few Snow Showers/Wind,snowshow.png
+103,Light Snow/Wind,flurries.png
+104,Wintry Mix,flurries.png
+105,AM Wintry Mix,rainsnow.png
+106,Hvy Rain/Freezing Rain,rainsnow.png
+108,AM Light Snow,flurries.png
+109,PM Rain/Snow/Wind,rainsnow.png
+114,Rain/Freezing Rain,showers.png
+118,T-Storms/Wind,thunshowers.png
+123,Sprinkles,lshowers.png
+125,AM Snow Showers,snowshow.png
+126,AM Clouds/PM Sun/Wind,pcloudy.png
+128,AM Rain/Snow/Wind,rainsnow.png
+130,Rain to Snow/Wind,rainsnow.png
+132,Snow to Wintry Mix,snowshow.png
+133,PM Snow Showers/Wind,snowshow.png
+135,Snow and Ice to Rain,rainsnow.png
+137,Heavy Rain,showers.png
+138,AM Rain/Ice,showers.png
+145,AM Snow Showers/Wind,snowshow.png
+146,AM Light Snow/Wind,flurries.png
+150,PM Light Rain/Wind,lshowers.png
+152,AM Light Wintry Mix,rainsnow.png
+153,PM Light Snow/Wind,flurries.png
+154,Heavy Rain/Wind,showers.png
+155,PM Snow Shower,snowshow.png
+158,Snow to Rain/Wind,rainsnow.png
+164,PM Light Rain/Ice,showers.png
+167,AM Snow,snowshow.png
+171,Snow to Ice,snowshow.png
+172,Wintry Mix/Wind,rainsnow.png
+175,PM Light Snow,flurries.png
+178,AM Drizzle,lshowers.png
+189,Strong Storms/Wind,thunshowers.png
+193,PM Drizzle,lshowers.png
+194,Drizzle,lshowers.png
+201,AM Light Rain/Wind,lshowers.png
+204,AM Rain/Wind,showers.png
+223,Wintry Mix to Snow,rainsnow.png
+231,Rain,showers.png
+240,AM Light Rain/Ice,rainsnow.png
+259,Hvy Rain/Freezing Rain,showers.png
+271,Snow Showers/Windy,snowshow.png
+988,Partly Cloudy/Windy,pcloudy.png
+989,Light Rain Shower,lshowers.png
+990,Light Rain with Thunder,thunshowers.png
+991,Light Drizzle,lshowers.png
+992,Mist,fog.png
+993,Smoke,fog.png
+994,Haze,fog.png
+995,Light Snow Shower,flurries.png
+996,Light Snow Shower/ Windy,flurries.png
+997,Clear,fair.png
+998,A Few Clouds,pcloudy.png
+999,Fair,fair.png

Added: branches/rel-1-5/freevo/share/skins/plugins/weather.fxd
==============================================================================
--- (empty file)
+++ branches/rel-1-5/freevo/share/skins/plugins/weather.fxd     Sat Sep 30 
15:22:34 2006
@@ -0,0 +1,25 @@
+<?xml version="1.0" ?>
+<freevo>
+
+  <skin geometry="800x600">
+    <!-- Items of the main menu -->
+    <main>
+      <item label="weather" name="Weather Forecast" 
image="weather/weather_watermark.png"/>
+    </main>
+
+    <weather>
+      <screen layout="screen" x="0" y="0" width="800" height="600"/>
+      <title visible="not idlebar" layout="title" x="10" y="10" width="780" 
height="80"/>
+      <subtitle layout="subtitle" x="15" y="100" width="770" height="50"/>
+      <content layout="weather" x="10" y="140" width="780" height="450"/>
+    </weather>
+
+    <layout label="weather">
+      <background>
+        <image filename="../images/weather/weather_frame.png"/>
+      </background>
+      <content x="15" y="13" width="MAX-31" height="MAX-28" spacing="10"/>
+    </layout>
+
+  </skin>
+</freevo>

Added: branches/rel-1-5/freevo/src/plugins/weather.py
==============================================================================
--- (empty file)
+++ branches/rel-1-5/freevo/src/plugins/weather.py      Sat Sep 30 15:22:34 2006
@@ -0,0 +1,933 @@
+#if 0 /*
+# -----------------------------------------------------------------------
+# weather.py - a plugin to obtain detailed weather forecast information
+# -----------------------------------------------------------------------
+# $Id: weather.py,v 1.15 2003/12/06 13:47:28 dischi Exp $
+#
+# Notes: 
+#
+# Todo: 
+#   X pull down weather on demand MENU_SELECT (need to fix popup behavior)
+#   X Ability to specify custom location name in PLUGIN_WEATHER_LOCATIONS
+#   - get location name back onto details screen
+#   - i18n support
+#   - a freevo helper to grab weather data behind the scenes
+#
+# activate:
+#
+#    plugin.activate('weather', level=45)
+#    PLUGIN_WEATHER_LOCATIONS = [ ("USNC0559", 0, "Home sweet home") ]
+#
+# -----------------------------------------------------------------------
+#
+# $Log: weather.py,v $
+# Revision .5  2003/12/30 13:47:28  jlaska
+# initial version - adapted from headlines.py
+
+# Revision .6  2004/01/20 13:47:28  jlaska
+#  - modified screen drawing to support different resolutions and OSD_OVERSCAN 
values
+#  - added support to retry radar map and data downloads on http 
timeouts/errors
+#  - rolled the date calculations back to python2.2 time module (no longer 
datetime)
+#
+# Revision .7  2004/01/20 13:47:28  jlaska
+#  - bug in my date math for calculating the day name (oops)
+#
+# Revision .8  2004/02/28 09:19:43  jlaska
+#  - double check visibility string is not empty when passing to float()
+#  - increased console error verbosity when attempting to parse weather data 
+#  - added MENU_SELECT handler for weather detail screen to update forecast 
data on demand
+#  - added ability to specify custom name in PLUGIN_WEATHER_LOCATIONS
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2003 Krister Lagerstrom, et al. 
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# This program 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY 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 program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# ----------------------------------------------------------------------- */
+#endif
+
+#python modules
+import os, stat, re, copy
+
+# date/time
+import time
+
+#regular expression
+import re
+
+# rdf modules
+from xml.dom.ext.reader import Sax2
+import urllib
+
+#freevo modules
+import config, menu, rc, plugin, skin, osd, util
+from gui.PopupBox import PopupBox
+from item import Item
+
+#get the singletons so we get skin info and access the osd
+skin = skin.get_singleton()
+osd  = osd.get_singleton()
+
+#check every 2 hours 
+WEATHER_AGE = 7200
+WEATHER_DIR = os.path.join(config.SHARE_DIR, 'images', 'weather')
+
+def wget(iUrl):
+    for i in range(3):
+        try:
+            fd = urllib.urlopen(iUrl)
+            data = fd.read()
+            fd.close()
+            return data
+        except IOError:
+            print "retrying wget '%s'" % (iUrl,)
+            pass
+
+def toCelcius(fTemp):
+    tTemp = float ( fTemp  )
+    nTemp = (5.0/9.0)*(tTemp - 32.0)
+    return "%d" % (nTemp,)
+
+def toKilometers(miles):
+    tTemp = float( miles )
+    nTemp = tTemp*1.6
+    return "%d" % ( int(nTemp),)
+
+def toBarometer( baro ):
+    tTemp = float( baro )
+    nTemp = tTemp*3.386
+    return "%.1f" % (nTemp,)
+
+
+class PluginInterface(plugin.MainMenuPlugin):
+    """
+    A plugin to obtain more detailed weather forecast information
+
+    To activate, put the following lines in local_conf.py:
+
+    plugin.activate('weather', level=45) 
+    PLUGIN_WEATHER_LOCATIONS = [ ("<val1>", <bool>, "<str>"), ("<val2>", 
<bool>, "<str>"), ...]
+    
+    where <val#> is a zipcode or 
+    and <bool> (1 == convert to SI Units; 0 == do not convert)
+    and <str> is a custom name you wish to use for this location
+    """
+    # make an init func that creates the cache dir if it don't exist
+    def __init__(self):
+        if not hasattr(config, 'PLUGIN_WEATHER_LOCATIONS'):
+            self.reason = 'PLUGIN_WEATHER_LOCATIONS not defined'
+            return
+        plugin.MainMenuPlugin.__init__(self)
+        
+    def config(self):
+        return [('PLUGIN_WEATHER_LOCATIONS', [("USNC0559", 0)],
+                 "Location codes to grab forecasts for" )]
+
+    def items(self, parent):
+        return [ WeatherMainMenu(parent) ]
+
+
+class WeatherItem(Item):
+    """
+    Item for the menu for one rss feed
+    """
+    def __init__(self, parent, iLocation):
+        Item.__init__(self, parent)
+    
+        self.parent       = parent
+
+        # Flag to indicate whether this item is able to be displayed        
+        self.error        = 0
+
+        self.location     = None
+        self.convertData  = 0
+        self.name         = None
+        self.city         = None
+        self.state        = None
+        self.country      = None
+        self.curTemp      = None
+        self.updated      = None
+        self.curIcon      = None
+        self.curWind      = None
+        self.windDir      = None
+        self.barometer    = None
+        self.curHumid     = None
+        self.curFeel      = None
+        self.uvIndex      = None
+        self.visibility   = None
+        self.shortdesc    = None
+        self.description  = None
+        self.forecastData = None
+        self.pastTime     = 0
+
+        self.date         = []
+        self.weatherIcon  = []
+        self.highTemp     = []
+        self.lowTemp      = []
+        self.weatherType  = []
+        self.wdata        = []
+
+        # were we asked to convert to SI units?
+        if isinstance( iLocation, tuple ):
+            self.location    = iLocation[0]
+            if len( iLocation ) > 1:
+                self.convertData = int(iLocation[1])
+            if len( iLocation ) > 2:
+                self.name        = str(iLocation[2])
+        else:
+            self.location    = iLocation
+            self.convertData = 0
+
+        self.dataurl        = 
"http://www.msnbc.com/m/chnk/d/weather_d_src.asp?acid=%s"; % (self.location,)
+        self.mapurl         = 
"http://w3.weather.com/weather/map/%s?from=LAPmaps"; % (self.location,)
+        self.mapurl2        = None
+        self.maplink        = None
+        self.weatherData    = None
+        self.weatherMapData = None
+
+        self.cacheDir  = '%s/weather_%s' % (config.FREEVO_CACHEDIR, 
self.location)
+        self.cacheFile = '%s/data' % (self.cacheDir,)
+        self.mapFile   = '%s/map' % (self.cacheDir,)
+        if not os.path.isdir(self.cacheDir):
+            os.mkdir(self.cacheDir, 
stat.S_IMODE(os.stat(config.FREEVO_CACHEDIR)[stat.ST_MODE]))
+        self.last_update = 0
+
+        #get forecast data
+        self.getForecast()
+
+    def isValid(self):
+        return not self.error
+
+    def getHumidity(self):
+        return "%s %%" % (self.curHumid,)
+
+    def getBarometer(self):
+        if self.convertData:
+            return "%s kPa" % (self.barometer,)
+        else:
+            return "%s in" % (self.barometer,)
+
+    def getWind(self):
+        if self.convertData:
+            return "%s km/h" % (self.curWind, )
+        else:
+            return "%s mph" % (self.curWind, )
+
+    def getTemp(self):
+        if self.convertData:
+            return u"%s\xb0 C" % (self.curTemp,) 
+        else:
+            return u"%s\xb0 F" % (self.curTemp,) 
+
+    def getLastUpdated(self):
+        if self.convertData:
+            # day / month / year  24hour:min:sec
+            return "Last updated: %s" % (time.strftime( "%d/%m/%Y %H:%M:%S", 
time.localtime( self.last_update) ), )
+        else:
+            # month / day / year  12hour:min:sec [AM|PM]
+            return "Last updated: %s" % (time.strftime( "%m/%d/%Y %I:%M:%S 
%p", time.localtime( self.last_update) ), )
+
+    def getFeel(self):
+        if self.convertData:
+            return u"%s\xb0 C" % (self.curFeel,) 
+        else:
+            return u"%s\xb0 F" % (self.curFeel,) 
+
+    def getVisibility(self):
+        if float(self.visibility) == 999.00:
+            return _("Unlimited")
+        elif self.convertData:
+            return "%s km" % (self.visibility,)
+        else:
+            return "%s mi" % (self.visibility,)
+        
+    def start_detailed_interface(self, arg=None, menuw=None):
+        WeatherDetailHandler(arg, menuw, self)
+
+    def actions(self):
+        """
+        return a list of actions for this item
+        """
+        return [ ( self.start_detailed_interface, _('Show Weather Details') ) ]
+    
+    def saveToCache(self):
+        util.save_pickle(self.weatherData, self.cacheFile)
+        # attempt to save weathermap
+        try:
+            if self.weatherMapData is not None:
+                imgfd = os.open( self.mapFile, os.O_CREAT|os.W_OK )
+                os.write( imgfd, self.weatherMapData )
+                os.close( imgfd )
+        except:
+            print "failed while saving weather map to cache '%s'" % 
(self.mapFile,)
+
+    def loadFromCache(self):
+        self.weatherData = util.read_pickle(self.cacheFile)
+
+        try: 
+            size = int( os.stat( self.mapFile )[6] )
+        except:
+            print "Weather ERROR: failed attempting to load %s radar map from 
cache" % (self.location,)
+            pass
+        else:
+            imgfd = os.open( self.mapFile, os.R_OK )
+            self.weatherMapData = os.read( imgfd, size )
+            os.close( imgfd )
+
+    def needRefresh(self):
+        '''is the cache too old?'''
+        if (os.path.isfile(self.cacheFile) == 0 or \
+            (abs(time.time() - os.path.getmtime(self.cacheFile)) > 
WEATHER_AGE)):
+            return 1
+        else:
+            return 0
+
+    def getForecast(self, force=0):
+        '''grab the forecast, updating for the website if needed'''
+    
+        # check cache 
+        try:
+            if force or self.needRefresh():
+                self.updateData()
+            else:
+                self.loadFromCache()
+        except:
+            self.error = 1
+            print "ERROR obtaining forecast data for '%s'" % (self.location, )
+        else:
+            # set the last update timestamp
+            self.last_update = os.path.getmtime(self.cacheFile)
+
+            # now convert the self.weatherData structure to parsable 
information
+            try:
+                self.convertWeatherData()
+            except:
+                self.error = 1
+                import traceback, sys
+                print "ERROR parsing forecast data for '%s'" % (self.location, 
)
+                print "\tThis could indicate a failed download of weather data 
from msnbc.  "\
+                      "You can confirm this by examining the contents of the 
file '%s'.  "\
+                      "Below is also the traceback indicating where we 
discovered the problem "\
+                      "with the weather file.  If the weather file appears 
intact, please report " \
+                      "this to the '[email protected]'\n" % 
(self.cacheFile, )
+                output = apply(traceback.format_exception, sys.exc_info())
+                output = ''.join(output)
+                output = urllib.unquote(output)
+                print output
+
+    def GetString(self, var):
+        '''when given a variable, it returns the value stored in the MSNBC 
forecast data'''
+        
+        regexp  = re.compile('%s = "[^"]*"' % (var,) )
+        results = regexp.search( self.weatherData )
+        (start, end) = results.span()
+        start += len(var) + 4 # the 4 chars is the ' = "'
+        end   -= 1  # strip off the right "
+        return self.weatherData[start:end] 
+
+    def updateData(self):
+        popup = PopupBox(text=_('Fetching Weather for %s...' % self.location))
+        popup.show()
+
+        # parse the document
+        try:
+            self.weatherData = wget( self.dataurl )
+        except:
+            popup.destroy()
+            raise WeatherError, 'Weather ERROR: failed attempting to grab 
forecast for %s' % self.location
+
+        #TODO: Get description from 
http://weather.noaa.gov/pub/data/forecasts/zone/nc/ncz041.txt
+
+        # obtain radar map
+        try:
+            if self.maplink is None:
+
+                # get the first web page
+                for attempt in range(3):
+                    weatherPage  = wget ( self.mapurl )
+                    try:
+                        # find link to map page
+                        regexp       = re.compile ( 'if \(isMinNS4\) var 
mapNURL = "[^"]*";', re.IGNORECASE  )
+                        results      = regexp.search( weatherPage )
+                        (start, end) = results.span()
+                        # TODO: I don't like having fixed length offsets from 
start, end
+                        weatherPage2 = "http://w3.weather.com/%s"; % 
(weatherPage[start+29:end-2], )
+
+                        mapPage      = wget ( weatherPage2 )
+                        # find a link to the real doplay map
+                        regexp       = re.compile('<img NAME="mapImg" 
SRC="http://image.weather.com[^"]*jpg";', re.IGNORECASE)
+                        results      = regexp.search( mapPage )
+                        (start, end) = results.span()
+                        # TODO: I don't like having fixed length offsets from 
start, end
+                        self.maplink = mapPage[start+24:end-1]
+                        break;
+                    except: 
+                        print "Retrying [%d] %s" % (attempt,self.mapurl)
+                        pass
+            
+            # pull down the map locally
+            try:
+                self.weatherMapData = wget( self.maplink )
+            except:
+                print 'Weather ERROR: failed attempting to download radar map 
from %s' % self.maplink
+        except:
+            print 'Weather ERROR: failed attempting to locate radar map URL 
for %s' % self.location
+            self.weatherMapData = None
+            pass
+
+            import traceback, sys
+            output = apply(traceback.format_exception, sys.exc_info())
+            output = ''.join(output)
+            output = urllib.unquote(output)
+            print output
+
+        #write the file
+        self.saveToCache()
+
+        popup.destroy()
+
+    def convertWeatherData(self):
+        self.city    = self.GetString("this.swCity")
+        self.state   = self.GetString("this.swSubDiv")
+        self.country = self.GetString("this.swCountry")
+        self.curTemp = self.GetString("this.swTemp")
+        self.updated = self.GetString("this.swLastUp")
+
+        # reset variables
+        self.date        = []
+        self.weatherIcon = []
+        self.highTemp    = []
+        self.lowTemp     = []
+        self.weatherType = []
+
+        # set the location name (if one was not specified in the 
WEATHER_LOCATIONS"
+        if self.name is None:
+            self.name    = "%s" % (self.city)
+
+        # convert temperature 
+        if self.curTemp is None or len(self.curTemp) == 0:
+            self.curTemp = "-na-"
+            self.updated = self.updated + " (Not All Information Available)"
+        else:
+            if self.convertData:
+                self.curTemp = toCelcius( self.curTemp )
+
+        self.curIcon   = self.GetString("this.swCIcon")
+        self.curWind   = self.GetString("this.swWindS")
+
+        # convert wind
+        if self.convertData:
+            self.curWind = toKilometers( self.curWind )
+
+        self.windDir   = self.GetString("this.swWindD")
+        self.barometer = self.GetString("this.swBaro")
+
+        # convert barometer
+        if self.convertData:
+            self.barometer = toBarometer( self.barometer )
+
+        self.curHumid = self.GetString("this.swHumid")
+        self.curFeel  = self.GetString("this.swReal")
+
+        # convert feels-like temp
+        if self.convertData:
+            self.curFeel = toCelcius( self.curFeel )
+
+        self.uvIndex     = self.GetString("this.swUV")
+        self.visibility  = self.GetString("this.swVis")
+
+        # convert visibility
+        if self.convertData and self.visibility and float(self.visibility)  != 
999.0:
+            self.visibility = toKilometers( self.visibility )
+
+        self.shortdesc = self.GetString("this.swConText")
+        if self.shortdesc is None or len( self.shortdesc ) == 0:
+            self.shortdesc = self.curIcon
+
+        self.forecastData = self.GetString("this.swFore")
+        holdings     = []
+        holdings     = self.forecastData.split("|")
+        dayNum       = int( holdings[0] )
+        curDay       = int( time.strftime("%u") ) + 1 # day of week 2(mon) - 
1(sun)
+
+        if dayNum != curDay:
+            self.pastTime = 1
+
+       ltime = time.localtime()
+       ctr   = 0
+       for i in range(5,10):
+               (mons, days, years) = holdings[i].split("/")
+               mons  = int( mons )
+               days  = int( days )
+               years = int( years )
+               dnum  = (ltime[6] + ctr) % 7
+               self.date.append( time.strftime( "%A", (years, mons, days, 
ltime[3], ltime[4], ltime[5], dnum, ltime[7], ltime[8]) ) )
+               ctr += 1
+
+        # weather icon
+        for i in (10,11,12,13):
+            self.weatherIcon.append( holdings[i] )
+
+        # calculate high temps
+        for i in (20,21,22,23):
+            if self.convertData:
+                holdings[i] = toCelcius( holdings[i] )
+            self.highTemp.append( holdings[i] )
+
+        # calculate low temps
+        for i in (40,41,42,43):
+            if self.convertData:
+                holdings[i] = toCelcius( holdings[i] )
+            self.lowTemp.append( holdings[i] )
+
+        for i in (15,16,17,18):
+            self.weatherType.append( holdings[i] )
+
+        self.setWeatherTypeIcon( )
+        self.setWeatherIcon( )
+
+        # Create description
+        self.description = "%s %s %s" % ( self.shortdesc, _("at"), 
self.getTemp() )
+
+    def setWeatherIcon(self):
+        '''set the weather icon given the short forecast description'''
+
+        match = weatherTypes.findType( name=self.shortdesc )
+        if match:
+            self.curIcon     = match.getIcon()
+        else:
+            self.curIcon     = "unknown.png"
+
+        # set the Item.image value
+        self.image = os.path.join(WEATHER_DIR, self.curIcon)
+        return
+
+    def setWeatherTypeIcon(self):
+        '''obtain the weather icons for multiple day forecast'''
+
+        #start = 1
+        #if self.pastTime:
+        #    start = 0
+        #i = start
+        i = 0
+        while i < 4:
+
+            match = weatherTypes.findType( number=self.weatherType[i] )
+            if match:
+                self.weatherType[i] = match.getName()
+                self.weatherIcon[i] = match.getIcon()
+            else:
+                self.weatherType[i] = "%s (%s)" % ( _("Unknown"), 
self.weatherType[i])
+                self.weatherIcon[i] = "unknown.png"
+            i += 1
+
+class WeatherType:
+    def __init__(self, iNum=0, iName="", iIcon=""):
+        self.number = iNum
+        self.name   = iName
+        self.icon   = iIcon
+    def setNumber(self, n):
+        self.number = n
+    def setName(self, n):
+        self.name = n
+    def setIcon(self, n):
+        self.icon = n
+    def getNumber(self):
+        return self.number
+    def getName(self):
+        return self.name
+    def getIcon(self):
+        return self.icon
+
+class WeatherTypesClass:
+    def __init__(self):
+        self.wtypes      = []
+        self.num_lookup  = {} # reverse hash to quickly get a Weathertype w/ a 
number
+        self.name_lookup = {} # reverse hash to quickly get a Weathertype w/ a 
name
+        self.icon_lookup = {} # reverse hash to quickly get a Weathertype w/ a 
icon
+        self.loadWeatherTypes()
+    def loadWeatherTypes(self):
+        '''load the weather icon mapping from the datafile'''
+        wtfile = os.path.join(WEATHER_DIR, 'weathertypes.dat')
+        wtdata = util.readfile( wtfile )
+         
+        for line in wtdata:
+            try:
+                icdata = line.split(",")
+                wtype = WeatherType()
+                wtype.setNumber ( icdata[0] )
+                wtype.setName   ( icdata[1] )
+                wtype.setIcon   ( (icdata[2])[0:-1] ) # strip newline
+
+                # populate reverse dictionaries
+                self.num_lookup[ wtype.getNumber() ] = len( self.wtypes )
+                self.name_lookup[ wtype.getName() ]  = len( self.wtypes )
+                self.icon_lookup[ wtype.getIcon() ]  = len( self.wtypes )
+
+                self.wtypes.append ( wtype )
+            except:
+                pass
+    def findType(self, number=None, name=None, icon=None):
+        ''' return a type given a type number '''
+        if number:
+            try:
+                idx = self.num_lookup[number] 
+                return self.wtypes[ idx ]
+            except:
+                return None
+        elif name:
+            try:
+                idx = self.name_lookup[name] 
+                return self.wtypes[ idx ]
+            except:
+                return None
+        elif icon:
+            try:
+                idx = self.icon_lookup[icon] 
+                return self.wtypes[ idx ]
+            except:
+                return None
+        else:
+            raise Exception, "unknown type requested in 
WeatherTypesClass::findType()"  
+        
+    def __len__(self): return len(self.wtypes)
+    def __getitem__(self, i): return self.wtypes[i]
+
+class WeatherMainMenu(Item):
+    """
+    this is the item for the main menu and creates the list
+    of Weather Locations in a submenu.
+    """
+    def __init__(self, parent):
+        Item.__init__(self, parent, skin_type='weather')
+        self.parent = parent
+        self.name   = _('Weather')
+
+    def actions(self):
+        """
+        return a list of actions for this item
+        """
+        items = [ ( self.create_locations_menu , _('Locations') ) ]
+        return items
+ 
+    def __call__(self, arg=None, menuw=None):
+        """
+        call first action in the actions() list
+        """
+        if self.actions():
+            return self.actions()[0][0](arg=arg, menuw=menuw)
+
+    def create_locations_menu(self, arg=None, menuw=None):
+        locations  = []
+        autoselect = 0
+        # create menu items
+        for location in config.PLUGIN_WEATHER_LOCATIONS:
+            weather_item = WeatherItem(self, location)
+            # Only display this entry if no errors were found
+            if weather_item.isValid():
+                locations.append ( weather_item )
+
+        # if only 1 valid location, autoselect it and go right to the detail 
screen
+        if locations.__len__() == 1:
+            autoselect = 1
+            menuw.hide(clear=False)
+
+        # if no locations were found, add a menu entry indicating that
+        if not locations:
+            nolocation = menu.MenuItem(_('No locations specified'), 
menuw.goto_prev_page, 0)
+            locations.append( nolocation )
+
+        # if only 1 valid menu entry present, autoselect it
+        if autoselect:
+            locations[0](menuw=menuw)
+        else:
+            # create menu
+            weather_site_menu = menu.Menu(_('Locations'), locations)
+            menuw.pushmenu(weather_site_menu)
+            menuw.refresh()
+
+class WeatherDetailHandler:
+    """
+    A handler class to display several detailed forecast screens and catch 
events
+    """
+    def __init__(self, iArg=None, iMenuw=None, iWeather=None ):
+        self.arg     = iArg
+        self.menuw   = iMenuw
+        self.weather = iWeather
+        self.menuw.hide(clear=False)
+        rc.app(self)
+
+        self.skins     = ('day', 'forecast', 'week', 'doplar')
+
+        self.subtitles = (_('Current Conditions'), _("Today's Forecast"),
+                          _("Extended Forecast"), _("Radar Map"))
+
+        self.curSkin   = 0
+
+        self.title    = self.weather.name
+        self.subtitle = self.subtitles[0]
+        
+        # Fire up splashscreen and load the plugins
+        skin.draw('weather', self)
+
+    def prevSkin(self):
+        '''decriment self.curSkin'''
+        self.curSkin -= 1
+        
+        # out of bounds check, reset to size of skins array
+        if self.curSkin < 0:
+            self.curSkin = len(self.skins)-1
+        self.subtitle = self.subtitles[self.curSkin]
+
+    def nextSkin(self):
+        '''increment self.curSkin'''
+        self.curSkin += 1
+        
+        # out of bounds check, reset to 0
+        if self.curSkin >= len(self.skins):
+            self.curSkin = 0
+        self.subtitle = self.subtitles[self.curSkin]
+
+    def eventhandler(self, event, menuw=None):
+        '''eventhandler'''
+        if event == 'MENU_BACK_ONE_MENU':
+            rc.app(None)
+            self.menuw.show()
+            return True
+
+        elif event == 'MENU_SELECT':
+            # TODO: update the current forecast data, and refresh
+            self.weather.getForecast(force=1)
+            skin.clear( )
+            skin.draw( 'weather', self )
+            return True
+
+        elif event in ('MENU_DOWN', 'MENU_RIGHT'):
+            # Fire up the next skin
+            self.nextSkin()
+            skin.draw( 'weather', self )
+            return True
+
+        elif event in ('MENU_UP', 'MENU_LEFT'):
+            # Fire up the previous skin
+            self.prevSkin()
+            skin.draw( 'weather', self )
+            return True
+        
+        return False
+
+class WeatherBaseScreen(skin.Area):
+    """
+    A base class for weather screens to inherit from, provides common 
members+methods
+    """
+    def __init__(self):
+        skin.Area.__init__(self, 'content')
+
+        # Weather display fonts
+        self.key_font      = skin.get_font('medium0')
+        self.val_font      = skin.get_font('medium1')
+        self.small_font    = skin.get_font('small0')
+        self.big_font      = skin.get_font('huge0')
+
+        # set the multiplier to be used in all screen drawing
+        self.xmult = float( osd.width  - 2*config.OSD_OVERSCAN_X ) / 800
+        self.ymult = float( osd.height - 2*config.OSD_OVERSCAN_Y ) / 600  
+
+        self.update_functions = (self.update_day, self.update_forecast,
+                                 self.update_week, self.update_doplar)
+
+    def update_day(self):
+        # display data
+        text      = _("Humidity")
+        value     = self.parent.weather.getHumidity()
+
+        x_col1   = self.content.x + (50  * self.xmult) 
+        x_col2   = self.content.x + (250 * self.xmult) 
+        y_start  = self.content.y + (60  * self.xmult) 
+        y_inc    = 40 * self.ymult
+
+        self.write_text(text,   self.key_font,   self.content,  
+            x=x_col1,  y=y_start, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start, height=-1, align_h='left')
+
+        text      = _("Pressure")
+        value     = self.parent.weather.getBarometer()
+        self.write_text(text,   self.key_font,   self.content,  
+            x=x_col1,  y=y_start+y_inc, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start+y_inc, height=-1, align_h='left')
+
+        text      = _("Wind")
+        value     = "%s %s %s" % ( self.parent.weather.windDir, _("at"), 
self.parent.weather.getWind())
+        y_start   += y_inc
+        self.write_text(text,   self.key_font,   self.content,  
+            x=x_col1,  y=y_start+y_inc, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start+y_inc, height=-1, align_h='left')
+
+        text      = _("Wind Chill")
+        value     = self.parent.weather.getFeel()
+        y_start   += y_inc
+        self.write_text(text,   self.key_font,   self.content,
+            x=x_col1,  y=y_start+y_inc, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start+y_inc, height=-1, align_h='left')
+
+        text      = _("Visibility")
+        value     = self.parent.weather.getVisibility()
+        y_start   += y_inc
+        self.write_text(text,   self.key_font,   self.content,  
+            x=x_col1,  y=y_start+y_inc, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start+y_inc, height=-1, align_h='left')
+
+        text      = _("UV Index")
+        value     = self.parent.weather.uvIndex
+        y_start   += y_inc
+        self.write_text(text,   self.key_font,   self.content,  
+            x=x_col1,  y=y_start+y_inc, height=-1, align_h='left')
+        self.write_text(value,  self.val_font,   self.content,  
+            x=x_col2,  y=y_start+y_inc, height=-1, align_h='left')
+
+        # draw current condition image
+        x_start = self.content.x + (450*self.xmult)
+        y_start = self.content.y + (40*self.ymult)
+        self.draw_image( self.parent.weather.image,
+            ( x_start, y_start, 
+              int(200*self.xmult), int(150*self.ymult) ) )
+
+        y_start = self.content.y + (200*self.ymult)
+        self.write_text(self.parent.weather.shortdesc,  
+            self.key_font,   self.content,  
+            x=x_start, y=y_start,
+            width=200*self.xmult, height=-1, align_h='center')
+        y_start = self.content.y + (250*self.ymult)
+        self.write_text(self.parent.weather.getTemp(),  
+            self.big_font,   self.content,  
+            x=x_start, y=y_start,
+            width=200*self.xmult, height=-1, align_h='center')
+
+        x_start = self.content.x + (40*self.xmult)
+        y_start = self.content.y + (350*self.ymult)
+        self.write_text(self.parent.weather.getLastUpdated() ,
+            self.small_font, self.content,  
+            x=x_start, y=y_start,
+            width=self.content.width, height=-1, align_h='left')
+
+    def update_forecast(self):
+        '''
+        this screen is extremely useless, all it\'s doing is putting text
+        around the day view. It would be nice if I could use the same source
+        that the gnome-applet weather applet uses for detailed forecast data
+        '''
+        x_start = self.content.x + (20  * self.xmult) 
+        y_start = self.content.y + (30  * self.xmult) 
+
+        lines = []
+        lines.append( "%s %s %s %s." % ( _("Today, a high of"),
+                                         self.parent.weather.highTemp[0],
+                                         _("and a low of"),
+                                         self.parent.weather.lowTemp[0] ) )
+        lines.append( "%s %s %s" \
+                  % ( _("Currently, there is a a humidity of"), 
+                      self.parent.weather.getHumidity(), 
+                      _("and"), ) )
+
+        text = _("the winds are ")
+        if self.parent.weather.windDir == "CALM":
+            text += "%s. " % ( _("calm"),)
+        else:
+            text += "%s %s %s %s." % ( _("coming in at"), 
self.parent.weather.getWind(), _("from the"), self.parent.weather.windDir )
+        lines.append( text) 
+
+        if float(self.parent.weather.visibility) == 999.00:
+            lines.append( _("Visibility will be unlimited today") )
+        else:
+            lines.append( "%s %s." % ( _("There will be a visibility of"), 
self.parent.weather.getVisibility(), ) )
+
+        y = y_start
+        for line in lines:
+            self.write_text(line,   self.key_font,   self.content,  
+            x=x_start,  y=y, height=-1, align_h='left')
+            y += (30 * self.ymult)
+
+    def update_week(self):
+
+        x_start = self.content.x + (10  * self.xmult) 
+        y_start = self.content.y + (10  * self.xmult) 
+
+        day = 0
+        #for x in (40, 220, 400, 580):
+        for x in (0, 180, 360, 540):
+
+            x2_start = x_start + (x *self.xmult)
+            y2_start = y_start
+            
+            self.write_text(self.parent.weather.date[day],
+                self.key_font,   self.content,  
+                x=x2_start,  y=y2_start,
+                width=150*self.xmult, height=-1, align_h='center')
+
+            iconFile = os.path.join(WEATHER_DIR, 
self.parent.weather.weatherIcon[day])
+            self.draw_image( iconFile, 
+                             ( x2_start,
+                               y2_start + (50*self.ymult),
+                               int(160*self.xmult), 
+                               int(120*self.ymult) ) )
+            self.write_text(self.parent.weather.weatherType[day],  
+                self.small_font,   self.content,  
+                x=x2_start,  y=y2_start + (200*self.ymult), 
+                width=160*self.xmult, height=-1, align_h='center')
+            self.write_text(_("LO"),
+                self.val_font,   self.content,  
+                x=x2_start,  y=y2_start + (260*self.ymult), 
+                width=90*self.xmult,  height=-1, align_h='center')
+            self.write_text(self.parent.weather.lowTemp[day],
+                self.key_font,   self.content,  
+                x=x2_start,  y=y2_start + (300*self.ymult), 
+                width=90*self.xmult, height=-1, align_h='center')
+            self.write_text(_("HI"),
+                self.val_font,   self.content,  
+                x=x2_start+(70*self.xmult),  y=y2_start + (260*self.ymult), 
+                width=90*self.xmult, height=-1, align_h='center')
+            self.write_text(self.parent.weather.highTemp[day],
+                self.key_font,   self.content,  
+                x=x2_start+(70*self.xmult),  y=y2_start + (300*self.ymult), 
+                width=90*self.xmult, height=-1, align_h='center')
+            day += 1
+
+    def update_doplar(self):
+        if self.parent.weather.weatherMapData is None:
+            x_start = self.content.x + (10  * self.xmult) 
+            y_start = self.content.y + (10  * self.xmult) 
+            self.write_text( _("Error encountered while trying to download 
Radar map"),
+                self.key_font,   self.content,  
+                x=x_start,  y=y_start,
+                width=self.content.width, height=-1, align_h='left')
+        else:
+            self.draw_image( self.parent.weather.mapFile, (self.content.x, 
self.content.y, self.content.width,
+                                                           
self.content.height) )
+
+    def update_content(self):
+        self.parent   = self.menu
+        self.content  = self.calc_geometry(self.layout.content,  
copy_object=True)
+        self.update_functions[self.menu.curSkin]()
+
+# create one instance of the WeatherType class
+weatherTypes = WeatherTypesClass()
+skin.register ( 'weather', ('screen', 'subtitle', 'title', 'plugin', 
WeatherBaseScreen()) )

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to