root/trunk/ctw/ctw

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

Presentation cleanup provided by Tim Yang. Thanks Tim!

  • Property svn:executable set to *
  • Property svn:keywords set to HeadURL Id
Line 
1#!/usr/bin/env python
2# $HeadURL$
3# $Id$
4# vim: ft=python ts=2 sw=2 et:
5
6# Copyright Dan Cardamore <dan@hld.ca>
7# Licensed under the GNU GPL version 2.0
8# See GPL.gz in source distribution for more information
9
10import sys,time,string,urllib2
11from threading import Timer
12
13import curses
14import weatherfeed
15
16version = "0.6"
17
18class asciiIcons:
19  def __init__(self):
20    self.blank = """
21                   
22                   
23                   
24                   
25                   
26                   
27"""
28
29    self.lightning = """
30                   
31    \\\\           
32     \\\\\\         
33        \\\\       
34          \\\       
35             \     
36"""
37
38    self.cloudy = """
39     __         _   
40   /-   \_/\---/ \ 
41   |         --   |
42   \-_/---\__--__-|
43                   
44                   
45"""
46
47    self.raining = """
48     __         _   
49   /-   \_/\---/ \ 
50   |         --   |
51   |______________|
52                   
53    | | | | | | |   
54"""
55
56
57    self.unknown = """
58                   
59                   
60                   
61                   
62                   
63                   
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
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
99    elif type == "Mostly Cloudy":
100      return self.partlycloudy
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
119    else:
120      return self.unknown
121
122def initColors():
123  try:
124    curses.start_color()
125    curses.use_default_colors()
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
131
132def scnTitle(win):
133  win.clear()
134  maxy,maxx = win.getmaxyx()
135  maxx -= 2
136  win.bkgd(" ",curses.color_pair(1))
137  try:
138    win.addstr(0,1, 
139        ("Location %s  --  Updated: %s" %
140        (weather.currentConditions["cityname"], 
141        weather.currentConditions["observed"] )).center(maxx),
142        curses.A_BOLD
143      )
144  except curses.error: pass
145  win.refresh()
146
147def scnCurrent(win):
148  win.clear()
149  maxy,maxx = win.getmaxyx()
150  maxx -= 2
151  line = 0
152  icons = asciiIcons()
153  win.addstr(line,2, icons.getIcon(weather.currentConditions["type"]));line+=6
154  win.addstr(line,2,("%s" %(weather.currentConditions["type"])).center(maxx),curses.color_pair(4));line+=2
155  win.addstr(line,2,"     Temp: %s" %(weather.currentConditions["temperature"]));line+=1
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"],
160                                    weather.currentConditions["wind"]["direction"])
161             );line+=1
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
170
171  win.box()
172  win.addstr(0,1,"Current Conditions",curses.color_pair(2)|curses.A_BOLD); line += 1
173  win.refresh()
174
175def scnForecast(win):
176  maxy,maxx = win.getmaxyx()
177  maxx -= 2
178  line = 0
179  col = 0
180  global dayWindows
181  global forecastWindowsCreated
182  if not forecastWindowsCreated:
183    forecastWindowsCreated = True
184    day = 0
185    dayWindows = []
186    while day < 5:
187      if day < 5:
188        try:
189          dayWindows.append(win.derwin(int(maxy/5),maxx,int(day*(maxy/5))+1,1))
190        except curses.error: pass
191      else:
192        try:
193          dayWindows.append(win.derwin(int(maxy/5),maxx,int((day-5)*(maxy/5))+1,int(maxx/2)+1))
194        except curses.error: pass
195      day += 1
196
197  day = 0
198  while day < 5:
199    scnDay(day)
200    day+=1
201  win.box()
202  try:
203    win.addstr(0,36,"Forecast Conditions",curses.color_pair(2)|curses.A_BOLD); line += 1
204  except curses.error: pass
205  win.refresh()
206
207def scnDay(day):
208  global dayWindows
209  try:
210    win = dayWindows.pop(0)
211  except:
212    return  #this one wasn't allocated a window so just return
213  win.clear()
214  maxy,maxx = win.getmaxyx()
215  maxx -= 13
216  line = 1
217
218  try:
219    win.addstr(line,1,"Hi/Lo: %s/%s" %(weather.forecast[day]["high"],
220                                         weather.forecast[day]["low"]));line+=0
221  except curses.error: pass
222  try:
223    win.addstr(line,maxx,"Wind:%s %s" %( weather.forecast[day]["day"]["wind"]["speed"],
224                                    weather.forecast[day]["day"]["wind"]["direction"])
225             );line+=1
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:
231    win.addstr(line,maxx,"POP: %s" %(weather.forecast[day]["day"]["pop"]));line+=1
232  except curses.error: pass
233
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
240  win.refresh()
241
242def quit():
243  refreshTimer.cancel()
244  sys.exit(1)
245
246def update(stdscr):
247  global weather
248  while 1:
249    try:
250      weather = weatherfeed.Weather(location, metric)
251    except urllib2.URLError:
252      time.sleep(60)
253      continue
254    break
255
256  scnTitle(twin)
257  scnCurrent(cwin)
258  scnForecast(fwin)
259  stdscr.refresh()
260
261  del weather
262  global refreshTimer
263  try:
264    refreshTimer.cancel()
265  except: pass
266  refreshTimer = Timer(refresh * 60, update, [stdscr])
267  refreshTimer.start()
268
269
270def main(stdscr):
271  global cwin,twin,fwin
272  global twidth,theight
273  theight, twidth = stdscr.getmaxyx()
274
275  global forecastWindowsCreated
276  forecastWindowsCreated = False
277
278  initColors()
279  twin = stdscr.derwin(1,twidth,0,0)
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))
282
283  update(stdscr)
284  stdscr.keypad(1)
285  while 1:
286    try:
287      c = stdscr.getch()
288    except:
289      quit()
290    if c == ord('q'): quit()  # quit
291    else:
292      update(stdscr)
293
294def printVersion():
295  print "ctw version %s"%(version)
296  print "weatherfeed.py backend version: %2.2f"%(weatherfeed.version)
297
298def usage():
299  print """
300  Welcome to "Curse the Weather" Version %s
301  This program will display the weather for a city on the console.
302  Copyright (c)2004 Dan Cardamore <dan@hld.ca>
303
304  ctw [options] LOCATION
305    options:
306      --refresh=<minutes> : the delay in minutes to refresh.
307      -h, --help : this help text
308      -d : debug output
309      --version: prints the version of this application
310      --nometric: print information in imperial units
311
312   To determine your location code (LOCATION):
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
320  """ %(version)
321
322if __name__ == "__main__":
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
329  import getopt
330  try:
331    opts, args = getopt.getopt(sys.argv[1:], 
332                      "hrv:d", ["help","refresh=","version","nometric"])
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
343  global metric
344  metric = True
345
346  for o, a in opts:
347    if o == "-d":
348      debug = True
349    if o in ("-v", "--version"):
350      printVersion()
351      sys.exit(1)
352    if o in ("-h", "--help"):
353      usage()
354      sys.exit()
355    if o in ("-r", "--refresh"):
356      refresh = int(a)
357    if o in ("--nometric"):
358      metric = False
359
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
368  if location == "":
369    print "Invalid location!"
370    usage()
371    sys.exit(2)
372
373  if refresh < 10:
374    print "Invalid refresh rate.  Must be at least 10 minute"
375    usage()
376    sys.exit(2)
377
378  curses.wrapper(main)
Note: See TracBrowser for help on using the browser.