ActiveState Code

Recipe 576830: analog clock


A very simple tkinter analog clock

Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# clock.py  By Anton Vredegoor (anton.vredegoor@gmail.com) 
# last edit: july 2009,
# license: GPL
# enjoy!

"""
A very simple  clock.

The program transforms worldcoordinates into screencoordinates 
and vice versa according to an algorithm found in: "Programming 
principles in computer graphics" by Leendert Ammeraal.

"""

from Tkinter import *
from time import localtime
from datetime import timedelta,datetime
from math import sin, cos, pi
import sys, types, os

_inidle = type(sys.stdin) == types.InstanceType and \
	  sys.stdin.__class__.__name__ == 'PyShell'

class transformer:

    def __init__(self, world, viewport):
        self.world = world 
        self.viewport = viewport

    def point(self, x, y):
        x_min, y_min, x_max, y_max = self.world
        X_min, Y_min, X_max, Y_max = self.viewport
        f_x = float(X_max-X_min) /float(x_max-x_min) 
        f_y = float(Y_max-Y_min) / float(y_max-y_min) 
        f = min(f_x,f_y)
        x_c = 0.5 * (x_min + x_max)
        y_c = 0.5 * (y_min + y_max)
        X_c = 0.5 * (X_min + X_max)
        Y_c = 0.5 * (Y_min + Y_max)
        c_1 = X_c - f * x_c
        c_2 = Y_c - f * y_c
        X = f * x + c_1
        Y = f * y + c_2
        return X , Y

    def twopoints(self,x1,y1,x2,y2):
        return self.point(x1,y1),self.point(x2,y2)

class clock:

    def __init__(self,root,deltahours = 0):
        self.world       = [-1,-1,1,1]
        self.bgcolor     = '#000000'
        self.circlecolor = '#808080'
        self.timecolor   = '#ffffff'
        self.circlesize  = 0.09
        self._ALL        = 'all'
        self.pad         = 25
        self.root        = root
        WIDTH, HEIGHT = 400, 400
        root.bind("<Escape>", lambda _ : root.destroy())
        self.delta = timedelta(hours = deltahours)  
        self.canvas = Canvas(root, 
                width       = WIDTH,
                height      = HEIGHT,
                background  = self.bgcolor)
        viewport = (self.pad,self.pad,WIDTH-self.pad,HEIGHT-self.pad)
        self.T = transformer(self.world,viewport)
        self.root.title('Clock')
        self.canvas.bind("<Configure>",self.configure())
        self.canvas.pack(fill=BOTH, expand=YES)
        self.poll()
 
    def configure(self):
        self.redraw()
    
    def redraw(self):
        sc = self.canvas
        sc.delete(self._ALL)
        width = sc.winfo_width()
        height =sc.winfo_height()
        sc.create_rectangle([[0,0],[width,height]],
                fill = self.bgcolor, tag = self._ALL)
        viewport = (self.pad,self.pad,width-self.pad,height-self.pad)
        self.T = transformer(self.world,viewport)
        self.paintgrafics()

    def paintgrafics(self):
        start = -pi/2
        step = pi/6
        for i in range(12):
            angle =  start+i*step
            x, y = cos(angle),sin(angle)
            self.paintcircle(x,y)
        self.painthms()
        self.paintcircle(0,0)
    
    def painthms(self):
        T = datetime.timetuple(datetime.utcnow()-self.delta)
        x,x,x,h,m,s,x,x,x = T
        self.root.title('%02i:%02i:%02i' %(h,m,s))
        angle = -pi/2 + (pi/6)*h + (pi/6)*(m/60.0)
        x, y = cos(angle)*.60,sin(angle)*.60   
        scl = self.canvas.create_line
        scl(apply(self.T.twopoints,[0,0,x,y]), fill = self.timecolor, 
                    tag =self._ALL, width = 6)
        angle = -pi/2 + (pi/30)*m + (pi/30)*(s/60.0)
        x, y = cos(angle)*.80,sin(angle)*.80   
        scl(apply(self.T.twopoints,[0,0,x,y]), fill = self.timecolor, 
                    tag =self._ALL, width = 3)
        angle = -pi/2 + (pi/30)*s
        x, y = cos(angle)*.95,sin(angle)*.95   
        scl(apply(self.T.twopoints, [0,0,x,y]), fill = self.timecolor,
                    tag =self._ALL, arrow = 'last')
    
    def paintcircle(self,x,y):
        ss = self.circlesize / 2.0
        mybbox = [-ss+x,-ss+y,ss+x,ss+y]
        sco = self.canvas.create_oval
        sco(apply(self.T.twopoints,mybbox), fill = self.circlecolor,
                    tag =self._ALL)
   
    def poll(self):
        self.configure()
        self.root.after(200,self.poll)

def main():
    root= Tk()
    # deltahours: how far are you from utc?
    # someone should automatize that, but I sometimes want to display
    # time as if I am in another timezone ...
    clock(root,deltahours = -2)
    if not _inidle:
        root.mainloop()

if __name__=='__main__':
  main()

Discussion

If one has a wide screen, the sides look so empty, and I like analog clocks. One issue is one has to manually edit the code to set the time zone, but that is a feature, not a bug!

Comments

  1. 1. At 7:10 p.m. on 4 jul 2009, James Mills said:

    Hi. Great clock :) I believe there might be a bug or two though as it showed the wrong time on my PC.

    --JamesMills (prologic)

  2. 2. At 2:12 a.m. on 11 jul 2009, Anton Vredegoor (the author) said:

    @James Mills: Was the time off by a round number of hours? If so, you could try adjusting the deltahours knob.

Sign in to comment