~ubuntu-branches/ubuntu/utopic/python-omniorb/utopic

« back to all changes in this revision

Viewing changes to examples/weather/gauge.py

  • Committer: Bazaar Package Importer
  • Author(s): Floris Bruynooghe
  • Date: 2008-03-26 22:17:38 UTC
  • Revision ID: james.westby@ubuntu.com-20080326221738-r20t9hmikbvcg2vh
Tags: upstream-3.2
ImportĀ upstreamĀ versionĀ 3.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- python -*-
 
2
#                           Package   : weather
 
3
# gauge.py                  Created on: 2000/01/06
 
4
#                           Author    : Duncan Grisby (dpg1)
 
5
#
 
6
#    Copyright (C) 2000 AT&T Laboratories Cambridge
 
7
#
 
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.
 
12
#
 
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.
 
17
#
 
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
 
21
#  02111-1307, USA.
 
22
#
 
23
# Description:
 
24
#   
 
25
#   Pretty circular gauges in Tk
 
26
 
 
27
import math, time, string, operator
 
28
from Tkinter import *
 
29
 
 
30
SCALEFONT = "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-*-*"
 
31
DIGITFONT = "-*-lucidatypewriter-*-r-*-*-*-120-*-*-m-*-*-*"
 
32
TITLEFONT = "-*-helvetica-regular-r-*-*-*-120-*-*-*-*-*-*"
 
33
 
 
34
WINDOWPAD = 5
 
35
 
 
36
class Gauge :
 
37
    def __init__(self, parentcanvas,
 
38
                 x, y,                  # Origin
 
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
 
61
                 title       = "",
 
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
 
67
                 bgcolour    = "blue",
 
68
                 interactive = 0):      # Is the gauge interactive
 
69
 
 
70
        if scalevalues is None:
 
71
            scalevalues = [min, (min + max) / 2, max]
 
72
 
 
73
        # Things we need to remember
 
74
        self.val           = None
 
75
        self.min           = min
 
76
        self.max           = max
 
77
        self.val           = initial
 
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
 
85
        self.radius        = radius
 
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
 
96
 
 
97
        # Polygon used to draw the hand
 
98
        self.handPolygon = [(-12,-12), (0,radius-5), (12,-12)]
 
99
 
 
100
        # Create a canvas for this gauge
 
101
        wwidth       = wheight = 2 * radius + 2 * WINDOWPAD
 
102
        tl           = WINDOWPAD
 
103
        br           = tl + radius * 2
 
104
        centre       = tl + radius
 
105
        self.centre  = centre
 
106
        self.gcanvas = Canvas(parentcanvas,
 
107
                              height=wheight, width=wwidth, bg=bgcolour,
 
108
                              borderwidth=0, highlightthickness=0)
 
109
 
 
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,
 
116
                                 fill=knobcolour)
 
117
        # Ticks
 
118
        tick = min
 
119
        while tick <= max:
 
120
            a  = self.angle(tick)
 
121
            sa =  math.sin(a)
 
122
            ca = -math.cos(a)
 
123
 
 
124
            if bigticks:
 
125
                if tick % bigticks == 0:
 
126
                    len = bigticklen
 
127
                else:
 
128
                    len = ticklen
 
129
            elif tick in scalevalues:
 
130
                len = bigticklen
 
131
            else:
 
132
                len = ticklen
 
133
 
 
134
            xof = (radius - len) * sa
 
135
            xot = radius         * sa
 
136
            yof = (radius - len) * ca
 
137
            yot = radius         * ca
 
138
 
 
139
            self.gcanvas.create_line(xof+centre, yof+centre,
 
140
                                     xot+centre, yot+centre,
 
141
                                     fill=linecolour, width=1)
 
142
 
 
143
            tick = tick + unitspertick
 
144
 
 
145
        # Title
 
