Graphs/statistics using wxPython

J

Jan Danielsson

Hello all,

I wanted to plot some statistics, so I wrote a simple wxPython class
to do it. Then I realized that I would like to draw bar graphs, so I
added that too.

Since I'm a complete Python newbie, I haven't done much of it the
"Python way", I suspect. So, I'm wondering if someone would like to show
me some of the tricks I should have used.

You can find the code at:
http://user.it.uu.se/~jada3673/drawtest.py

It's not finished[*], but it is usable. Oh, you can do whatever you
want with it. Since I had never touched wxPython before working with
this, I used some doodle application as a starting point.

Btw... Has anyone already written statistics classes for wxPython? If
so, where can I find them? If nothing similar exists, I'll probably add
support for loading data from a file, and a few other features. Feel
free to make requests or enhancements.

[*] There are some size/offset bugs which I am aware of, but they are
easily fixed. I just haven't got around to it yet.
 
R

Robert Kern

Jan said:
Hello all,

I wanted to plot some statistics, so I wrote a simple wxPython class
to do it. Then I realized that I would like to draw bar graphs, so I
added that too.

Since I'm a complete Python newbie, I haven't done much of it the
"Python way", I suspect. So, I'm wondering if someone would like to show
me some of the tricks I should have used.

Trick #1:

import matplotlib

;-)

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
J

Jan Danielsson

Robert said:
Trick #1:

import matplotlib

Oh. :)

That's a pretty neat trick -- now can you make my embarrassment go away?

I did do a quick search to see if anyone had done anything similar;
but I guess I wasn't using the right keywords.

You may point and laugh.

Damn, that's a neat toolkit! Thanks for pointing me to it!
 
R

Robert Kern

Jan said:
Robert Kern wrote:

Oh. :)

That's a pretty neat trick -- now can you make my embarrassment go away?

I did do a quick search to see if anyone had done anything similar;
but I guess I wasn't using the right keywords.


You may point and laugh.

Damn, that's a neat toolkit! Thanks for pointing me to it!

It's okay. Just about every Pythonista in the sciences has, at one time
or another, started a plotting library. It's a rite of passage. Welcome
to the club. :)

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
J

Jan Danielsson

Robert Kern wrote:
[---]
It's okay. Just about every Pythonista in the sciences has, at one time
or another, started a plotting library. It's a rite of passage. Welcome
to the club. :)

Question: I need to install SciPy in order to use matplotlib, but on
the download page I see that there are versions for Python 2.2.x and
2.3.x. I use 2.4.1 -- will bad things happen if I try to use the build
for 2.3.x?
 
R

Robert Kern

Jan said:
Robert Kern wrote:
[---]
It's okay. Just about every Pythonista in the sciences has, at one time
or another, started a plotting library. It's a rite of passage. Welcome
to the club. :)

Question: I need to install SciPy in order to use matplotlib,

No you don't.
but on
the download page I see that there are versions for Python 2.2.x and
2.3.x. I use 2.4.1 -- will bad things happen if I try to use the build
for 2.3.x?

Yes. Scipy has many extension modules; such modules built for 2.3.x
won't work for 2.4.x, etc.

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
J

Jan Danielsson

Robert said:
[---]
It's okay. Just about every Pythonista in the sciences has, at one time
or another, started a plotting library. It's a rite of passage. Welcome
to the club. :)


Question: I need to install SciPy in order to use matplotlib,

No you don't.

Ah.. I misread. I read
http://matplotlib.sourceforge.net/installing.html, the section which
describes what "enthought python" contains, and thought it meant "this
is relevant to matplot lib".
Yes. Scipy has many extension modules; such modules built for 2.3.x
won't work for 2.4.x, etc.

So, for future reference: I should *never* mix x and y versions in
verion: x.y.z. I've wondered why there are versions of libraries for
different versions of Python..
 
R

Robert Kern

Jan said:
So, for future reference: I should *never* mix x and y versions in
verion: x.y.z. I've wondered why there are versions of libraries for
different versions of Python..

For packages with extension modules at least. Python maintains binary
compatibility between micro-releases (i.e. different z).

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
W

Will McGugan

Jan said:
Hello all,

I wanted to plot some statistics, so I wrote a simple wxPython class
to do it. Then I realized that I would like to draw bar graphs, so I
added that too.

Since I'm a complete Python newbie, I haven't done much of it the
"Python way", I suspect. So, I'm wondering if someone would like to show
me some of the tricks I should have used.

You can find the code at:
http://user.it.uu.se/~jada3673/drawtest.py

It's not finished[*], but it is usable. Oh, you can do whatever you
want with it. Since I had never touched wxPython before working with
this, I used some doodle application as a starting point.

Btw... Has anyone already written statistics classes for wxPython? If
so, where can I find them? If nothing similar exists, I'll probably add
support for loading data from a file, and a few other features. Feel
free to make requests or enhancements.

[*] There are some size/offset bugs which I am aware of, but they are
easily fixed. I just haven't got around to it yet.

If you ever want to add an extra dimension, then using OpenGL with
wxWindows is a breeze. See attached file for a 3d pie char..

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.)
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 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)

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()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top