# --------------------------------------------------------------------------- #
# PIECTRL Control wxPython IMPLEMENTATION
# Python Code By:
#
# Andrea Gavana, @ 31 Oct 2005
# Latest Revision: 17 Aug 2011, 15.00 GMT
#
#
# TODO List/Caveats
#
# 1. Maybe Integrate The Very Nice PyOpenGL Implementation Of A PieChart Coded
#    By Will McGugan?
#
# 2. Not Tested On Other Platforms, Only On Windows 2000/XP, With Python 2.4.1
#    And wxPython 2.6.1.0
#
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
# Write To Me At:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, Obviously, To The wxPython Mailing List!!!
#
#
# End Of Comments
# --------------------------------------------------------------------------- #


"""
:class:`PieCtrl` and :class:`ProgressPie` are simple classes that reproduce the behavior of a pie
chart.


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

:class:`PieCtrl` and :class:`ProgressPie` are simple classes that reproduce the behavior of a pie
chart. They use only pure wxPython classes/methods, without external dependencies.
:class:`PieCtrl` is somewhat a "static" control, that you may create in order to display
a simple pie chart on a :class:`Panel` or similar. :class:`ProgressPie` tries to emulate the
behavior of :class:`ProgressDialog`, but using a pie chart instead of a gauge.


Usage
=====

Usage example::

    import wx    
    import wx.lib.agw.piectrl as PC

    class MyFrame(wx.Frame):

        def __init__(self, parent):
        
            wx.Frame.__init__(self, parent, -1, "PieCtrl Demo")

            panel = wx.Panel(self)
    
            # create a simple PieCtrl with 3 sectors
            mypie = PC.PieCtrl(panel, -1, wx.DefaultPosition, wx.Size(180,270))

            part = PC.PiePart()

            part.SetLabel("Label 1")
            part.SetValue(300)
            part.SetColour(wx.Colour(200, 50, 50))
            mypie._series.append(part)

            part = PC.PiePart()

            part.SetLabel("Label 2")
            part.SetValue(200)
            part.SetColour(wx.Colour(50, 200, 50))
            mypie._series.append(part)

            part = PC.PiePart()

            part.SetLabel("helloworld label 3")
            part.SetValue(50)
            part.SetColour(wx.Colour(50, 50, 200))
            mypie._series.append(part)

            # create a ProgressPie
            progress_pie = PC.ProgressPie(panel, 100, 50, -1, wx.DefaultPosition,
                                          wx.Size(180, 200), wx.SIMPLE_BORDER)

            progress_pie.SetBackColour(wx.Colour(150, 200, 255))
            progress_pie.SetFilledcolour(wx.Colour(255, 0, 0))
            progress_pie.SetUnfilledColour(wx.WHITE)
            progress_pie.SetHeight(20)

            main_sizer = wx.BoxSizer(wx.HORIZONTAL)

            main_sizer.Add(mypie, 1, wx.EXPAND | wx.ALL, 5)
            main_sizer.Add(progress_pie, 1, wx.EXPAND | wx.ALL, 5)

            panel.SetSizer(main_sizer)
            main_sizer.Layout()


    # our normal wxApp-derived class, as usual

    app = wx.App(0)

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

    app.MainLoop()



Methods and Settings
====================

With :class:`PieCtrl` you can:

- Create a :class:`PieCtrl` with different sectors;
- Set the sector values, colours and labels;
- Assign a legend to the :class:`PieCtrl`;
- Use an image as the :class:`PieCtrl` background;
- Change the vertical rotation (perspective) of the :class:`PieCtrl`;
- Show/hide the segment edges.


Window Styles
=============

`No particular window styles are available for this class.`


Events Processing
=================

`No custom events are available for this class.`


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

:class:`PieCtrl` is distributed under the wxPython license.

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

Version 0.2

"""


#----------------------------------------------------------------------
# Beginning Of PIECTRL wxPython Code
#----------------------------------------------------------------------


import wx

from math import pi, sin, cos

#----------------------------------------------------------------------
# Class PieCtrlLegend
# This Class Handles The Legend For The Classic PieCtrl.
#----------------------------------------------------------------------

