root/trunk/ctw/ctw @ 317

Revision 317, 9.3 kB (checked in by dan, 11 months ago)

Presentation cleanup provided by Tim Yang. Thanks Tim!

  • Property svn:executable set to *
  • Property svn:keywords set to HeadURL Id
RevLine 
[219]1#!/usr/bin/env python
[243]2# $HeadURL$
[244]3# $Id$
[219]4# vim: ft=python ts=2 sw=2 et:
5
6# Copyright Dan Cardamore <dan@hld.ca>
[230]7# Licensed under the GNU GPL version 2.0
8# See GPL.gz in source distribution for more information
[219]9
[311]10import sys,time,string,urllib2
[220]11from threading import Timer
12
[219]13import curses
[244]14import weatherfeed
[219]15
[317]16version = "0.6"
[244]17
[221]18class asciiIcons:
19  def __init__(self):
20    self.blank = """
21                   
22                   
23                   
24                   
25                   
26                   
27"""
28
[227]29    self.lightning = """
30                   
31    \\\\           
32     \\\\\\         
33        \\\\       
34          \\\       
35             \     
36"""
[221]37
[227]38    self.cloudy = """
39     __         _   
40   /-   \_/\---/ \ 
41   |         --   |
[259]42   \-_/---\__--__-|
[227]43                   
44                   
45"""
46
47    self.raining = """
48     __         _   
49   /-   \_/\---/ \ 
50   |         --   |
51   |______________|
52                   
53    | | | | | | |   
54"""
55
56
57    self.unknown = """
58                   
59                   
60                   
61                   
62                   
[308]63                   
[227]64"""
65
66    self.sunny = """
67 \    ___           
68    /     \ /       
69 - |       | -     
70    \ ___ /  -     
71  /        \       
72     |  |           
73"""
74
75
76    self.clear = """
77      ___           
78    /     \         
79   |       |       
80    \ ___ /         
81                   
82                   
83"""
84
[221]85    self.partlycloudy = """
86   \  --- ____     
87  - /....|    \/ \ 
88  - |...|         |
89   / \___\   __  / 
90    / |   --/  \   
91                   
92"""
93
94  def getIcon(self, type):
95    """Returns a string with an ascii icon 5 rows by 18 wide
96    type is a string which refers to the icon wanted"""
97    if type == "Partly Cloudy":
98      return self.partlycloudy
[259]99    elif type == "Mostly Cloudy":
[308]100      return self.partlycloudy
[227]101    elif type == "Cloudy":
102      return self.cloudy
103    elif type == "Clear":
104      return self.clear
105    elif type == "Light Rain":
106      return self.raining
107    elif type == "Showers":
108      return self.raining
109    elif type == "AM Showers":
110      return self.raining
111    elif type == "PM Showers":
112      return self.raining
113    elif type == "Isolated T-Storms":
114      return self.lightning
115    elif type == "Mostly Sunny":
116      return self.sunny
117    elif type == "Sunny":
118      return self.sunny
[221]119    else:
120      return self.unknown
121
122def initColors():
[225]123  try:
124    curses.start_color()
[314]125    curses.use_default_colors()
[225]126    curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
127    curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_RED)
128    curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
129    curses.init_pair(4, curses.COLOR_GREEN, curses.COLOR_BLACK)
130  except curses.error: pass
[221]131
[220]132def scnTitle(win):
[260]133  win.clear()
[221]134  maxy,maxx = win.getmaxyx()
135  maxx -= 2
136  win.bkgd(" ",curses.color_pair(1))
[226]137  try:
138    win.addstr(0,1, 
139        ("Location %s  --  Updated: %s" %
140        (weather.currentConditions["cityname"], 
[254]141        weather.currentConditions["observed"] )).center(maxx),
142        curses.A_BOLD
[226]143      )
144  except curses.error: pass
[220]145  win.refresh()
[219]146
[220]147def scnCurrent(win):
[260]148  win.clear()
[221]149  maxy,maxx = win.getmaxyx()
150  maxx -= 2
151  line = 0
152  icons = asciiIcons()
[222]153  win.addstr(line,2, icons.getIcon(weather.currentConditions["type"]));line+=6
[225]154  win.addstr(line,2,("%s" %(weather.currentConditions["type"])).center(maxx),curses.color_pair(4));line+=2
[255]155  win.addstr(line,2,"     Temp: %s" %(weather.currentConditions["temperature"]));line+=1
[278]156  if weather.currentConditions["wind"]["speed"] == "calm":
157    win.addstr(line,2,"     Wind: Calm") ;line+=1
158  else:
159    win.addstr(line,2,"     Wind: %s %s" %( weather.currentConditions["wind"]["speed"],
[221]160                                    weather.currentConditions["wind"]["direction"])
161             );line+=1
[255]162  win.addstr(line,2,"Visbility: %s" %(weather.currentConditions["visibility"]));line+=1
163  win.addstr(line,2," Humidity: %s" %(weather.currentConditions["humidity"]));line+=1
164  line += 1
165  win.addstr(line,2,"  Sunrise: %s" %(weather.currentConditions["sunrise"]));line+=1
166  win.addstr(line,2,"   Sunset: %s" %(weather.currentConditions["sunset"]));line+=1
167  line += 1
168  win.addstr(line,2," UV index: %s" %(weather.currentConditions["uv"]["index"]));line+=1
169  win.addstr(line,2,"     risk: %s" %(weather.currentConditions["uv"]["risk"]));line+=1
[221]170
171  win.box()
[254]172  win.addstr(0,1,"Current Conditions",curses.color_pair(2)|curses.A_BOLD); line += 1
[220]173  win.refresh()
[219]174
[221]175def scnForecast(win):
176  maxy,maxx = win.getmaxyx()
177  maxx -= 2
178  line = 0
[222]179  col = 0
180  global dayWindows
181  global forecastWindowsCreated
182  if not forecastWindowsCreated:
183    forecastWindowsCreated = True
184    day = 0
185    dayWindows = []
[315]186    while day < 5:
[222]187      if day < 5:
[226]188        try:
[317]189          dayWindows.append(win.derwin(int(maxy/5),maxx,int(day*(maxy/5))+1,1))
[226]190        except curses.error: pass
[222]191      else:
[226]192        try:
[317]193          dayWindows.append(win.derwin(int(maxy/5),maxx,int((day-5)*(maxy/5))+1,int(maxx/2)+1))
[226]194        except curses.error: pass
[222]195      day += 1
196
197  day = 0
[315]198  while day < 5:
[222]199    scnDay(day)
200    day+=1
[221]201  win.box()
[226]202  try:
[254]203    win.addstr(0,36,"Forecast Conditions",curses.color_pair(2)|curses.A_BOLD); line += 1
[226]204  except curses.error: pass
[221]205  win.refresh()
206
[222]207def scnDay(day):
208  global dayWindows
[226]209  try:
210    win = dayWindows.pop(0)
211  except:
212    return  #this one wasn't allocated a window so just return
[260]213  win.clear()
[222]214  maxy,maxx = win.getmaxyx()
[317]215  maxx -= 13
[222]216  line = 1
[221]217
[226]218  try:
219    win.addstr(line,1,"Hi/Lo: %s/%s" %(weather.forecast[day]["high"],
[222]220                                         weather.forecast[day]["low"]));line+=0
[226]221  except curses.error: pass
222  try:
[317]223    win.addstr(line,maxx,"Wind:%s %s" %( weather.forecast[day]["day"]["wind"]["speed"],
[222]224                                    weather.forecast[day]["day"]["wind"]["direction"])
225             );line+=1
[226]226  except curses.error: pass
227  try:
228    win.addstr(line,1,"%s" %(weather.forecast[day]["day"]["type"]));line+=0
229  except curses.error: pass
230  try:
[317]231    win.addstr(line,maxx,"POP: %s" %(weather.forecast[day]["day"]["pop"]));line+=1
[226]232  except curses.error: pass
[222]233
[226]234  try:
235    win.box()
236  except curses.error: pass
237  try:
238    win.addstr(0,2,"%s: %s"%(weather.forecast[day]["Date"],weather.forecast[day]["Day"]),curses.color_pair(3))
239  except curses.error: pass
[222]240  win.refresh()
241
[220]242def quit():
243  refreshTimer.cancel()
244  sys.exit(1)
[219]245
[220]246def update(stdscr):
247  global weather
[262]248  while 1:
249    try:
[277]250      weather = weatherfeed.Weather(location, metric)
[262]251    except urllib2.URLError:
252      time.sleep(60)
253      continue
254    break
[219]255
[220]256  scnTitle(twin)
257  scnCurrent(cwin)
[221]258  scnForecast(fwin)
[220]259  stdscr.refresh()
[219]260
[220]261  del weather
262  global refreshTimer
[257]263  try:
264    refreshTimer.cancel()
265  except: pass
[224]266  refreshTimer = Timer(refresh * 60, update, [stdscr])
[220]267  refreshTimer.start()
[219]268
[220]269
[219]270def main(stdscr):
[221]271  global cwin,twin,fwin
[220]272  global twidth,theight
[221]273  theight, twidth = stdscr.getmaxyx()
[219]274
[222]275  global forecastWindowsCreated
276  forecastWindowsCreated = False
277
[221]278  initColors()
279  twin = stdscr.derwin(1,twidth,0,0)
[222]280  cwin = stdscr.derwin(theight-1,int(0.3*twidth),1,0)
281  fwin = stdscr.derwin(theight-1,int(0.7*twidth),1,int(0.3*twidth))
[219]282
[220]283  update(stdscr)
[221]284  stdscr.keypad(1)
[220]285  while 1:
286    try:
287      c = stdscr.getch()
288    except:
289      quit()
290    if c == ord('q'): quit()  # quit
[257]291    else:
292      update(stdscr)
[219]293
[244]294def printVersion():
295  print "ctw version %s"%(version)
296  print "weatherfeed.py backend version: %2.2f"%(weatherfeed.version)
297
[220]298def usage():
299  print """
[244]300  Welcome to "Curse the Weather" Version %s
[220]301  This program will display the weather for a city on the console.
302  Copyright (c)2004 Dan Cardamore <dan@hld.ca>
303
[253]304  ctw [options] LOCATION
[220]305    options:
[244]306      --refresh=<minutes> : the delay in minutes to refresh.
[220]307      -h, --help : this help text
308      -d : debug output
[244]309      --version: prints the version of this application
[277]310      --nometric: print information in imperial units
[220]311
[253]312   To determine your location code (LOCATION):
[220]313    1. visit: http://www.weather.com
314    2. get your local forecast
315    3. look at the URL, it will look similar to:
316       http://www.weather.com/outlook/travel/local/CAXX0343?from=search_city
317    4. Your location code is the part after "local/" and before "?from"
318       in this example it is CAXX0343
319
[244]320  """ %(version)
[220]321
[219]322if __name__ == "__main__":
[255]323  if weatherfeed.version < 0.2:
324    print "This version of ctw requires weatherfeed.py version 0.2 and above"
325    print "See: http://opensource.hld.ca/trac.cgi/wiki/CurseTheWeather"
326    print "to get the latest version."
327    sys.exit(1)
328
[220]329  import getopt
330  try:
331    opts, args = getopt.getopt(sys.argv[1:], 
[277]332                      "hrv:d", ["help","refresh=","version","nometric"])
[220]333  except getopt.GetoptError:
334    usage()
335    sys.exit(2)
336
337  global debug
338  debug = False
339  global location
340  location = ""
341  global refresh
342  refresh = 60
[277]343  global metric
344  metric = True
[220]345
346  for o, a in opts:
347    if o == "-d":
348      debug = True
[244]349    if o in ("-v", "--version"):
350      printVersion()
351      sys.exit(1)
[220]352    if o in ("-h", "--help"):
353      usage()
354      sys.exit()
355    if o in ("-r", "--refresh"):
356      refresh = int(a)
[277]357    if o in ("--nometric"):
358      metric = False
[220]359
[253]360  if len(args) != 1:
361    print "invalid location, or too many arguments"
362    usage()
363    sys.exit(2)
364
365  for arg in args:
366    location = arg
367
[220]368  if location == "":
369    print "Invalid location!"
370    usage()
371    sys.exit(2)
372
373  if refresh < 10:
[224]374    print "Invalid refresh rate.  Must be at least 10 minute"
[220]375    usage()
376    sys.exit(2)
377
378  curses.wrapper(main)
Note: See TracBrowser for help on using the browser.