146
        self.gcanvas.create_text(centre, centre - titlepos,
 
147
                                 anchor=CENTER, fill=titlecolour,
 
148
                                 font=titlefont, text=title,
 
149
                                 justify=CENTER, tags="title")
 
150
 
 
151
        # Scale and hand
 
152
        self.drawScale()
 
153
        self.drawHand()
 
154
 
 
155
        # Put gauge into parent canvas
 
156
        parentcanvas.create_window(x, y, width=wwidth, height=wheight,
 
157
                                   anchor=CENTER, window=self.gcanvas)
 
158
 
 
159
    def drawScale(self):
 
160
        scalerad = self.radius - self.scalesep
 
161
        centre   = self.centre
 
162
        for val in self.scalevalues:
 
163
            a = self.angle(val)
 
164
 
 
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))
 
169
 
 
170
    def drawHand(self):
 
171
        a          = self.angle(self.val)
 
172
        centre     = self.centre
 
173
        hp         = self.rotatePoints(self.handPolygon, a)
 
174
        hp         = self.translatePoints(hp, centre, centre)
 
175
        hl         = self.flattenPoints(hp)
 
176
 
 
177
        self.hand  = self.gcanvas.create_polygon(hl, fill=self.handcolour,
 
178
                                                 tags="hand")
 
179
 
 
180
        self.digit = self.gcanvas.create_text(centre, centre + self.digitpos,
 
181
                                              anchor=CENTER,
 
182
                                              fill=self.digitcolour,
 
183
                                              font=self.digitfont,
 
184
                                              text=self.digitformat % self.val,
 
185
                                              tags="digit")
 
186
 
 
187
        self.gcanvas.tag_raise(self.hand, "face")
 
188
 
 
189
        if self.interactive:
 
190
            self.gcanvas.tag_bind(self.hand, "<B1-Motion>", self.motionhandler)
 
191
 
 
192
 
 
193
    def set(self, value):
 
194
        """Set value of gauge"""
 
195
 
 
196
        if self.val == value:
 
197
            return
 
198
 
 
199
        self.val = value
 
200
        a        = self.angle(value)
 
201
        centre   = self.centre
 
202
        hp       = self.rotatePoints(self.handPolygon, a)
 
203
        hp       = self.translatePoints(hp, centre, centre)
 
204
        hl       = self.flattenPoints(hp)
 
205
 
 
206
        apply(self.gcanvas.coords, [self.hand] + hl)
 
207
        self.gcanvas.itemconfigure(self.digit, text=self.digitformat % value)
 
208
 
 
209
 
 
210
    def get(self):
 
211
        """Get value of gauge"""
 
212
        return self.val
 
213
 
 
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
 
218
 
 
219
        while a < 0: a = a + 2 * math.pi
 
220
 
 
221
        frac = a / self.scalearc_r
 
222
 
 
223
        if frac <= 1.0:
 
224
            if self.reverse:
 
225
                self.set(self.max - (self.max - self.min) * frac)
 
226
            else:
 
227
                self.set(self.min + (self.max - self.min) * frac)
 
228
 
 
229
    def angle(self, value):
 
230
        """Convert value to an angle in radians"""
 
231
 
 
232
        if self.reverse == 1:
 
233
            value = self.max - value
 
234
 
 
235
        if value < self.min:
 
236
            a = self.minlocation_r
 
237
        elif value > self.max:
 
238
            a = self.minlocation_r + self.scalearc_r
 
239
        else:
 
240
            a = self.minlocation_r + \
 
241
                self.scalearc_r * (value - self.min) / (self.max - self.min)
 
242
 
 
243
        while a > 2*math.pi:
 
244
            a = a - 2 * math.pi
 
245
 
 
246
        return a
 
247
 
 
248
    def rotatePoint(self, point, theta):
 
249
        """Rotate a point clockwise about the origin by an angle in radians"""
 
250
 
 
251
        x,y = point
 
252
        r   = math.sqrt(x*x + y*y)
 
