##// END OF EJS Templates
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.
Nicolas Rougier -
Show More
@@ -0,0 +1,176 b''
1 # coding: utf-8
2 """
3 GLUT Inputhook support functions
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 # GLUT is quite an old library and it is difficult to ensure proper
14 # integration within IPython since original GLUT does not allow to handle
15 # events one by one. Instead, it requires for the mainloop to be entered
16 # and never returned (there is not even a function to exit he
17 # mainloop). Fortunately, there are alternatives such as freeglut
18 # (available for linux and windows) and the OSX implementation gives
19 # access to a glutCheckLoop() function that blocks itself until a new
20 # event is received. This means we have to setup the idle callback to
21 # ensure we got at least one event that will unblock the function.
22 #
23 # Furthermore, it is not possible to install these handlers without a window
24 # being first created. We choose to make this window invisible. This means that
25 # display mode options are set at this level and user won't be able to change
26 # them later without modifying the code. This should probably be made available
27 # via IPython options system.
28
29 #-----------------------------------------------------------------------------
30 # Imports
31 #-----------------------------------------------------------------------------
32 import os
33 import sys
34 import time
35 import signal
36 import OpenGL
37 import OpenGL.GLUT as glut
38 import OpenGL.platform as platform
39 from timeit import default_timer as clock
40
41 #-----------------------------------------------------------------------------
42 # Constants
43 #-----------------------------------------------------------------------------
44
45 # Frame per second : 60
46 # Should probably be an IPython option
47 glut_fps = 60
48
49
50 # Display mode : double buffeed + rgba + depth
51 # Should probably be an IPython option
52 glut_display_mode = (glut.GLUT_DOUBLE |
53 glut.GLUT_RGBA |
54 glut.GLUT_DEPTH)
55
56 glutMainLoopEvent = None
57 if sys.platform == 'darwin':
58 try:
59 glutCheckLoop = platform.createBaseFunction(
60 'glutCheckLoop', dll=platform.GLUT, resultType=None,
61 argTypes=[],
62 doc='glutCheckLoop( ) -> None',
63 argNames=(),
64 )
65 except AttributeError:
66 raise RuntimeError(
67 '''Your glut implementation does not allow interactive sessions'''
68 '''Consider installing freeglut.''')
69 glutMainLoopEvent = glutCheckLoop
70 elif glut.HAVE_FREEGLUT:
71 glutMainLoopEvent = glut.glutMainLoopEvent
72 else:
73 raise RuntimeError(
74 '''Your glut implementation does not allow interactive sessions. '''
75 '''Consider installing freeglut.''')
76
77
78 #-----------------------------------------------------------------------------
79 # Platform-dependent imports and functions
80 #-----------------------------------------------------------------------------
81
82 if os.name == 'posix':
83 import select
84
85 def stdin_ready():
86 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
87 if infds:
88 return True
89 else:
90 return False
91
92 elif sys.platform == 'win32':
93 import msvcrt
94
95 def stdin_ready():
96 return msvcrt.kbhit()
97
98 #-----------------------------------------------------------------------------
99 # Callback functions
100 #-----------------------------------------------------------------------------
101
102 def glut_display():
103 # Dummy display function
104 pass
105
106 def glut_idle():
107 # Dummy idle function
108 pass
109
110 def glut_close():
111 # Close function only hides the current window
112 glut.glutHideWindow()
113 glutMainLoopEvent()
114
115 def glut_int_handler(signum, frame):
116 # Catch sigint and print the defautl message
117 signal.signal(signal.SIGINT, signal.default_int_handler)
118 print '\nKeyboardInterrupt'
119 # Need to reprint the prompt at this stage
120
121
122
123 #-----------------------------------------------------------------------------
124 # Code
125 #-----------------------------------------------------------------------------
126 def inputhook_glut():
127 """Run the pyglet event loop by processing pending events only.
128
129 This keeps processing pending events until stdin is ready. After
130 processing all pending events, a call to time.sleep is inserted. This is
131 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
132 though for best performance.
133 """
134 # We need to protect against a user pressing Control-C when IPython is
135 # idle and this is running. We trap KeyboardInterrupt and pass.
136
137 signal.signal(signal.SIGINT, glut_int_handler)
138
139 try:
140 t = clock()
141
142 # Make sure the default window is set after a window has been closed
143 if glut.glutGetWindow() == 0:
144 glut.glutSetWindow( 1 )
145 glutMainLoopEvent()
146 return 0
147
148 while not stdin_ready():
149 glutMainLoopEvent()
150 # We need to sleep at this point to keep the idle CPU load
151 # low. However, if sleep to long, GUI response is poor. As
152 # a compromise, we watch how often GUI events are being processed
153 # and switch between a short and long sleep time. Here are some
154 # stats useful in helping to tune this.
155 # time CPU load
156 # 0.001 13%
157 # 0.005 3%
158 # 0.01 1.5%
159 # 0.05 0.5%
160 used_time = clock() - t
161 if used_time > 5*60.0:
162 # print 'Sleep for 5 s' # dbg
163 time.sleep(5.0)
164 elif used_time > 10.0:
165 # print 'Sleep for 1 s' # dbg
166 time.sleep(1.0)
167 elif used_time > 0.1:
168 # Few GUI events coming in, so we can sleep longer
169 # print 'Sleep for 0.05 s' # dbg
170 time.sleep(0.05)
171 else:
172 # Many GUI events coming in, so sleep only very little
173 time.sleep(0.001)
174 except KeyboardInterrupt:
175 pass
176 return 0
@@ -1,441 +1,447 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 Inputhook management for GUI event loop integration.
3 Inputhook management for GUI event loop integration.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import ctypes
17 import ctypes
18 import sys
18 import sys
19 import warnings
19 import warnings
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Constants
22 # Constants
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 # Constants for identifying the GUI toolkits.
25 # Constants for identifying the GUI toolkits.
26 GUI_WX = 'wx'
26 GUI_WX = 'wx'
27 GUI_QT = 'qt'
27 GUI_QT = 'qt'
28 GUI_QT4 = 'qt4'
28 GUI_QT4 = 'qt4'
29 GUI_GTK = 'gtk'
29 GUI_GTK = 'gtk'
30 GUI_TK = 'tk'
30 GUI_TK = 'tk'
31 GUI_OSX = 'osx'
31 GUI_OSX = 'osx'
32 GUI_GLUT = 'glut'
32 GUI_GLUT = 'glut'
33 GUI_PYGLET = 'pyglet'
33 GUI_PYGLET = 'pyglet'
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Utility classes
36 # Utility classes
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Main InputHookManager class
41 # Main InputHookManager class
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44
44
45 class InputHookManager(object):
45 class InputHookManager(object):
46 """Manage PyOS_InputHook for different GUI toolkits.
46 """Manage PyOS_InputHook for different GUI toolkits.
47
47
48 This class installs various hooks under ``PyOSInputHook`` to handle
48 This class installs various hooks under ``PyOSInputHook`` to handle
49 GUI event loop integration.
49 GUI event loop integration.
50 """
50 """
51
51
52 def __init__(self):
52 def __init__(self):
53 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
53 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
54 self._apps = {}
54 self._apps = {}
55 self._reset()
55 self._reset()
56
56
57 def _reset(self):
57 def _reset(self):
58 self._callback_pyfunctype = None
58 self._callback_pyfunctype = None
59 self._callback = None
59 self._callback = None
60 self._installed = False
60 self._installed = False
61 self._current_gui = None
61 self._current_gui = None
62
62
63 def get_pyos_inputhook(self):
63 def get_pyos_inputhook(self):
64 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
64 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
65 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
65 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
66
66
67 def get_pyos_inputhook_as_func(self):
67 def get_pyos_inputhook_as_func(self):
68 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
68 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
69 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
69 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
70
70
71 def set_inputhook(self, callback):
71 def set_inputhook(self, callback):
72 """Set PyOS_InputHook to callback and return the previous one."""
72 """Set PyOS_InputHook to callback and return the previous one."""
73 self._callback = callback
73 self._callback = callback
74 self._callback_pyfunctype = self.PYFUNC(callback)
74 self._callback_pyfunctype = self.PYFUNC(callback)
75 pyos_inputhook_ptr = self.get_pyos_inputhook()
75 pyos_inputhook_ptr = self.get_pyos_inputhook()
76 original = self.get_pyos_inputhook_as_func()
76 original = self.get_pyos_inputhook_as_func()
77 pyos_inputhook_ptr.value = \
77 pyos_inputhook_ptr.value = \
78 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
78 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
79 self._installed = True
79 self._installed = True
80 return original
80 return original
81
81
82 def clear_inputhook(self, app=None):
82 def clear_inputhook(self, app=None):
83 """Set PyOS_InputHook to NULL and return the previous one.
83 """Set PyOS_InputHook to NULL and return the previous one.
84
84
85 Parameters
85 Parameters
86 ----------
86 ----------
87 app : optional, ignored
87 app : optional, ignored
88 This parameter is allowed only so that clear_inputhook() can be
88 This parameter is allowed only so that clear_inputhook() can be
89 called with a similar interface as all the ``enable_*`` methods. But
89 called with a similar interface as all the ``enable_*`` methods. But
90 the actual value of the parameter is ignored. This uniform interface
90 the actual value of the parameter is ignored. This uniform interface
91 makes it easier to have user-level entry points in the main IPython
91 makes it easier to have user-level entry points in the main IPython
92 app like :meth:`enable_gui`."""
92 app like :meth:`enable_gui`."""
93 pyos_inputhook_ptr = self.get_pyos_inputhook()
93 pyos_inputhook_ptr = self.get_pyos_inputhook()
94 original = self.get_pyos_inputhook_as_func()
94 original = self.get_pyos_inputhook_as_func()
95 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
95 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
96 self._reset()
96 self._reset()
97 return original
97 return original
98
98
99 def clear_app_refs(self, gui=None):
99 def clear_app_refs(self, gui=None):
100 """Clear IPython's internal reference to an application instance.
100 """Clear IPython's internal reference to an application instance.
101
101
102 Whenever we create an app for a user on qt4 or wx, we hold a
102 Whenever we create an app for a user on qt4 or wx, we hold a
103 reference to the app. This is needed because in some cases bad things
103 reference to the app. This is needed because in some cases bad things
104 can happen if a user doesn't hold a reference themselves. This
104 can happen if a user doesn't hold a reference themselves. This
105 method is provided to clear the references we are holding.
105 method is provided to clear the references we are holding.
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109 gui : None or str
109 gui : None or str
110 If None, clear all app references. If ('wx', 'qt4') clear
110 If None, clear all app references. If ('wx', 'qt4') clear
111 the app for that toolkit. References are not held for gtk or tk
111 the app for that toolkit. References are not held for gtk or tk
112 as those toolkits don't have the notion of an app.
112 as those toolkits don't have the notion of an app.
113 """
113 """
114 if gui is None:
114 if gui is None:
115 self._apps = {}
115 self._apps = {}
116 elif self._apps.has_key(gui):
116 elif self._apps.has_key(gui):
117 del self._apps[gui]
117 del self._apps[gui]
118
118
119 def enable_wx(self, app=None):
119 def enable_wx(self, app=None):
120 """Enable event loop integration with wxPython.
120 """Enable event loop integration with wxPython.
121
121
122 Parameters
122 Parameters
123 ----------
123 ----------
124 app : WX Application, optional.
124 app : WX Application, optional.
125 Running application to use. If not given, we probe WX for an
125 Running application to use. If not given, we probe WX for an
126 existing application object, and create a new one if none is found.
126 existing application object, and create a new one if none is found.
127
127
128 Notes
128 Notes
129 -----
129 -----
130 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
130 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
131 the wxPython to integrate with terminal based applications like
131 the wxPython to integrate with terminal based applications like
132 IPython.
132 IPython.
133
133
134 If ``app`` is not given we probe for an existing one, and return it if
134 If ``app`` is not given we probe for an existing one, and return it if
135 found. If no existing app is found, we create an :class:`wx.App` as
135 found. If no existing app is found, we create an :class:`wx.App` as
136 follows::
136 follows::
137
137
138 import wx
138 import wx
139 app = wx.App(redirect=False, clearSigInt=False)
139 app = wx.App(redirect=False, clearSigInt=False)
140 """
140 """
141 from IPython.lib.inputhookwx import inputhook_wx
141 from IPython.lib.inputhookwx import inputhook_wx
142 self.set_inputhook(inputhook_wx)
142 self.set_inputhook(inputhook_wx)
143 self._current_gui = GUI_WX
143 self._current_gui = GUI_WX
144 import wx
144 import wx
145 if app is None:
145 if app is None:
146 app = wx.GetApp()
146 app = wx.GetApp()
147 if app is None:
147 if app is None:
148 app = wx.App(redirect=False, clearSigInt=False)
148 app = wx.App(redirect=False, clearSigInt=False)
149 app._in_event_loop = True
149 app._in_event_loop = True
150 self._apps[GUI_WX] = app
150 self._apps[GUI_WX] = app
151 return app
151 return app
152
152
153 def disable_wx(self):
153 def disable_wx(self):
154 """Disable event loop integration with wxPython.
154 """Disable event loop integration with wxPython.
155
155
156 This merely sets PyOS_InputHook to NULL.
156 This merely sets PyOS_InputHook to NULL.
157 """
157 """
158 if self._apps.has_key(GUI_WX):
158 if self._apps.has_key(GUI_WX):
159 self._apps[GUI_WX]._in_event_loop = False
159 self._apps[GUI_WX]._in_event_loop = False
160 self.clear_inputhook()
160 self.clear_inputhook()
161
161
162 def enable_qt4(self, app=None):
162 def enable_qt4(self, app=None):
163 """Enable event loop integration with PyQt4.
163 """Enable event loop integration with PyQt4.
164
164
165 Parameters
165 Parameters
166 ----------
166 ----------
167 app : Qt Application, optional.
167 app : Qt Application, optional.
168 Running application to use. If not given, we probe Qt for an
168 Running application to use. If not given, we probe Qt for an
169 existing application object, and create a new one if none is found.
169 existing application object, and create a new one if none is found.
170
170
171 Notes
171 Notes
172 -----
172 -----
173 This methods sets the PyOS_InputHook for PyQt4, which allows
173 This methods sets the PyOS_InputHook for PyQt4, which allows
174 the PyQt4 to integrate with terminal based applications like
174 the PyQt4 to integrate with terminal based applications like
175 IPython.
175 IPython.
176
176
177 If ``app`` is not given we probe for an existing one, and return it if
177 If ``app`` is not given we probe for an existing one, and return it if
178 found. If no existing app is found, we create an :class:`QApplication`
178 found. If no existing app is found, we create an :class:`QApplication`
179 as follows::
179 as follows::
180
180
181 from PyQt4 import QtCore
181 from PyQt4 import QtCore
182 app = QtGui.QApplication(sys.argv)
182 app = QtGui.QApplication(sys.argv)
183 """
183 """
184 from IPython.external.qt_for_kernel import QtCore, QtGui
184 from IPython.external.qt_for_kernel import QtCore, QtGui
185
185
186 if 'pyreadline' in sys.modules:
186 if 'pyreadline' in sys.modules:
187 # see IPython GitHub Issue #281 for more info on this issue
187 # see IPython GitHub Issue #281 for more info on this issue
188 # Similar intermittent behavior has been reported on OSX,
188 # Similar intermittent behavior has been reported on OSX,
189 # but not consistently reproducible
189 # but not consistently reproducible
190 warnings.warn("""PyReadline's inputhook can conflict with Qt, causing delays
190 warnings.warn("""PyReadline's inputhook can conflict with Qt, causing delays
191 in interactive input. If you do see this issue, we recommend using another GUI
191 in interactive input. If you do see this issue, we recommend using another GUI
192 toolkit if you can, or disable readline with the configuration option
192 toolkit if you can, or disable readline with the configuration option
193 'TerminalInteractiveShell.readline_use=False', specified in a config file or
193 'TerminalInteractiveShell.readline_use=False', specified in a config file or
194 at the command-line""",
194 at the command-line""",
195 RuntimeWarning)
195 RuntimeWarning)
196
196
197 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
197 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
198 # was set when QtCore was imported, but if it ever got removed,
198 # was set when QtCore was imported, but if it ever got removed,
199 # you couldn't reset it. For earlier versions we can
199 # you couldn't reset it. For earlier versions we can
200 # probably implement a ctypes version.
200 # probably implement a ctypes version.
201 try:
201 try:
202 QtCore.pyqtRestoreInputHook()
202 QtCore.pyqtRestoreInputHook()
203 except AttributeError:
203 except AttributeError:
204 pass
204 pass
205
205
206 self._current_gui = GUI_QT4
206 self._current_gui = GUI_QT4
207 if app is None:
207 if app is None:
208 app = QtCore.QCoreApplication.instance()
208 app = QtCore.QCoreApplication.instance()
209 if app is None:
209 if app is None:
210 app = QtGui.QApplication([" "])
210 app = QtGui.QApplication([" "])
211 app._in_event_loop = True
211 app._in_event_loop = True
212 self._apps[GUI_QT4] = app
212 self._apps[GUI_QT4] = app
213 return app
213 return app
214
214
215 def disable_qt4(self):
215 def disable_qt4(self):
216 """Disable event loop integration with PyQt4.
216 """Disable event loop integration with PyQt4.
217
217
218 This merely sets PyOS_InputHook to NULL.
218 This merely sets PyOS_InputHook to NULL.
219 """
219 """
220 if self._apps.has_key(GUI_QT4):
220 if self._apps.has_key(GUI_QT4):
221 self._apps[GUI_QT4]._in_event_loop = False
221 self._apps[GUI_QT4]._in_event_loop = False
222 self.clear_inputhook()
222 self.clear_inputhook()
223
223
224 def enable_gtk(self, app=None):
224 def enable_gtk(self, app=None):
225 """Enable event loop integration with PyGTK.
225 """Enable event loop integration with PyGTK.
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 app : ignored
229 app : ignored
230 Ignored, it's only a placeholder to keep the call signature of all
230 Ignored, it's only a placeholder to keep the call signature of all
231 gui activation methods consistent, which simplifies the logic of
231 gui activation methods consistent, which simplifies the logic of
232 supporting magics.
232 supporting magics.
233
233
234 Notes
234 Notes
235 -----
235 -----
236 This methods sets the PyOS_InputHook for PyGTK, which allows
236 This methods sets the PyOS_InputHook for PyGTK, which allows
237 the PyGTK to integrate with terminal based applications like
237 the PyGTK to integrate with terminal based applications like
238 IPython.
238 IPython.
239 """
239 """
240 import gtk
240 import gtk
241 try:
241 try:
242 gtk.set_interactive(True)
242 gtk.set_interactive(True)
243 self._current_gui = GUI_GTK
243 self._current_gui = GUI_GTK
244 except AttributeError:
244 except AttributeError:
245 # For older versions of gtk, use our own ctypes version
245 # For older versions of gtk, use our own ctypes version
246 from IPython.lib.inputhookgtk import inputhook_gtk
246 from IPython.lib.inputhookgtk import inputhook_gtk
247 self.set_inputhook(inputhook_gtk)
247 self.set_inputhook(inputhook_gtk)
248 self._current_gui = GUI_GTK
248 self._current_gui = GUI_GTK
249
249
250 def disable_gtk(self):
250 def disable_gtk(self):
251 """Disable event loop integration with PyGTK.
251 """Disable event loop integration with PyGTK.
252
252
253 This merely sets PyOS_InputHook to NULL.
253 This merely sets PyOS_InputHook to NULL.
254 """
254 """
255 self.clear_inputhook()
255 self.clear_inputhook()
256
256
257 def enable_tk(self, app=None):
257 def enable_tk(self, app=None):
258 """Enable event loop integration with Tk.
258 """Enable event loop integration with Tk.
259
259
260 Parameters
260 Parameters
261 ----------
261 ----------
262 app : toplevel :class:`Tkinter.Tk` widget, optional.
262 app : toplevel :class:`Tkinter.Tk` widget, optional.
263 Running toplevel widget to use. If not given, we probe Tk for an
263 Running toplevel widget to use. If not given, we probe Tk for an
264 existing one, and create a new one if none is found.
264 existing one, and create a new one if none is found.
265
265
266 Notes
266 Notes
267 -----
267 -----
268 If you have already created a :class:`Tkinter.Tk` object, the only
268 If you have already created a :class:`Tkinter.Tk` object, the only
269 thing done by this method is to register with the
269 thing done by this method is to register with the
270 :class:`InputHookManager`, since creating that object automatically
270 :class:`InputHookManager`, since creating that object automatically
271 sets ``PyOS_InputHook``.
271 sets ``PyOS_InputHook``.
272 """
272 """
273 self._current_gui = GUI_TK
273 self._current_gui = GUI_TK
274 if app is None:
274 if app is None:
275 import Tkinter
275 import Tkinter
276 app = Tkinter.Tk()
276 app = Tkinter.Tk()
277 app.withdraw()
277 app.withdraw()
278 self._apps[GUI_TK] = app
278 self._apps[GUI_TK] = app
279 return app
279 return app
280
280
281 def disable_tk(self):
281 def disable_tk(self):
282 """Disable event loop integration with Tkinter.
282 """Disable event loop integration with Tkinter.
283
283
284 This merely sets PyOS_InputHook to NULL.
284 This merely sets PyOS_InputHook to NULL.
285 """
285 """
286 self.clear_inputhook()
286 self.clear_inputhook()
287
287
288
288
289 def enable_glut(self, app=None):
289 def enable_glut(self, app=None):
290 """ Enable event loop integration with GLUT.
290 """ Enable event loop integration with GLUT.
291
291
292 Parameters
292 Parameters
293 ----------
293 ----------
294
294
295 app : ignored
295 app : ignored
296 Ignored, it's only a placeholder to keep the call signature of all
296 Ignored, it's only a placeholder to keep the call signature of all
297 gui activation methods consistent, which simplifies the logic of
297 gui activation methods consistent, which simplifies the logic of
298 supporting magics.
298 supporting magics.
299
299
300 Notes
300 Notes
301 -----
301 -----
302
302
303 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
303 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
304 integrate with terminal based applications like IPython. Due to GLUT
304 integrate with terminal based applications like IPython. Due to GLUT
305 limitations, it is currently not possible to start the event loop
305 limitations, it is currently not possible to start the event loop
306 without first creating a window. You should thus not create another
306 without first creating a window. You should thus not create another
307 window but use instead the created one. See 'gui-glut.py' in the
307 window but use instead the created one. See 'gui-glut.py' in the
308 docs/examples/lib directory.
308 docs/examples/lib directory.
309
309
310 The default screen mode is set to:
310 The default screen mode is set to:
311 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
311 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
312 """
312 """
313
313
314 from glut_support import *
314 import OpenGL.GLUT as glut
315 from IPython.lib.inputhookglut import *
315
316
316 if not self._apps.has_key(GUI_GLUT):
317 if not self._apps.has_key(GUI_GLUT):
317 glut.glutInit(sys.argv)
318 glut.glutInit(sys.argv)
318 glut.glutInitDisplayMode(glut_display_mode)
319 glut.glutInitDisplayMode(glut_display_mode)
320 # This is specific to freeglut
321 if bool(glut.glutSetOption):
322 glut.glutSetOption(glut.GLUT_ACTION_ON_WINDOW_CLOSE,
323 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS)
319 glut.glutCreateWindow(sys.argv[0])
324 glut.glutCreateWindow(sys.argv[0])
325 glut.glutReshapeWindow( 1, 1 )
320 glut.glutHideWindow()
326 glut.glutHideWindow()
321 glut.glutWMCloseFunc(glut_close)
327 glut.glutWMCloseFunc(glut_close)
322 glut.glutDisplayFunc(glut_display)
328 glut.glutDisplayFunc(glut_display)
323 glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps)
329 glut.glutIdleFunc( glut_idle)
324 else:
330 else:
325 glut.glutWMCloseFunc(glut_close)
331 glut.glutWMCloseFunc(glut_close)
326 glut.glutDisplayFunc(glut_display)
332 glut.glutDisplayFunc(glut_display)
327 glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps)
333 glut.glutIdleFunc( glut_idle)
328 self.set_inputhook(inputhook_glut)
334 self.set_inputhook(inputhook_glut)
329 self._current_gui = GUI_GLUT
335 self._current_gui = GUI_GLUT
330 self._apps[GUI_GLUT] = True
336 self._apps[GUI_GLUT] = True
331
337
332
338
333 def disable_glut(self):
339 def disable_glut(self):
334 """Disable event loop integration with glut.
340 """Disable event loop integration with glut.
335
341
336 This sets PyOS_InputHook to NULL and set the display function to a
342 This sets PyOS_InputHook to NULL and set the display function to a
337 dummy one and set the timer to a dummy timer that will be triggered
343 dummy one and set the timer to a dummy timer that will be triggered
338 very far in the future.
344 very far in the future.
339 """
345 """
340 from glut_support import *
346 from glut_support import *
341
347
342 glut.glutHideWindow() # This is an event to be processed below
348 glut.glutHideWindow() # This is an event to be processed below
343 glutMainLoopEvent()
349 glutMainLoopEvent()
344 self.clear_inputhook()
350 self.clear_inputhook()
345
351
346 def enable_pyglet(self, app=None):
352 def enable_pyglet(self, app=None):
347 """Enable event loop integration with pyglet.
353 """Enable event loop integration with pyglet.
348
354
349 Parameters
355 Parameters
350 ----------
356 ----------
351 app : ignored
357 app : ignored
352 Ignored, it's only a placeholder to keep the call signature of all
358 Ignored, it's only a placeholder to keep the call signature of all
353 gui activation methods consistent, which simplifies the logic of
359 gui activation methods consistent, which simplifies the logic of
354 supporting magics.
360 supporting magics.
355
361
356 Notes
362 Notes
357 -----
363 -----
358 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
364 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
359 pyglet to integrate with terminal based applications like
365 pyglet to integrate with terminal based applications like
360 IPython.
366 IPython.
361
367
362 """
368 """
363 import pyglet
369 import pyglet
364 from IPython.lib.inputhookpyglet import inputhook_pyglet
370 from IPython.lib.inputhookpyglet import inputhook_pyglet
365 self.set_inputhook(inputhook_pyglet)
371 self.set_inputhook(inputhook_pyglet)
366 self._current_gui = GUI_PYGLET
372 self._current_gui = GUI_PYGLET
367 return app
373 return app
368
374
369 def disable_pyglet(self):
375 def disable_pyglet(self):
370 """Disable event loop integration with pyglet.
376 """Disable event loop integration with pyglet.
371
377
372 This merely sets PyOS_InputHook to NULL.
378 This merely sets PyOS_InputHook to NULL.
373 """
379 """
374 self.clear_inputhook()
380 self.clear_inputhook()
375
381
376 def current_gui(self):
382 def current_gui(self):
377 """Return a string indicating the currently active GUI or None."""
383 """Return a string indicating the currently active GUI or None."""
378 return self._current_gui
384 return self._current_gui
379
385
380 inputhook_manager = InputHookManager()
386 inputhook_manager = InputHookManager()
381
387
382 enable_wx = inputhook_manager.enable_wx
388 enable_wx = inputhook_manager.enable_wx
383 disable_wx = inputhook_manager.disable_wx
389 disable_wx = inputhook_manager.disable_wx
384 enable_qt4 = inputhook_manager.enable_qt4
390 enable_qt4 = inputhook_manager.enable_qt4
385 disable_qt4 = inputhook_manager.disable_qt4
391 disable_qt4 = inputhook_manager.disable_qt4
386 enable_gtk = inputhook_manager.enable_gtk
392 enable_gtk = inputhook_manager.enable_gtk
387 disable_gtk = inputhook_manager.disable_gtk
393 disable_gtk = inputhook_manager.disable_gtk
388 enable_tk = inputhook_manager.enable_tk
394 enable_tk = inputhook_manager.enable_tk
389 disable_tk = inputhook_manager.disable_tk
395 disable_tk = inputhook_manager.disable_tk
390 enable_glut = inputhook_manager.enable_glut
396 enable_glut = inputhook_manager.enable_glut
391 disable_glut = inputhook_manager.disable_glut
397 disable_glut = inputhook_manager.disable_glut
392 enable_pyglet = inputhook_manager.enable_pyglet
398 enable_pyglet = inputhook_manager.enable_pyglet
393 disable_pyglet = inputhook_manager.disable_pyglet
399 disable_pyglet = inputhook_manager.disable_pyglet
394 clear_inputhook = inputhook_manager.clear_inputhook
400 clear_inputhook = inputhook_manager.clear_inputhook
395 set_inputhook = inputhook_manager.set_inputhook
401 set_inputhook = inputhook_manager.set_inputhook
396 current_gui = inputhook_manager.current_gui
402 current_gui = inputhook_manager.current_gui
397 clear_app_refs = inputhook_manager.clear_app_refs
403 clear_app_refs = inputhook_manager.clear_app_refs
398
404
399
405
400 # Convenience function to switch amongst them
406 # Convenience function to switch amongst them
401 def enable_gui(gui=None, app=None):
407 def enable_gui(gui=None, app=None):
402 """Switch amongst GUI input hooks by name.
408 """Switch amongst GUI input hooks by name.
403
409
404 This is just a utility wrapper around the methods of the InputHookManager
410 This is just a utility wrapper around the methods of the InputHookManager
405 object.
411 object.
406
412
407 Parameters
413 Parameters
408 ----------
414 ----------
409 gui : optional, string or None
415 gui : optional, string or None
410 If None, clears input hook, otherwise it must be one of the recognized
416 If None, clears input hook, otherwise it must be one of the recognized
411 GUI names (see ``GUI_*`` constants in module).
417 GUI names (see ``GUI_*`` constants in module).
412
418
413 app : optional, existing application object.
419 app : optional, existing application object.
414 For toolkits that have the concept of a global app, you can supply an
420 For toolkits that have the concept of a global app, you can supply an
415 existing one. If not given, the toolkit will be probed for one, and if
421 existing one. If not given, the toolkit will be probed for one, and if
416 none is found, a new one will be created. Note that GTK does not have
422 none is found, a new one will be created. Note that GTK does not have
417 this concept, and passing an app if `gui`=="GTK" will raise an error.
423 this concept, and passing an app if `gui`=="GTK" will raise an error.
418
424
419 Returns
425 Returns
420 -------
426 -------
421 The output of the underlying gui switch routine, typically the actual
427 The output of the underlying gui switch routine, typically the actual
422 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
428 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
423 one.
429 one.
424 """
430 """
425 guis = {None: clear_inputhook,
431 guis = {None: clear_inputhook,
426 GUI_OSX: lambda app=False: None,
432 GUI_OSX: lambda app=False: None,
427 GUI_TK: enable_tk,
433 GUI_TK: enable_tk,
428 GUI_GTK: enable_gtk,
434 GUI_GTK: enable_gtk,
429 GUI_WX: enable_wx,
435 GUI_WX: enable_wx,
430 GUI_QT: enable_qt4, # qt3 not supported
436 GUI_QT: enable_qt4, # qt3 not supported
431 GUI_QT4: enable_qt4,
437 GUI_QT4: enable_qt4,
432 GUI_GLUT: enable_glut,
438 GUI_GLUT: enable_glut,
433 GUI_PYGLET: enable_pyglet,
439 GUI_PYGLET: enable_pyglet,
434 }
440 }
435 try:
441 try:
436 gui_hook = guis[gui]
442 gui_hook = guis[gui]
437 except KeyError:
443 except KeyError:
438 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
444 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
439 raise ValueError(e)
445 raise ValueError(e)
440 return gui_hook(app)
446 return gui_hook(app)
441
447
@@ -1,46 +1,51 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Simple GLUT example to manually test event loop integration.
2 """Simple GLUT example to manually test event loop integration.
3
3
4 This is meant to run tests manually in ipython as:
4 This is meant to run tests manually in ipython as:
5
5
6 In [5]: %gui glut
6 In [5]: %gui glut
7
7
8 In [6]: %run gui-glut.py
8 In [6]: %run gui-glut.py
9
9
10 In [7]: gl.glClearColor(1,1,1,1)
10 In [7]: gl.glClearColor(1,1,1,1)
11 """
11 """
12
12
13 #!/usr/bin/env python
13 #!/usr/bin/env python
14 import sys
14 import sys
15 import OpenGL.GL as gl
15 import OpenGL.GL as gl
16 import OpenGL.GLUT as glut
16 import OpenGL.GLUT as glut
17
17
18 def close():
19 glut.glutDestroyWindow(glut.glutGetWindow())
20
18 def display():
21 def display():
19 gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
22 gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
20 glut.glutSwapBuffers()
23 glut.glutSwapBuffers()
21
24
22 def resize(width,height):
25 def resize(width,height):
23 gl.glViewport(0, 0, width, height+4)
26 gl.glViewport(0, 0, width, height+4)
24 gl.glMatrixMode(gl.GL_PROJECTION)
27 gl.glMatrixMode(gl.GL_PROJECTION)
25 gl.glLoadIdentity()
28 gl.glLoadIdentity()
26 gl.glOrtho(0, width, 0, height+4, -1, 1)
29 gl.glOrtho(0, width, 0, height+4, -1, 1)
27 gl.glMatrixMode(gl.GL_MODELVIEW)
30 gl.glMatrixMode(gl.GL_MODELVIEW)
28
31
29
30 if glut.glutGetWindow() > 0:
32 if glut.glutGetWindow() > 0:
31 interactive = True
33 interactive = True
32 glut.glutInit(sys.argv)
34 glut.glutInit(sys.argv)
33 glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
35 glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
34 glut.GLUT_RGBA |
36 glut.GLUT_RGBA |
35 glut.GLUT_DEPTH)
37 glut.GLUT_DEPTH)
36 glut.glutShowWindow()
37 else:
38 else:
38 glut.glutCreateWindow('gui-glut')
39 interactive = False
39 interactive = False
40
40
41 glut.glutCreateWindow('gui-glut')
41 glut.glutDisplayFunc(display)
42 glut.glutDisplayFunc(display)
42 glut.glutReshapeFunc(resize)
43 glut.glutReshapeFunc(resize)
44 # This is necessary on osx to be able to close the window
45 # (else the close button is disabled)
46 if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT):
47 glut.glutWMCloseFunc(close)
43 gl.glClearColor(0,0,0,1)
48 gl.glClearColor(0,0,0,1)
44
49
45 if not interactive:
50 if not interactive:
46 glut.glutMainLoop()
51 glut.glutMainLoop()
General Comments 0
You need to be logged in to leave comments. Login now