ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/ns_dev/Python/NinoCode/Active_prgs/PyClock/clock.pyw
Revision: 8
Committed: Sat May 5 04:21:19 2012 UTC (13 years, 10 months ago) by ninoborges
File size: 9899 byte(s)
Log Message:
Initial Import

File Contents

# User Rev Content
1 ninoborges 8 ###############################################################################
2     # PyClock: a clock GUI, with both analog and digital display modes, a
3     # popup date label, clock face images, resizing, etc. May be run both
4     # stand-alone, or embbeded (attached) in other GUIs that need a clock.
5     ###############################################################################
6    
7     from Tkinter import *
8     import math, time, string
9    
10    
11     ###############################################################################
12     # Option configuration classes
13     ###############################################################################
14    
15    
16     class ClockConfig:
17     # defaults--override in instance or subclass
18     size = 200 # width=height
19     bg, fg = 'beige', 'brown' # face, tick colors
20     hh, mh, sh, cog = 'black', 'navy', 'blue', 'red' # clock hands, center
21     picture = None # face photo file
22    
23     class PhotoClockConfig(ClockConfig):
24     # sample configuration
25     size = 320
26     picture = 'gifs/ora-pp.gif'
27     bg, hh, mh = 'white', 'blue', 'orange'
28    
29    
30     ###############################################################################
31     # Digital display object
32     ###############################################################################
33    
34     class DigitalDisplay(Frame):
35     def __init__(self, parent, cfg):
36     Frame.__init__(self, parent)
37     self.hour = Label(self)
38     self.mins = Label(self)
39     self.secs = Label(self)
40     self.ampm = Label(self)
41     for label in self.hour, self.mins, self.secs, self.ampm:
42     label.config(bd=4, relief=SUNKEN, bg=cfg.bg, fg=cfg.fg)
43     label.pack(side=LEFT)
44    
45     def onUpdate(self, hour, mins, secs, ampm, cfg):
46     mins = string.zfill(str(mins), 2)
47     self.hour.config(text=str(hour), width=4)
48     self.mins.config(text=str(mins), width=4)
49     self.secs.config(text=str(secs), width=4)
50     self.ampm.config(text=str(ampm), width=4)
51    
52     def onResize(self, newWidth, newHeight, cfg):
53     pass # nothing to redraw here
54    
55    
56     ###############################################################################
57     # Analog display object
58     ###############################################################################
59    
60     class AnalogDisplay(Canvas):
61     def __init__(self, parent, cfg):
62     Canvas.__init__(self, parent,
63     width=cfg.size, height=cfg.size, bg=cfg.bg)
64     self.drawClockface(cfg)
65     self.hourHand = self.minsHand = self.secsHand = self.cog = None
66    
67     def drawClockface(self, cfg): # on start and resize
68     if cfg.picture: # draw ovals, picture
69     try:
70     self.image = PhotoImage(file=cfg.picture) # bkground
71     except:
72     self.image = BitmapImage(file=cfg.picture) # save ref
73     imgx = (cfg.size - self.image.width( )) / 2 # center it
74     imgy = (cfg.size - self.image.height( )) / 2
75     self.create_image(imgx+1, imgy+1, anchor=NW, image=self.image)
76     originX = originY = radius = cfg.size/2
77     for i in range(60):
78     x, y = self.point(i, 60, radius-6, originX, originY)
79     self.create_rectangle(x-1, y-1, x+1, y+1, fill=cfg.fg) # mins
80     for i in range(12):
81     x, y = self.point(i, 12, radius-6, originX, originY)
82     self.create_rectangle(x-3, y-3, x+3, y+3, fill=cfg.fg) # hours
83     self.ampm = self.create_text(3, 3, anchor=NW, fill=cfg.fg)
84    
85     def point(self, tick, units, radius, originX, originY):
86     angle = tick * (360.0 / units)
87     radiansPerDegree = math.pi / 180
88     pointX = int( round( radius * math.sin(angle * radiansPerDegree) ))
89     pointY = int( round( radius * math.cos(angle * radiansPerDegree) ))
90     return (pointX + originX+1), (originY+1 - pointY)
91    
92     def onUpdate(self, hour, mins, secs, ampm, cfg): # on timer callback
93     if self.cog: # redraw hands, cog
94     self.delete(self.cog)
95     self.delete(self.hourHand)
96     self.delete(self.minsHand)
97     self.delete(self.secsHand)
98     originX = originY = radius = cfg.size/2
99     hour = hour + (mins / 60.0)
100     hx, hy = self.point(hour, 12, (radius * .80), originX, originY)
101     mx, my = self.point(mins, 60, (radius * .90), originX, originY)
102     sx, sy = self.point(secs, 60, (radius * .95), originX, originY)
103     self.hourHand = self.create_line(originX, originY, hx, hy,
104     width=(cfg.size * .04),
105     arrow='last', arrowshape=(25,25,15), fill=cfg.hh)
106     self.minsHand = self.create_line(originX, originY, mx, my,
107     width=(cfg.size * .03),
108     arrow='last', arrowshape=(20,20,10), fill=cfg.mh)
109     self.secsHand = self.create_line(originX, originY, sx, sy,
110     width=1,
111     arrow='last', arrowshape=(5,10,5), fill=cfg.sh)
112     cogsz = cfg.size * .01
113     self.cog = self.create_oval(originX-cogsz, originY+cogsz,
114     originX+cogsz, originY-cogsz, fill=cfg.cog)
115     self.dchars(self.ampm, 0, END)
116     self.insert(self.ampm, END, ampm)
117    
118     def onResize(self, newWidth, newHeight, cfg):
119     newSize = min(newWidth, newHeight)
120     #print 'analog onResize', cfg.size+4, newSize
121     if newSize != cfg.size+4:
122     cfg.size = newSize-4
123     self.delete('all')
124     self.drawClockface(cfg) # onUpdate called next
125    
126    
127     ###############################################################################
128     # Clock composite object
129     ###############################################################################
130    
131     ChecksPerSec = 10 # second change timer
132    
133     class Clock(Frame):
134     def __init__(self, config=ClockConfig, parent=None):
135     Frame.__init__(self, parent)
136     self.cfg = config
137     self.makeWidgets(parent) # children are packed but
138     self.labelOn = 0 # clients pack or grid me
139     self.display = self.digitalDisplay
140     self.lastSec = -1
141     self.onSwitchMode(None)
142     self.onTimer( )
143    
144     def makeWidgets(self, parent):
145     self.digitalDisplay = DigitalDisplay(self, self.cfg)
146     self.analogDisplay = AnalogDisplay(self, self.cfg)
147     self.dateLabel = Label(self, bd=3, bg='red', fg='blue')
148     parent.bind('<ButtonPress-1>', self.onSwitchMode)
149     parent.bind('<ButtonPress-3>', self.onToggleLabel)
150     parent.bind('<Configure>', self.onResize)
151    
152     def onSwitchMode(self, event):
153     self.display.pack_forget( )
154     if self.display == self.analogDisplay:
155     self.display = self.digitalDisplay
156     else:
157     self.display = self.analogDisplay
158     self.display.pack(side=TOP, expand=YES, fill=BOTH)
159    
160     def onToggleLabel(self, event):
161     self.labelOn = self.labelOn + 1
162     if self.labelOn % 2:
163     self.dateLabel.pack(side=BOTTOM, fill=X)
164     else:
165     self.dateLabel.pack_forget( )
166     self.update( )
167    
168     def onResize(self, event):
169     if event.widget == self.display:
170     self.display.onResize(event.width, event.height, self.cfg)
171    
172     def onTimer(self):
173     secsSinceEpoch = time.time( )
174     timeTuple = time.localtime(secsSinceEpoch)
175     hour, min, sec = timeTuple[3:6]
176     if sec != self.lastSec:
177     self.lastSec = sec
178     ampm = ((hour >= 12) and 'PM') or 'AM' # 0...23
179     hour = (hour % 12) or 12 # 12..11
180     self.display.onUpdate(hour, min, sec, ampm, self.cfg)
181     self.dateLabel.config(text=time.ctime(secsSinceEpoch))
182     self.after(1000 / ChecksPerSec, self.onTimer) # run N times per second
183    
184    
185     ###############################################################################
186     # Stand-alone clocks
187     ###############################################################################
188    
189     class ClockWindow(Clock):
190     def __init__(self, config=ClockConfig, parent=None, name=''):
191     Clock.__init__(self, config, parent)
192     self.pack(expand=YES, fill=BOTH)
193     title = 'PyClock 1.0'
194     if name: title = title + ' - ' + name
195     self.master.title(title) # master=parent or default
196     self.master.protocol('WM_DELETE_WINDOW', self.quit)
197    
198    
199     ###############################################################################
200     # Program run
201     ###############################################################################
202    
203     if __name__ == '__main__':
204     def getOptions(config, argv):
205     for attr in dir(ClockConfig): # fill default config obj,
206     try: # from "-attr val" cmd args
207     ix = argv.index('-' + attr)
208     except:
209     continue
210     else:
211     if ix in range(1, len(argv)-1):
212     if type(getattr(ClockConfig, attr)) == type(0):
213     setattr(config, attr, int(argv[ix+1]))
214     else:
215     setattr(config, attr, argv[ix+1])
216     import sys
217     config = ClockConfig( )
218     #config = PhotoClockConfig( )
219     if len(sys.argv) >= 2:
220     getOptions(config, sys.argv) # clock.py -size n -bg 'blue'...
221     myclock = ClockWindow(config, Tk( )) # parent is Tk root if standalone
222     myclock.mainloop( )