mirror of https://github.com/python/cpython
194 lines
5.0 KiB
Python
Executable File
194 lines
5.0 KiB
Python
Executable File
#! /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()
|