class PieCtrlLegend(wx.Window):
    """
    This class displays a legend window for the classic :class:`PieCtrl`.
    """

    def __init__(self, parent, title, id=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0):
        """
        Default class constructor.

        :param `parent`: the :class:`PieCtrlLegend` parent;
        :param `title`: the legend title;
        :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 window style (unused).
        """

        wx.Window.__init__(self, parent, id, pos, size, style)

        self._title = title
        self._istransparent = False
        self._horborder = 5
        self._verborder = 5
        self._titlecolour = wx.Colour(0, 0, 127)
        self._labelcolour = wx.BLACK
        self._backcolour = wx.Colour(255, 255, 0)
        self._backgroundDC = wx.MemoryDC()
        self._parent = parent

        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)


    def SetTransparent(self, value=False):
        """
        Toggles the legend transparency (visibility).

        :param `value`: ``True`` to set the legend as transparent, ``False`` otherwise.
        """

        self._istransparent = value
        self.Refresh()


    def RecreateBackground(self, parentdc):
        """
        Recreates the legend background.

        :param `parentdc`: an instance of :class:`DC`.
        """

        w, h = self.GetSize()
        self._background = wx.EmptyBitmap(w, h)
        self._backgroundDC.SelectObject(self._background)

        if self.IsTransparent():

            self._backgroundDC.Blit(0, 0, w, h, parentdc, self.GetPosition().x,
                                    self.GetPosition().y)

        else:

            self._backgroundDC.SetBackground(wx.Brush(self._backcolour))
            self._backgroundDC.Clear()

        self.Refresh()


    def SetHorizontalBorder(self, value):
        """
        Sets the legend's horizontal border.

        :param `value`: the horizontal border thickness, in pixels.
        """

        self._horborder = value
        self.Refresh()


    def GetHorizontalBorder(self):
        """ Returns the legend's horizontal border, in pixels. """

        return self._horborder


    def SetVerticalBorder(self, value):
        """
        Sets the legend's vertical border.

        :param `value`: the horizontal border thickness, in pixels.
        """

        self._verborder = value
        self.Refresh()


    def GetVerticalBorder(self):
        """ Returns the legend's vertical border, in pixels. """

        return self._verborder


    def SetLabelColour(self, colour):
        """
        Sets the legend label colour.

        :param `colour`: a valid :class:`Colour` object.
        """

        self._labelcolour = colour
        self.Refresh()


    def GetLabelColour(self):
        """ Returns the legend label colour. """

        return self._labelcolour


    def SetLabelFont(self, font):
        """
        Sets the legend label font.

        :param `font`: a valid :class:`Font` object.
        """

        self._labelfont = font
        self.Refresh()


    def GetLabelFont(self):
        """ Returns the legend label font. """

        return self._labelfont


    def SetBackColour(self, colour):
        """
        Sets the legend background colour.

        :param `colour`: a valid :class:`Colour` object.
        """

        self._backcolour = colour
        self.Refresh()


    def GetBackColour(self):
        """ Returns the legend background colour. """

        return self._backcolour


    def IsTransparent(self):
        """ Returns whether the legend background is transparent or not. """

        return self._istransparent


    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for :class:`PieCtrlLegend`.

        :param `event`: a :class:`PaintEvent` event to be processed.
        """

        pdc = wx.PaintDC(self)

        w, h = self.GetSize()
        bmp = wx.EmptyBitmap(w, h)
        mdc = wx.MemoryDC()
        mdc.SelectObject(bmp)

        if self.IsTransparent():

            parentdc = wx.ClientDC(self.GetParent())
            mdc.Blit(0, 0, w, h, self._backgroundDC, 0, 0)

        else:

            mdc.SetBackground(wx.Brush(self._backcolour))
            mdc.Clear()

        dy = self._verborder
        mdc.SetFont(self._labelfont)
        mdc.SetTextForeground(self._labelcolour)
        maxwidth = 0

        for ii in xrange(len(self._parent._series)):

            tw, th = mdc.GetTextExtent(self._parent._series[ii].GetLabel())
            mdc.SetBrush(wx.Brush(self._parent._series[ii].GetColour()))
            mdc.DrawCircle(self._horborder+5, dy+th/2, 5)
            mdc.DrawText(self._parent._series[ii].GetLabel(), self._horborder+15, dy)
            dy = dy + th + 3
            maxwidth = max(maxwidth, int(2*self._horborder+tw+15))

        dy = dy + self._verborder
        if w != maxwidth or h != dy:
            self.SetSize((maxwidth, dy))

        pdc.Blit(0, 0, w, h, mdc, 0, 0)


#----------------------------------------------------------------------
# Class PiePart
# This Class Handles The Legend Segments Properties, Such As Value,
# Colour And Label.
#----------------------------------------------------------------------

class PiePart(object):
    """
    This class handles the legend segments properties, such as value,
    colour and label.
    """

    def __init__(self, value=0, colour=wx.BLACK, label=""):
        """
        Default class constructor.

        :param `value`: the pie part value;
        :param `colour`: the pie part colour;
        :param `label`: the pie part text label.
        """

        self._value = value
        self._colour = colour
        self._label = label


    def SetValue(self, value):
        """
        Sets the segment absolute value.

        :param `value`: a floating point number representing the :class:`PiePart` value.
        """

        self._value = value


    def GetValue(self):
        """ Returns the segment absolute value. """

        return self._value


    def SetColour(self, colour):
        """
        Sets the segment colour.

        :param `colour`: a valid :class:`Colour` object.
        """

        self._colour = colour


    def GetColour(self):
        """ Returns the segment colour. """

        return self._colour


    def SetLabel(self, label):
        """
        Sets the segment label.

        :param `label`: the pie part text label.
        """

        self._label = label


    def GetLabel(self):
        """ Returns the segment label. """

        return self._label


#----------------------------------------------------------------------
# Class PieCtrl
# This Is The Main PieCtrl Implementation, Used Also By ProgressPie.
#----------------------------------------------------------------------

class PieCtrl(wx.Window):
    """
    :class:`PieCtrl` is somewhat a "static" control, that you may create in order to display
    a simple pie chart on a :class:`Panel` or similar.
    """
    
    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0, name="PieCtrl"):
        """
        Default class constructor.

        :param `parent`: the :class:`PieCtrl` parent. 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 window style (unused);
        :param `name`: the window name.
        """

        wx.Window.__init__(self, parent, id, pos, size, style, name)

        self._angle = pi/12
        self._rotationangle = 0
        self._height = 10
        self._background = wx.NullBitmap
        self._canvasbitmap = wx.EmptyBitmap(1, 1)
        self._canvasDC = wx.MemoryDC()
        self._backcolour = wx.WHITE
        self._showedges = True
        self._series = []

        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

        self.RecreateCanvas()
        self._legend = PieCtrlLegend(self, "PieCtrl", -1, wx.Point(10,10), wx.Size(100,75))


    def SetBackground(self, bmp):
        """
        Sets the :class:`PieCtrl` background image.

        :param `bmp`: a valid :class:`Bitmap` object.
        """

        self._background = bmp
        self.Refresh()


    def GetBackground(self):
        """ Returns the :class:`PieCtrl` background image. """

        return self._background


    def OnSize(self, event):
        """
        Handles the ``wx.EVT_SIZE`` event for :class:`PieCtrl`.

        :param `event`: a :class:`SizeEvent` event to be processed.
        """

        self.RecreateCanvas()
        self.Refresh()
        event.Skip()


    def RecreateCanvas(self):
        """ Recreates the :class:`PieCtrl` container (canvas). """

        self._canvasbitmap = wx.EmptyBitmap(self.GetSize().GetWidth(),
                                            self.GetSize().GetHeight())
        self._canvasDC.SelectObject(self._canvasbitmap)


    def GetPartAngles(self):
        """ Returns the angles associated to all segments. """

        angles = []
        total = 0.0

        for ii in xrange(len(self._series)):
            total = total + self._series[ii].GetValue()

        current = 0.0
        angles.append(current)

        for ii in xrange(len(self._series)):

            current = current + self._series[ii].GetValue()
            angles.append(360.0*current/total)

        return angles


    def SetAngle(self, angle):
        """
        Sets the orientation angle for :class:`PieCtrl`.

        :param `angle`: the orientation angle for :class:`PieCtrl`, in radians.
        """

        if angle < 0:
            angle = 0
        if angle > pi/2:
            angle = pi/2

        self._angle = angle
        self.Refresh()


    def GetAngle(self):
        """ Returns the orientation angle for :class:`PieCtrl`, in radians. """

        return self._angle


    def SetRotationAngle(self, angle):
        """
        Sets the angle at which the first sector starts.

        :param `angle`: the first sector angle, in radians.
        """

        if angle < 0:
            angle = 0
        if angle > 2*pi:
            angle = 2*pi

        self._rotationangle = angle
        self.Refresh()


    def GetRotationAngle(self):
        """ Returns the angle at which the first sector starts, in radians. """

        return self._rotationangle


    def SetShowEdges(self, value=True):
        """
        Sets whether the :class:`PieCtrl` edges are visible or not.

        :param `value`: ``True`` to show the edges, ``False`` to hide them.
        """

        self._showedges = value
        self.Refresh()


    def GetShowEdges(self):
        """ Returns whether the :class:`PieCtrl` edges are visible or not. """

        return self._showedges


    def SetBackColour(self, colour):
        """
        Sets the :class:`PieCtrl` background colour.

        :param `colour`: a valid :class:`Colour` object.
        """

        self._backcolour = colour
        self.Refresh()


    def GetBackColour(self):
        """ Returns the :class:`PieCtrl` background colour. """

        return self._backcolour


    def SetHeight(self, value):
        """
        Sets the height (in pixels) of the :class:`PieCtrl`.

        :param `value`: the new height of the widget, in pixels.
        """

        self._height = value


    def GetHeight(self):
        """ Returns the height (in pixels) of the :class:`PieCtrl`. """

        return self._height


    def GetLegend(self):
        """ Returns the :class:`PieCtrl` legend. """

        return self._legend


    def DrawParts(self, dc, cx, cy, w, h):
        """
        Draws the :class:`PieCtrl` external edges.

        :param `dc`: an instance of :class:`DC`;
        :param `cx`: the part `x` coordinate;
        :param `cy`: the part `y` coordinate;
        :param `w`: the control's width;
        :param `h`: the control's height.
        """

        angles = self.GetPartAngles()
        oldpen = dc.GetPen()

        if self._showedges:
            dc.SetPen(wx.BLACK_PEN)

        for ii in xrange(len(angles)):

            if ii > 0:

                if not self._showedges:
                    dc.SetPen(wx.Pen(self._series[ii-1].GetColour()))

                dc.SetBrush(wx.Brush(self._series[ii-1].GetColour()))

                if angles[ii-1] != angles[ii]:
                    dc.DrawEllipticArc(0, int((1-sin(self._angle))*(h/2)+cy), w,
                                       int(h*sin(self._angle)),
                                       angles[ii-1]+self._rotationangle/pi*180,
                                       angles[ii]+self._rotationangle/pi*180)


        if len(self._series) == 1:

            dc.SetBrush(wx.Brush(self._series[0].GetColour()))
            dc.DrawEllipticArc(0, int((1-sin(self._angle))*(h/2)+cy), w,
                               int(h*sin(self._angle)), 0, 360)

        dc.SetPen(oldpen)


    def Draw(self, pdc):
        """
        Draws all the sectors of :class:`PieCtrl`.

        :param `dc`: an instance of :class:`DC`.        
        """

        w, h = self.GetSize()

        self._canvasDC.BeginDrawing()
        self._canvasDC.SetBackground(wx.WHITE_BRUSH)
        self._canvasDC.Clear()

        if self._background != wx.NullBitmap:

            for ii in xrange(0, w, self._background.GetWidth()):

                for jj in xrange(0, h, self._background.GetHeight()):

                    self._canvasDC.DrawBitmap(self._background, ii, jj)

        else:

            self._canvasDC.SetBackground(wx.Brush(self._backcolour))
            self._canvasDC.Clear()

        if len(self._series) > 0:

            if self._angle <= pi/2:
                self.DrawParts(self._canvasDC, 0, int(self._height*cos(self._angle)), w, h)
            else:
                self.DrawParts(self._canvasDC, 0, 0, w, h)

            points = [[0, 0]]*4
            triangle = [[0, 0]]*3
            self._canvasDC.SetPen(wx.Pen(wx.BLACK))
            angles = self.GetPartAngles()
            angleindex = 0
            self._canvasDC.SetBrush(wx.Brush(wx.Colour(self._series[angleindex].GetColour().Red(),
                                                       self._series[angleindex].GetColour().Green(),
                                                       self._series[angleindex].GetColour().Blue())))
            changeangle = False
            x = 0.0

            while x <= 2*pi:

                changeangle = False

                if angleindex < len(angles):

                    if x/pi*180.0 >= angles[angleindex+1]:

                        changeangle = True
                        x = angles[angleindex+1]*pi/180.0

                points[0] = points[1]
                px = int(w/2*(1+cos(x+self._rotationangle)))
                py = int(h/2-sin(self._angle)*h/2*sin(x+self._rotationangle)-1)
                points[1] = [px, py]
                triangle[0] = [w / 2, h / 2]
                triangle[1] = points[0]
                triangle[2] = points[1]

                if x > 0:

                    self._canvasDC.SetBrush(wx.Brush(self._series[angleindex].GetColour()))
                    oldPen = self._canvasDC.GetPen()
                    self._canvasDC.SetPen(wx.Pen(self._series[angleindex].GetColour()))
                    self._canvasDC.DrawPolygon([wx.Point(pts[0], pts[1]) for pts in triangle])
                    self._canvasDC.SetPen(oldPen)

                if changeangle:

                    angleindex = angleindex + 1

                x = x + 0.05

            x = 2*pi
            points[0] = points[1]
            px = int(w/2 * (1+cos(x+self._rotationangle)))
            py = int(h/2-sin(self._angle)*h/2*sin(x+self._rotationangle)-1)
            points[1] = [px, py]
            triangle[0] = [w / 2, h / 2]
            triangle[1] = points[0]
            triangle[2] = points[1]

            self._canvasDC.SetBrush(wx.Brush(self._series[angleindex].GetColour()))
            oldPen = self._canvasDC.GetPen()
            self._canvasDC.SetPen(wx.Pen(self._series[angleindex].GetColour()))
            self._canvasDC.DrawPolygon([wx.Point(pts[0], pts[1]) for pts in triangle])

            self._canvasDC.SetPen(oldPen)
            angleindex = 0

            x = 0.0

            while x <= 2*pi:

                changeangle = False
                if angleindex < len(angles):

                    if x/pi*180 >= angles[angleindex+1]:

                        changeangle = True
                        x = angles[angleindex+1]*pi/180

                points[0] = points[1]
                points[3] = points[2]
                px = int(w/2 * (1+cos(x+self._rotationangle)))
                py = int(h/2-sin(self._angle)*h/2*sin(x+self._rotationangle)-1)
                points[1] = [px, py]
                points[2] = [px, int(py+self._height*cos(self._angle))]

                if w > 0:

                    curColour = wx.Colour(self._series[angleindex].GetColour().Red()*(1.0-float(px)/w),
                                          self._series[angleindex].GetColour().Green()*(1.0-float(px)/w),
                                          self._series[angleindex].GetColour().Blue()*(1.0-float(px)/w))

                    if not self._showedges:
                        self._canvasDC.SetPen(wx.Pen(curColour))

                    self._canvasDC.SetBrush(wx.Brush(curColour))

                if sin(x+self._rotationangle) < 0 and sin(x-0.05+self._rotationangle) <= 0 and x > 0:
                    self._canvasDC.DrawPolygon([wx.Point(pts[0], pts[1]) for pts in points])

                if changeangle:

                    angleindex = angleindex + 1

                x = x + 0.05

            x = 2*pi
            points[0] = points[1]
            points[3] = points[2]
            px = int(w/2 * (1+cos(x+self._rotationangle)))
            py = int(h/2-sin(self._angle)*h/2*sin(x+self._rotationangle)-1)
            points[1] = [px, py]
            points[2] = [px, int(py+self._height*cos(self._angle))]

            if w > 0:

                curColour = wx.Colour(self._series[angleindex].GetColour().Red()*(1.0-float(px)/w),
                                          self._series[angleindex].GetColour().Green()*(1.0-float(px)/w),
                                          self._series[angleindex].GetColour().Blue()*(1.0-float(px)/w))

                if not self._showedges:
                    self._canvasDC.SetPen(wx.Pen(curColour))

                self._canvasDC.SetBrush(wx.Brush(curColour))

            if sin(x+self._rotationangle) < 0 and sin(x-0.05+self._rotationangle) <= 0:
                self._canvasDC.DrawPolygon([wx.Point(pts[0], pts[1]) for pts in points])

            if self._angle <= pi/2:
                self.DrawParts(self._canvasDC, 0, 0, w, h)
            else:
                self.DrawParts(self._canvasDC, 0, int(self._height*cos(self._angle)), w, h)

        self._canvasDC.EndDrawing()

        pdc.Blit(0, 0, w, h, self._canvasDC, 0, 0)
        self._legend.RecreateBackground(self._canvasDC)


    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for :class:`PieCtrl`.

        :param `event`: a :class:`PaintEvent` event to be processed.
        """

        pdc = wx.PaintDC(self)
        self.Draw(pdc)


