3
# gauge.py Created on: 2000/01/06
4
# Author : Duncan Grisby (dpg1)
6
# Copyright (C) 2000 AT&T Laboratories Cambridge
8
# This is free software; you can redistribute it and/or modify it
9
# under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
# General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25
# Pretty circular gauges in Tk
27
import math, time, string, operator
30
SCALEFONT = "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-*-*"
31
DIGITFONT = "-*-lucidatypewriter-*-r-*-*-*-120-*-*-m-*-*-*"
32
TITLEFONT = "-*-helvetica-regular-r-*-*-*-120-*-*-*-*-*-*"
37
def __init__(self, parentcanvas,
39
min = 0, # Minimum value
40
max = 100, # Maximum value
41
initial = 0, # Initial value
42
scalevalues = None, # List, eg. [0, 50, 100]
43
scalearc = 240, # Angle of scale in degrees
44
minlocation = 240, # Angle in degrees of min value
45
reverse = 0, # Default clockwise; 1 for anti-
46
radius = 65, # Radius in pixels
47
handcolour = "red", # Colour of hand
48
ticklen = 8, # Length of ticks in pixels
49
bigticks = None, # Set big ticks when value % n == 0
50
bigticklen = 12, # Length of big ticks
51
unitspertick= 5, # Units per tick
52
knobradius = 4, # Radius of knob in pixels
53
knobcolour = "black", # Colour of knob
54
scalefont = SCALEFONT,
55
scalecolour = "black",
56
scalesep = 20, # Separation of scale text from rim
57
digitformat = "%d", # Format of digital display
58
digitfont = DIGITFONT,
59
digitcolour = "black",
60
digitpos = 35, # Position of centre of digital display
62
titlefont = TITLEFONT,
63
titlecolour = "black",
64
titlepos = 25, # Position of centre of title
65
linecolour = "black", # Colour of lines
66
facecolour = "white", # Colour of gauge face
68
interactive = 0): # Is the gauge interactive
70
if scalevalues is None:
71
scalevalues = [min, (min + max) / 2, max]
73
# Things we need to remember
78
self.scalesep = scalesep
79
self.scalevalues = scalevalues
80
self.scalefont = scalefont
81
self.scalecolour = scalecolour
82
self.scalearc_r = scalearc * math.pi / 180
83
self.minlocation_r = minlocation * math.pi / 180
84
self.reverse = reverse
86
self.handcolour = handcolour
87
self.knobradius = knobradius
88
self.digitformat = digitformat
89
self.digitfont = digitfont
90
self.digitcolour = digitcolour
91
self.digitpos = digitpos
92
self.titlefont = titlefont
93
self.titlecolour = titlecolour
94
self.titlepos = titlepos
95
self.interactive = interactive
97
# Polygon used to draw the hand
98
self.handPolygon = [(-12,-12), (0,radius-5), (12,-12)]
100
# Create a canvas for this gauge
101
wwidth = wheight = 2 * radius + 2 * WINDOWPAD
106
self.gcanvas = Canvas(parentcanvas,
107
height=wheight, width=wwidth, bg=bgcolour,
108
borderwidth=0, highlightthickness=0)
110
# Draw the gauge and knob
111
self.gcanvas.create_oval(tl,tl, br,br,
112
fill=facecolour, outline=linecolour,
113
width=3, tags="face")
114
self.gcanvas.create_oval(centre - knobradius, centre - knobradius,
115
centre + knobradius, centre + knobradius,
125
if tick % bigticks == 0:
129
elif tick in scalevalues:
134
xof = (radius - len) * sa
136
yof = (radius - len) * ca
139
self.gcanvas.create_line(xof+centre, yof+centre,
140
xot+centre, yot+centre,
141
fill=linecolour, width=1)
143
tick = tick + unitspertick
146
self.gcanvas.create_text(centre, centre - titlepos,
147
anchor=CENTER, fill=titlecolour,
148
font=titlefont, text=title,
149
justify=CENTER, tags="title")
155
# Put gauge into parent canvas
156
parentcanvas.create_window(x, y, width=wwidth, height=wheight,
157
anchor=CENTER, window=self.gcanvas)
160
scalerad = self.radius - self.scalesep
162
for val in self.scalevalues:
165
self.gcanvas.create_text(centre + scalerad * math.sin(a),
166
centre + scalerad * -math.cos(a),
167
anchor=CENTER, fill=self.scalecolour,
168
font=self.scalefont, text=str(val))
171
a = self.angle(self.val)
173
hp = self.rotatePoints(self.handPolygon, a)
174
hp = self.translatePoints(hp, centre, centre)
175
hl = self.flattenPoints(hp)
177
self.hand = self.gcanvas.create_polygon(hl, fill=self.handcolour,
180
self.digit = self.gcanvas.create_text(centre, centre + self.digitpos,
182
fill=self.digitcolour,
184
text=self.digitformat % self.val,
187
self.gcanvas.tag_raise(self.hand, "face")
190
self.gcanvas.tag_bind(self.hand, "<B1-Motion>", self.motionhandler)
193
def set(self, value):
194
"""Set value of gauge"""
196
if self.val == value:
200
a = self.angle(value)
202
hp = self.rotatePoints(self.handPolygon, a)
203
hp = self.translatePoints(hp, centre, centre)
204
hl = self.flattenPoints(hp)
206
apply(self.gcanvas.coords, [self.hand] + hl)
207
self.gcanvas.itemconfigure(self.digit, text=self.digitformat % value)
211
"""Get value of gauge"""
214
def motionhandler(self, event):
215
x = event.x - self.centre
216
y = event.y - self.centre
217
a = math.atan2(x,-y) - self.minlocation_r
219
while a < 0: a = a + 2 * math.pi
221
frac = a / self.scalearc_r
225
self.set(self.max - (self.max - self.min) * frac)
227
self.set(self.min + (self.max - self.min) * frac)
229
def angle(self, value):
230
"""Convert value to an angle in radians"""
232
if self.reverse == 1:
233
value = self.max - value
236
a = self.minlocation_r
237
elif value > self.max:
238
a = self.minlocation_r + self.scalearc_r
240
a = self.minlocation_r + \
241
self.scalearc_r * (value - self.min) / (self.max - self.min)
248
def rotatePoint(self, point, theta):
249
"""Rotate a point clockwise about the origin by an angle in radians"""
252
r = math.sqrt(x*x + y*y)
253
a = math.atan2(y,x) - theta
255
return (r * math.cos(a), r * -math.sin(a))
257
def rotatePoints(self, points, theta):
258
return map(lambda p, t=theta, rp=self.rotatePoint: rp(p, t), points)
260
def translatePoints(self, points, x, y):
261
return map(lambda p, x=x, y=y: (p[0]+x,p[1]+y), points)
263
def flattenPoints(self, points):
264
return list(reduce(operator.add, points))
267
class Compass(Gauge):
268
def __init__(self, parentcanvas,
270
radius = 65, # Radius in pixels
271
handcolour = "red", # Colour of hand
272
ticklen = 8, # Length of ticks in pixels
273
bigticklen = 12, # Length of big ticks
274
knobradius = 4, # Radius of knob in pixels
275
knobcolour = "black", # Colour of knob
276
scalefont = SCALEFONT,
277
scalecolour = "black",
278
scalesep = 20, # Separation of scale text from rim
279
digitfont = DIGITFONT,
280
digitcolour = "black",
281
digitpos = 25, # Position of centre of digital display
283
titlefont = TITLEFONT,
284
titlecolour = "black",
285
titlepos = 25, # Position of centre of title
286
linecolour = "black", # Colour of lines
287
facecolour = "white", # Colour of gauge face
291
Gauge.__init__(self, parentcanvas, x, y, initial = 0,
293
minlocation = 0, scalearc = 360,
294
radius = radius, handcolour = handcolour,
295
ticklen = ticklen, bigticks = 45,
296
bigticklen = bigticklen, unitspertick = 15,
297
knobradius = knobradius, knobcolour = knobcolour,
298
scalefont = scalefont, scalecolour = scalecolour,
299
scalesep = scalesep, digitformat = "%d deg",
300
digitfont = digitfont, digitcolour = digitcolour,
301
digitpos = digitpos, title = title,
302
titlefont = titlefont, titlecolour = titlecolour,
303
titlepos = titlepos, linecolour = linecolour,
304
facecolour = facecolour, bgcolour = bgcolour,
305
interactive = interactive)
308
scalerad = self.radius - self.scalesep
310
for val,txt in [(0,"N"), (90,"E"), (180,"S"), (270,"W")]:
313
self.gcanvas.create_text(centre + scalerad * math.sin(a),
314
centre + scalerad * -math.cos(a),
315
anchor=CENTER, fill=self.scalecolour,
316
font=self.scalefont, text=txt)
320
def __init__(self, parentcanvas,
322
initial = time.time(),
324
radius = 65, # Radius in pixels
325
handcolour = "red", # Colour of hand
326
ticklen = 8, # Length of ticks in pixels
327
bigticklen = 12, # Length of big ticks
328
knobradius = 4, # Radius of knob in pixels
329
knobcolour = "black", # Colour of knob
330
scalefont = SCALEFONT,
331
scalecolour = "black",
332
scalesep = 20, # Separation of scale text from rim
333
digitfont = DIGITFONT,
334
digitcolour = "black",
335
digitpos = 25, # Position of centre of digital display
337
titlefont = TITLEFONT,
338
titlecolour = "black",
339
titlepos = 25, # Position of centre of title
340
linecolour = "black", # Colour of lines
341
facecolour = "white", # Colour of gauge face
345
Gauge.__init__(self, parentcanvas, x, y,
346
initial = initial, reverse = reverse,
348
minlocation = 0, scalearc = 360,
349
radius = radius, handcolour = handcolour,
350
ticklen = ticklen, bigticks = 90,
351
bigticklen = bigticklen, unitspertick = 30,
352
knobradius = knobradius, knobcolour = knobcolour,
353
scalefont = scalefont, scalecolour = scalecolour,
354
scalesep = scalesep, digitformat = None,
355
digitfont = digitfont, digitcolour = digitcolour,
356
digitpos = digitpos, title = "",
357
titlefont = titlefont, titlecolour = titlecolour,
358
titlepos = titlepos, linecolour = linecolour,
359
facecolour = facecolour, bgcolour = bgcolour,
360
interactive = interactive)
366
scalerad = self.radius - self.scalesep
368
for val,txt in [(0,"12"), (90,"3"), (180,"6"), (270,"9")]:
371
self.gcanvas.create_text(centre + scalerad * math.sin(a),
372
centre + scalerad * -math.cos(a),
373
anchor=CENTER, fill=self.scalecolour,
374
font=self.scalefont, text=txt)
377
tt = time.gmtime(self.val)
378
hm = string.lower(time.strftime("%I:%M %p", tt))
385
ha = self.angle(hours * 30 + mins / 2)
386
ma = self.angle(mins * 6)
387
mradius = self.radius - 5
388
hradius = mradius * .75
391
self.hand_h = self.gcanvas.create_line(centre, centre,
392
centre + hradius * math.sin(ha),
393
centre + hradius *-math.cos(ha),
394
fill = self.handcolour,
396
arrow = "last", tags = "hand")
398
self.hand_m = self.gcanvas.create_line(centre, centre,
399
centre + mradius * math.sin(ma),
400
centre + mradius *-math.cos(ma),
401
fill = self.handcolour,
403
arrow = "last", tags = "hand")
405
self.digit = self.gcanvas.create_text(centre, centre + self.digitpos,
407
fill=self.digitcolour,
412
self.gcanvas.tag_raise("hand", "face")
415
self.gcanvas.tag_bind(self.hand_h,
416
"<B1-Motion>", self.motionhandler_h)
417
self.gcanvas.tag_bind(self.hand_m,
418
"<B1-Motion>", self.motionhandler_m)
420
date = time.strftime("%d %b %Y", tt)
424
self.gcanvas.itemconfigure("title", text=date)
427
def set(self, value):
428
"""Set time. value is seconds since Unix epoch"""
431
tt = time.gmtime(value)
432
hm = string.lower(time.strftime("%I:%M %p", tt))
434
if hm[0] == "0": hm = hm[1:]
442
ha = self.angle(hours * 30 + mins / 2)
443
ma = self.angle(mins * 6)
444
mradius = self.radius - 5
445
hradius = mradius * .75
448
self.gcanvas.coords(self.hand_h,
450
centre + hradius * math.sin(ha),
451
centre + hradius * -math.cos(ha))
453
self.gcanvas.coords(self.hand_m,
455
centre + mradius * math.sin(ma),
456
centre + mradius * -math.cos(ma))
458
self.gcanvas.itemconfigure(self.digit, text=hm)
460
date = time.strftime("%d %b %Y", tt)
464
if self.date == date:
467
self.gcanvas.itemconfigure("title", text=date)
470
def motionhandler_h(self, event):
471
x = event.x - self.centre
472
y = event.y - self.centre
475
while a < 0: a = a + 2 * math.pi
477
nh = int((a / (2*math.pi)) * 12 + 0.5) % 12
478
if self.reverse: nh = (12 - nh) % 12
480
tt = time.gmtime(self.val)
516
self.set(time.mktime(tl))
519
def motionhandler_m(self, event):
520
x = event.x - self.centre
521
y = event.y - self.centre
524
while a < 0: a = a + 2 * math.pi
526
mins = int((a / (2*math.pi)) * 60 + 0.5) % 60
527
if self.reverse: mins = (60 - mins) % 60
529
tl = list(time.gmtime(self.val))
531
if tl[4] > 55 and mins < 5: tl[3] = tl[3] + 1
532
elif tl[4] < 5 and mins > 55: tl[3] = tl[3] - 1
535
self.set(time.mktime(tl))