summaryrefslogtreecommitdiffstats
path: root/Demo/stdwin/clock.py
diff options
context:
space:
mode:
Diffstat (limited to 'Demo/stdwin/clock.py')
-rwxr-xr-xDemo/stdwin/clock.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/Demo/stdwin/clock.py b/Demo/stdwin/clock.py
new file mode 100755
index 0000000..0dc2296
--- /dev/null
+++ b/Demo/stdwin/clock.py
@@ -0,0 +1,203 @@
+#! /usr/local/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
+
+# TZDIFF = 5*3600 # THINK C 3.0 returns UCT if local time is EST
+TZDIFF = 0 # THINK C 4.0 always returns local time
+
+
+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)
+
+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 = getlocaltime()
+ win.times = calctime(now)
+ delay = 61 - now % 60
+ win.settimer(10 * delay)
+ minutes = (now/60) % 720
+ 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 = dd(hours) + ':' + dd(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@' + dd(hh) + ':' + dd(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):
+ seconds = now % 60
+ minutes = (now/60) % 60
+ hours = (now/3600) % 12
+ return hours, minutes, seconds
+
+def dd(n):
+ s = `n`
+ return '0'*(2-len(s)) + s
+
+def getlocaltime():
+ return time.time() - TZDIFF
+
+main()