2009年2月12日 星期四

Embedded Python + wxPython + OpenGL

先看一下wxPython官網關於opengl的教學
try:
import wx
from wx import glcanvas
except ImportError:
raise ImportError, "Required dependency wx.glcanvas not present"

try:
from OpenGL.GL import *
except ImportError:
raise ImportError, "Required dependency OpenGL not present"

class GLFrame(wx.Frame):
"""A simple class for using OpenGL with wxPython."""

def __init__(self, parent, id, title, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name='frame'):
#
# Forcing a specific style on the window.
# Should this include styles passed?
style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE

super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)

self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit

#
# Create the canvas
self.canvas = glcanvas.GLCanvas(self, attribList=attribList)

#
# Set the event handlers.
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)

#
# Canvas Proxy Methods

def GetGLExtents(self):
"""Get the extents of the OpenGL canvas."""
return self.canvas.GetClientSize()

def SwapBuffers(self):
"""Swap the OpenGL buffers."""
self.canvas.SwapBuffers()

#
# wxPython Window Handlers

def processEraseBackgroundEvent(self, event):
"""Process the erase background event."""
pass # Do nothing, to avoid flashing on MSWin

def processSizeEvent(self, event):
"""Process the resize event."""
if self.canvas.GetContext():
# Make sure the frame is shown before calling SetCurrent.
self.Show()
self.canvas.SetCurrent()

size = self.GetGLExtents()
self.OnReshape(size.width, size.height)
self.canvas.Refresh(False)
event.Skip()

def processPaintEvent(self, event):
"""Process the drawing event."""
self.canvas.SetCurrent()

# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True

self.OnDraw()
event.Skip()

#
# GLFrame OpenGL Event Handlers

def OnInitGL(self):
"""Initialize OpenGL for use in the window."""
glClearColor(1, 1, 1, 1)

def OnReshape(self, width, height):
"""Reshape the OpenGL viewport based on the dimensions of the window."""
glViewport(0, 0, width, height)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

def OnDraw(self, *args, **kwargs):
"Draw the window."
glClear(GL_COLOR_BUFFER_BIT)

# Drawing an example triangle in the middle of the screen
glBegin(GL_TRIANGLES)
glColor(0, 0, 0)
glVertex(-.25, -.25)
glVertex(.25, -.25)
glVertex(0, .25)
glEnd()

self.SwapBuffers()

app = wx.PySimpleApp()
frame = GLFrame(None, -1, 'GL Window')
frame.Show()

app.MainLoop()
app.Destroy()
但是不能執行,這是因為mac中沒有pyOpenGL。而我們也不想安裝pyOpenGL,所以先把opengl的部份換掉
try:
import wx
import mymodule
from wx import glcanvas
except ImportError:
raise ImportError, "Required dependency wx.glcanvas not present"

class GLFrame(wx.Frame):
"""A simple class for using OpenGL with wxPython."""

def __init__(self, parent, id, title, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name='frame'):
#
# Forcing a specific style on the window.
# Should this include styles passed?
style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE

super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)

self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit

#
# Create the canvas
self.canvas = glcanvas.GLCanvas(self, attribList=attribList)

#
# Set the event handlers.
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)

#
# Canvas Proxy Methods

def GetGLExtents(self):
"""Get the extents of the OpenGL canvas."""
return self.canvas.GetClientSize()

def SwapBuffers(self):
"""Swap the OpenGL buffers."""
self.canvas.SwapBuffers()

#
# wxPython Window Handlers

def processEraseBackgroundEvent(self, event):
"""Process the erase background event."""
pass # Do nothing, to avoid flashing on MSWin

def processSizeEvent(self, event):
"""Process the resize event."""
if self.canvas.GetContext():
# Make sure the frame is shown before calling SetCurrent.
self.Show()
self.canvas.SetCurrent()

size = self.GetGLExtents()
self.OnReshape(size.width, size.height)
self.canvas.Refresh(False)
event.Skip()

