Browse files

Merge branch 'glut-rebased' of git://github.com/fperez/ipython into glut

* 'glut-rebased' of git://github.com/fperez/ipython:
  Added the command line option
  Fix code in disable_glut which was not tested and quite buggy
  Tried to fix the CTRL-C problem (ipython#742) and take other comments/typos into account
  Replaced deprecated raise call
  Fixed typos in comments
  Canceled window reshape to 1x1 since the idea is now for the user to use this window as the main one because of weird seg-faults problem after user creates its own window (any subsequent gl error would lead to a segfault, even a simple one line requiring a non existent function
  Event loop integration example
  Added code for the GLUT interactive session
  • Loading branch information...
2 parents 14ae4c0 + 0c6df48 commit 89161a5b187ff4b50fcc16449a23e5fe2d4a040a @rougier committed Sep 15, 2011
Showing with 253 additions and 3 deletions.
  1. +2 −2 IPython/frontend/terminal/ipapp.py
  2. +1 −0 IPython/lib/__init__.py
  3. +204 −1 IPython/lib/inputhook.py
  4. +46 −0 docs/examples/lib/gui-glut.py
View
4 IPython/frontend/terminal/ipapp.py
@@ -229,8 +229,8 @@ def _quick_changed(self, name, old, new):
self.load_config_file = lambda *a, **kw: None
self.ignore_old_config=True
- gui = CaselessStrEnum(('qt','wx','gtk', 'pyglet'), config=True,
- help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'pyglet')."
+ gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet'), config=True,
+ help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet')."
)
pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
config=True,
View
1 IPython/lib/__init__.py
@@ -19,6 +19,7 @@
enable_gtk, disable_gtk,
enable_qt4, disable_qt4,
enable_tk, disable_tk,
+ enable_glut, disable_glut,
enable_pyglet, disable_pyglet,
set_inputhook, clear_inputhook,
current_gui
View
205 IPython/lib/inputhook.py
@@ -29,6 +29,7 @@
GUI_GTK = 'gtk'
GUI_TK = 'tk'
GUI_OSX = 'osx'
+GUI_GLUT = 'glut'
GUI_PYGLET = 'pyglet'
#-----------------------------------------------------------------------------
@@ -284,7 +285,207 @@ def disable_tk(self):
"""
self.clear_inputhook()
+ def enable_glut(self, app=None):
+ """Enable event loop integration with GLUT.
+ Parameters
+ ----------
+ app : ignored
+ Ignored, it's only a placeholder to keep the call signature of all
+ gui activation methods consistent, which simplifies the logic of
+ supporting magics.
+
+ Notes
+ -----
+
+ This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
+ integrate with terminal based applications like IPython. Due to GLUT
+ limitations, it is currently not possible to start the event loop
+ without first creating a window. You should thus not create another
+ window but use instead the created one. See 'gui-glut.py' in the
+ docs/examples/lib directory.
+
+ The default screen mode is set to:
+
+ glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
+
+ Script integration
+ ------------------
+
+ if glut.glutGetWindow() > 0:
+ interactive = True
+ glut.glutShowWindow()
+ else:
+ interactive = False
+ glut.glutInit(sys.argv)
+ glut.glutInitDisplayMode( glut.GLUT_DOUBLE |
+ glut.GLUT_RGBA |
+ glut.GLUT_DEPTH )
+ ...
+ if not interactive:
+ glut.glutMainLoop()
+ """
+ # GLUT is quite an old library and it is difficult to ensure proper
+ # integration within IPython since original GLUT does not allow to handle
+ # events one by one. Instead, it requires for the mainloop to be entered
+ # and never returned (there is not even a function to exit he
+ # mainloop). Fortunately, there are alternatives such as freeglut
+ # (available for linux and windows) and the OSX implementation gives
+ # access to a glutCheckLoop() function that blocks itself until a new
+ # event is received. This means we have to setup a default timer to
+ # ensure we got at least one event that will unblock the function. We set
+ # a default timer of 60fps.
+ #
+ # Furthermore, it is not possible to install these handlers without a
+ # window being first created. We choose to make this window invisible and
+ # the user is supposed to make it visible when needed (see gui-glut.py in
+ # the docs/examples/lib directory). This means that display mode options
+ # are set at this level and user won't be able to change them later
+ # without modifying the code. This should probably be made available via
+ # IPython options system.
+
+ import OpenGL
+ OpenGL.ERROR_CHECKING = False
+ import OpenGL.GLUT as glut
+ import OpenGL.platform as platform
+ import time
+
+
+ # Frame per second : 60
+ # Should probably be an IPython option
+ glut_fps = 60
+
+
+ # Display mode : double buffeed + rgba + depth
+ # Should probably be an IPython option
+ glut_display_mode = (glut.GLUT_DOUBLE |
+ glut.GLUT_RGBA |
+ glut.GLUT_DEPTH)
+
+ glut_interrupted = False
+
+ def display():
+ ''' Dummy display function '''
+ pass
+
+ def timer(fps):
+ # We should normally set the active window to 1 and post a
+ # redisplay for each window. The problem is that we do not know
+ # how much active windows we have and there is no function in glut
+ # to get that number.
+ # glut.glutSetWindow(1)
+ glut.glutTimerFunc( int(1000.0/fps), timer, fps)
+ glut.glutPostRedisplay()
+
+ def close():
+ glut.glutHideWindow()
+
+ glutMainLoopEvent = None
+ if sys.platform == 'darwin':
+ try:
+ glutCheckLoop = platform.createBaseFunction(
+ 'glutCheckLoop', dll=platform.GLUT, resultType=None,
+ argTypes=[],
+ doc='glutCheckLoop( ) -> None',
+ argNames=(),
+ )
+ except AttributeError:
+ raise RuntimeError(
+ '''Your glut implementation does not allow interactive sessions'''
+ '''Consider installing freeglut.''')
+ glutMainLoopEvent = glutCheckLoop
+ elif glut.HAVE_FREEGLUT:
+ glutMainLoopEvent = glut.glutMainLoopEvent
+ else:
+ raise RuntimeError(
+ '''Your glut implementation does not allow interactive sessions. '''
+ '''Consider installing freeglut.''')
+
+ def inputhook_glut():
+ """ Process pending GLUT events only. """
+
+ # We need to protect against a user pressing Control-C when IPython
+ # is idle and this is running. We should trap KeyboardInterrupt and
+ # pass but it does not seem to work with glutMainLoopEvent.
+ # Instead, we setup a signal handler on SIGINT and returns after
+ # having restored the default python SIGINT handler.
+ import signal
+ def handler(signum, frame):
+ signal.signal(signal.SIGINT, signal.default_int_handler)
+ print '\nKeyboardInterrupt'
+ # Need to reprint the prompt at this stage
+
+ signal.signal(signal.SIGINT, handler)
+
+ try:
+ glutMainLoopEvent()
+ except KeyboardInterrupt: # this catch doesn't work for some reasons...
+ pass
+
+ return 0
+
+ if not self._apps.has_key(GUI_GLUT):
+ glut.glutInit(sys.argv)
+ # Display mode should be also an Ipython option since user won't be able
+ # to change it later
+ glut.glutInitDisplayMode(glut_display_mode)
+ glut.glutCreateWindow(sys.argv[0])
+ glut.glutHideWindow()
+ glut.glutWMCloseFunc(close)
+ glut.glutDisplayFunc(display)
+ glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
+ else:
+ glut.glutWMCloseFunc(close)
+ glut.glutDisplayFunc(display)
+ glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
+
+ self.set_inputhook(inputhook_glut)
+ self._current_gui = GUI_GLUT
+ self._apps[GUI_GLUT] = True
+
+ def disable_glut(self):
+ """Disable event loop integration with glut.
+
+ This sets PyOS_InputHook to NULL and set the display function to a
+ dummy one and set the timer to a dummy timer that will be triggered
+ very far in the future.
+ """
+ import signal
+ import OpenGL
+ OpenGL.ERROR_CHECKING = False
+ import OpenGL.GLUT as glut
+ import OpenGL.platform as platform
+
+ def timer_none(fps):
+ ''' Dummy timer function '''
+ pass
+
+ glutMainLoopEvent = None
+ if sys.platform == 'darwin':
+ try:
+ glutCheckLoop = platform.createBaseFunction(
+ 'glutCheckLoop', dll=platform.GLUT, resultType=None,
+ argTypes=[],
+ doc='glutCheckLoop( ) -> None',
+ argNames=(),
+ )
+ except AttributeError:
+ raise RuntimeError(
+ '''Your glut implementation does not allow interactive sessions'''
+ '''Consider installing freeglut.''')
+ glutMainLoopEvent = glutCheckLoop
+ elif glut.HAVE_FREEGLUT:
+ glutMainLoopEvent = glut.glutMainLoopEvent
+ else:
+ raise RuntimeError(
+ '''Your glut implementation does not allow interactive sessions. '''
+ '''Consider installing freeglut.''')
+
+ glut.glutHideWindow() # This is an event to be processed below
+ glutMainLoopEvent()
+ #glut.glutTimerFunc( sys.maxint-1, timer_none, 0)
+ self.clear_inputhook()
+ #signal.signal(signal.SIGINT, signal.default_int_handler)
def enable_pyglet(self, app=None):
"""Enable event loop integration with pyglet.
@@ -316,7 +517,6 @@ def disable_pyglet(self):
"""
self.clear_inputhook()
-
def current_gui(self):
"""Return a string indicating the currently active GUI or None."""
return self._current_gui
@@ -331,6 +531,8 @@ def current_gui(self):
disable_gtk = inputhook_manager.disable_gtk
enable_tk = inputhook_manager.enable_tk
disable_tk = inputhook_manager.disable_tk
+enable_glut = inputhook_manager.enable_glut
+disable_glut = inputhook_manager.disable_glut
enable_pyglet = inputhook_manager.enable_pyglet
disable_pyglet = inputhook_manager.disable_pyglet
clear_inputhook = inputhook_manager.clear_inputhook
@@ -371,6 +573,7 @@ def enable_gui(gui=None, app=None):
GUI_WX: enable_wx,
GUI_QT: enable_qt4, # qt3 not supported
GUI_QT4: enable_qt4,
+ GUI_GLUT: enable_glut,
GUI_PYGLET: enable_pyglet,
}
try:
View
46 docs/examples/lib/gui-glut.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+"""Simple GLUT example to manually test event loop integration.
+
+This is meant to run tests manually in ipython as:
+
+In [5]: %gui glut
+
+In [6]: %run gui-glut.py
+
+In [7]: gl.glClearColor(1,1,1,1)
+"""
+
+#!/usr/bin/env python
+import sys
+import OpenGL.GL as gl
+import OpenGL.GLUT as glut
+
+def display():
+ gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
+ glut.glutSwapBuffers()
+
+def resize(width,height):
+ gl.glViewport(0, 0, width, height+4)
+ gl.glMatrixMode(gl.GL_PROJECTION)
+ gl.glLoadIdentity()
+ gl.glOrtho(0, width, 0, height+4, -1, 1)
+ gl.glMatrixMode(gl.GL_MODELVIEW)
+
+
+if glut.glutGetWindow() > 0:
+ interactive = True
+ glut.glutInit(sys.argv)
+ glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
+ glut.GLUT_RGBA |
+ glut.GLUT_DEPTH)
+ glut.glutShowWindow()
+else:
+ glut.glutCreateWindow('gui-glut')
+ interactive = False
+
+glut.glutDisplayFunc(display)
+glut.glutReshapeFunc(resize)
+gl.glClearColor(0,0,0,1)
+
+if not interactive:
+ glut.glutMainLoop()

0 comments on commit 89161a5

Please sign in to comment.