Files
pyfa/gui/attribute_gauge.py
2022-05-05 18:41:17 +04:00

277 lines
8.3 KiB
Python

import math
import wx
from gui.utils import anim_effects
# todo: clean class up. Took from pyfa gauge, has a bunch of extra shit we don't need
class AttributeGauge(wx.Window):
def __init__(
self, parent, max_range=100, animate=True, leading_edge=True,
edge_on_neutral=True, guide_lines=False, size=(-1, 30), *args, **kwargs
):
super().__init__(parent, size=size, *args, **kwargs)
self._size = size
self.guide_lines = guide_lines
self._border_colour = wx.BLACK
self.leading_edge = leading_edge
self.edge_on_neutral = edge_on_neutral
self._border_padding = 0
self._max_range = max_range
self._value = 0
self._timer_id = wx.NewId()
self._timer = None
self._oldValue = 0
self._animate = animate
self._anim_duration = 500
self._anim_step = 0
self._period = 20
self._anim_value = 0
self._anim_direction = 0
self.anim_effect = anim_effects.OUT_QUAD
self.goodColor = wx.Colour(96, 191, 0)
self.badColor = wx.Colour(255, 64, 0)
self.gradient_effect = -35
self._percentage = 0
self._old_percentage = 0
self._show_remaining = False
self.SetBackgroundColour(wx.Colour(51, 51, 51))
self._tooltip = wx.ToolTip("0.00/100.00")
self.SetToolTip(self._tooltip)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.SetBackgroundStyle(wx.BG_STYLE_PAINT)
def OnEraseBackground(self, event):
pass
def GetBorderColour(self):
return self._border_colour
def SetBorderColour(self, colour):
self._border_colour = colour
def GetBorderPadding(self):
return self._border_padding
def SetBorderPadding(self, padding):
self._border_padding = padding
def GetRange(self):
""" Returns the maximum value of the gauge. """
return self._max_range
def Animate(self):
if self._animate:
if not self._timer:
self._timer = wx.Timer(self, self._timer_id)
self._anim_step = 0
self._timer.Start(self._period)
else:
self._anim_value = self._percentage
self.Refresh()
def FreezeAnimation(self):
self._animate = False
if self._timer:
self._timer.Stop()
def SetRange(self, range, reinit=False, animate=True):
"""
Sets the range of the gauge. The gauge length is its
value as a proportion of the range.
"""
if self._max_range == range:
return
# we cannot have a range of zero (laws of physics, etc), so we set it
if range <= 0:
self._max_range = 0.01
else:
self._max_range = range
if reinit is False:
self._old_percentage = self._percentage
self._percentage = (self._value / self._max_range) * 100
else:
self._old_percentage = self._percentage
self._percentage = 0
self._value = 0
if animate:
self.Animate()
self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range > 0.01 else 0))
def GetValue(self):
return self._value
def SetValue(self, value, animate=True):
""" Sets the current position of the gauge. """
if self._value == value:
return
self._old_percentage = self._percentage
self._value = value
self._percentage = (self._value / self._max_range) * 100
if animate:
self.Animate()
self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range))
def SetValueRange(self, value, range, reinit=False):
""" Set both value and range of the gauge. """
range_ = float(range)
if range_ <= 0:
self._max_range = 0.01
else:
self._max_range = range_
value = float(value)
self._value = value
if reinit is False:
self._old_percentage = self._percentage
self._percentage = (self._value / self._max_range) * 100
else:
self._old_percentage = self._percentage
self._percentage = 0
self.Animate()
self._tooltip.SetTip("%.2f/%.2f" %
(self._value, self._max_range if float(self._max_range) > 0.01 else 0))
def OnPaint(self, event):
dc = wx.AutoBufferedPaintDC(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.DrawRectangle(rect)
value = self._percentage
if self._timer:
if self._timer.IsRunning():
value = self._anim_value
if self._border_colour:
dc.SetPen(wx.Pen(self.GetBorderColour()))
dc.DrawRectangle(rect)
pad = 1 + self.GetBorderPadding()
rect.Deflate(pad, pad)
# if we have a bar color set, then we will use this
colour = self.goodColor if value >= 0 else self.badColor
is_even = rect.width % 2 == 0
# the size of half our available drawing area (since we're only working in halves)
half = (rect.width / 2)
# calculate width of bar as a percentage of half the space
w = abs(half * (value / 100))
w = min(w, half) # Ensure that we don't overshoot our drawing area
w = math.ceil(w) # round up to nearest pixel, this ensures that we don't lose representation for sub pixels
# print("Percentage: {}\t\t\t\t\tValue: {}\t\t\t\t\tWidth: {}\t\t\t\t\tHalf: {}\t\t\t\t\tRect Width: {}".format(
# round(self._percentage, 3), round(value,3), w, half, rect.width))
# set guide_lines every 10 pixels of the main gauge (not including borders)
if self.guide_lines:
for x in range(1, 20):
dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
dc.SetPen(wx.Pen(wx.LIGHT_GREY))
dc.DrawRectangle(round(x * 10), 1, 1, round(rect.height))
dc.SetBrush(wx.Brush(colour))
dc.SetPen(wx.Pen(colour))
# If we have an even width, we can simply dedicate the middle-most pixels to both sides
# However, if there is an odd width, the middle pixel is shared between the left and right gauge
if value >= 0:
padding = (half if is_even else math.ceil(half - 1)) + 1
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
else:
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1)
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
if self.leading_edge and (self.edge_on_neutral or value != 0):
dc.SetPen(wx.Pen(wx.WHITE))
dc.SetBrush(wx.Brush(wx.WHITE))
if value > 0:
dc.DrawRectangle(round(min(padding + w, rect.width)), 1, 1, round(rect.height))
else:
dc.DrawRectangle(round(max(padding - 1, 1)), 1, 1, round(rect.height))
def OnTimer(self, event):
old_value = self._old_percentage
value = self._percentage
start = 0
# -1 = left direction, 1 = right direction
direction = 1 if old_value < value else -1
end = direction * (value - old_value)
self._anim_direction = direction
step = self.anim_effect(self._anim_step, start, end, self._anim_duration)
self._anim_step += self._period
if self._timer_id == event.GetId():
stop_timer = False
if self._anim_step > self._anim_duration:
stop_timer = True
# add new value to the animation if we haven't reached our goal
# otherwise, stop animation
if direction == 1:
if old_value + step < value:
self._anim_value = old_value + step
else:
stop_timer = True
else:
if old_value - step > value:
self._anim_value = old_value - step
else:
stop_timer = True
if stop_timer:
self._timer.Stop()
self.Refresh()