#----------------------------------------------------------------------
# Class ProgressPie
# This Is The Main ProgressPie Implementation. Is Is A Subclassing Of
# PieCtrl, With 2 Sectors.
#----------------------------------------------------------------------

class ProgressPie(PieCtrl):
    """
    :class:`ProgressPie` tries to emulate the behavior of :class:`ProgressDialog`, but
    using a pie chart instead of a gauge.
    """
    
    def __init__(self, parent, maxvalue, value, id=wx.ID_ANY,
                 pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
        """
        Default class constructor.

        :param `parent`: the :class:`PieCtrl` parent. 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 window style (unused);
        :param `name`: the window name.
        """

        PieCtrl.__init__(self, parent, id, pos, size, style)

        self._maxvalue = maxvalue
        self._value = value
        self.GetLegend().Hide()

        self._filledcolour = wx.Colour(0, 0, 127)
        self._unfilledcolour = wx.WHITE
        part = PiePart()
        part.SetColour(self._filledcolour)
        a = min(float(value), maxvalue)
        part.SetValue(max(a, 0.0))
        self._series.append(part)
        part = PiePart()
        part.SetColour(self._unfilledcolour)
        part.SetValue(max(0.0, maxvalue-part.GetValue()))
        self._series.append(part)


    def SetValue(self, value):
        """
        Sets the :class:`ProgressPie` value.

        :param `value`: a floating point number representing the new value.
        """

        self._value = min(value, self._maxvalue)
        self._series[0].SetValue(max(self._value, 0.0))
        self._series[1].SetValue(max(self._maxvalue-self._value, 0.0))
        self.Refresh()


    def GetValue(self):
        """ Returns the :class:`ProgressPie` value. """

        return self._value


    def SetMaxValue(self, value):
        """
        Sets the :class:`ProgressPie` maximum value.

        :param `value`: a floating point number representing the maximum value.
        """

        self._maxvalue = value
        self._value = min(self._value, self._maxvalue)
        self._series[0].SetValue(max(self._value, 0.0))
        self._series[1].SetValue(max(self._maxvalue-self._value, 0.0))
        self.Refresh()


    def GetMaxValue(self):
        """ Returns the :class:`ProgressPie`  maximum value. """

        return self._maxvalue


    def SetFilledColour(self, colour):
        """
        Sets the colour that progressively fills the :class:`ProgressPie` .

        :param `colour`: a valid :class:`Colour` object.
        """

        self._filledcolour = colour
        self._series[0].SetColour(self._filledcolour)
        self.Refresh()


    def SetUnfilledColour(self, colour):
        """
        Sets the colour that is filled.

        :param `colour`: a valid :class:`Colour` object.
        """

        self._unfilledcolour= colour
        self._series[1].SetColour(self._unfilledcolour)
        self.Refresh()


    def GetFilledColour(self):
        """ Returns the colour that progressively fills the :class:`ProgressPie`. """

        return self._filledcolour


    def GetUnfilledColour(self):
        """ Returns the colour that is filled. """

        return self._unfilledcolour

