From c6756143130a2532a765942d886eec53dcd51ea4 2011-09-16 11:13:56 From: Nicolas Rougier Date: 2011-09-16 11:13:56 Subject: [PATCH] Removed the timer callback in favor of the idle one and re-use wx waiting time after an event is processed. This make things more reactive. Also, the created window is now made insivisible and is not supposed to be ever show or detroyed. Finally, fixed the bug in window closing for linux platform using the glutSetOption available on Freeglut. --- diff --git a/IPython/lib/inputhook.py b/IPython/lib/inputhook.py index d435a8e..d34e96d 100755 --- a/IPython/lib/inputhook.py +++ b/IPython/lib/inputhook.py @@ -311,20 +311,26 @@ class InputHookManager(object): glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH """ - from glut_support import * + import OpenGL.GLUT as glut + from IPython.lib.inputhookglut import * if not self._apps.has_key(GUI_GLUT): glut.glutInit(sys.argv) glut.glutInitDisplayMode(glut_display_mode) + # This is specific to freeglut + if bool(glut.glutSetOption): + glut.glutSetOption(glut.GLUT_ACTION_ON_WINDOW_CLOSE, + glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS) glut.glutCreateWindow(sys.argv[0]) + glut.glutReshapeWindow( 1, 1 ) glut.glutHideWindow() glut.glutWMCloseFunc(glut_close) glut.glutDisplayFunc(glut_display) - glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps) + glut.glutIdleFunc( glut_idle) else: glut.glutWMCloseFunc(glut_close) glut.glutDisplayFunc(glut_display) - glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps) + glut.glutIdleFunc( glut_idle) self.set_inputhook(inputhook_glut) self._current_gui = GUI_GLUT self._apps[GUI_GLUT] = True diff --git a/IPython/lib/inputhookglut.py b/IPython/lib/inputhookglut.py new file mode 100644 index 0000000..83c61da --- /dev/null +++ b/IPython/lib/inputhookglut.py @@ -0,0 +1,176 @@ +# coding: utf-8 +""" +GLUT Inputhook support functions +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +# 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 the idle callback to +# ensure we got at least one event that will unblock the function. +# +# Furthermore, it is not possible to install these handlers without a window +# being first created. We choose to make this window invisible. 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. + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import os +import sys +import time +import signal +import OpenGL +import OpenGL.GLUT as glut +import OpenGL.platform as platform +from timeit import default_timer as clock + +#----------------------------------------------------------------------------- +# Constants +#----------------------------------------------------------------------------- + +# 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) + +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.''') + + +#----------------------------------------------------------------------------- +# Platform-dependent imports and functions +#----------------------------------------------------------------------------- + +if os.name == 'posix': + import select + + def stdin_ready(): + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + if infds: + return True + else: + return False + +elif sys.platform == 'win32': + import msvcrt + + def stdin_ready(): + return msvcrt.kbhit() + +#----------------------------------------------------------------------------- +# Callback functions +#----------------------------------------------------------------------------- + +def glut_display(): + # Dummy display function + pass + +def glut_idle(): + # Dummy idle function + pass + +def glut_close(): + # Close function only hides the current window + glut.glutHideWindow() + glutMainLoopEvent() + +def glut_int_handler(signum, frame): + # Catch sigint and print the defautl message + signal.signal(signal.SIGINT, signal.default_int_handler) + print '\nKeyboardInterrupt' + # Need to reprint the prompt at this stage + + + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- +def inputhook_glut(): + """Run the pyglet event loop by processing pending events only. + + This keeps processing pending events until stdin is ready. After + processing all pending events, a call to time.sleep is inserted. This is + needed, otherwise, CPU usage is at 100%. This sleep time should be tuned + though for best performance. + """ + # We need to protect against a user pressing Control-C when IPython is + # idle and this is running. We trap KeyboardInterrupt and pass. + + signal.signal(signal.SIGINT, glut_int_handler) + + try: + t = clock() + + # Make sure the default window is set after a window has been closed + if glut.glutGetWindow() == 0: + glut.glutSetWindow( 1 ) + glutMainLoopEvent() + return 0 + + while not stdin_ready(): + glutMainLoopEvent() + # We need to sleep at this point to keep the idle CPU load + # low. However, if sleep to long, GUI response is poor. As + # a compromise, we watch how often GUI events are being processed + # and switch between a short and long sleep time. Here are some + # stats useful in helping to tune this. + # time CPU load + # 0.001 13% + # 0.005 3% + # 0.01 1.5% + # 0.05 0.5% + used_time = clock() - t + if used_time > 5*60.0: + # print 'Sleep for 5 s' # dbg + time.sleep(5.0) + elif used_time > 10.0: + # print 'Sleep for 1 s' # dbg + time.sleep(1.0) + elif used_time > 0.1: + # Few GUI events coming in, so we can sleep longer + # print 'Sleep for 0.05 s' # dbg + time.sleep(0.05) + else: + # Many GUI events coming in, so sleep only very little + time.sleep(0.001) + except KeyboardInterrupt: + pass + return 0 diff --git a/docs/examples/lib/gui-glut.py b/docs/examples/lib/gui-glut.py index c9982e1..2643b3e 100755 --- a/docs/examples/lib/gui-glut.py +++ b/docs/examples/lib/gui-glut.py @@ -15,6 +15,9 @@ import sys import OpenGL.GL as gl import OpenGL.GLUT as glut +def close(): + glut.glutDestroyWindow(glut.glutGetWindow()) + def display(): gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) glut.glutSwapBuffers() @@ -26,20 +29,22 @@ def resize(width,height): 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.glutCreateWindow('gui-glut') glut.glutDisplayFunc(display) glut.glutReshapeFunc(resize) +# This is necessary on osx to be able to close the window +# (else the close button is disabled) +if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT): + glut.glutWMCloseFunc(close) gl.glClearColor(0,0,0,1) if not interactive: