| | """Traditional PyOpenGL interface to Togl |
| | |
| | This module provides access to the Tkinter Togl widget |
| | with a relatively "thick" wrapper API that creates a set |
| | of default "examination" operations. |
| | |
| | Note that many (most) Linux distributions have |
| | now split out the Tkinter bindings into a separate package, |
| | and that Togl must be separately downloaded (a script is |
| | provided in the source distribution which downloads and |
| | installs Togl 2.0 binaries for you). |
| | |
| | Because of the complexity and fragility of the installation, |
| | it is recommended that you use Pygame, wxPython, PyGtk, or |
| | PyQt for real-world projects, and GLUT or Pygame for simple |
| | demo/testing interfaces. |
| | |
| | The Togl project is located here: |
| | |
| | http://togl.sourceforge.net/ |
| | """ |
| | |
| | |
| | |
| | |
| | import os,sys, logging |
| | _log = logging.getLogger( 'OpenGL.Tk' ) |
| | from OpenGL.GL import * |
| | from OpenGL.GLU import * |
| | try: |
| | from tkinter import _default_root |
| | from tkinter import * |
| | from tkinter import dialog |
| | except ImportError as err: |
| | try: |
| | from Tkinter import _default_root |
| | from Tkinter import * |
| | import Dialog as dialog |
| | except ImportError as err: |
| | _log.error( """Unable to import Tkinter, likely need to install a separate package (python-tk) to have Tkinter support. You likely also want to run the src/togl.py script in the PyOpenGL source distribution to install the Togl widget""" ) |
| | raise |
| | import math |
| |
|
| | def glTranslateScene(s, x, y, mousex, mousey): |
| | glMatrixMode(GL_MODELVIEW) |
| | mat = glGetDoublev(GL_MODELVIEW_MATRIX) |
| | glLoadIdentity() |
| | glTranslatef(s * (x - mousex), s * (mousey - y), 0.0) |
| | glMultMatrixd(mat) |
| |
|
| |
|
| | def glRotateScene(s, xcenter, ycenter, zcenter, x, y, mousex, mousey): |
| | glMatrixMode(GL_MODELVIEW) |
| | mat = glGetDoublev(GL_MODELVIEW_MATRIX) |
| | glLoadIdentity() |
| | glTranslatef(xcenter, ycenter, zcenter) |
| | glRotatef(s * (y - mousey), 1., 0., 0.) |
| | glRotatef(s * (x - mousex), 0., 1., 0.) |
| | glTranslatef(-xcenter, -ycenter, -zcenter) |
| | glMultMatrixd(mat) |
| |
|
| |
|
| | def sub(x, y): |
| | return list(map(lambda a, b: a-b, x, y)) |
| |
|
| |
|
| | def dot(x, y): |
| | t = 0 |
| | for i in range(len(x)): |
| | t = t + x[i]*y[i] |
| | return t |
| |
|
| |
|
| | def glDistFromLine(x, p1, p2): |
| | f = list(map(lambda x, y: x-y, p2, p1)) |
| | g = list(map(lambda x, y: x-y, x, p1)) |
| | return dot(g, g) - dot(f, g)**2/dot(f, f) |
| |
|
| |
|
| | |
| | TOGL_NORMAL = 1 |
| | TOGL_OVERLAY = 2 |
| |
|
| | def v3distsq(a,b): |
| | d = ( a[0] - b[0], a[1] - b[1], a[2] - b[2] ) |
| | return d[0]*d[0] + d[1]*d[1] + d[2]*d[2] |
| |
|
| | |
| | |
| | if _default_root is None: |
| | _default_root = Tk() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | import sys |
| | if sys.maxint > 2**32: |
| | suffix = '-64' |
| | else: |
| | suffix = '' |
| | try: |
| | TOGL_DLL_PATH = os.path.join( |
| | os.path.dirname(__file__), |
| | 'togl-'+ sys.platform + suffix, |
| | ) |
| | except NameError as err: |
| | |
| | TOGL_DLL_PATH = "" |
| |
|
| | if not os.path.isdir( TOGL_DLL_PATH ): |
| | _log.warn( 'Expected Tk Togl installation in %s', TOGL_DLL_PATH ) |
| | _log.info( 'Loading Togl from: %s', TOGL_DLL_PATH ) |
| | _default_root.tk.call('lappend', 'auto_path', TOGL_DLL_PATH) |
| | _default_root.tk.call('package', 'require', 'Togl') |
| | try: |
| | _default_root.tk.eval('load {} Togl') |
| | except TclError as err: |
| | _log.error( """Failure loading Togl package: %s""", err ) |
| | raise |
| |
|
| | |
| | |
| | import atexit |
| | def cleanup(): |
| | try: |
| | import tkinter |
| | except ImportError as err: |
| | import Tkinter as tkinter |
| | try: |
| | if tkinter._default_root: tkinter._default_root.destroy() |
| | except (TclError,AttributeError) as err: |
| | pass |
| | tkinter._default_root = None |
| | atexit.register( cleanup ) |
| |
|
| | class Togl(Widget): |
| | """ |
| | Togl Widget |
| | Keith Junius |
| | Department of Biophysical Chemistry |
| | University of Groningen, The Netherlands |
| | Very basic widget which provides access to Togl functions. |
| | """ |
| | def __init__(self, master=None, cnf={}, **kw): |
| | Widget.__init__(self, master, 'togl', cnf, kw) |
| |
|
| |
|
| | def render(self): |
| | return |
| |
|
| |
|
| | def swapbuffers(self): |
| | self.tk.call(self._w, 'swapbuffers') |
| |
|
| |
|
| | def makecurrent(self): |
| | self.tk.call(self._w, 'makecurrent') |
| |
|
| |
|
| | def alloccolor(self, red, green, blue): |
| | return self.tk.getint(self.tk.call(self._w, 'alloccolor', red, green, blue)) |
| |
|
| |
|
| | def freecolor(self, index): |
| | self.tk.call(self._w, 'freecolor', index) |
| |
|
| |
|
| | def setcolor(self, index, red, green, blue): |
| | self.tk.call(self._w, 'setcolor', index, red, green, blue) |
| |
|
| |
|
| | def loadbitmapfont(self, fontname): |
| | return self.tk.getint(self.tk.call(self._w, 'loadbitmapfont', fontname)) |
| |
|
| |
|
| | def unloadbitmapfont(self, fontbase): |
| | self.tk.call(self._w, 'unloadbitmapfont', fontbase) |
| |
|
| |
|
| | def uselayer(self, layer): |
| | self.tk.call(self._w, 'uselayer', layer) |
| |
|
| |
|
| | def showoverlay(self): |
| | self.tk.call(self._w, 'showoverlay') |
| |
|
| |
|
| | def hideoverlay(self): |
| | self.tk.call(self._w, 'hideoverlay') |
| |
|
| |
|
| | def existsoverlay(self): |
| | return self.tk.getboolean(self.tk.call(self._w, 'existsoverlay')) |
| |
|
| |
|
| | def getoverlaytransparentvalue(self): |
| | return self.tk.getint(self.tk.call(self._w, 'getoverlaytransparentvalue')) |
| |
|
| |
|
| | def ismappedoverlay(self): |
| | return self.tk.getboolean(self.tk.call(self._w, 'ismappedoverlay')) |
| |
|
| |
|
| | def alloccoloroverlay(self, red, green, blue): |
| | return self.tk.getint(self.tk.call(self._w, 'alloccoloroverlay', red, green, blue)) |
| |
|
| |
|
| | def freecoloroverlay(self, index): |
| | self.tk.call(self._w, 'freecoloroverlay', index) |
| |
|
| |
|
| |
|
| | class RawOpengl(Widget, Misc): |
| | """Widget without any sophisticated bindings\ |
| | by Tom Schwaller""" |
| |
|
| |
|
| | def __init__(self, master=None, cnf={}, **kw): |
| | Widget.__init__(self, master, 'togl', cnf, kw) |
| | self.bind('<Map>', self.tkMap) |
| | self.bind('<Expose>', self.tkExpose) |
| | self.bind('<Configure>', self.tkExpose) |
| |
|
| |
|
| | def tkRedraw(self, *dummy): |
| | |
| | |
| | self.update_idletasks() |
| | self.tk.call(self._w, 'makecurrent') |
| | _mode = glGetDoublev(GL_MATRIX_MODE) |
| | try: |
| | glMatrixMode(GL_PROJECTION) |
| | glPushMatrix() |
| | try: |
| | self.redraw() |
| | glFlush() |
| | finally: |
| | glPopMatrix() |
| | finally: |
| | glMatrixMode(_mode) |
| | self.tk.call(self._w, 'swapbuffers') |
| |
|
| |
|
| | def tkMap(self, *dummy): |
| | self.tkExpose() |
| |
|
| |
|
| | def tkExpose(self, *dummy): |
| | self.tkRedraw() |
| |
|
| |
|
| |
|
| |
|
| | class Opengl(RawOpengl): |
| | """\ |
| | Tkinter bindings for an Opengl widget. |
| | Mike Hartshorn |
| | Department of Chemistry |
| | University of York, UK |
| | http://www.yorvic.york.ac.uk/~mjh/ |
| | """ |
| |
|
| | def __init__(self, master=None, cnf={}, **kw): |
| | """\ |
| | Create an opengl widget. |
| | Arrange for redraws when the window is exposed or when |
| | it changes size.""" |
| |
|
| | |
| | RawOpengl.__init__(*(self, master, cnf), **kw) |
| | self.initialised = 0 |
| |
|
| | |
| | self.xmouse = 0 |
| | self.ymouse = 0 |
| |
|
| | |
| | self.xcenter = 0.0 |
| | self.ycenter = 0.0 |
| | self.zcenter = 0.0 |
| |
|
| | |
| | self.r_back = 1. |
| | self.g_back = 0. |
| | self.b_back = 1. |
| |
|
| | |
| | self.distance = 10.0 |
| |
|
| | |
| | self.fovy = 30.0 |
| |
|
| | |
| | self.near = 0.1 |
| | self.far = 1000.0 |
| |
|
| | |
| | self.autospin_allowed = 0 |
| |
|
| | |
| | self.autospin = 0 |
| |
|
| | |
| | self.bind('<Map>', self.tkMap) |
| | self.bind('<Expose>', self.tkExpose) |
| | self.bind('<Configure>', self.tkExpose) |
| | self.bind('<Shift-Button-1>', self.tkHandlePick) |
| | |
| | self.bind('<Button-1>', self.tkRecordMouse) |
| | self.bind('<B1-Motion>', self.tkTranslate) |
| | self.bind('<Button-2>', self.StartRotate) |
| | self.bind('<B2-Motion>', self.tkRotate) |
| | self.bind('<ButtonRelease-2>', self.tkAutoSpin) |
| | self.bind('<Button-3>', self.tkRecordMouse) |
| | self.bind('<B3-Motion>', self.tkScale) |
| |
|
| |
|
| | def help(self): |
| | """Help for the widget.""" |
| |
|
| | import Dialog |
| | d = Dialog.Dialog(None, {'title': 'Viewer help', |
| | 'text': 'Button-1: Translate\n' |
| | 'Button-2: Rotate\n' |
| | 'Button-3: Zoom\n' |
| | 'Reset: Resets transformation to identity\n', |
| | 'bitmap': 'questhead', |
| | 'default': 0, |
| | 'strings': ('Done', 'Ok')}) |
| |
|
| |
|
| | def activate(self): |
| | """Cause this Opengl widget to be the current destination for drawing.""" |
| |
|
| | self.tk.call(self._w, 'makecurrent') |
| |
|
| |
|
| | |
| | |
| | def basic_lighting(self): |
| | """\ |
| | Set up some basic lighting (single infinite light source). |
| | |
| | Also switch on the depth buffer.""" |
| | |
| | self.activate() |
| | light_position = (1, 1, 1, 0) |
| | glLightfv(GL_LIGHT0, GL_POSITION, light_position) |
| | glEnable(GL_LIGHTING) |
| | glEnable(GL_LIGHT0) |
| | glDepthFunc(GL_LESS) |
| | glEnable(GL_DEPTH_TEST) |
| | glMatrixMode(GL_MODELVIEW) |
| | glLoadIdentity() |
| |
|
| | def set_background(self, r, g, b): |
| | """Change the background colour of the widget.""" |
| |
|
| | self.r_back = r |
| | self.g_back = g |
| | self.b_back = b |
| |
|
| | self.tkRedraw() |
| |
|
| |
|
| | def set_centerpoint(self, x, y, z): |
| | """Set the new center point for the model. |
| | This is where we are looking.""" |
| |
|
| | self.xcenter = x |
| | self.ycenter = y |
| | self.zcenter = z |
| |
|
| | self.tkRedraw() |
| |
|
| |
|
| | def set_eyepoint(self, distance): |
| | """Set how far the eye is from the position we are looking.""" |
| |
|
| | self.distance = distance |
| | self.tkRedraw() |
| |
|
| |
|
| | def reset(self): |
| | """Reset rotation matrix for this widget.""" |
| |
|
| | glMatrixMode(GL_MODELVIEW) |
| | glLoadIdentity() |
| | self.tkRedraw() |
| |
|
| |
|
| | def tkHandlePick(self, event): |
| | """Handle a pick on the scene.""" |
| |
|
| | if hasattr(self, 'pick'): |
| | |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | realy = self.winfo_height() - event.y |
| |
|
| | p1 = gluUnProject(event.x, realy, 0.) |
| | p2 = gluUnProject(event.x, realy, 1.) |
| |
|
| | if self.pick(self, p1, p2): |
| | """If the pick method returns true we redraw the scene.""" |
| |
|
| | self.tkRedraw() |
| |
|
| |
|
| | def tkRecordMouse(self, event): |
| | """Record the current mouse position.""" |
| |
|
| | self.xmouse = event.x |
| | self.ymouse = event.y |
| |
|
| |
|
| | def StartRotate(self, event): |
| | |
| |
|
| | self.autospin = 0 |
| | self.tkRecordMouse(event) |
| |
|
| |
|
| | def tkScale(self, event): |
| | """Scale the scene. Achieved by moving the eye position. |
| | |
| | Dragging up zooms in, while dragging down zooms out |
| | """ |
| | scale = 1 - 0.01 * (event.y - self.ymouse) |
| | |
| | |
| | if scale < 0.001: |
| | scale = 0.001 |
| | elif scale > 1000: |
| | scale = 1000 |
| | self.distance = self.distance * scale |
| | self.tkRedraw() |
| | self.tkRecordMouse(event) |
| |
|
| |
|
| | def do_AutoSpin(self): |
| | s = 0.5 |
| | self.activate() |
| |
|
| | glRotateScene(0.5, self.xcenter, self.ycenter, self.zcenter, self.yspin, self.xspin, 0, 0) |
| | self.tkRedraw() |
| |
|
| | if self.autospin: |
| | self.after(10, self.do_AutoSpin) |
| |
|
| |
|
| | def tkAutoSpin(self, event): |
| | """Perform autospin of scene.""" |
| |
|
| | self.after(4) |
| | self.update_idletasks() |
| |
|
| | |
| | |
| | |
| |
|
| | x = self.tk.getint(self.tk.call('winfo', 'pointerx', self._w)) |
| | y = self.tk.getint(self.tk.call('winfo', 'pointery', self._w)) |
| |
|
| | if self.autospin_allowed: |
| | if x != event.x_root and y != event.y_root: |
| | self.autospin = 1 |
| |
|
| | self.yspin = x - event.x_root |
| | self.xspin = y - event.y_root |
| |
|
| | self.after(10, self.do_AutoSpin) |
| |
|
| |
|
| | def tkRotate(self, event): |
| | """Perform rotation of scene.""" |
| |
|
| | self.activate() |
| | glRotateScene(0.5, self.xcenter, self.ycenter, self.zcenter, event.x, event.y, self.xmouse, self.ymouse) |
| | self.tkRedraw() |
| | self.tkRecordMouse(event) |
| |
|
| |
|
| | def tkTranslate(self, event): |
| | """Perform translation of scene.""" |
| |
|
| | self.activate() |
| |
|
| | |
| | win_height = max( 1,self.winfo_height() ) |
| | obj_c = ( self.xcenter, self.ycenter, self.zcenter ) |
| | win = gluProject( obj_c[0], obj_c[1], obj_c[2]) |
| | obj = gluUnProject( win[0], win[1] + 0.5 * win_height, win[2]) |
| | dist = math.sqrt( v3distsq( obj, obj_c ) ) |
| | scale = abs( dist / ( 0.5 * win_height ) ) |
| |
|
| | glTranslateScene(scale, event.x, event.y, self.xmouse, self.ymouse) |
| | self.tkRedraw() |
| | self.tkRecordMouse(event) |
| |
|
| |
|
| | def tkRedraw(self, *dummy): |
| | """Cause the opengl widget to redraw itself.""" |
| |
|
| | if not self.initialised: return |
| | self.activate() |
| |
|
| | glPushMatrix() |
| | self.update_idletasks() |
| | self.activate() |
| | w = self.winfo_width() |
| | h = self.winfo_height() |
| | glViewport(0, 0, w, h) |
| |
|
| | |
| | glClearColor(self.r_back, self.g_back, self.b_back, 0.) |
| | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) |
| |
|
| | glMatrixMode(GL_PROJECTION) |
| | glLoadIdentity() |
| | gluPerspective(self.fovy, float(w)/float(h), self.near, self.far) |
| |
|
| | if 0: |
| | |
| | glMatrixMode(GL_MODELVIEW) |
| | mat = glGetDoublev(GL_MODELVIEW_MATRIX) |
| | glLoadIdentity() |
| | glTranslatef(-self.xcenter, -self.ycenter, -(self.zcenter+self.distance)) |
| | glMultMatrixd(mat) |
| | else: |
| | gluLookAt(self.xcenter, self.ycenter, self.zcenter + self.distance, |
| | self.xcenter, self.ycenter, self.zcenter, |
| | 0., 1., 0.) |
| | glMatrixMode(GL_MODELVIEW) |
| | |
| | |
| | self.redraw(self) |
| | glFlush() |
| | glPopMatrix() |
| |
|
| | self.tk.call(self._w, 'swapbuffers') |
| | def redraw( self, *args, **named ): |
| | """Prevent access errors if user doesn't set redraw fast enough""" |
| |
|
| |
|
| | def tkMap(self, *dummy): |
| | """Cause the opengl widget to redraw itself.""" |
| |
|
| | self.tkExpose() |
| |
|
| |
|
| | def tkExpose(self, *dummy): |
| | """Redraw the widget. |
| | Make it active, update tk events, call redraw procedure and |
| | swap the buffers. Note: swapbuffers is clever enough to |
| | only swap double buffered visuals.""" |
| |
|
| | self.activate() |
| | if not self.initialised: |
| | self.basic_lighting() |
| | self.initialised = 1 |
| | self.tkRedraw() |
| |
|
| |
|
| | def tkPrint(self, file): |
| | """Turn the current scene into PostScript via the feedback buffer.""" |
| |
|
| | self.activate() |
| |
|