mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-27 21:52:25 -06:00
1028 lines
33 KiB
Python
1028 lines
33 KiB
Python
# --------------------------------------------------------------------------------- #
|
|
# PEAKMETERCTRL wxPython IMPLEMENTATION
|
|
#
|
|
# Andrea Gavana, @ 07 October 2008
|
|
# Latest Revision: 31 Jul 2014, 21.00 GMT
|
|
#
|
|
#
|
|
# TODO List
|
|
#
|
|
# 1) Possibly some nicer drawing of bands and leds (using GraphicsContext).
|
|
#
|
|
#
|
|
# For all kind of problems, requests of enhancements and bug reports, please
|
|
# write to me at:
|
|
#
|
|
# andrea.gavana@gmail.com
|
|
# andrea.gavana@maerskoil.com
|
|
#
|
|
# Or, obviously, to the wxPython mailing list!!!
|
|
#
|
|
# Tags: phoenix-port, unittest, documented, py3-port
|
|
#
|
|
# End Of Comments
|
|
# --------------------------------------------------------------------------------- #
|
|
|
|
"""
|
|
:class:`PeakMeterCtrl` mimics the behaviour of equalizers that are usually found in stereos
|
|
and MP3 players.
|
|
|
|
|
|
Description
|
|
===========
|
|
|
|
:class:`PeakMeterCtrl` mimics the behaviour of equalizers that are usually found in stereos
|
|
and MP3 players. This widgets supports:
|
|
|
|
* Vertical and horizontal led bands;
|
|
* Settings number of bands and leds per band;
|
|
* Possibility to change the colour for low/medium/high band frequencies;
|
|
* Falloff effects;
|
|
* Showing a background grid for the bands.
|
|
|
|
And a lot more. Check the demo for an almost complete review of the functionalities.
|
|
|
|
|
|
Usage
|
|
=====
|
|
|
|
Usage example::
|
|
|
|
import wx
|
|
import random
|
|
|
|
import wx.lib.agw.peakmeter as PM
|
|
|
|
class MyFrame(wx.Frame):
|
|
|
|
def __init__(self, parent):
|
|
|
|
wx.Frame.__init__(self, parent, -1, "PeakMeterCtrl Demo")
|
|
|
|
panel = wx.Panel(self)
|
|
|
|
# Initialize Peak Meter control 1
|
|
self.vertPeak = PM.PeakMeterCtrl(panel, -1, style=wx.SIMPLE_BORDER, agwStyle=PM.PM_VERTICAL)
|
|
# Initialize Peak Meter control 2
|
|
self.horzPeak = PM.PeakMeterCtrl(panel, -1, style=wx.SUNKEN_BORDER, agwStyle=PM.PM_HORIZONTAL)
|
|
|
|
self.vertPeak.SetMeterBands(10, 15)
|
|
self.horzPeak.SetMeterBands(10, 15)
|
|
|
|
# Layout the two PeakMeterCtrl
|
|
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
mainSizer.Add(self.vertPeak, 0, wx.EXPAND | wx.ALL, 15)
|
|
mainSizer.Add(self.horzPeak, 0, wx.EXPAND | wx.ALL, 15)
|
|
|
|
panel.SetSizer(mainSizer)
|
|
mainSizer.Layout()
|
|
|
|
self.timer = wx.Timer(self)
|
|
self.Bind(wx.EVT_TIMER, self.OnTimer)
|
|
|
|
wx.CallLater(500, self.Start)
|
|
|
|
|
|
def Start(self):
|
|
''' Starts the PeakMeterCtrl. '''
|
|
|
|
self.timer.Start(1000//2) # 2 fps
|
|
|
|
self.vertPeak.Start(1000//18) # 18 fps
|
|
self.horzPeak.Start(1000//20) # 20 fps
|
|
|
|
|
|
def OnTimer(self, event):
|
|
'''
|
|
Handles the ``wx.EVT_TIMER`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`TimerEvent` event to be processed.
|
|
'''
|
|
|
|
# Generate 15 random number and set them as data for the meter
|
|
nElements = 15
|
|
arrayData = []
|
|
|
|
for i in xrange(nElements):
|
|
nRandom = random.randint(0, 100)
|
|
arrayData.append(nRandom)
|
|
|
|
self.vertPeak.SetData(arrayData, 0, nElements)
|
|
self.horzPeak.SetData(arrayData, 0, nElements)
|
|
|
|
|
|
# our normal wxApp-derived class, as usual
|
|
|
|
app = wx.App(0)
|
|
|
|
frame = MyFrame(None)
|
|
app.SetTopWindow(frame)
|
|
frame.Show()
|
|
|
|
app.MainLoop()
|
|
|
|
|
|
|
|
Supported Platforms
|
|
===================
|
|
|
|
:class:`PeakMeterCtrl` has been tested on the following platforms:
|
|
* Windows (Windows XP).
|
|
|
|
|
|
Window Styles
|
|
=============
|
|
|
|
This class supports the following window styles:
|
|
|
|
======================== =========== ========================================================
|
|
Window Styles Hex Value Description
|
|
======================== =========== ========================================================
|
|
``PM_HORIZONTAL`` 0x0 Shows horizontal bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL`` 0x1 Shows vertical bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL_INVERTED`` 0x2 Shows inverted vertical bands in :class:`PeakMeterCtrl`.
|
|
======================== =========== ========================================================
|
|
|
|
|
|
Events Processing
|
|
=================
|
|
|
|
`No custom events are available for this class.`
|
|
|
|
|
|
License And Version
|
|
===================
|
|
|
|
:class:`PeakMeterCtrl` is distributed under the wxPython license.
|
|
|
|
Latest Revision: Andrea Gavana @ 31 Jul 2014, 21.00 GMT
|
|
|
|
Version 0.4
|
|
|
|
"""
|
|
|
|
import wx
|
|
|
|
# Horizontal or vertical PeakMeterCtrl
|
|
PM_HORIZONTAL = 0
|
|
""" Shows horizontal bands in :class:`PeakMeterCtrl`. """
|
|
PM_VERTICAL = 1
|
|
""" Shows vertical bands in :class:`PeakMeterCtrl`. """
|
|
PM_VERTICAL_INVERTED = 2
|
|
""" Shows inverted vertical bands in :class:`PeakMeterCtrl`. """
|
|
|
|
# Some useful constants...
|
|
BAND_DEFAULT = 8
|
|
""" Number of bands in the :class:`PeakMeterCtrl`. """
|
|
LEDS_DEFAULT = 8
|
|
""" Number of leds per band in the :class:`PeakMeterCtrl`. """
|
|
BAND_PERCENT = 10 # 10% of Max Range (Auto Decrease)
|
|
""" 10% of max range (auto decrease). """
|
|
GRID_INCREASEBY = 15 # Increase Grid colour based on Background colour
|
|
""" Increase grid colour based on background colour. """
|
|
FALL_INCREASEBY = 60 # Increase Falloff colour based on Background
|
|
""" Increase falloff colour based on background colour. """
|
|
DEFAULT_SPEED = 10
|
|
""" Default increase/decrease speed. """
|
|
|
|
|
|
def InRange(val, valMin, valMax):
|
|
"""
|
|
Returns whether the value `val` is between `valMin` and `valMax`.
|
|
|
|
:param `val`: the value to test;
|
|
:param `valMin`: the minimum range value;
|
|
:param `valMax`: the maximum range value.
|
|
"""
|
|
|
|
return val >= valMin and val <= valMax
|
|
|
|
|
|
def LightenColour(crColour, byIncreaseVal):
|
|
"""
|
|
Lightens a colour.
|
|
|
|
:param `crColour`: a valid :class:`Colour` object;
|
|
:param `byIncreaseVal`: an integer specifying the amount for which the input
|
|
colour should be brightened.
|
|
"""
|
|
|
|
byRed = crColour.Red()
|
|
byGreen = crColour.Green()
|
|
byBlue = crColour.Blue()
|
|
|
|
byRed = (byRed + byIncreaseVal <= 255 and [byRed + byIncreaseVal] or [255])[0]
|
|
byGreen = (byGreen + byIncreaseVal <= 255 and [byGreen + byIncreaseVal] or [255])[0]
|
|
byBlue = (byBlue + byIncreaseVal <= 255 and [byBlue + byIncreaseVal] or [255])[0]
|
|
|
|
return wx.Colour(byRed, byGreen, byBlue)
|
|
|
|
|
|
def DarkenColour(crColour, byReduceVal):
|
|
"""
|
|
Darkens a colour.
|
|
|
|
:param `crColour`: a valid :class:`Colour` object;
|
|
:param `byReduceVal`: an integer specifying the amount for which the input
|
|
colour should be darkened.
|
|
"""
|
|
|
|
byRed = crColour.Red()
|
|
byGreen = crColour.Green()
|
|
byBlue = crColour.Blue()
|
|
|
|
byRed = (byRed >= byReduceVal and [byRed - byReduceVal] or [0])[0]
|
|
byGreen = (byGreen >= byReduceVal and [byGreen - byReduceVal] or [0])[0]
|
|
byBlue = (byBlue >= byReduceVal and [byBlue - byReduceVal] or [0])[0]
|
|
|
|
return wx.Colour(byRed, byGreen, byBlue)
|
|
|
|
|
|
class PeakMeterData(object):
|
|
""" A simple class which holds data for our :class:`PeakMeterCtrl`. """
|
|
|
|
def __init__(self, value=0, falloff=0, peak=0):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `value`: the current :class:`PeakMeterCtrl` value;
|
|
:param `falloff`: the falloff effect. ``True`` to enable it, ``False`` to
|
|
disable it;
|
|
:param `peak`: the peak value.
|
|
"""
|
|
|
|
self._value = value
|
|
self._falloff = falloff
|
|
self._peak = peak
|
|
|
|
|
|
def IsEqual(self, pm):
|
|
"""
|
|
Returns whether 2 instances of :class:`PeakMeterData` are the same.
|
|
|
|
:param `pm`: another instance of :class:`PeakMeterData`.
|
|
"""
|
|
|
|
return self._value == pm._value
|
|
|
|
|
|
def IsGreater(self, pm):
|
|
"""
|
|
Returns whether one :class:`PeakMeterData` is greater than another.
|
|
|
|
:param `pm`: another instance of :class:`PeakMeterData`.
|
|
"""
|
|
|
|
return self._value > pm._value
|
|
|
|
|
|
def IsLower(self, pm):
|
|
"""
|
|
Returns whether one :class:`PeakMeterData` is smaller than another.
|
|
|
|
:param `pm`: another instance of :class:`PeakMeterData`.
|
|
"""
|
|
|
|
return self._value < pm._value
|
|
|
|
|
|
class PeakMeterCtrl(wx.Control):
|
|
""" The main :class:`PeakMeterCtrl` implementation. """
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=0, agwStyle=PM_VERTICAL):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param parent: the :class:`PeakMeterCtrl` parent. Must not be ``None``
|
|
:param `id`: window identifier. A value of -1 indicates a default value;
|
|
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `size`: the control size. A value of (-1, -1) indicates a default size,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `style`: the underlying :class:`Control` window style;
|
|
:param `agwStyle`: the AGW-specific window style, which can be one of the following bits:
|
|
|
|
======================== =========== ========================================================
|
|
Window Styles Hex Value Description
|
|
======================== =========== ========================================================
|
|
``PM_HORIZONTAL`` 0x0 Shows horizontal bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL`` 0x1 Shows vertical bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL_INVERTED`` 0x2 Shows inverted vertical bands in :class:`PeakMeterCtrl`.
|
|
======================== =========== ========================================================
|
|
|
|
"""
|
|
|
|
wx.Control.__init__(self, parent, id, pos, size, style)
|
|
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
|
|
|
|
self._agwStyle = agwStyle
|
|
# Initializes all data
|
|
self.InitData()
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
self.Bind(wx.EVT_TIMER, self.OnTimer)
|
|
|
|
|
|
def InitData(self):
|
|
""" Initializes the control. """
|
|
|
|
colLime = wx.Colour(0, 255, 0)
|
|
colRed = wx.Colour(255, 0, 0)
|
|
colYellow = wx.Colour(255, 255, 0)
|
|
|
|
self._showGrid = False
|
|
self._showFalloff = True
|
|
self._delay = 10
|
|
self._minValue = 60 # Min Range 0-60
|
|
self._medValue = 80 # Med Range 60-80
|
|
self._maxValue = 100 # Max Range 80-100
|
|
self._numBands = BAND_DEFAULT
|
|
self._ledBands = LEDS_DEFAULT
|
|
self._clrBackground = self.GetBackgroundColour()
|
|
self._clrNormal = colLime
|
|
self._clrMedium = colYellow
|
|
self._clrHigh = colRed
|
|
self._speed = DEFAULT_SPEED
|
|
self._timer = wx.Timer(self)
|
|
|
|
# clear vector data
|
|
self._meterData = []
|
|
|
|
|
|
def SetAGWWindowStyleFlag(self, agwStyle):
|
|
"""
|
|
Sets the :class:`PeakMeterCtrl` window style flags.
|
|
|
|
:param `agwStyle`: the AGW-specific window style. This can be a combination of the
|
|
following bits:
|
|
|
|
======================== =========== ========================================================
|
|
Window Styles Hex Value Description
|
|
======================== =========== ========================================================
|
|
``PM_HORIZONTAL`` 0x0 Shows horizontal bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL`` 0x1 Shows vertical bands in :class:`PeakMeterCtrl`.
|
|
``PM_VERTICAL_INVERTED`` 0x2 Shows inverted vertical bands in :class:`PeakMeterCtrl`.
|
|
======================== =========== ========================================================
|
|
|
|
"""
|
|
|
|
self._agwStyle = agwStyle
|
|
self.Refresh()
|
|
|
|
|
|
def GetAGWWindowStyleFlag(self):
|
|
"""
|
|
Returns the :class:`PeakMeterCtrl` window style.
|
|
|
|
:see: :meth:`PeakMeterCtrl.SetAGWWindowStyleFlag` for a list of possible window style flags.
|
|
"""
|
|
|
|
return self._agwStyle
|
|
|
|
|
|
def ResetControl(self):
|
|
""" Resets the :class:`PeakMeterCtrl`. """
|
|
|
|
# Initialize vector
|
|
for i in range(self._numBands):
|
|
pm = PeakMeterData(self._maxValue, self._maxValue, self._speed)
|
|
self._meterData.append(pm)
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def SetBackgroundColour(self, colourBgnd):
|
|
"""
|
|
Changes the background colour of :class:`PeakMeterCtrl`.
|
|
|
|
:param `colourBgnd`: the colour to be used as the background colour, pass
|
|
:class:`NullColour` to reset to the default colour.
|
|
|
|
:note: The background colour is usually painted by the default :class:`EraseEvent`
|
|
event handler function under Windows and automatically under GTK.
|
|
|
|
:note: Setting the background colour does not cause an immediate refresh, so
|
|
you may wish to call :meth:`Window.ClearBackground` or :meth:`Window.Refresh` after
|
|
calling this function.
|
|
|
|
:note: Overridden from :class:`Control`.
|
|
"""
|
|
|
|
wx.Control.SetBackgroundColour(self, colourBgnd)
|
|
self._clrBackground = colourBgnd
|
|
self.Refresh()
|
|
|
|
|
|
def SetBandsColour(self, colourNormal, colourMedium, colourHigh):
|
|
"""
|
|
Set bands colour for :class:`PeakMeterCtrl`.
|
|
|
|
:param `colourNormal`: the colour for normal (low) bands, a valid :class:`Colour`
|
|
object;
|
|
:param `colourMedium`: the colour for medium bands, a valid :class:`Colour`
|
|
object;
|
|
:param `colourHigh`: the colour for high bands, a valid :class:`Colour`
|
|
object.
|
|
"""
|
|
|
|
self._clrNormal = colourNormal
|
|
self._clrMedium = colourMedium
|
|
self._clrHigh = colourHigh
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def SetMeterBands(self, numBands, ledBands):
|
|
"""
|
|
Set number of vertical or horizontal bands to display.
|
|
|
|
:param `numBands`: number of bands to display (either vertical or horizontal);
|
|
:param `ledBands`: the number of leds per band.
|
|
|
|
:note: You can obtain a smooth effect by setting `nHorz` or `nVert` to "1", these
|
|
cannot be 0.
|
|
"""
|
|
|
|
assert (numBands > 0 and ledBands > 0)
|
|
|
|
self._numBands = numBands
|
|
self._ledBands = ledBands
|
|
|
|
# Reset vector
|
|
self.ResetControl()
|
|
|
|
|
|
def SetRangeValue(self, minVal, medVal, maxVal):
|
|
"""
|
|
Sets the ranges for low, medium and high bands.
|
|
|
|
:param `minVal`: the value for low bands;
|
|
:param `medVal`: the value for medium bands;
|
|
:param `maxVal`: the value for high bands.
|
|
|
|
:note: The conditions to be satisfied are:
|
|
|
|
Min: [0 - nMin[, Med: [nMin - nMed[, Max: [nMed - nMax]
|
|
|
|
"""
|
|
|
|
assert (maxVal > medVal and medVal > minVal and minVal > 0)
|
|
|
|
self._minValue = minVal
|
|
self._medValue = medVal
|
|
self._maxValue = maxVal
|
|
|
|
|
|
def GetRangeValue(self):
|
|
""" Get range value of :class:`PeakMeterCtrl`. """
|
|
|
|
return self._minValue, self._medValue, self._maxValue
|
|
|
|
|
|
def SetFalloffDelay(self, speed):
|
|
"""
|
|
Set peak value speed before falling off.
|
|
|
|
:param `speed`: the speed at which the falloff happens.
|
|
"""
|
|
|
|
self._speed = speed
|
|
|
|
|
|
def SetFalloffEffect(self, falloffEffect):
|
|
"""
|
|
Set falloff effect flag.
|
|
|
|
:param `falloffEffect`: ``True`` to enable the falloff effect, ``False``
|
|
to disable it.
|
|
"""
|
|
|
|
if self._showFalloff != falloffEffect:
|
|
|
|
self._showFalloff = falloffEffect
|
|
self.Refresh()
|
|
|
|
|
|
def GetFalloffEffect(self):
|
|
""" Returns the falloff effect flag. """
|
|
|
|
return self._showFalloff
|
|
|
|
|
|
def ShowGrid(self, showGrid):
|
|
"""
|
|
Request to have gridlines visible or not.
|
|
|
|
:param `showGrid`: ``True`` to show grid lines, ``False`` otherwise.
|
|
"""
|
|
|
|
if self._showGrid != showGrid:
|
|
|
|
self._showGrid = showGrid
|
|
self.Refresh()
|
|
|
|
|
|
def IsGridVisible(self):
|
|
""" Returns if gridlines are visible. """
|
|
|
|
return self._showGrid
|
|
|
|
|
|
def SetData(self, arrayValue, offset, size):
|
|
"""
|
|
Change data value. Use this function to change only
|
|
a set of values. All bands can be changed or only 1 band,
|
|
depending on the application.
|
|
|
|
:param `arrayValue`: a Python list containing the :class:`PeakMeterData` values;
|
|
:param `offset`: the (optional) offset where to start applying the new data;
|
|
:param `size`: the size of the input data.
|
|
"""
|
|
|
|
assert (offset >= 0 and arrayValue != [])
|
|
|
|
isRunning = self.IsStarted()
|
|
|
|
# Stop timer if Animation is active
|
|
if isRunning:
|
|
self.Stop()
|
|
|
|
maxSize = offset + size
|
|
|
|
for i in range(offset, maxSize):
|
|
|
|
if i < len(self._meterData):
|
|
|
|
pm = self._meterData[i]
|
|
pm._value = arrayValue[i]
|
|
|
|
if pm._falloff < pm._value:
|
|
|
|
pm._falloff = pm._value
|
|
pm._peak = self._speed
|
|
|
|
self._meterData[i] = pm
|
|
|
|
# Auto-restart
|
|
if isRunning:
|
|
return self.Start(self._delay)
|
|
|
|
self.Refresh()
|
|
|
|
return True
|
|
|
|
|
|
def IsStarted(self):
|
|
""" Check if animation is active. """
|
|
|
|
return self._timer.IsRunning()
|
|
|
|
|
|
def Start(self, delay):
|
|
"""
|
|
Start the timer and animation effect.
|
|
|
|
:param `delay`: the animation effect delay, in milliseconds.
|
|
"""
|
|
|
|
if not self.IsStarted():
|
|
self._delay = delay
|
|
self._timer.Start(self._delay)
|
|
else:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def Stop(self):
|
|
""" Stop the timer and animation effect. """
|
|
|
|
if self.IsStarted():
|
|
self._timer.Stop()
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def DoTimerProcessing(self):
|
|
""" :class:`PeakMeterCtrl` animation, does the ``wx.EVT_TIMER`` processing. """
|
|
|
|
self.Refresh()
|
|
|
|
decValue = self._maxValue//self._ledBands
|
|
noChange = True
|
|
|
|
for pm in self._meterData:
|
|
|
|
if pm._value > 0:
|
|
|
|
pm._value -= (self._ledBands > 1 and [decValue] or [self._maxValue*BAND_PERCENT//100])[0]
|
|
if pm._value < 0:
|
|
pm._value = 0
|
|
|
|
noChange = False
|
|
|
|
if pm._peak > 0:
|
|
|
|
pm._peak -= 1
|
|
noChange = False
|
|
|
|
|
|
if pm._peak == 0 and pm._falloff > 0:
|
|
|
|
pm._falloff -= (self._ledBands > 1 and [decValue >> 1] or [5])[0]
|
|
if pm._falloff < 0:
|
|
pm._falloff = 0
|
|
|
|
noChange = False
|
|
|
|
if noChange: # Stop timer if no more data
|
|
|
|
self.Stop()
|
|
|
|
|
|
def DoGetBestSize(self):
|
|
"""
|
|
Gets the size which best suits the window: for a control, it would be the
|
|
minimal size which doesn't truncate the control, for a panel - the same size
|
|
as it would have after a call to `Fit()`.
|
|
|
|
:note: Overridden from :class:`Control`.
|
|
"""
|
|
|
|
# something is better than nothing...
|
|
return wx.Size(200, 150)
|
|
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.AutoBufferedPaintDC(self)
|
|
self._clrBackground = self.GetBackgroundColour()
|
|
dc.SetBackground(wx.Brush(self._clrBackground))
|
|
dc.Clear()
|
|
rc = self.GetClientRect()
|
|
|
|
pen = wx.Pen(self._clrBackground)
|
|
dc.SetPen(pen)
|
|
|
|
if self.GetAGWWindowStyleFlag() & PM_VERTICAL:
|
|
self.DrawVertBand(dc, rc)
|
|
elif self.GetAGWWindowStyleFlag() & PM_VERTICAL_INVERTED:
|
|
self.DrawVertBandInverted(dc, rc)
|
|
else:
|
|
self.DrawHorzBand(dc, rc)
|
|
|
|
|
|
def OnEraseBackground(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
# This is intentionally empty, to reduce flicker
|
|
pass
|
|
|
|
|
|
def OnSize(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SIZE`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`SizeEvent` event to be processed.
|
|
"""
|
|
|
|
self.Refresh()
|
|
event.Skip()
|
|
|
|
|
|
def OnTimer(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_TIMER`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`TimerEvent` event to be processed.
|
|
"""
|
|
|
|
self.DoTimerProcessing()
|
|
|
|
|
|
def DrawHorzBand(self, dc, rect):
|
|
"""
|
|
Draws horizontal bands.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the horizontal bands client rectangle.
|
|
|
|
.. todo:: Implement falloff effect for horizontal bands.
|
|
"""
|
|
|
|
horzBands = (self._ledBands > 1 and [self._ledBands] or [self._maxValue*BAND_PERCENT//100])[0]
|
|
minHorzLimit = self._minValue*horzBands//self._maxValue
|
|
medHorzLimit = self._medValue*horzBands//self._maxValue
|
|
maxHorzLimit = horzBands
|
|
|
|
size = wx.Size(rect.width//horzBands, rect.height//self._numBands)
|
|
rectBand = wx.Rect(rect.GetTopLeft(), size)
|
|
|
|
# Draw band from top
|
|
rectBand.Offset(0, rect.height-size.y*self._numBands)
|
|
xDecal = (self._ledBands > 1 and [1] or [0])[0]
|
|
yDecal = (self._numBands > 1 and [1] or [0])[0]
|
|
rectPrev = wx.Rect(*rectBand)
|
|
|
|
for vert in range(self._numBands):
|
|
|
|
self._value = self._meterData[vert]._value
|
|
horzLimit = self._value*horzBands//self._maxValue
|
|
|
|
for horz in range(horzBands):
|
|
|
|
rectBand.Deflate(0, yDecal)
|
|
|
|
# Find colour based on range value
|
|
colourRect = self._clrBackground
|
|
if self._showGrid:
|
|
colourRect = DarkenColour(self._clrBackground, GRID_INCREASEBY)
|
|
|
|
if self._showGrid and (horz == minHorzLimit or horz == (horzBands-1)):
|
|
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].x = rectBand.GetTopLeft().x + (rectBand.width >> 1)
|
|
points[0].y = rectBand.GetTopLeft().y - yDecal
|
|
points[1].x = points[0].x
|
|
points[1].y = rectBand.GetBottomRight().y + yDecal
|
|
dc.DrawLine(points[0], points[1])
|
|
|
|
if horz < horzLimit:
|
|
|
|
if InRange(horz, 0, minHorzLimit-1):
|
|
colourRect = self._clrNormal
|
|
elif InRange(horz, minHorzLimit, medHorzLimit-1):
|
|
colourRect = self._clrMedium
|
|
elif InRange(horz, medHorzLimit, maxHorzLimit):
|
|
colourRect = self._clrHigh
|
|
|
|
dc.SetBrush(wx.Brush(colourRect))
|
|
dc.DrawRectangle(rectBand)
|
|
|
|
rectBand.Inflate(0, yDecal)
|
|
rectBand.Offset(size.x, 0)
|
|
|
|
# Draw falloff effect (Seems to be working now.)
|
|
if self._showFalloff:
|
|
oldPen = dc.GetPen()
|
|
pen = wx.Pen(DarkenColour(self._clrBackground, FALL_INCREASEBY))
|
|
maxWidth = size.x*horzBands
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].y = rectPrev.GetTopRight().y - yDecal
|
|
points[0].x = rectPrev.GetBottomLeft().x + self._meterData[vert]._falloff*maxWidth/self._maxValue
|
|
points[1].y = rectPrev.GetBottomLeft().y + yDecal
|
|
points[1].x = points[0].x
|
|
dc.SetPen(pen)
|
|
dc.DrawLine(points[0], points[1])
|
|
dc.SetPen(oldPen)
|
|
|
|
# Move to Next Vertical band
|
|
rectBand.Offset(-size.x*horzBands, size.y)
|
|
|
|
|
|
def DrawVertBand(self, dc, rect):
|
|
"""
|
|
Draws vertical bands.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the vertical bands client rectangle.
|
|
"""
|
|
|
|
vertBands = (self._ledBands > 1 and [self._ledBands] or [self._maxValue*BAND_PERCENT//100])[0]
|
|
minVertLimit = self._minValue*vertBands//self._maxValue
|
|
medVertLimit = self._medValue*vertBands//self._maxValue
|
|
maxVertLimit = vertBands
|
|
|
|
size = wx.Size(rect.width//self._numBands, rect.height//vertBands)
|
|
rectBand = wx.Rect(rect.GetTopLeft(), size)
|
|
|
|
# Draw band from bottom
|
|
rectBand.Offset(0, rect.bottom-size.y)
|
|
xDecal = (self._numBands > 1 and [1] or [0])[0]
|
|
yDecal = (self._ledBands > 1 and [1] or [0])[0]
|
|
|
|
for horz in range(self._numBands):
|
|
|
|
self._value = self._meterData[horz]._value
|
|
vertLimit = self._value*vertBands//self._maxValue
|
|
rectPrev = wx.Rect(*rectBand)
|
|
|
|
for vert in range(vertBands):
|
|
|
|
rectBand.Deflate(xDecal, 0)
|
|
|
|
# Find colour based on range value
|
|
colourRect = self._clrBackground
|
|
if self._showGrid:
|
|
colourRect = DarkenColour(self._clrBackground, GRID_INCREASEBY)
|
|
|
|
# Draw grid line (level) bar
|
|
if self._showGrid and (vert == minVertLimit or vert == (vertBands-1)):
|
|
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].x = rectBand.GetTopLeft().x - xDecal
|
|
points[0].y = rectBand.GetTopLeft().y + (rectBand.height >> 1)
|
|
points[1].x = rectBand.GetBottomRight().x + xDecal
|
|
points[1].y = points[0].y
|
|
dc.DrawLine(points[0], points[1])
|
|
|
|
if vert < vertLimit:
|
|
|
|
if InRange(vert, 0, minVertLimit-1):
|
|
colourRect = self._clrNormal
|
|
elif InRange(vert, minVertLimit, medVertLimit-1):
|
|
colourRect = self._clrMedium
|
|
elif InRange(vert, medVertLimit, maxVertLimit):
|
|
colourRect = self._clrHigh
|
|
|
|
dc.SetBrush(wx.Brush(colourRect))
|
|
dc.DrawRectangle(rectBand)
|
|
|
|
rectBand.Inflate(xDecal, 0)
|
|
rectBand.Offset(0, -size.y)
|
|
|
|
# Draw falloff effect
|
|
if self._showFalloff:
|
|
|
|
oldPen = dc.GetPen()
|
|
pen = wx.Pen(DarkenColour(self._clrBackground, FALL_INCREASEBY))
|
|
maxHeight = size.y*vertBands
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].x = rectPrev.GetTopLeft().x + xDecal
|
|
points[0].y = rectPrev.GetBottomRight().y - self._meterData[horz]._falloff*maxHeight//self._maxValue
|
|
points[1].x = rectPrev.GetBottomRight().x - xDecal
|
|
points[1].y = points[0].y
|
|
dc.SetPen(pen)
|
|
dc.DrawLine(points[0], points[1])
|
|
dc.SetPen(oldPen)
|
|
|
|
# Move to Next Horizontal band
|
|
rectBand.Offset(size.x, size.y*vertBands)
|
|
|
|
|
|
def DrawVertBandInverted(self, dc, rect):
|
|
"""
|
|
Draws vertical bands inverted.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the vertical bands client rectangle.
|
|
"""
|
|
|
|
vertBands = (self._ledBands > 1 and [self._ledBands] or [self._maxValue*BAND_PERCENT//100])[0]
|
|
minVertLimit = self._minValue*vertBands//self._maxValue
|
|
medVertLimit = self._medValue*vertBands//self._maxValue
|
|
maxVertLimit = vertBands
|
|
|
|
size = wx.Size(rect.width//self._numBands, rect.height//vertBands)
|
|
rectBand = wx.Rect(rect.GetTopLeft(), size)
|
|
|
|
# Draw band from top?
|
|
rectBand.Offset(0, 0)
|
|
xDecal = (self._numBands > 1 and [1] or [0])[0]
|
|
yDecal = (self._ledBands > 1 and [1] or [0])[0]
|
|
|
|
for horz in range(self._numBands):
|
|
|
|
self._value = self._meterData[horz]._value
|
|
vertLimit = self._value*vertBands//self._maxValue
|
|
rectPrev = wx.Rect(*rectBand)
|
|
|
|
for vert in range(vertBands):
|
|
|
|
rectBand.Deflate(xDecal, 0)
|
|
|
|
# Find colour based on range value
|
|
colourRect = self._clrBackground
|
|
if self._showGrid:
|
|
colourRect = DarkenColour(self._clrBackground, GRID_INCREASEBY)
|
|
|
|
# Draw grid line (level) bar
|
|
if self._showGrid and (vert == minVertLimit or vert == (vertBands-1)):
|
|
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].x = rectBand.GetTopLeft().x - xDecal
|
|
points[0].y = rectBand.GetTopLeft().y + (rectBand.height >> 1)
|
|
points[1].x = rectBand.GetBottomRight().x + xDecal
|
|
points[1].y = points[0].y
|
|
dc.DrawLine(points[0], points[1])
|
|
|
|
if vert < vertLimit:
|
|
|
|
if InRange(vert, 0, minVertLimit-1):
|
|
colourRect = self._clrNormal
|
|
elif InRange(vert, minVertLimit, medVertLimit-1):
|
|
colourRect = self._clrMedium
|
|
elif InRange(vert, medVertLimit, maxVertLimit):
|
|
colourRect = self._clrHigh
|
|
|
|
dc.SetBrush(wx.Brush(colourRect))
|
|
dc.DrawRectangle(rectBand)
|
|
|
|
rectBand.Inflate(xDecal, 0)
|
|
rectBand.Offset(0, size.y)
|
|
|
|
# Draw falloff effect
|
|
if self._showFalloff:
|
|
|
|
oldPen = dc.GetPen()
|
|
pen = wx.Pen(DarkenColour(self._clrBackground, FALL_INCREASEBY))
|
|
maxHeight = -size.y*vertBands
|
|
points = [wx.Point() for i in range(2)]
|
|
points[0].x = rectPrev.GetBottomLeft().x + xDecal
|
|
points[0].y = rectPrev.GetTopRight().y - self._meterData[horz]._falloff*maxHeight/self._maxValue
|
|
points[1].x = rectPrev.GetTopRight().x - xDecal
|
|
points[1].y = points[0].y
|
|
dc.SetPen(pen)
|
|
dc.DrawLine(points[0], points[1])
|
|
dc.SetPen(oldPen)
|
|
|
|
# Move to Next Horizontal band
|
|
rectBand.Offset(size.x, -size.y*vertBands)
|
|
|
|
if __name__ == '__main__':
|
|
|
|
import wx
|
|
import random
|
|
|
|
class MyFrame(wx.Frame):
|
|
|
|
def __init__(self, parent):
|
|
|
|
wx.Frame.__init__(self, parent, -1, "PeakMeterCtrl Demo")
|
|
|
|
panel = wx.Panel(self)
|
|
|
|
# Initialize Peak Meter control 1
|
|
self.vertPeak = PeakMeterCtrl(panel, -1, style=wx.SIMPLE_BORDER, agwStyle=PM_VERTICAL)
|
|
# Initialize Peak Meter control 2
|
|
self.horzPeak = PeakMeterCtrl(panel, -1, style=wx.SUNKEN_BORDER, agwStyle=PM_HORIZONTAL)
|
|
|
|
self.vertPeak.SetMeterBands(10, 15)
|
|
self.horzPeak.SetMeterBands(10, 15)
|
|
|
|
# Layout the two PeakMeterCtrl
|
|
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
mainSizer.Add(self.vertPeak, 0, wx.EXPAND|wx.ALL, 15)
|
|
mainSizer.Add(self.horzPeak, 0, wx.EXPAND|wx.ALL, 15)
|
|
|
|
panel.SetSizer(mainSizer)
|
|
mainSizer.Layout()
|
|
|
|
self.timer = wx.Timer(self)
|
|
self.Bind(wx.EVT_TIMER, self.OnTimer)
|
|
|
|
wx.CallLater(500, self.Start)
|
|
|
|
|
|
def Start(self):
|
|
''' Starts the PeakMeterCtrl. '''
|
|
|
|
self.timer.Start(1000//2) # 2 fps
|
|
|
|
self.vertPeak.Start(1000//18) # 18 fps
|
|
self.horzPeak.Start(1000//20) # 20 fps
|
|
|
|
|
|
def OnTimer(self, event):
|
|
'''
|
|
Handles the ``wx.EVT_TIMER`` event for :class:`PeakMeterCtrl`.
|
|
|
|
:param `event`: a :class:`TimerEvent` event to be processed.
|
|
'''
|
|
|
|
# Generate 15 random number and set them as data for the meter
|
|
nElements = 15
|
|
arrayData = []
|
|
|
|
for i in range(nElements):
|
|
nRandom = random.randint(0, 100)
|
|
arrayData.append(nRandom)
|
|
|
|
self.vertPeak.SetData(arrayData, 0, nElements)
|
|
self.horzPeak.SetData(arrayData, 0, nElements)
|
|
|
|
|
|
# our normal wxApp-derived class, as usual
|
|
|
|
app = wx.App(0)
|
|
|
|
frame = MyFrame(None)
|
|
app.SetTopWindow(frame)
|
|
frame.Show()
|
|
|
|
app.MainLoop()
|