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 Description
29 ===========
30
31 PeakMeterCtrl mimics the behaviour of equalizers that are usually found in stereos
32 and MP3 players. This widgets supports:
33
34 * Vertical and horizontal led bands;
35 * Settings number of bands and leds per band;
36 * Possibility to change the colour for low/medium/high band frequencies;
37 * Falloff effects;
38 * Showing a background grid for the bands.
39
40 And a lot more. Check the demo for an almost complete review of the functionalities.
41
42
43 Supported Platforms
44 ===================
45
46 PeakMeterCtrl has been tested on the following platforms:
47 * Windows (Windows XP).
48
49
50 Latest Revision: Andrea Gavana @ 07 October 2008, 22.00 GMT
51 Version 0.1
52
53 """
54
55 import wx
56
57
58 PM_HORIZONTAL = 0
59 PM_VERTICAL = 1
60
61
62 BAND_DEFAULT = 8
63 LEDS_DEFAULT = 8
64 BAND_PERCENT = 10
65 GRID_INCREASEBY = 15
66 FALL_INCREASEBY = 60
67 DEFAULT_SPEED = 10
68
69
71 """ Returns whether the value val is between valMin and valMax. """
72
73 return val >= valMin and val <= valMax
74
75
77 """ Lightens a colour. """
78
79 byRed = crColor.Red()
80 byGreen = crColor.Green()
81 byBlue = crColor.Blue()
82
83 byRed = (byRed + byIncreaseVal <= 255 and [byRed + byIncreaseVal] or [255])[0]
84 byGreen = (byGreen + byIncreaseVal <= 255 and [byGreen + byIncreaseVal] or [255])[0]
85 byBlue = (byBlue + byIncreaseVal <= 255 and [byBlue + byIncreaseVal] or [255])[0]
86
87 return wx.Colour(byRed, byGreen, byBlue)
88
89
91 """ Darkens a colour. """
92
93 byRed = crColor.Red()
94 byGreen = crColor.Green()
95 byBlue = crColor.Blue()
96
97 byRed = (byRed >= byReduceVal and [byRed - byReduceVal] or [0])[0]
98 byGreen = (byGreen >= byReduceVal and [byGreen - byReduceVal] or [0])[0]
99 byBlue = (byBlue >= byReduceVal and [byBlue - byReduceVal] or [0])[0]
100
101 return wx.Colour(byRed, byGreen, byBlue)
102
103
105 """ A simple class which holds data for our L{PeakMeterCtrl}. """
106
107 - def __init__(self, value=0, falloff=0, peak=0):
108 """
109 Default class constructor.
110
111 @param value: the current value;
112 @param falloff: the falloff effect;
113 @param peak: the peak value.
114 """
115
116 self._value = value
117 self._falloff = falloff
118 self._peak = peak
119
120
122 """ Returns whether 2 PeakMeterData are the same. """
123
124 return self._value == pm._value
125
126
128 """ Returns whether one PeakMeterData is greater than another. """
129
130 return self._value > pm._value
131
132
134 """ Returns whether one PeakMeterData is smaller than another. """
135
136 return self._value < pm._value
137
138
140 """ The main L{PeakMeterCtrl} implementation. """
141
142 - def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
143 style=PM_VERTICAL):
144 """
145 Default class constructor.
146
147 @param parent: the L{PeakMeterCtrl} parent;
148 @param id: the widget id;
149 @param pos: the control position;
150 @param size: the L{PeakMeterCtrl} size;
151 @param style: the widget style, which can be PM_VERTICAL for a vertical
152 L{PeakMeterCtrl} or PM_HORIZONTAL for an horizontal one.
153 """
154
155 wx.PyControl.__init__(self, parent, id, pos, size, style)
156 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
157
158
159 self.InitData()
160
161 self.Bind(wx.EVT_PAINT, self.OnPaint)
162 self.Bind(wx.EVT_SIZE, self.OnSize)
163 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
164 self.Bind(wx.EVT_TIMER, self.OnTimer)
165
166
168 """ Initializes the control. """
169
170 colLime = wx.Colour(0, 255, 0)
171 colRed = wx.Colour(255, 0, 0)
172 colYellow = wx.Colour(255, 255, 0)
173
174 self._showGrid = False
175 self._showFalloff = True
176 self._delay = 10
177 self._minValue = 60
178 self._medValue = 80
179 self._maxValue = 100
180 self._numBands = BAND_DEFAULT
181 self._ledBands = LEDS_DEFAULT
182 self._clrBackground = self.GetBackgroundColour()
183 self._clrNormal = colLime
184 self._clrMedium = colYellow
185 self._clrHigh = colRed
186 self._speed = DEFAULT_SPEED
187 self._timer = wx.Timer(self)
188
189
190 self._meterData = []
191
192
194 """ Resets the L{PeakMeterCtrl}. """
195
196
197 for i in xrange(self._numBands):
198 pm = PeakMeterData(self._maxValue, self._maxValue, self._speed)
199 self._meterData.append(pm)
200
201 self.Refresh()
202
203
205 """
206 Set background color for L{PeakMeterCtrl}.
207
208 @param colorBgnd: the background colour to apply. """
209
210 wx.PyControl.SetBackgroundColour(self, colorBgnd)
211 self._clrBackground = colorBgnd
212 self.Refresh()
213
214
216 """
217 Set bands color for L{PeakMeterCtrl}.
218
219 @param colorNormal: the color for normal (low) bands;
220 @param colorMedium: the color for medium bands;
221 @param colorHigh: the color for high bands.
222 """
223
224 self._clrNormal = colorNormal
225 self._clrMedium = colorMedium
226 self._clrHigh = colorHigh
227
228 self.Refresh()
229
230
232 """
233 Set number of Vertical or Horizontal bands to display.
234
235 @note: obtain smooth effect by setting nHorz or nVert to "1", these
236 cannot be 0.
237 """
238
239 assert (numBands > 0 and ledBands > 0)
240
241 self._numBands = numBands
242 self._ledBands = ledBands
243
244
245 self.ResetControl()
246
247
249 """
250 Sets the ranges for low, medium and high bands.
251 @note: condition to be satisfied is that:
252 Min: [0 - nMin[, Med: [nMin - nMed[, Max: [nMed - nMax]
253 """
254
255 assert (maxVal > medVal and medVal > minVal and minVal > 0)
256
257 self._minValue = minVal
258 self._medValue = medVal
259 self._maxValue = maxVal
260
261
263 """ Get Range value of L{PeakMeterCtrl}. """
264
265 return self._minValue, self._medValue, self._maxValue
266
267
269 """ Set Peak value speed before falling off. """
270
271 self._speed = speed
272
273
275 """ Set falloff effect flag. """
276
277 if self._showFalloff != falloffEffect:
278
279 self._showFalloff = falloffEffect
280 self.Refresh()
281
282
284 """ Read falloff effect flag. """
285
286 return self._showFalloff
287
288
290 """ Request to have gridlines visible or not. """
291
292 if self._showGrid != showGrid:
293
294 self._showGrid = showGrid
295 self.Refresh()
296
297
299 """ Returns if gridlines are visible. """
300
301 return self._showGrid
302
303
304 - def SetData(self, arrayValue, offset, size):
305 """
306 Change data value. Use this function to change only
307 a set of values. All bands can be changed or only 1 band,
308 depending on the application.
309 """
310
311 assert (offset >= 0 and arrayValue != [])
312
313 isRunning = self.IsStarted()
314
315
316 if isRunning:
317 self.Stop()
318
319 maxSize = offset + size
320
321 for i in xrange(offset, maxSize):
322
323 if i < len(self._meterData):
324
325 pm = self._meterData[i]
326 pm._value = arrayValue[i]
327
328 if pm._falloff < pm._value:
329
330 pm._falloff = pm._value
331 pm._peak = self._speed
332
333 self._meterData[i] = pm
334
335
336 if isRunning:
337 return self.Start(self._delay)
338
339 self.Refresh()
340
341 return True
342
343
345 """ Check if animation is active. """
346
347 return self._timer.IsRunning()
348
349
351 """Start the timer and animation effect. """
352
353 if not self.IsStarted():
354 self._delay = delay
355 self._timer.Start(self._delay)
356 else:
357 return False
358
359 return True
360
361
363 """ Stop the timer and animation effect. """
364
365 if self.IsStarted():
366 self._timer.Stop()
367 return True
368
369 return False
370
371
373 """ L{PeakMeterCtrl} animation, does the wx.EVT_TIMER processing. """
374
375 self.Refresh()
376
377 decValue = self._maxValue/self._ledBands
378 noChange = True
379
380 for pm in self._meterData:
381
382 if pm._value > 0:
383
384 pm._value -= (self._ledBands > 1 and [decValue] or [self._maxValue*BAND_PERCENT/100])[0]
385 if pm._value < 0:
386 pm._value = 0
387
388 noChange = False
389
390 if pm._peak > 0:
391
392 pm._peak -= 1
393 noChange = False
394
395
396 if pm._peak == 0 and pm._falloff > 0:
397
398 pm._falloff -= (self._ledBands > 1 and [decValue >> 1] or [5])[0]
399 if pm._falloff < 0:
400 pm._falloff = 0
401
402 noChange = False
403
404 if noChange:
405
406 self.Stop()
407
408
410 """ Returns the best size for L{PeakMeterCtrl} (arbitrary). """
411
412 return wx.Size(200, 150)
413
414
416 """ Handles the wx.EVT_PAINT event for L{PeakMeterCtrl}. """
417
418 dc = wx.AutoBufferedPaintDC(self)
419 self._clrBackground = self.GetBackgroundColour()
420 dc.SetBackground(wx.Brush(self._clrBackground))
421 dc.Clear()
422 rc = self.GetClientRect()
423
424 pen = wx.Pen(self._clrBackground)
425 dc.SetPen(pen)
426
427 if self.GetWindowStyleFlag() & PM_VERTICAL:
428 self.DrawVertBand(dc, rc)
429 else:
430 self.DrawHorzBand(dc, rc)
431
432
434 """ Handles the wx.EVT_ERASE_BACKGROUND event for L{PeakMeterCtrl}. """
435
436
437 pass
438
439
441 """ Handles the wx.EVT_SIZE event for L{PeakMeterCtrl}. """
442
443 self.Refresh()
444 event.Skip()
445
446
448 """ Handles the wx.EVT_TIMER events for L{PeakMeterCtrl}. """
449
450 self.DoTimerProcessing()
451
452
454 """ Draw Vertical bands - No falloff effect for vertical bands. """
455
456 horzBands = (self._ledBands > 1 and [self._ledBands] or [self._maxValue*BAND_PERCENT/100])[0]
457 minHorzLimit = self._minValue*horzBands/self._maxValue
458 medHorzLimit = self._medValue*horzBands/self._maxValue
459 maxHorzLimit = horzBands
460
461 size = wx.Size(rect.width/horzBands, rect.height/self._numBands)
462 rectBand = wx.RectPS(rect.GetTopLeft(), size)
463
464
465 rectBand.OffsetXY(0, rect.height-size.y*self._numBands)
466 xDecal = (self._ledBands > 1 and [1] or [0])[0]
467 yDecal = (self._numBands > 1 and [1] or [0])[0]
468
469 for vert in xrange(self._numBands):
470
471 self._value = self._meterData[vert]._value
472 horzLimit = self._value*horzBands/self._maxValue
473
474 for horz in xrange(horzBands):
475
476 rectBand.Deflate(0, yDecal)
477
478
479 colorRect = self._clrBackground
480 if self._showGrid:
481 colorRect = DarkenColor(self._clrBackground, GRID_INCREASEBY)
482
483 if self._showGrid and (horz == minHorzLimit or horz == (horzBands-1)):
484
485 points = [wx.Point() for i in xrange(2)]
486 points[0].x = rectBand.GetTopLeft().x + (rectBand.width >> 1)
487 points[0].y = rectBand.GetTopLeft().y - yDecal
488 points[1].x = points[0].x
489 points[1].y = rectBand.GetBottomRight().y + yDecal
490 dc.DrawLinePoint(points[0], points[1])
491
492 if horz < horzLimit:
493
494 if InRange(horz, 0, minHorzLimit-1):
495 colorRect = self._clrNormal
496 elif InRange(horz, minHorzLimit, medHorzLimit-1):
497 colorRect = self._clrMedium
498 elif InRange(horz, medHorzLimit, maxHorzLimit):
499 colorRect = self._clrHigh
500
501 dc.SetBrush(wx.Brush(colorRect))
502 dc.DrawRectangleRect(rectBand)
503
504 rectBand.Inflate(0, yDecal)
505 rectBand.OffsetXY(size.x, 0)
506
507
508 rectBand.OffsetXY(-size.x*horzBands, size.y)
509
510
512 """ Draw Horizontal bands - with Falloff effect. """
513
514 vertBands = (self._ledBands > 1 and [self._ledBands] or [self._maxValue*BAND_PERCENT/100])[0]
515 minVertLimit = self._minValue*vertBands/self._maxValue
516 medVertLimit = self._medValue*vertBands/self._maxValue
517 maxVertLimit = vertBands
518
519 size = wx.Size(rect.width/self._numBands, rect.height/vertBands)
520 rectBand = wx.RectPS(rect.GetTopLeft(), size)
521
522
523 rectBand.OffsetXY(0, rect.bottom-size.y)
524 xDecal = (self._numBands > 1 and [1] or [0])[0]
525 yDecal = (self._ledBands > 1 and [1] or [0])[0]
526
527 for horz in xrange(self._numBands):
528
529 self._value = self._meterData[horz]._value
530 vertLimit = self._value*vertBands/self._maxValue
531 rectPrev = wx.Rect(*rectBand)
532
533 for vert in xrange(vertBands):
534
535 rectBand.Deflate(xDecal, 0)
536
537
538 colorRect = self._clrBackground
539 if self._showGrid:
540 colorRect = DarkenColor(self._clrBackground, GRID_INCREASEBY)
541
542
543 if self._showGrid and (vert == minVertLimit or vert == (vertBands-1)):
544
545 points = [wx.Point() for i in xrange(2)]
546 points[0].x = rectBand.GetTopLeft().x - xDecal
547 points[0].y = rectBand.GetTopLeft().y + (rectBand.height >> 1)
548 points[1].x = rectBand.GetBottomRight().x + xDecal
549 points[1].y = points[0].y
550 dc.DrawLinePoint(points[0], points[1])
551
552 if vert < vertLimit:
553
554 if InRange(vert, 0, minVertLimit-1):
555 colorRect = self._clrNormal
556 elif InRange(vert, minVertLimit, medVertLimit-1):
557 colorRect = self._clrMedium
558 elif InRange(vert, medVertLimit, maxVertLimit):
559 colorRect = self._clrHigh
560
561 dc.SetBrush(wx.Brush(colorRect))
562 dc.DrawRectangleRect(rectBand)
563
564 rectBand.Inflate(xDecal, 0)
565 rectBand.OffsetXY(0, -size.y)
566
567
568 if self._showFalloff:
569
570 oldPen = dc.GetPen()
571 pen = wx.Pen(DarkenColor(self._clrBackground, FALL_INCREASEBY))
572 maxHeight = size.y*vertBands
573 points = [wx.Point() for i in xrange(2)]
574 points[0].x = rectPrev.GetTopLeft().x + xDecal
575 points[0].y = rectPrev.GetBottomRight().y - self._meterData[horz]._falloff*maxHeight/self._maxValue
576 points[1].x = rectPrev.GetBottomRight().x - xDecal
577 points[1].y = points[0].y
578 dc.SetPen(pen)
579 dc.DrawLinePoint(points[0], points[1])
580 dc.SetPen(oldPen)
581
582
583 rectBand.OffsetXY(size.x, size.y*vertBands)
584