Thierry Lam wrote:
Let's say I have the following data:

500 objects:
-100 are red
-300 are blue
-the rest are green

Is there some python package which can represent the above information
in a pie chart?


I wrote a wxPython control to render pretty 3D pie charts (see attached piechartwindow.py). It can also be used to pre-generate images such as this..

http://www.foodfileonline.com/static/piecharts/pie01009.jpg

Code is public domain.


Will McGugan
--
http://www.willmcgugan.com
"".join({'*':'@','^':'.'}.get(c,0) or chr(97+(ord(c)-84)%26) for c in "jvyy*jvyyzpthtna^pbz")
# Pie Chart Window
# 2005 Will McGugan ([EMAIL PROTECTED])


import wx

from wx import glcanvas
from OpenGL.GL import *


import math
import time

sin = math.sin
cos = math.cos


PI = math.pi
def DegToRad(deg):
    return deg*PI/180.

class PieSegment(object):

    def __init__(self, radius, angle1, angle2, colour, depth):

        self.display_list = glGenLists(1)

        glNewList(self.display_list, GL_COMPILE)

        self.angle1 = angle1
        self.angle2 = angle2
        self.explode_angle = DegToRad( (angle2 + angle1) / 2.)
        DrawPieSegment(radius, angle1, angle2, colour, depth)

        glEndList()

    def __del__(self):
        glDeleteLists(self.display_list, 1)

    def Draw(self, angle1, angle2, explode):

        glPushMatrix()

        glRotate(angle1, 1.0, 0.0, 0.0);
        glRotate(angle2, 0.0, 0.0, 1.0);

        glTranslatef( sin(self.explode_angle)*explode, 
cos(self.explode_angle)*explode, 0. )

        glCallList(self.display_list)
        glPopMatrix()




def DrawPieSegment(radius, angle1, angle2, colour, depth):

    angle1 = DegToRad(angle1)
    angle2 = DegToRad(angle2)

    # Number of divisions in 360 degrees
    RES = 100.

    fan2D = []

    step_degree = ((2*PI)/RES)
    step_count = int( (angle2 - angle1) / step_degree ) + 1

    step_degree = ( angle2 - angle1 ) / float(step_count)


    # Precalculate the trig
    sina1 = sin(angle1)
    cosa1 = cos(angle1)

    sina2 = sin(angle2)
    cosa2 = cos(angle2)

    # Calculate the points in an arc
    for p in xrange(step_count+1):

        a = angle1 + p * step_degree

        x = sin(a)
        y = cos(a)

        fan2D.append( (x,y) )

    z1 = +depth/2.
    z2 = -depth/2.

    glMaterial(GL_FRONT, GL_DIFFUSE, [colour[0], colour[1], colour[2], 1.0])

    # Create a fan for the top and bottom of the pie segment
    glNormal(0, 0, +1.)
    glBegin(GL_TRIANGLE_FAN)
    glVertex(0, 0, z1)
    for x, y in fan2D:
        glVertex(x*radius, y*radius, z1)
    glEnd()

    glNormal(0, 0, -1.)
    glBegin(GL_TRIANGLE_FAN)
    glVertex(0, 0, z2)
    for x, y in fan2D:
        glVertex(x*radius, y*radius, z2)
    glEnd()

    # A strip for the curved edge
    glBegin(GL_TRIANGLE_STRIP)
    for x, y in fan2D:
        glNormal(x, y, 0.)
        xr = x * radius
        yr = y * radius
        glVertex(xr, yr, z1)
        glVertex(xr, yr, z2)
    glEnd()


    n1 = -cosa1, sina1, 0.
    n2 = cosa2, -sina2, 0.

    x1, y1 = sina1 * radius, cosa1 * radius
    x2, y2 = sina2 * radius, cosa2 * radius


    # Two quads for the radius edges

    glNormal(n1)
    glBegin(GL_QUADS)

    glVertex(0, 0, z1)
    glVertex(x1, y1, z1)
    glVertex(x1, y1, z2)
    glVertex(0, 0, z2)

    glEnd()

    glNormal(n2)
    glBegin(GL_QUADS)

    glVertex(0, 0, z1)
    glVertex(x2, y2, z1)
    glVertex(x2, y2, z2)
    glVertex(0, 0, z2)

    glEnd()



