AGW Logo

Source code for pygauge

# --------------------------------------------------------------------------------- #
# PYGAUGE wxPython IMPLEMENTATION
#
# Mark Reed, @ 28 Jul 2010
# Latest Revision: 17 Aug 2011, 15.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
# --------------------------------------------------------------------------------- #

"""
L{PyGauge} is a generic `wx.Gauge` implementation.


Description
===========

L{PyGauge} supports the determinate mode functions as `wx.Gauge` and adds an L{Update} function
which takes a value and a time parameter. The `value` is added to the current value over 
a period of `time` milliseconds.


Usage
=====

Usage example::

    import wx
    import wx.lib.agw.pygauge as PG

    class MyFrame(wx.Frame):

        def __init__(self, parent):

            wx.Frame.__init__(self, parent, -1, "PyGauge Demo")

            panel = wx.Panel(self)
            
            gauge1 = PG.PyGauge(panel, -1, size=(100, 25), style=wx.GA_HORIZONTAL)
            gauge1.SetValue(80)
            gauge1.SetBackgroundColour(wx.WHITE)
            gauge1.SetBorderColor(wx.BLACK)
            
            gauge2 = PG.PyGauge(panel, -1, size=(100, 25), style=wx.GA_HORIZONTAL)
            gauge2.SetValue([20, 80])
            gauge2.SetBarColor([wx.Colour(162, 255, 178), wx.Colour(159, 176, 255)])
            gauge2.SetBackgroundColour(wx.WHITE)
            gauge2.SetBorderColor(wx.BLACK)
            gauge2.SetBorderPadding(2)
            gauge2.Update([30, 0], 2000)
            
            gauge3 = PG.PyGauge(panel, -1, size=(100, 25), style=wx.GA_HORIZONTAL)
            gauge3.SetValue(50)
            gauge3.SetBarColor(wx.GREEN)
            gauge3.SetBackgroundColour(wx.WHITE)
            gauge3.SetBorderColor(wx.BLACK)

            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(gauge1, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 20)
            sizer.Add(gauge2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 20)
            sizer.Add(gauge3, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 20)
        
            panel.SetSizer(sizer)
            sizer.Layout()


    # our normal wxApp-derived class, as usual

    app = wx.PySimpleApp()

    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()

    app.MainLoop()


Supported Platforms
===================

L{PyGauge} has been tested on the following platforms:
  * Windows (Windows XP);
  

License And Version
===================

L{PyGauge} is distributed under the wxPython license.

L{PyGauge} has been kindly contributed to the AGW library by Mark Reed.

Latest Revision: Andrea Gavana @ 17 Aug 2011, 15.00 GMT

Version 0.1

"""

import wx
import copy


[docs]class PyGauge(wx.PyWindow): """ This class provides a visual alternative for `wx.Gauge`. It currently only support determinate mode (see L{PyGauge.SetValue} and L{PyGauge.SetRange}) """
[docs] 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; :param `style`: the underlying `wx.PyWindow` window style. """ wx.PyWindow.__init__(self, parent, id, pos, size, style) self._size = size self._border_colour = None 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._timerId = wx.NewId() self._timer = None self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer)
[docs] def DoGetBestSize(self): """ Gets the size which best suits L{PyGauge}: 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 `wx.PyWindow`. """ return wx.Size(self._size[0], self._size[1])
[docs] def GetBorderColour(self): """ Returns the L{PyGauge} border colour. """ return self._border_colour
[docs] def SetBorderColour(self, colour): """ Sets the L{PyGauge} border colour. :param `colour`: an instance of `wx.Colour`. """ self._border_colour = colour
SetBorderColor = SetBorderColour GetBorderColor = GetBorderColour
[docs] def GetBarColour(self): """ Returns the L{PyGauge} main bar colour. """ return self._barColour[0]
[docs] def SetBarColour(self, colour): """ Sets the L{PyGauge} main bar colour. :param `colour`: an instance of `wx.Colour`. """ if type(colour) != type([]): self._barColour = [colour] else: self._barColour = list(colour) self.SortForDisplay()
SetBarColor = SetBarColour GetBarColor = GetBarColour
[docs] def GetBarGradient(self): """ Returns a tuple containing the gradient start and end colours. """ if self._barGradient == None: return None return self._barGradient[0]
[docs] def SetBarGradient(self, gradient): """ Sets the bar gradient. :param `gradient`: a tuple containing the gradient start and end colours. :note: This overrides the bar colour previously set with L{SetBarColour}. """ if type(gradient) != type([]): self._barGradient = [gradient] else: self._barGradient = list(gradient) self.SortForDisplay()
[docs] def GetBorderPadding(self): """ Gets the border padding. """ return self._border_padding
[docs] def SetBorderPadding(self, padding): """ Sets the border padding. :param `padding`: pixels between the border and the progress bar. """ self._border_padding = padding
[docs] def GetRange(self): """ Returns the maximum value of the gauge. """ return self._range
[docs] 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
[docs] def GetValue(self): """ Returns the current position of the gauge. """ return self._value[0]
[docs] def SetValue(self, value): """ Sets the current position of the gauge. :param `value`: an integer specifying 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 its range.")
[docs] 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
[docs] 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)
[docs] def OnTimer(self,event): """ Handles the ``wx.EVT_TIMER`` event for L{PyGauge}. :param `event`: a `wx.TimerEvent` event to be processed. """ 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()
[docs] def Update(self, value, time=0): """ Update the gauge by adding `value` to it over `time` milliseconds. The `time` parameter **must** be a multiple of 50 milliseconds. :param `value`: The value to be added to the gauge; :param `time`: The length of time in milliseconds that it will take to move the gauge. """ 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("ERROR:\n Gauge value must be between 0 and its 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)
[docs] 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)