diff --git a/gui/pygauge.py b/gui/pygauge.py new file mode 100644 index 000000000..482d9e2bd --- /dev/null +++ b/gui/pygauge.py @@ -0,0 +1,351 @@ +# --------------------------------------------------------------------------------- # +# PYGAUGE wxPython IMPLEMENTATION +# +# Mark Reed, @ 28 Jul 2010 +# Latest Revision: 14 Apr 2010, 12.00 GMT +# +# TODO List +# +# 1. Indeterminate mode (see wx.Gauge) +# 2. Vertical bar +# 3. Bitmap support (bar, background) +# 4. UpdateFunction - Pass a function to PyGauge which will be called every X +# milliseconds and the value will be updated to the returned value. +# 5. Currently the full gradient is drawn from 0 to value. Perhaps the gradient +# should be drawn from 0 to range and clipped at 0 to value. +# 6. Add a label? +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To The: +# +# wxPython Mailing List!!! +# +# End Of Comments +# --------------------------------------------------------------------------------- # + +""" +PyGauge is a generic Gauge implementation. + +Description +=========== + +PyGauge supports the determinate mode functions as wxGauge and adds an Update function +which takes a value and a time parameter. The value is added to the current value over +a period of time milliseconds. + +Supported Platforms +=================== + +PyGauge has been tested on the following platforms: + * Windows (Windows XP); + +License And Version +=================== + +PyGauge is distributed under the wxPython license. + +Latest Revision: Andrea Gavana @ 14 Apr 2010, 12.00 GMT + +Version 0.1 +""" + +import wx +import copy + +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._valueSorted = [0] + self._skipDigits = True + self._timerId = wx.NewId() + self._timer = None + + self.SetBarGradient((wx.Colour(153,153,153),wx.Colour(204,204,204))) + self.SetBackgroundColour(wx.Colour(102,102,102)) + + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_TIMER, self.OnTimer) + + + 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) + + self.SortForDisplay() + + SetBarColor = SetBarColour + GetBarColor = GetBarColour + + def SetSkipDigitsFlag(self,flag): + self._skipDigits=flag + + 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) + + self.SortForDisplay() + + + 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 SetRange(self, range): + """ + 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 range <= 0: + raise Exception("ERROR:\n Gauge range must be greater than 0.") + + self._range = range + + + def GetValue(self): + """ Returns the current position of the gauge. """ + + return self._value[0] + + def SetValue(self, value): + """ Sets the current position of the gauge. """ + + if type(value) != type([]): + self._value = [value] + else: + self._value = list(value) + + self.SortForDisplay() + + for v in self._value: + if v <= 0 or v > self._range: + raise Exception("ERROR:\n Gauge value must be between 0 and it's range. ") + + + 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) + + + if self._border_colour: + dc.SetPen(wx.Pen(self.GetBorderColour())) + dc.DrawRectangleRect(rect) + pad = 1 + self.GetBorderPadding() + rect.Deflate(pad,pad) + + + if self.GetBarGradient(): + for i, gradient in enumerate(self._barGradientSorted): + c1,c2 = gradient + w = rect.width * (float(self._valueSorted[i]) / self._range) + r = copy.copy(rect) + r.width = w + dc.GradientFillLinear(r, c1, c2, wx.EAST) + else: + for i, colour in enumerate(self._barColourSorted): + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + w = rect.width * (float(self._valueSorted[i]) / self._range) + r = copy.copy(rect) + r.width = w + dc.DrawRectangleRect(r) + + dc.SetTextForeground(wx.Colour(255,255,255)) + font1 = wx.Font(7, wx.SWISS, wx.NORMAL, wx.NORMAL) + dc.SetFont(font1) + if self._skipDigits == True: + textWidth=len(str(int(self._value[0]*100/self._range)) + "%") * 7 + dc.DrawText(str(int(self._value[0]*100/self._range)) + "%",rect.width/2-textWidth/2,(rect.height-8)/2) + else: + textWidth=len(str(round(float(self._value[0]*100/self._range),2))+ "%") * 7 + dc.DrawText(str(round(float(self._value[0]*100/self._range),2)) + "%",rect.width/2-textWidth/2,(rect.height-8)/2) + + def OnTimer(self,event): + """ + Handles the ``wx.EVT_TIMER`` event for L{PyGauge}. + + :param `event`: a timer event + """ + + if self._timerId == event.GetId(): + stop_timer = True + for i, v in enumerate(self._value): + self._value[i] += self._update_step[i] + + if self._update_step[i] > 0: + if self._value[i] > self._update_value[i]: + self._value[i] = self._update_value[i] + else: stop_timer = False + else: + if self._value[i] < self._update_value[i]: + self._value[i] = self._update_value[i] + else: stop_timer = False + + if stop_timer: + self._timer.Stop() + + self.SortForDisplay() + + self.Refresh() + + + def Update(self, value, time=0, index=0): + """ + Update the gauge by adding value to it over time milliseconds. Time + must be a multiple of 50 milliseconds. + + :param `value`: The value to be added to the gauge. + :param `time`: The length of time in ms that it will take to move the gauge + """ + time=abs(value)*50 + if time>500: time=500 + if type(value) != type([]): + value = [value] + + if len(value) != len(self._value): + raise Exception("ERROR:\n len(value) != len(self.GetValue()) ") + + self._update_value = [] + self._update_step = [] + for i, v in enumerate(self._value): + if value[i]+v < 0 or value[i]+v > self._range: + raise Exception("ERROR2:\n Gauge value must be between 0 and it's range. ") + + self._update_value.append( value[i] +v ) + self._update_step.append( float(value[i]) / ( time/50 ) ) + + #print self._update_ + + if not self._timer: + self._timer = wx.Timer(self, self._timerId) + + self._timer.Start(100) + + def SortForDisplay(self): + """ Internal method which sorts things so we draw the longest bar first. """ + + if self.GetBarGradient(): + tmp = sorted(zip(self._value,self._barGradient)); tmp.reverse() + a,b = zip(*tmp) + self._valueSorted = list(a) + self._barGradientSorted = list(b) + else: + tmp = sorted(zip(self._value,self._barColour)); tmp.reverse() + a,b = zip(*tmp) + self._valueSorted = list(a) + self._barColourSorted = list(b) + + diff --git a/gui/statsPane.py b/gui/statsPane.py index 7eee12858..5d3fb186e 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -23,6 +23,8 @@ import gui.fittingView as fv import gui.mainFrame import controller from eos.types import Slot, Hardpoint +from gui import pygauge as PG + class StatsPane(wx.Panel): def collapseChanged(self, event): collapsed = event.Collapsed @@ -139,6 +141,7 @@ class StatsPane(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) + self._showNormalGauges = False self.mainFrame = gui.mainFrame.MainFrame.getInstance() # Register events @@ -265,11 +268,21 @@ class StatsPane(wx.Panel): setattr(self, "label%sTotal%s" % (panel.capitalize(), capitalizedType), lbl) absolute.Add(lbl, 0, wx.ALIGN_LEFT) - gauge = wx.Gauge(parent, wx.ID_ANY, 100) - gauge.SetMinSize((80, 20)) - setattr(self, "gauge%s" % capitalizedType, gauge) - stats.Add(gauge, 0, wx.ALIGN_CENTER) +# Gauges modif. - Darriele + if self._showNormalGauges == True: + gauge = wx.Gauge(parent, wx.ID_ANY, 100) + gauge.SetMinSize((80, 20)) + setattr(self, "gauge%s" % capitalizedType, gauge) + stats.Add(gauge, 0, wx.ALIGN_CENTER) + else: + gauge = PG.PyGauge(parent, wx.ID_ANY, 100) + gauge.SetMinSize((80, 16)) + gauge.SetSkipDigitsFlag(True) + gauge.SetValue(100) + gauge.Update(-100,250) + setattr(self, "gauge%s" % capitalizedType, gauge) + stats.Add(gauge, 0, wx.ALIGN_CENTER) if panel == "mini": base.Add(wx.StaticLine(parent, wx.ID_ANY, style=wx.HORIZONTAL), 0, wx.EXPAND) @@ -307,19 +320,38 @@ class StatsPane(wx.Panel): sizerResistances.Add(bitmapLoader.getStaticBitmap("%s_big" % damageType, self.fullPanel, "icons"), 0, wx.ALIGN_CENTER) sizerResistances.Add(wx.StaticText(self.fullPanel, wx.ID_ANY, "EHP"), 0, wx.ALIGN_CENTER) + gaugeColours=( ((26,94,140),(30,60,69)), ((140,26,26),(58,46,47)), ((115,115,115),(52,63,65)), ((140,94,26),(57,58,47)) ) for tankType in ("damagePattern", "shield", "armor", "hull"): sizerResistances.Add(bitmapLoader.getStaticBitmap("%s_big" % tankType, self.fullPanel, "icons"), 0, wx.ALIGN_CENTER) + currGColour=0 for damageType in ("em", "thermal", "kinetic", "explosive"): box = wx.BoxSizer(wx.HORIZONTAL) sizerResistances.Add(box, 1, wx.ALIGN_CENTER) - lbl = wx.StaticText(self.fullPanel, wx.ID_ANY, "0.00") +#Fancy gauges addon + + pgColour= gaugeColours[currGColour] + fc = pgColour[0] + bc = pgColour[1] + currGColour+=1 + if self._showNormalGauges == True: + lbl = wx.StaticText(self.fullPanel, wx.ID_ANY, "0.00") + else: + lbl = PG.PyGauge(self.fullPanel, wx.ID_ANY, 100) + lbl.SetMinSize((40, 16)) + lbl.SetBackgroundColour(wx.Colour(bc[0],bc[1],bc[2])) + lbl.SetBarColour(wx.Colour(fc[0],fc[1],fc[2])) + lbl.SetBarGradient() + lbl.SetValue(100) + lbl.SetSkipDigitsFlag(False) + lbl.Update(-100,250) setattr(self, "labelResistance%s%s" % (tankType.capitalize(), damageType.capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) - box.Add(wx.StaticText(self.fullPanel, wx.ID_ANY, "%"), 0, wx.ALIGN_CENTER) + if self._showNormalGauges == True: + box.Add(wx.StaticText(self.fullPanel, wx.ID_ANY, "%"), 0, wx.ALIGN_CENTER) lbl = wx.StaticText(self.fullPanel, wx.ID_ANY, "0" if tankType != "damagePattern" else "") @@ -485,7 +517,7 @@ class StatsPane(wx.Panel): sizerHeaderCapacitor.Add(wx.StaticLine(self.fullPanel, wx.ID_ANY), 1, wx.ALIGN_CENTER) sizerCapacitor = wx.GridSizer(1, 2) self.sizerBase.Add(sizerCapacitor, 0, wx.EXPAND | wx.LEFT, 1) - + # Capacitor capacity and time baseBox = wx.BoxSizer(wx.HORIZONTAL)