| #! /usr/bin/env python |
| |
| # 'clock' -- A simple alarm clock |
| |
| # The alarm can be set at 5 minute intervals on a 12 hour basis. |
| # It is controlled with the mouse: |
| # - Click and drag around the circle to set the alarm. |
| # - Click far outside the circle to clear the alarm. |
| # - Click near the center to set the alarm at the last time set. |
| # The alarm time is indicated by a small triangle just outside the circle, |
| # and also by a digital time at the bottom. |
| # The indicators disappear when the alarm is not set. |
| # When the alarm goes off, it beeps every minute for five minutes, |
| # and the clock turns into inverse video. |
| # Click or activate the window to turn the ringing off. |
| |
| import stdwin |
| from stdwinevents import WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP, \ |
| WE_TIMER, WE_DRAW, WE_SIZE, WE_CLOSE, WE_ACTIVATE |
| import mainloop |
| import time |
| from math import sin, cos, atan2, pi, sqrt |
| |
| DEFWIDTH, DEFHEIGHT = 200, 200 |
| |
| MOUSE_EVENTS = (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP) |
| ORIGIN = 0, 0 |
| FARAWAY = 2000, 2000 |
| EVERYWHERE = ORIGIN, FARAWAY |
| |
| |
| def main(): |
| win = makewindow() |
| del win |
| mainloop.mainloop() |
| |
| def makewindow(): |
| stdwin.setdefwinsize(DEFWIDTH, DEFHEIGHT + stdwin.lineheight()) |
| win = stdwin.open('clock') |
| setdimensions(win) |
| win.set = 1 # True when alarm is set |
| win.time = 11*60 + 40 # Time when alarm must go off |
| win.ring = 0 # True when alarm is ringing |
| win.dispatch = cdispatch |
| mainloop.register(win) |
| settimer(win) |
| return win |
| |
| def cdispatch(event): |
| type, win, detail = event |
| if type == WE_DRAW: |
| drawproc(win, detail) |
| elif type == WE_TIMER: |
| settimer(win) |
| drawproc(win, EVERYWHERE) |
| elif type in MOUSE_EVENTS: |
| mouseclick(win, type, detail) |
| elif type == WE_ACTIVATE: |
| if win.ring: |
| # Turn the ringing off |
| win.ring = 0 |
| win.begindrawing().invert(win.mainarea) |
| elif type == WE_SIZE: |
| win.change(EVERYWHERE) |
| setdimensions(win) |
| elif type == WE_CLOSE: |
| mainloop.unregister(win) |
| win.close() |
| |
| def setdimensions(win): |
| width, height = win.getwinsize() |
| height = height - stdwin.lineheight() |
| if width < height: size = width |
| else: size = height |
| halfwidth = width/2 |
| halfheight = height/2 |
| win.center = halfwidth, halfheight |
| win.radius = size*45/100 |
| win.width = width |
| win.height = height |
| win.corner = width, height |
| win.mainarea = ORIGIN, win.corner |
| win.lineheight = stdwin.lineheight() |
| win.farcorner = width, height + win.lineheight |
| win.statusarea = (0, height), win.farcorner |
| win.fullarea = ORIGIN, win.farcorner |
| |
| def settimer(win): |
| now = time.time() |
| hours, minutes, seconds = win.times = calctime(now) |
| delay = 61 - seconds |
| win.settimer(10 * delay) |
| minutes = minutes + hours*60 |
| if win.ring: |
| # Is it time to stop the alarm ringing? |
| since = (minutes - win.time + 720) % 720 |
| if since >= 5: |
| # Stop it now |
| win.ring = 0 |
| else: |
| # Ring again, once every minute |
| stdwin.fleep() |
| elif win.set and minutes == win.time: |
| # Start the alarm ringing |
| win.ring = 1 |
| stdwin.fleep() |
| |
| def drawproc(win, area): |
| hours, minutes, seconds = win.times |
| d = win.begindrawing() |
| d.cliprect(area) |
| d.erase(EVERYWHERE) |
| d.circle(win.center, win.radius) |
| d.line(win.center, calcpoint(win, hours*30 + minutes/2, 0.6)) |
| d.line(win.center, calcpoint(win, minutes*6, 1.0)) |
| str = "%02d:%02d" % (hours, minutes) |
| p = (win.width - d.textwidth(str))/2, win.height * 3 / 4 |
| d.text(p, str) |
| if win.set: |
| drawalarm(win, d) |
| drawalarmtime(win, d) |
| if win.ring: |
| d.invert(win.mainarea) |
| |
| def mouseclick(win, type, detail): |
| d = win.begindrawing() |
| if win.ring: |
| # First turn the ringing off |
| win.ring = 0 |
| d.invert(win.mainarea) |
| h, v = detail[0] |
| ch, cv = win.center |
| x, y = h-ch, cv-v |
| dist = sqrt(x*x + y*y) / float(win.radius) |
| if dist > 1.2: |
| if win.set: |
| drawalarm(win, d) |
| erasealarmtime(win, d) |
| win.set = 0 |
| elif dist < 0.8: |
| if not win.set: |
| win.set = 1 |
| drawalarm(win, d) |
| drawalarmtime(win, d) |
| else: |
| # Convert to half-degrees (range 0..720) |
| alpha = atan2(y, x) |
| hdeg = alpha*360.0/pi |
| hdeg = 180.0 - hdeg |
| hdeg = (hdeg + 720.0) % 720.0 |
| atime = 5*int(hdeg/5.0 + 0.5) |
| if atime <> win.time or not win.set: |
| if win.set: |
| drawalarm(win, d) |
| erasealarmtime(win, d) |
| win.set = 1 |
| win.time = atime |
| drawalarm(win, d) |
| drawalarmtime(win, d) |
| |
| def drawalarm(win, d): |
| p1 = calcpoint(win, float(win.time)/2.0, 1.02) |
| p2 = calcpoint(win, float(win.time)/2.0 - 4.0, 1.1) |
| p3 = calcpoint(win, float(win.time)/2.0 + 4.0, 1.1) |
| d.xorline(p1, p2) |
| d.xorline(p2, p3) |
| d.xorline(p3, p1) |
| |
| def erasealarmtime(win, d): |
| d.erase(win.statusarea) |
| |
| def drawalarmtime(win, d): |
| # win.time is in the range 0..720 with origin at 12 o'clock |
| # Convert to hours (0..12) and minutes (12*(0..60)) |
| hh = win.time/60 |
| mm = win.time%60 |
| str = 'Alarm@%02d:%02d' % (hh, mm) |
| p1 = (win.width - d.textwidth(str))/2, win.height |
| d.text(p1, str) |
| |
| def calcpoint(win, degrees, size): |
| alpha = pi/2.0 - float(degrees) * pi/180.0 |
| x, y = cos(alpha), sin(alpha) |
| h, v = win.center |
| r = float(win.radius) |
| return h + int(x*size*r), v - int(y*size*r) |
| |
| def calctime(now): |
| hours, minutes, seconds = time.localtime(now)[3:6] |
| hours = hours % 12 |
| return hours, minutes, seconds |
| |
| main() |