class PieChartWindow(glcanvas.GLCanvas):
    def __init__(self, parent):

        attriblist = None
        glcanvas.GLCanvas.__init__(self, parent, -1, wx.DefaultPosition, 
wx.DefaultSize, 0, "GLCanvas", attriblist, wx.NullPalette)

        self.init = False

        self.segments = []


        self.animating = False
        self.angle = 0.
        self.explode = 0.

        self.downx = 0
        self.rot = 0.

        self.captured_mouse = False

        self.timer = wx.Timer(self)

        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def Animate(self):
        self.animating = True
        self.start_animate = time.clock()
        self.timer.Start(1000/20)


    def OnTimer(self, event):
        if self.IsShown():
            self.Refresh()
        if not self.animating:
            self.timer.Stop();


    def SetSegments(self, segments, angle=0., radius=150., depth=50.):

        self.segments = []

        First = True

        if len(segments):
            angle = -(segments[0][0]/2. *360./100.)

        for seg in segments:

            seg_percent, colour = seg

            if seg_percent == 0:
                continue

            if seg_percent < .2:
                angle = angle + (seg_percent*360./100.)
                continue

            if First:
                angle = -(seg_percent/2. * 360./100.)
                First = False

            next_angle = angle + (seg_percent*360./100.)
            if colour is not None:
                self.segments.append(PieSegment(radius, angle, next_angle, 
colour, depth))
            angle=next_angle



    def PerspectiveProjection(self):

        w, h = 300., 300.
        a = 30.

        tana = math.tan(DegToRad(a/2.))

        glMatrixMode(GL_PROJECTION);

        v = (w/2) / tana

        near = v / 10.

        vw = tana * near
        vh = tana * near

        glFrustum(-vw, vw, -vh, vh, near, v*3.0);

        glMatrixMode(GL_MODELVIEW);
        glTranslatef(0.0, 0.0, -v*1.2);

        glLight(GL_LIGHT0, GL_POSITION, [w/2, h/2, v*1.2, 0.0]);

    def InitGL(self):

        self.PerspectiveProjection()

        glMaterial(GL_FRONT, GL_AMBIENT, [0.3, 0.3, 0.3, 1.0])
        glMaterial(GL_FRONT, GL_DIFFUSE, [0.9, 0.9, 0.9, 1.0])
        glMaterial(GL_FRONT, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
        glMaterial(GL_FRONT, GL_SHININESS, 70.0)
        glLight(GL_LIGHT0, GL_AMBIENT, [0.25, 0.25, 0.25, 1.0])
        glLight(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1.0])
        glLight(GL_LIGHT0, GL_SPECULAR, [.1, .1, .1, 1.0])

        glLightModel(GL_LIGHT_MODEL_AMBIENT, [0.2, 0.2, 0.2, 1.0])
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glDepthFunc(GL_LESS)
        glEnable(GL_DEPTH_TEST)

        glClearColor(1,1,1,1)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW);


    def OnLeftButtonDown(self, event):
        self.downx = event.GetPosition()[0]
        self.captured_mouse = True
        self.CaptureMouse()

    def OnLeftButtonUp(self, event):
        if self.captured_mouse and self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            mx, my = event.GetPosition()
            rot_delta = self.downx - mx
            self.rot+= rot_delta
            self.downx = mx
            self.Refresh()

    def OnEraseBackground(self, event):
        pass


    def OnSize(self, event):
        size = self.GetClientSize()
        if self.GetContext():
            self.SetCurrent()
            glViewport(0, 0, size.width, size.height)
            self.Refresh()
        event.Skip()

    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        self.SetCurrent()
        if not self.init:
            self.InitGL()
            self.init = True

        self.OnDraw()

    def DoPaint(self):
        self.SetCurrent()
        if not self.init:
            self.InitGL()
            self.init = True

        self.OnDraw()

    def OnDraw(self):

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if self.animating:

            t = time.clock() - self.start_animate
            if t < 1.:
                self.angle = 0.
                self.explode = 0.
            elif t < 2.:
                ts = t - 1.
                self.angle = -50. * ts
            elif t < 3.:
                ts = t - 2.
                self.angle = -50.
                self.explode = 15. * ts
            else:
                self.animating = False
                self.angle = -50.
                self.explode = 15.

        for segment in self.segments:

            segment.Draw(self.angle, self.rot, self.explode)

        glFlush()
        self.SwapBuffers()

if __name__ == "__main__":

    class TestFrame(wx.Frame):
        def __init__(self):
            super(TestFrame, self).__init__(None, -1, "Pie Chart Test", 
style=wx.DEFAULT_FRAME_STYLE)
            self.SetClientSize(wx.Size(300,300))

            pie_chart = PieChartWindow(self)

            # List of tuples ( <Percentage of Pie>, ( <Red>, <Green>, <Blue> ) )
            seg = [ ( 20, (1., 0.1, 0.1) ),
                    ( 25, (0.05, .9, .1) ),
                    ( 10, (.2, 0.1, 1.) ),
                    ( 30, (.8, .9, .1) ) ]

            pie_chart.SetSegments( seg )
            pie_chart.Animate()



    app = wx.App()
    frame = TestFrame()
    frame.Show(True)
    app.MainLoop()

-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to