diff --git a/gui/attribute_gauge.py b/gui/attribute_gauge.py new file mode 100644 index 000000000..d667b44c1 --- /dev/null +++ b/gui/attribute_gauge.py @@ -0,0 +1,344 @@ +# =============================================================================== +# PyfaGauge is a generic Gauge implementation tailored for pyfa (the Python +# Fitting Assistant). It uses the easeOutQuad equation from +# caurina.transitions.Tweener to do animations +# +# ToDo: make SetGradient(from and not dependant on value) +# ToDo: fix 0 range (currently resets range to 0.01, but this causes problems if +# we really set range at 0.01). Perhaps make it -1 and test percentage as +# a negativeor something. +# ToDo: possibly devise a way to determine transition percents on init +# (currently hardcoded) +# +# =============================================================================== + +import copy +import wx + +from gui.utils import color as color_utils +from gui.utils import draw, anim_effects +from service.fit import Fit + + +class AttributeGauge(wx.Window): + def __init__(self, parent, max_range=100, size=(-1, 30), *args, + **kargs): + + super().__init__(parent, size=size, *args, **kargs) + + self._size = size + + self._border_colour = wx.BLACK + self._bar_colour = None + self._bar_gradient = None + + self._border_padding = 0 + self._max_range = max_range + self._value = 0 + + self._fraction_digits = 0 + + self._timer_id = wx.NewId() + self._timer = None + + self._oldValue = 0 + + 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 + + # transition colors used based on how full (or overfilled) the gauge is. + self.transition_colors = [ + (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% + (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% + (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% + (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% + ] + + 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.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + + def OnEraseBackground(self, event): + pass + + def OnWindowEnter(self, event): + self._show_remaining = True + self.Refresh() + + def OnWindowLeave(self, event): + self._show_remaining = False + self.Refresh() + + def GetBorderColour(self): + return self._border_colour + + def SetBorderColour(self, colour): + self._border_colour = colour + + def GetBarColour(self): + return self._bar_colour + + def SetBarColour(self, colour): + self._bar_colour = colour + + def SetFractionDigits(self, digits): + self._fraction_digits = digits + + def GetBarGradient(self): + if self._bar_gradient is None: + return None + + return self._bar_gradient[0] + + def SetBarGradient(self, gradient=None): + if gradient is None: + self._bar_gradient = None + else: + if not isinstance(gradient, list): + self._bar_gradient = [gradient] + else: + self._bar_gradient = list(gradient) + + 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): + #sFit = Fit.getInstance() + if False: + 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 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 value < 0: + self._value = float(0) + + 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 = abs(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 self.GetBarColour(): + # if we have a bar color set, then we will use this + + colour = self.GetBarColour() + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + half = (rect.width / 2) + # calculate width of bar and draw it + w = (rect.width * (value / 100)) / 2 + w = min(w, half) + + print(half, w) + + if self._percentage >= 0: + padding = half + else: + padding = min(half-w, half) + + #r = copy.copy(rect) + dc.DrawRectangle(padding+1, 1, w, 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() + + +if __name__ == "__main__": + def frange(x, y, jump): + while x < y: + yield x + x += jump + + class MyPanel(wx.Panel): + def __init__(self, parent, size=(500, 500)): + wx.Panel.__init__(self, parent, size=size) + box = wx.BoxSizer(wx.VERTICAL) + + font = wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + gauge = AttributeGauge(self, size=(100, 25)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(-25) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL, 2) + + self.SetSizer(box) + self.Layout() + + # see animation going backwards with last gauge + #wx.CallLater(2000, self.ChangeValues) + + def ChangeValues(self): + self.gauge.SetValueRange(4, 100) + + class Frame(wx.Frame): + def __init__(self, title, size=(500, 800)): + wx.Frame.__init__(self, None, title=title, size=size) + self.statusbar = self.CreateStatusBar() + main_sizer = wx.BoxSizer(wx.VERTICAL) + panel = MyPanel(self, size=size) + main_sizer.Add(panel) + self.SetSizer(main_sizer) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Chrome Tabs") + top.Show() + app.MainLoop() diff --git a/gui/builtinItemStatsViews/attributeSlider.py b/gui/builtinItemStatsViews/attributeSlider.py index 75aca7061..548c4cdf6 100644 --- a/gui/builtinItemStatsViews/attributeSlider.py +++ b/gui/builtinItemStatsViews/attributeSlider.py @@ -19,8 +19,11 @@ class AttributeSlider(wx.Panel): # be centered. We use a range of -100,100 so that we can depend on the SliderValue to contain the percentage # toward one end - self.SliderMinValue = -100 - self.SliderMaxValue = 100 + # Additionally, since we want the slider to be accurate to 3 decimal places, we need to blow out the two ends here + # (if we have a slider that needs to land on 66.66% towards the right, it will actually be converted to 66%. Se we need it to support 6,666) + + self.SliderMinValue = -100_000 + self.SliderMaxValue = 100_000 self.SliderValue = 0 self.statxt1 = wx.StaticText(self, wx.ID_ANY, 'left', @@ -70,10 +73,10 @@ class AttributeSlider(wx.Panel): slider_percentage = 0 if mod < 1: modEnd = -1 * self.UserMinValue - slider_percentage = (modEnd / mod) * 100 + slider_percentage = (modEnd / mod) * 10_000 elif mod > 1: modEnd = self.UserMaxValue - slider_percentage = ((mod-1)/(modEnd-1)) * 100 + slider_percentage = ((mod-1)/(modEnd-1)) * 100_000 self.slider.SetValue(slider_percentage) self.CalculateUserValue() @@ -90,19 +93,19 @@ class AttributeSlider(wx.Panel): mod = self.UserMaxValue # Get the slider value percentage as an absolute value - slider_mod = abs(self.SliderValue) / 100 + slider_mod = abs(self.SliderValue/1_000) / 100 # Gets our new mod by use the slider's percentage to determine where in the spectrum it is new_mod = mod + ((1 - mod) - ((1 - mod) * slider_mod)) - # Modifies our base vale, to get out modified value + # Modifies our base value, to get out modified value newValue = new_mod * self.base_value if mod == 1: self.statxt2.SetLabel("{0:.3f}".format(newValue)) else: self.statxt2.SetLabel("{0:.3f} ({1:+.3f})".format(newValue, newValue - self.base_value, )) - self.statxt2.SetToolTip("{0:+f}%".format(newValue)) + self.statxt2.SetToolTip("{0:+f}%".format(new_mod*100)) class TestAttributeSlider(wx.Frame): @@ -114,8 +117,8 @@ class TestAttributeSlider(wx.Frame): sty = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, parent, id, title, pos, size, sty) - self.panel = AttributeSlider(self, 200, .80, 2.5) - self.panel.SetValue(400) + self.panel = AttributeSlider(self, 10, .80, 1.5) + self.panel.SetValue(8.805) self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) def OnCloseWindow(self, event): diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index 0144f651c..43b1dc728 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -130,8 +130,8 @@ class PyGauge(wx.Window): return self._max_range def Animate(self): - sFit = Fit.getInstance() - if sFit.serviceFittingOptions["enableGaugeAnimation"]: + #sFit = Fit.getInstance() + if True: if not self._timer: self._timer = wx.Timer(self, self._timer_id) @@ -425,6 +425,13 @@ if __name__ == "__main__": gauge.SetFractionDigits(1) box.Add(gauge, 0, wx.ALL, 2) + gauge = PyGauge(self, font, size=(100, 5)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(59) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL, 2) + self.SetSizer(box) self.Layout()