253
        a   = math.atan2(y,x) - theta
 
254
 
 
255
        return (r * math.cos(a), r * -math.sin(a))
 
256
 
 
257
    def rotatePoints(self, points, theta):
 
258
        return map(lambda p, t=theta, rp=self.rotatePoint: rp(p, t), points)
 
259
 
 
260
    def translatePoints(self, points, x, y):
 
261
        return map(lambda p, x=x, y=y: (p[0]+x,p[1]+y), points)
 
262
 
 
263
    def flattenPoints(self, points):
 
264
        return list(reduce(operator.add, points))
 
265
 
 
266
 
 
267
class Compass(Gauge):
 
268
    def __init__(self, parentcanvas,
 
269
                 x, y,                  # Origin
 
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
 
282
                 title       = "",
 
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
 
288
                 bgcolour    = "blue",
 
289
                 interactive = 0):
 
290
 
 
291
        Gauge.__init__(self, parentcanvas, x, y, initial      = 0,
 
292
                       min         = 0,          max          = 360,
 
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)
 
306
 
 
307
    def drawScale(self):
 
308
        scalerad = self.radius - self.scalesep
 
309
        centre   = self.centre
 
310
        for val,txt in [(0,"N"), (90,"E"), (180,"S"), (270,"W")]:
 
311
            a = self.angle(val)
 
312
 
 
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)
 
317
 
 
318
 
 
319
class Clock(Gauge):
 
320
    def __init__(self, parentcanvas,
 
321
                 x, y,                  # Origin
 
322
                 initial     = time.time(),
 
323
                 reverse     = 0,
 
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
 
336
                 title       = "",
 
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
 
342
                 bgcolour    = "blue",
 
343
                 interactive = 0):
 
344
 
 
345
        Gauge.__init__(self, parentcanvas, x, y,
 
346
                       initial     = initial,    reverse      = reverse,
 
347
                       min         = 0,          max          = 360,
 
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)
 
361
 
 
362
        self.hm   = None
 
363
        self.date = None
 
364
 
 
365
    def drawScale(self):
 
366
        scalerad = self.radius - self.scalesep
 
367
        centre   = self.centre
 
368
        for val,txt in [(0,"12"), (90,"3"), (180,"6"), (270,"9")]:
 
369
            a = self.angle(val)
 
370
 
 
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)
 
375
 
 
376
    def drawHand(self):
 
377
        tt = time.gmtime(self.val)
 
378
        hm = string.lower(time.strftime("%I:%M %p", tt))
 
379
        if hm[0] == "0":
 
380
            hm = hm[1:]
 
381
        
 
382
        self.hm = hm
 
383
        hours   = tt[3] % 12
 
384
        mins    = tt[4]
 
385
        ha      = self.angle(hours * 30 + mins / 2)
 
386
        ma      = self.angle(mins  * 6)
 
387
        mradius = self.radius - 5
 
388
        hradius = mradius * .75
 
389
        centre  = self.centre
 
390
 
 
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,
 
395
                                               width = 5,
 
396
                                               arrow = "last", tags = "hand")
 
397
 
 
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,
 
402
                                               width = 5,
 
403
                                               arrow = "last", tags = "hand")
 
404
 
 
405
        self.digit = self.gcanvas.create_text(centre, centre + self.digitpos,
 
406
                                              anchor=CENTER,
 
407
                                              fill=self.digitcolour,
 
408
                                              font=self.digitfont,
 
409
                                              text=hm,
 
410
                                              tags="digit")
 
411
 
 
412
        self.gcanvas.tag_raise("hand", "face")
 
413
 
 
414
        if self.interactive:
 
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)
 
419
 
 
420
        date = time.strftime("%d %b %Y", tt)
 
421
        if date[0] == "0":
 
422
            date = date[1:]
 
423
 
 
424
        self.gcanvas.itemconfigure("title", text=date)
 
425
 
 
426
 
 
427
    def set(self, value):
 
