436 lines
13 KiB
Python
436 lines
13 KiB
Python
# --------------------------------------------------------------------------------- #
|
|
# PYFAGAUGE wxPython IMPLEMENTATION
|
|
#
|
|
# Darriele, @ 08/30/2010
|
|
# Updated: 09/07/2010
|
|
# Based on AWG : pygauge code
|
|
# --------------------------------------------------------------------------------- #
|
|
|
|
"""
|
|
PyfaGauge is a generic Gauge implementation tailored for PYFA (Python Fitting Assistant)
|
|
It uses the easeOutQuad equation from caurina.transitions.Tweener to do the animation stuff
|
|
"""
|
|
|
|
import wx
|
|
import copy
|
|
import math
|
|
|
|
from gui.utils import colorUtils
|
|
import gui.utils.drawUtils as drawUtils
|
|
import gui.utils.animEffects as animEffects
|
|
|
|
class PyGauge(wx.PyWindow):
|
|
"""
|
|
This class provides a visual alternative for `wx.Gauge`. It currently
|
|
only support determinant mode (see SetValue and SetRange)
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, range=100, pos=wx.DefaultPosition,
|
|
size=(-1,30), style=0):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: parent window. 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.
|
|
"""
|
|
|
|
wx.PyWindow.__init__(self, parent, id, pos, size, style)
|
|
|
|
self._size = size
|
|
|
|
self._border_colour = wx.BLACK
|
|
self._barColour = self._barColourSorted = [wx.Colour(212,228,255)]
|
|
self._barGradient = self._barGradientSorted = None
|
|
|
|
self._border_padding = 0
|
|
self._range = range
|
|
self._value = 0
|
|
|
|
self._fractionDigits = 0
|
|
|
|
self._timerId = wx.NewId()
|
|
self._timer = None
|
|
|
|
self._oldValue = 0
|
|
|
|
self._animDuration = 500
|
|
self._animStep = 0
|
|
self._period = 20
|
|
self._animValue = 0
|
|
self._animDirection = 0
|
|
self.animEffect = animEffects.OUT_QUAD
|
|
|
|
self.transitionsColors = [( wx.Colour(191, 191, 191, 255) , wx.Colour(96, 191, 0, 255) ),
|
|
( wx.Colour(191, 167, 96, 255) , wx.Colour(255, 191, 0, 255) ),
|
|
( wx.Colour(255, 191, 0, 255) , wx.Colour(255, 128, 0, 255) ),
|
|
( wx.Colour(255, 128, 0, 255) , wx.Colour(255, 0, 0, 255) )]
|
|
self.gradientEffect = -35
|
|
|
|
self._percentage = 0
|
|
self._oldPercentage = 0
|
|
self._showRemaining = False
|
|
|
|
self.font = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False)
|
|
|
|
self.SetBarGradient((wx.Colour(119,119,119),wx.Colour(153,153,153)))
|
|
self.SetBackgroundColour(wx.Colour(51,51,51))
|
|
self._tooltip = wx.ToolTip("")
|
|
self.SetToolTip(self._tooltip)
|
|
self._tooltip.SetTip("0/0")
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
self.Bind(wx.EVT_TIMER, self.OnTimer)
|
|
self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)
|
|
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
|
|
|
|
def OnWindowEnter(self, event):
|
|
self._showRemaining = True
|
|
self.Refresh()
|
|
|
|
def OnWindowLeave(self, event):
|
|
self._showRemaining = False
|
|
self.Refresh()
|
|
|
|
def DoGetBestSize(self):
|
|
"""
|
|
Overridden base class virtual. Determines the best size of the
|
|
button based on the label and bezel size.
|
|
"""
|
|
|
|
return wx.Size(self._size[0], self._size[1])
|
|
|
|
|
|
def GetBorderColour(self):
|
|
return self._border_colour
|
|
|
|
def SetBorderColour(self, colour):
|
|
self._border_colour = colour
|
|
|
|
SetBorderColor = SetBorderColour
|
|
GetBorderColor = GetBorderColour
|
|
|
|
def GetBarColour(self):
|
|
return self._barColour[0]
|
|
|
|
def SetBarColour(self, colour):
|
|
if type(colour) != type([]):
|
|
self._barColour = [colour]
|
|
else:
|
|
self._barColour = list(colour)
|
|
|
|
SetBarColor = SetBarColour
|
|
GetBarColor = GetBarColour
|
|
|
|
def SetFractionDigits(self, digits):
|
|
self._fractionDigits=digits
|
|
|
|
def GetBarGradient(self):
|
|
""" Returns a tuple containing the gradient start and end colours. """
|
|
|
|
if self._barGradient == None:
|
|
return None
|
|
|
|
return self._barGradient[0]
|
|
|
|
def SetBarGradient(self, gradient = None):
|
|
"""
|
|
Sets the bar gradient. This overrides the BarColour.
|
|
|
|
:param `gradient`: a tuple containing the gradient start and end colours.
|
|
"""
|
|
if gradient == None:
|
|
self._barGradient = None
|
|
else:
|
|
if type(gradient) != type([]):
|
|
self._barGradient = [gradient]
|
|
else:
|
|
self._barGradient = list(gradient)
|
|
|
|
|
|
def GetBorderPadding(self):
|
|
""" Gets the border padding. """
|
|
|
|
return self._border_padding
|
|
|
|
def SetBorderPadding(self, padding):
|
|
"""
|
|
Sets the border padding.
|
|
|
|
:param `padding`: pixels between the border and the progress bar.
|
|
"""
|
|
|
|
self._border_padding = padding
|
|
|
|
|
|
def GetRange(self):
|
|
""" Returns the maximum value of the gauge. """
|
|
|
|
return self._range
|
|
|
|
def Animate(self):
|
|
if not self._timer:
|
|
self._timer = wx.Timer(self, self._timerId)
|
|
self._animStep = 0
|
|
self._timer.Start(self._period)
|
|
|
|
def SetRange(self, range, reinit = False):
|
|
"""
|
|
Sets the range of the gauge. The gauge length is its
|
|
value as a proportion of the range.
|
|
|
|
:param `range`: The maximum value of the gauge.
|
|
"""
|
|
|
|
if self._range == range:
|
|
return
|
|
|
|
range = float(range)
|
|
|
|
if range <= 0:
|
|
self._range = 0.01
|
|
else:
|
|
self._range = range
|
|
|
|
if reinit is False:
|
|
self._oldPercentage = self._percentage
|
|
self._percentage = (self._value/self._range) * 100
|
|
else:
|
|
self._oldPercentage = self._percentage
|
|
self._percentage = 0
|
|
self._value = 0
|
|
|
|
self.Animate()
|
|
|
|
|
|
self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0))
|
|
|
|
|
|
def GetValue(self):
|
|
""" Returns the current position of the gauge. """
|
|
|
|
return self._value
|
|
|
|
def SetValue(self, value):
|
|
""" Sets the current position of the gauge. """
|
|
if self._value == value:
|
|
return
|
|
|
|
value = float(value)
|
|
self._oldPercentage = self._percentage
|
|
self._value = value
|
|
if value < 0:
|
|
self._value = 0
|
|
self._percentage = (self._value/self._range) * 100
|
|
|
|
self.Animate()
|
|
|
|
self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range))
|
|
|
|
def SetValueRange(self, value, range, reinit = False):
|
|
if self._value == value and self._range == range:
|
|
return
|
|
|
|
range = float(range)
|
|
|
|
if range <= 0:
|
|
self._range = 0.01
|
|
else:
|
|
self._range = range
|
|
|
|
value = float(value)
|
|
|
|
self._value = value
|
|
if value < 0:
|
|
self._value = float(0)
|
|
|
|
if reinit is False:
|
|
self._oldPercentage = self._percentage
|
|
self._percentage = (self._value/self._range) * 100
|
|
|
|
else:
|
|
self._oldPercentage = self._percentage
|
|
self._percentage = 0
|
|
|
|
|
|
self.Animate()
|
|
self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0))
|
|
|
|
def OnEraseBackground(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyGauge}.
|
|
|
|
:param `event`: a `wx.EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
pass
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for L{PyGauge}.
|
|
|
|
:param `event`: a `wx.PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.BufferedPaintDC(self)
|
|
rect = self.GetClientRect()
|
|
|
|
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
|
|
dc.Clear()
|
|
|
|
colour = self.GetBackgroundColour()
|
|
|
|
dc.SetBrush(wx.Brush(colour))
|
|
dc.SetPen(wx.Pen(colour))
|
|
|
|
dc.DrawRectangleRect(rect)
|
|
|
|
value = self._percentage
|
|
if self._timer:
|
|
if self._timer.IsRunning():
|
|
value = self._animValue
|
|
|
|
if self._border_colour:
|
|
dc.SetPen(wx.Pen(self.GetBorderColour()))
|
|
dc.DrawRectangleRect(rect)
|
|
pad = 1 + self.GetBorderPadding()
|
|
rect.Deflate(pad,pad)
|
|
|
|
if self.GetBarGradient():
|
|
|
|
if value > 100:
|
|
w = rect.width
|
|
else:
|
|
w = rect.width * (float(value) / 100)
|
|
r = copy.copy(rect)
|
|
r.width = w
|
|
|
|
|
|
pv = value
|
|
xv=1
|
|
transition = 0
|
|
|
|
if pv <= 100:
|
|
xv = pv/100
|
|
transition = 0
|
|
|
|
elif pv <=101:
|
|
xv = pv -100
|
|
transition = 1
|
|
|
|
elif pv <= 103:
|
|
xv = (pv -101)/2
|
|
transition = 2
|
|
|
|
elif pv <= 105:
|
|
xv = (pv -103)/2
|
|
transition = 3
|
|
|
|
else:
|
|
pv = 106
|
|
xv = pv -100
|
|
transition = -1
|
|
|
|
if transition != -1:
|
|
colorS,colorE = self.transitionsColors[transition]
|
|
color = colorUtils.CalculateTransitionColor(colorS, colorE, xv)
|
|
else:
|
|
color = wx.Colour(191,48,48)
|
|
|
|
if self.gradientEffect > 0:
|
|
gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100)
|
|
gMid = colorUtils.BrightenColor(color, float(self.gradientEffect/2) / 100)
|
|
else:
|
|
gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100)
|
|
gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect/2) / 100)
|
|
|
|
gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor)
|
|
dc.DrawBitmap(gBmp,r.left, r.top)
|
|
else:
|
|
colour=self.GetBarColour()
|
|
dc.SetBrush(wx.Brush(colour))
|
|
dc.SetPen(wx.Pen(colour))
|
|
if value > 100:
|
|
w = rect.width
|
|
else:
|
|
w = rect.width * (float(value) / 100)
|
|
r = copy.copy(rect)
|
|
r.width = w
|
|
dc.DrawRectangleRect(r)
|
|
|
|
dc.SetFont(self.font)
|
|
|
|
r = copy.copy(rect)
|
|
r.left +=1
|
|
r.top +=1
|
|
if self._range == 0.01 and self._value > 0:
|
|
formatStr = u'\u221e'
|
|
dc.SetTextForeground(wx.Colour(80,80,80))
|
|
dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER)
|
|
|
|
dc.SetTextForeground(wx.Colour(255,255,255))
|
|
dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER)
|
|
else:
|
|
if self.GetBarGradient() and self._showRemaining:
|
|
formatStr = "{0:." + str(self._fractionDigits) + "f} left"
|
|
range = self._range if self._range > 0.01 else 0
|
|
value = max( range - self._value , 0)
|
|
else:
|
|
formatStr = "{0:." + str(self._fractionDigits) + "f}%"
|
|
|
|
dc.SetTextForeground(wx.Colour(80,80,80))
|
|
dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER)
|
|
|
|
dc.SetTextForeground(wx.Colour(255,255,255))
|
|
dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER)
|
|
|
|
|
|
def OnTimer(self,event):
|
|
"""
|
|
Handles the ``wx.EVT_TIMER`` event for L{PyfaGauge}.
|
|
|
|
:param `event`: a timer event
|
|
"""
|
|
oldValue=self._oldPercentage
|
|
value=self._percentage
|
|
start = 0
|
|
|
|
direction = 1 if oldValue < value else -1
|
|
|
|
end = direction * (value - oldValue)
|
|
|
|
self._animDirection = direction
|
|
step = self.animEffect(self._animStep, start, end, self._animDuration)
|
|
|
|
self._animStep += self._period
|
|
|
|
if self._timerId == event.GetId():
|
|
stop_timer = False
|
|
|
|
if self._animStep > self._animDuration:
|
|
stop_timer = True
|
|
|
|
if direction == 1:
|
|
if (oldValue+step) < value:
|
|
self._animValue = oldValue+step
|
|
else:
|
|
stop_timer = True
|
|
else:
|
|
if (oldValue-step) > value:
|
|
self._animValue = oldValue-step
|
|
|
|
else:
|
|
stop_timer = True
|
|
|
|
if stop_timer:
|
|
self._timer.Stop()
|
|
|
|
self.Refresh()
|
|
|