def processPaintEvent(self, event):
"""Process the drawing event."""
self.canvas.SetCurrent()

# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True

self.OnDraw()
event.Skip()

#
# GLFrame OpenGL Event Handlers

def OnInitGL(self):
"""Initialize OpenGL for use in the window."""
mymodule.OnInitGL()

def OnReshape(self, width, height):
"""Reshape the OpenGL viewport based on the dimensions of the window."""
mymodule.OnReshape(width,height)

def OnDraw(self, *args, **kwargs):
"Draw the window."
glClear(GL_COLOR_BUFFER_BIT)

# Drawing an example triangle in the middle of the screen
mymodule.OnDraw()

self.SwapBuffers()

app = wx.PySimpleApp()
frame = GLFrame(None, -1, 'GL Window')
frame.Show()

app.MainLoop()
app.Destroy()
這時你因該注意到mymodule這個未知的Module吧。當然現在還不能執行。
我們必須在C的檔案中,定義mymodule(詳細說明請參閱thinker的為 Embedded Python 擴充 Submodules)。
PyObject *OnInitGL(PyObject *self) {
glClearColor(1, 1, 1, 1);
return PyString_FromString("GL Init");
}
PyObject *OnReshape(PyObject *self, PyObject *args) {
int a, b;

if(!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
glViewport(0, 0, a, b);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return PyString_FromString("Reshape");
}
PyObject *OnDraw(PyObject *self) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 0, 0);
glVertex2f(-0.25, -0.25);
glVertex2f(0.25, -0.25);
glVertex2f(0, 0.25);
glEnd();
return PyString_FromString("Draw");
}

PyMethodDef methods[] = {
{"OnInitGL", (PyCFunction)OnInitGL, METH_NOARGS, "OnInitGL document"},
{"OnReshape", (PyCFunction)OnReshape, METH_VARARGS, "OnReshape document"},
{"OnDraw", (PyCFunction)OnDraw, METH_NOARGS, "OnDraw document"},
{ NULL, NULL, 0, NULL}
};
在main()中加入
Py_InitModule("mymodule", methods);
整個主程式
//將SCRIPT_PATH設定成py檔和執行檔的相對路徑
#define SCRIPT_PATH "/"
//設定mymodule
PyObject *OnInitGL(PyObject *self) {
glClearColor(1, 1, 1, 1);
return PyString_FromString("GL Init");
}
PyObject *OnReshape(PyObject *self, PyObject *args) {
int a, b;

if(!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
glViewport(0, 0, a, b);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return PyString_FromString("Reshape");
}
PyObject *OnDraw(PyObject *self) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 0, 0);
glVertex2f(-0.25, -0.25);
glVertex2f(0.25, -0.25);
glVertex2f(0, 0.25);
glEnd();
return PyString_FromString("Draw");
}

PyMethodDef methods[] = {
{"OnInitGL", (PyCFunction)OnInitGL, METH_NOARGS, "OnInitGL document"},
{"OnReshape", (PyCFunction)OnReshape, METH_VARARGS, "OnReshape document"},
{"OnDraw", (PyCFunction)OnDraw, METH_NOARGS, "OnDraw document"},
{ NULL, NULL, 0, NULL}
};
int main(int argc, char* argv[]) {
char pch[1024]={0};
char scriptpath[1024]={0};
int i=strrchr(argv[0],'/')-argv[0];
strncpy (pch,argv[0],i);
strcpy(scriptpath,SCRIPT_PATH);
strcat(pch,scriptpath);
strcat(pch,"/gui.py");
FILE *fp;

Py_Initialize();
//啓動mymodule
Py_InitModule("mymodule", methods);
fp = fopen(pch, "r");
PyRun_SimpleFile(fp, "gui.py");
fclose(fp);

Py_Finalize();
}
編譯時記得連結 -lpython2.5 -framework OpenGL

1 則留言:

Cătălin George Feștilă 提到...

Good tutorial about python. I used PyQt5 with OpenGL and working well.
Best regards.