428
        """Set time. value is seconds since Unix epoch"""
 
429
 
 
430
        self.val = value
 
431
        tt       = time.gmtime(value)
 
432
        hm       = string.lower(time.strftime("%I:%M %p", tt))
 
433
 
 
434
        if hm[0] == "0": hm = hm[1:]
 
435
        
 
436
        if self.hm == hm:
 
437
            return
 
438
 
 
439
        self.hm = hm
 
440
        hours   = tt[3] % 12
 
441
        mins    = tt[4]
 
442
        ha      = self.angle(hours * 30 + mins / 2)
 
443
        ma      = self.angle(mins  * 6)
 
444
        mradius = self.radius - 5
 
445
        hradius = mradius * .75
 
446
        centre  = self.centre
 
447
 
 
448
        self.gcanvas.coords(self.hand_h,
 
449
                            centre, centre,
 
450
                            centre + hradius *  math.sin(ha),
 
451
                            centre + hradius * -math.cos(ha))
 
452
 
 
453
        self.gcanvas.coords(self.hand_m,
 
454
                            centre, centre,
 
455
                            centre + mradius *  math.sin(ma),
 
456
                            centre + mradius * -math.cos(ma))
 
457
 
 
458
        self.gcanvas.itemconfigure(self.digit, text=hm)
 
459
 
 
460
        date = time.strftime("%d %b %Y", tt)
 
461
        if date[0] == "0":
 
462
            date = date[1:]
 
463
 
 
464
        if self.date == date:
 
465
            return
 
466
 
 
467
        self.gcanvas.itemconfigure("title", text=date)
 
468
 
 
469
 
 
470
    def motionhandler_h(self, event):
 
471
        x = event.x - self.centre
 
472
        y = event.y - self.centre
 
473
        a = math.atan2(x,-y)
 
474
 
 
475
        while a < 0: a = a + 2 * math.pi
 
476
 
 
477
        nh = int((a / (2*math.pi)) * 12 + 0.5) % 12
 
478
        if self.reverse: nh = (12 - nh) % 12
 
479
 
 
480
        tt = time.gmtime(self.val)
 
481
        tl = list(tt)
 
482
        oh = tl[3]
 
483
 
 
484
        if oh == 0:
 
485
            if nh == 11:
 
486
                tl[2] = tl[2] - 1
 
487
                tl[3] = 23
 
488
            else:
 
489
                tl[3] = nh
 
490
 
 
491
        elif oh <= 10:
 
492
            tl[3] = nh
 
493
 
 
494
        elif oh == 11:
 
495
            if nh == 0:
 
496
                tl[3] = 12
 
497
            else:
 
498
                tl[3] = nh
 
499
 
 
500
        elif oh == 12:
 
501
            if nh == 11:
 
502
                tl[3] = 11
 
503
            else:
 
504
                tl[3] = nh + 12
 
505
 
 
506
        elif oh <= 22:
 
507
            tl[3] = nh + 12
 
508
 
 
509
        else: # oh == 23
 
510
            if nh == 0:
 
511
                tl[2] = tl[2] + 1
 
512
                tl[3] = 0
 
513
            else:
 
514
                tl[3] = nh + 12
 
515
 
 
516
        self.set(time.mktime(tl))
 
517
 
 
518
 
 
519
    def motionhandler_m(self, event):
 
520
        x = event.x - self.centre
 
521
        y = event.y - self.centre
 
522
        a = math.atan2(x,-y)
 
523
 
 
524
        while a < 0: a = a + 2 * math.pi
 
525
 
 
526
        mins = int((a / (2*math.pi)) * 60 + 0.5) % 60
 
527
        if self.reverse: mins = (60 - mins) % 60
 
528
 
 
529
        tl   = list(time.gmtime(self.val))
 
530
 
 
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
 
533
 
 
534
        tl[4] = mins
 
535
        self.set(time.mktime(tl))