blob: 975154a5e8404abb5346783b65f7446b20ddddeb [file] [log] [blame]
Guido van Rossum93a35f41992-12-14 14:12:10 +00001#! /usr/local/bin/python
Guido van Rossum9cf8f331992-03-30 10:54:51 +00002
3# 'clock' -- A simple alarm clock
4
5# The alarm can be set at 5 minute intervals on a 12 hour basis.
6# It is controlled with the mouse:
7# - Click and drag around the circle to set the alarm.
8# - Click far outside the circle to clear the alarm.
9# - Click near the center to set the alarm at the last time set.
10# The alarm time is indicated by a small triangle just outside the circle,
11# and also by a digital time at the bottom.
12# The indicators disappear when the alarm is not set.
13# When the alarm goes off, it beeps every minute for five minutes,
14# and the clock turns into inverse video.
15# Click or activate the window to turn the ringing off.
16
17import stdwin
18from stdwinevents import WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP, \
19 WE_TIMER, WE_DRAW, WE_SIZE, WE_CLOSE, WE_ACTIVATE
20import mainloop
21import time
22from math import sin, cos, atan2, pi, sqrt
23
24DEFWIDTH, DEFHEIGHT = 200, 200
25
26MOUSE_EVENTS = (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP)
27ORIGIN = 0, 0
28FARAWAY = 2000, 2000
29EVERYWHERE = ORIGIN, FARAWAY
30
31# TZDIFF = 5*3600 # THINK C 3.0 returns UCT if local time is EST
32TZDIFF = 0 # THINK C 4.0 always returns local time
33
34
35def main():
36 win = makewindow()
37 del win
38 mainloop.mainloop()
39
40def makewindow():
41 stdwin.setdefwinsize(DEFWIDTH, DEFHEIGHT + stdwin.lineheight())
42 win = stdwin.open('clock')
43 setdimensions(win)
44 win.set = 1 # True when alarm is set
45 win.time = 11*60 + 40 # Time when alarm must go off
46 win.ring = 0 # True when alarm is ringing
47 win.dispatch = cdispatch
48 mainloop.register(win)
49 settimer(win)
50 return win
51
52def cdispatch(event):
53 type, win, detail = event
54 if type == WE_DRAW:
55 drawproc(win, detail)
56 elif type == WE_TIMER:
57 settimer(win)
58 drawproc(win, EVERYWHERE)
59 elif type in MOUSE_EVENTS:
60 mouseclick(win, type, detail)
61 elif type == WE_ACTIVATE:
62 if win.ring:
63 # Turn the ringing off
64 win.ring = 0
65 win.begindrawing().invert(win.mainarea)
66 elif type == WE_SIZE:
67 win.change(EVERYWHERE)
68 setdimensions(win)
69 elif type == WE_CLOSE:
70 mainloop.unregister(win)
Guido van Rossum7b741761993-01-13 12:45:41 +000071 win.close()
Guido van Rossum9cf8f331992-03-30 10:54:51 +000072
73def setdimensions(win):
74 width, height = win.getwinsize()
75 height = height - stdwin.lineheight()
76 if width < height: size = width
77 else: size = height
78 halfwidth = width/2
79 halfheight = height/2
80 win.center = halfwidth, halfheight
81 win.radius = size*45/100
82 win.width = width
83 win.height = height
84 win.corner = width, height
85 win.mainarea = ORIGIN, win.corner
86 win.lineheight = stdwin.lineheight()
87 win.farcorner = width, height + win.lineheight
88 win.statusarea = (0, height), win.farcorner
89 win.fullarea = ORIGIN, win.farcorner
90
91def settimer(win):
92 now = getlocaltime()
93 win.times = calctime(now)
94 delay = 61 - now % 60
95 win.settimer(10 * delay)
96 minutes = (now/60) % 720
97 if win.ring:
98 # Is it time to stop the alarm ringing?
99 since = (minutes - win.time + 720) % 720
100 if since >= 5:
101 # Stop it now
102 win.ring = 0
103 else:
104 # Ring again, once every minute
105 stdwin.fleep()
106 elif win.set and minutes == win.time:
107 # Start the alarm ringing
108 win.ring = 1
109 stdwin.fleep()
110
111def drawproc(win, area):
112 hours, minutes, seconds = win.times
113 d = win.begindrawing()
114 d.cliprect(area)
115 d.erase(EVERYWHERE)
116 d.circle(win.center, win.radius)
117 d.line(win.center, calcpoint(win, hours*30 + minutes/2, 0.6))
118 d.line(win.center, calcpoint(win, minutes*6, 1.0))
119 str = dd(hours) + ':' + dd(minutes)
120 p = (win.width - d.textwidth(str))/2, win.height * 3 / 4
121 d.text(p, str)
122 if win.set:
123 drawalarm(win, d)
124 drawalarmtime(win, d)
125 if win.ring:
126 d.invert(win.mainarea)
127
128def mouseclick(win, type, detail):
129 d = win.begindrawing()
130 if win.ring:
131 # First turn the ringing off
132 win.ring = 0
133 d.invert(win.mainarea)
134 h, v = detail[0]
135 ch, cv = win.center
136 x, y = h-ch, cv-v
137 dist = sqrt(x*x + y*y) / float(win.radius)
138 if dist > 1.2:
139 if win.set:
140 drawalarm(win, d)
141 erasealarmtime(win, d)
142 win.set = 0
143 elif dist < 0.8:
144 if not win.set:
145 win.set = 1
146 drawalarm(win, d)
147 drawalarmtime(win, d)
148 else:
149 # Convert to half-degrees (range 0..720)
150 alpha = atan2(y, x)
151 hdeg = alpha*360.0/pi
152 hdeg = 180.0 - hdeg
153 hdeg = (hdeg + 720.0) % 720.0
154 atime = 5*int(hdeg/5.0 + 0.5)
155 if atime <> win.time or not win.set:
156 if win.set:
157 drawalarm(win, d)
158 erasealarmtime(win, d)
159 win.set = 1
160 win.time = atime
161 drawalarm(win, d)
162 drawalarmtime(win, d)
163
164def drawalarm(win, d):
165 p1 = calcpoint(win, float(win.time)/2.0, 1.02)
166 p2 = calcpoint(win, float(win.time)/2.0 - 4.0, 1.1)
167 p3 = calcpoint(win, float(win.time)/2.0 + 4.0, 1.1)
168 d.xorline(p1, p2)
169 d.xorline(p2, p3)
170 d.xorline(p3, p1)
171
172def erasealarmtime(win, d):
173 d.erase(win.statusarea)
174
175def drawalarmtime(win, d):
176 # win.time is in the range 0..720 with origin at 12 o'clock
177 # Convert to hours (0..12) and minutes (12*(0..60))
178 hh = win.time/60
179 mm = win.time%60
180 str = 'Alarm@' + dd(hh) + ':' + dd(mm)
181 p1 = (win.width - d.textwidth(str))/2, win.height
182 d.text(p1, str)
183
184def calcpoint(win, degrees, size):
185 alpha = pi/2.0 - float(degrees) * pi/180.0
186 x, y = cos(alpha), sin(alpha)
187 h, v = win.center
188 r = float(win.radius)
189 return h + int(x*size*r), v - int(y*size*r)
190
191def calctime(now):
192 seconds = now % 60
193 minutes = (now/60) % 60
194 hours = (now/3600) % 12
195 return hours, minutes, seconds
196
197def dd(n):
198 s = `n`
199 return '0'*(2-len(s)) + s
200
201def getlocaltime():
Guido van Rossum7b741761993-01-13 12:45:41 +0000202 return int(time.time() - TZDIFF)
Guido van Rossum9cf8f331992-03-30 10:54:51 +0000203
204main()