The Python Tk-OpenGL Module was written by (in alphabetical order)
The reason why the openGL module was not released was the rather complicated installation of the TIGER Tk-Widget, although everything worked nicely with glut. The Matrix module is still not official yet, so we introduced a Compiler switch to allow people using the OpenGL module without the numerical extension, although this will certainly become obsolete very soon.
Fairly recently Mike announced his work, which used the SWIG-generated OpenGL module and a rewritten (In C not C++) TIGER widget. After a day (and a small discussion) he presented a new version of his Opengl-Tk Widget based on Togl. That's what you get here! Mike added a lot of really cool stuff (auto-rotation, picking and the spinning PyOpenGL Logo).
This and the Matrix OpenGL module shold be considered the Official OpenGL module, so please do not rewrite them. Enhancing the work already done seems much better (this is my opinion!). Well, at least have fun with it. It was tested on Linux, SGI and Windows 95/NT except the Tk-OpenGL widget, whis is a pure X11-Widget until now. Would be nice to have a Mac and Windows Port.
classical way
, so you don't have
to bother with Tk finding shared objects.
First copy al *.c, *.h
files in the src subdirectory
to the Modules
subdirectory of the Python distribution. The file
tkInt.h of the tk4.1 Distribution is included here for
convenience to all those who do not have it on their system. (It's not installed by tk4.1!).
After that change the line in Setup to something like:
_tkinter _tkinter.c tkappinit.c togl.c -DWITH_APPINIT -lGL -lXext -lXmu -ltk4.1 -ltcl7.5 -lX11This is for Linux, maybe you do not need all the X-libraries on other systems. Finally you have to add the the new
Togl_Init
command defined in togl.c to
tkappinit.c. Here's how this looks like:
#includeWe assume that you will also use the openglmodule in conjunction with the new widget to do rapid application development with Python and OpenGL. You can use this module eihter statically linked or as a dynamic module. For that purpose add#include #include "togl.h" int Tcl_AppInit (interp) Tcl_Interp *interp; { Tk_Window main; main = Tk_MainWindow(interp); if (Tcl_Init (interp) == TCL_ERROR) return TCL_ERROR; if (Tk_Init (interp) == TCL_ERROR) return TCL_ERROR; if (Togl_Init (interp) == TCL_ERROR) return TCL_ERROR; ... return TCL_OK; }
opengl openglmodule.c glut_shapes.c -lGLU glu glumodule.cto the Setup file and rebuild the Python interpreter as usual with
make
. If you do not have the numerical extension available (which will
be part of the official Python distribution soon), you have to uncomment the line
#define NUMERICin openglmodule.c. In this new version we have included standard geometric shapes of the
glut
library. I you don't like that just uncomment
the line
#define GLUTin openglmodule.c and remove glut_shapes.c in the above example of the Setup file.
Know you are ready to go! Opengl.py provides the
Python wrapper code for the new Tcl/Tk command we just incorporated. Make shure it's
somewhere in your PYTHONPATH
. The same applies to
glconst.py and gluconst.py.
redraw
to it. Then pack the widget and run the Tk-mainloop.
When you run the follwing code in first.py
from Opengl import * def redraw(o): gl.ClearColor(0, 0, 1, 0) gl.Clear(GL_COLOR_BUFFER_BIT) o = Opengl(width = 200, height = 200, double = 1) o.redraw = redraw o.pack(side = 'top', expand = 1, fill = 'both') o.mainloop()you get a blue window on the screen. What you can't see in this simple example is the default behavior of the OpenGL-widget. With the left mousebutton you can translate a scene, with the middle one you can rotate it and the right Mousebutton allows scaling. Of course it's possible to override this key-bindings by your own code, bu't it's certainly usefull for non experts at this point. Let's look at another example which should give you an impression of this default behavior:
from Opengl import * import sys def redraw(o): gl.ClearColor(1, 0, 1, 0) gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) gl.Color3f(0, 1, 0) #draw checkerboard N = 4 gl.Disable(GL_LIGHTING) for x in range(-N, N): for y in range(-N, N): if (x + y) % 2 == 0: gl.Color3f(1, 1, 1) else: gl.Color3f(0, 0, 0) gl.Rectf(x, y, x + 1, y + 1) gl.Enable(GL_LIGHTING) gl.PushMatrix() gl.Translatef(0., 0., 1.) gl.SolidSphere(1.0) gl.PopMatrix() def main(): f = Frame() f.pack(side = 'top') o = Opengl(width = 200, height = 200, double = 1) o.redraw = redraw quit = Button(f, text = 'Quit', command = sys.exit) quit.pack(side = 'top', side = 'left') help = Button(f, text = 'Help', command = o.help) help.pack(side = 'top', side = 'left') reset = Button(f, text = 'Reset', command = o.reset) reset.pack(side = 'top', side = 'left') o.pack(side = 'top', expand = 1, fill = 'both') o.set_eyepoint(20.) o.mainloop() main()What we get here is a black and white checkerboard with a small shaded Sphere sitting on it.
Use the mouse buttons to see what happens. The three remaining widgets (quit,help
and redraw
buttons) should be self-explaining and show
the painless integration of usual Tk-widgets with the new OpenGL-widget. Note also the very simple
lighting model built into the basic_lighting
method:
def basic_lighting(self): self.activate() light_position = (1, 1, 1, 0); gl.Lightf(GL_LIGHT0, GL_POSITION, light_position); gl.Enable(GL_LIGHTING); gl.Enable(GL_LIGHT0); gl.DepthFunc(GL_LESS); gl.Enable(GL_DEPTH_TEST); gl.MatrixMode(GL_MODELVIEW); gl.LoadIdentity()This method can of course be overriden by derived classes of
OpenGL
or you can just reset the lights as in the follwing example fog.py:
from Opengl import * def init(): mat_ambient = [0.2, 0.2, 0.2, 1.0] mat_diffuse = [0.8, 0.8, 0.8, 1.0] mat_specular = [1.0, 0.0, 1.0, 1.0] mat_shininess = [50.0] light_ambient = [0.0, 1.0, 0.0, 1.0] light_diffuse = [1.0, 1.0, 1.0, 1.0] light_specular = [1.0, 1.0, 1.0, 1.0] light_position = [1.0, 1.0, 1.0, 0.0] lmodel_ambient = [0.2, 0.2, 0.2, 1.0] gl.Materialfv(GL_FRONT, GL_AMBIENT, mat_ambient) gl.Materialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse) gl.Materialfv(GL_FRONT, GL_SPECULAR, mat_specular) gl.Materialfv(GL_FRONT, GL_SHININESS, mat_shininess) gl.Lightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) gl.Lightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse) gl.Lightfv(GL_LIGHT0, GL_SPECULAR, light_specular) gl.Lightfv(GL_LIGHT0, GL_POSITION, light_position); gl.LightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient) gl.Enable(GL_LIGHTING) gl.Enable(GL_LIGHT0) gl.DepthFunc(GL_LESS) gl.Enable(GL_DEPTH_TEST) def redraw(o): gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) gl.PushMatrix() gl.Translatef(0, -1, 0) gl.Rotatef(250, 1, 0, 0) gl.SolidCone(1, 2, 50, 10) gl.PopMatrix() def main(): o = Opengl(width = 200, height = 200, double = 1) o.redraw = redraw o.pack(side = 'top', expand = 1, fill = 'both') init() o.mainloop() main()Here are two screenshots of this example:
Up to now we used a procedural approach. Let's use some class Fog
in our last example fog.py. We draw five red doughnuts
and add two Radiobuttons to switch between different fog models.
from Opengl import * class Fog: def __init__(self): self.o = Opengl(width = 250, height = 140, double = 1) self.o.redraw = self.redraw self.o.pack(side = 'top', expand = 1, fill = 'both') self.mode = IntVar(self.o) self.mode.set(GL_EXP) r1 = Radiobutton(text='GL_LINEAR', anchor=W, variable=self.mode, value=GL_LINEAR, command=self.selectFog) r1.pack(side = 'top', expand = 1, fill = 'both') r2 = Radiobutton(text='GL_EXP', anchor=W, variable=self.mode, value=GL_EXP, command=self.selectFog) r2.pack(side = 'top', expand = 1, fill = 'both') def run(self): self.init() self.o.mainloop() def selectFog(self): val = self.mode.get() if val == GL_LINEAR: gl.Fogf(GL_FOG_START, 1.0) gl.Fogf(GL_FOG_END, 5.0) gl.Fogi(GL_FOG_MODE, val) elif val == GL_EXP: gl.Fogi(GL_FOG_MODE, val) self.o.tkRedraw() def init(self): position = [0.0, 3.0, 3.0, 0.0] local_view = [0.0] gl.Disable(GL_DITHER) gl.Enable(GL_DEPTH_TEST) gl.DepthFunc(GL_LESS) gl.Lightfv(GL_LIGHT0, GL_POSITION, position) gl.LightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view) gl.FrontFace(GL_CW) gl.Enable(GL_LIGHTING) gl.Enable(GL_LIGHT0) gl.Enable(GL_AUTO_NORMAL) gl.Enable(GL_NORMALIZE) gl.Enable(GL_FOG) fogColor = [0.5, 0.5, 0.5, 1.0] gl.Fogi(GL_FOG_MODE, GL_EXP) gl.Fogfv(GL_FOG_COLOR, fogColor) gl.Fogf(GL_FOG_DENSITY, 0.35) gl.Hint(GL_FOG_HINT, GL_DONT_CARE) gl.ClearColor(0.5, 0.5, 0.5, 1.0) def drawTorus(self, x, y, z): gl.PushMatrix(); gl.Translatef(x, y, z); gl.Materialfv(GL_FRONT, GL_AMBIENT, [0.1745, 0.01175, 0.01175, 1.0]) gl.Materialfv(GL_FRONT, GL_DIFFUSE, [0.61424, 0.04136, 0.04136]) gl.Materialfv(GL_FRONT, GL_SPECULAR, [0.727811, 0.626959, 0.626959]) gl.Materialf(GL_FRONT, GL_SHININESS, 0.6 * 128.0) gl.SolidTorus(0.275, 0.85, 20, 20) gl.PopMatrix() def redraw(self, o): gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self.drawTorus(-4.0, -0.5, -1.0) self.drawTorus(-2.0, -0.5, -2.0) self.drawTorus(0.0, -0.5, -3.0) self.drawTorus(2.0, -0.5, -4.0) self.drawTorus(4.0, -0.5, -5.0) def main(): f = Fog() f.run() main()What we get you can see in the following pictures (all screenshots on a Linux box!).
August 1996 (all images on this page were created with PyOpenGL/Linux2.0)