##// END OF EJS Templates
Tried to fix the CTRL-C problem (https://github.com/ipython/ipython/pull/742) and take other comments/typos into account
Nicolas Rougier -
Show More
@@ -1,522 +1,585 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
289 <<<<<<< HEAD
290
291 def enable_pyglet(self, app=None):
292 """Enable event loop integration with pyglet.
293 =======
294 def enable_glut(self, app=None):
288 def enable_glut(self, app=None):
295 """Enable event loop integration with GLUT.
289 """Enable event loop integration with GLUT.
296 >>>>>>> Added code for the GLUT interactive session
297
290
298 Parameters
291 Parameters
299 ----------
292 ----------
300 app : ignored
293 app : ignored
301 Ignored, it's only a placeholder to keep the call signature of all
294 Ignored, it's only a placeholder to keep the call signature of all
302 gui activation methods consistent, which simplifies the logic of
295 gui activation methods consistent, which simplifies the logic of
303 supporting magics.
296 supporting magics.
304
297
305 Notes
298 Notes
306 -----
299 -----
307 <<<<<<< HEAD
308 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
309 pyglet to integrate with terminal based applications like
310 IPython.
311
312 """
313 import pyglet
314 from IPython.lib.inputhookpyglet import inputhook_pyglet
315 self.set_inputhook(inputhook_pyglet)
316 self._current_gui = GUI_PYGLET
317 return app
318
319 def disable_pyglet(self):
320 """Disable event loop integration with pyglet.
321
300
322 This merely sets PyOS_InputHook to NULL.
301 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
323 """
302 integrate with terminal based applications like IPython. Due to GLUT
324 =======
303 limitations, it is currently not possible to start the event loop
325 This methods sets the PyOS_InputHook for GLUT, which allows
304 without first creating a window. You should thus not create another
326 the GLUT to integrate with terminal based applications like
305 window but use instead the created one. See 'gui-glut.py' in the
327 IPython.
306 docs/examples/lib directory.
307
308 The default screen mode is set to:
328
309
329 GLUT is quite an old library and it is difficult to ensure proper
310 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
330 integration within IPython since original GLUT does not allow to handle
331 events one by one. Instead, it requires for the mainloop to be entered
332 and never returned (there is not even a function to exit he
333 mainloop). Fortunately, there are alternatives such as freeglut
334 (available for linux and windows) and the OSX implementation gives
335 access to a glutCheckLoop() function that blocks itself until a new
336 event is received. This means we have to setup a default timer to
337 ensure we got at least one event that will unblock the function. We set
338 a default timer of 60fps.
339
340 Furthermore, it is not possible to install these handlers without a
341 window being first created. We choose to make this window invisible and
342 the user is supposed to make it visible when needed (see gui-glut.py in
343 the docs/examples/lib directory). This means that display mode options
344 are set at this level and user won't be able to change them later
345 without modifying the code. This should probably be made available via
346 IPython options system.
347
311
348 Script integration
312 Script integration
349 ------------------
313 ------------------
350
314
351 if glut.glutGetWindow() > 0:
315 if glut.glutGetWindow() > 0:
352 interactive = True
316 interactive = True
353 glut.glutShowWindow()
317 glut.glutShowWindow()
354 else:
318 else:
355 interactive = False
319 interactive = False
356 glut.glutInit(sys.argv)
320 glut.glutInit(sys.argv)
357 glut.glutInitDisplayMode( glut.GLUT_DOUBLE |
321 glut.glutInitDisplayMode( glut.GLUT_DOUBLE |
358 glut.GLUT_RGBA |
322 glut.GLUT_RGBA |
359 glut.GLUT_DEPTH )
323 glut.GLUT_DEPTH )
360 ...
324 ...
361 if not interactive:
325 if not interactive:
362 glut.glutMainLoop()
326 glut.glutMainLoop()
363 """
327 """
328 # GLUT is quite an old library and it is difficult to ensure proper
329 # integration within IPython since original GLUT does not allow to handle
330 # events one by one. Instead, it requires for the mainloop to be entered
331 # and never returned (there is not even a function to exit he
332 # mainloop). Fortunately, there are alternatives such as freeglut
333 # (available for linux and windows) and the OSX implementation gives
334 # access to a glutCheckLoop() function that blocks itself until a new
335 # event is received. This means we have to setup a default timer to
336 # ensure we got at least one event that will unblock the function. We set
337 # a default timer of 60fps.
338 #
339 # Furthermore, it is not possible to install these handlers without a
340 # window being first created. We choose to make this window invisible and
341 # the user is supposed to make it visible when needed (see gui-glut.py in
342 # the docs/examples/lib directory). This means that display mode options
343 # are set at this level and user won't be able to change them later
344 # without modifying the code. This should probably be made available via
345 # IPython options system.
346
347 import OpenGL
348 OpenGL.ERROR_CHECKING = False
364 import OpenGL.GLUT as glut
349 import OpenGL.GLUT as glut
365 import OpenGL.platform as platform
350 import OpenGL.platform as platform
351 import time
366
352
367 def timer_none(fps):
353
368 ''' Dummy timer function '''
354 # Frame per second : 60
369 pass
355 # Should probably be an IPython option
356 glut_fps = 60
357
358
359 # Display mode : double buffeed + rgba + depth
360 # Should probably be an IPython option
361 glut_display_mode = (glut.GLUT_DOUBLE |
362 glut.GLUT_RGBA |
363 glut.GLUT_DEPTH)
364
365 glut_interrupted = False
370
366
371 def display():
367 def display():
372 ''' Dummy display function '''
368 ''' Dummy display function '''
373 pass
369 pass
374
370
375 def timer(fps):
371 def timer(fps):
376 # We should normally set the active window to 1 and post a
372 # We should normally set the active window to 1 and post a
377 # redisplay for each window. The problem is that we do not know
373 # redisplay for each window. The problem is that we do not know
378 # how much active windows we have and there is no function in glut
374 # how much active windows we have and there is no function in glut
379 # to get that number.
375 # to get that number.
380 # glut.glutSetWindow(1)
376 # glut.glutSetWindow(1)
381 glut.glutTimerFunc( int(1000.0/fps), timer, fps)
377 glut.glutTimerFunc( int(1000.0/fps), timer, fps)
382 glut.glutPostRedisplay()
378 glut.glutPostRedisplay()
383
379
380 def close():
381 glut.glutHideWindow()
382
384 glutMainLoopEvent = None
383 glutMainLoopEvent = None
385 if sys.platform == 'darwin':
384 if sys.platform == 'darwin':
386 try:
385 try:
387 glutCheckLoop = platform.createBaseFunction(
386 glutCheckLoop = platform.createBaseFunction(
388 'glutCheckLoop', dll=platform.GLUT, resultType=None,
387 'glutCheckLoop', dll=platform.GLUT, resultType=None,
389 argTypes=[],
388 argTypes=[],
390 doc='glutCheckLoop( ) -> None',
389 doc='glutCheckLoop( ) -> None',
391 argNames=(),
390 argNames=(),
392 )
391 )
393 except AttributeError:
392 except AttributeError:
394 raise RuntimeError(
393 raise RuntimeError(
395 '''Your glut implementation does not allow interactive sessions'''
394 '''Your glut implementation does not allow interactive sessions'''
396 '''Consider installing freeglut.''')
395 '''Consider installing freeglut.''')
397 glutMainLoopEvent = glutCheckLoop
396 glutMainLoopEvent = glutCheckLoop
398 elif glut.HAVE_FREEGLUT:
397 elif glut.HAVE_FREEGLUT:
399 glutMainLoopEvent = glut.glutMainLoopEvent
398 glutMainLoopEvent = glut.glutMainLoopEvent
400 else:
399 else:
401 raise RuntimeError(
400 raise RuntimeError(
402 '''Your glut implementation does not allow interactive sessions. '''
401 '''Your glut implementation does not allow interactive sessions. '''
403 '''Consider installing freeglut.''')
402 '''Consider installing freeglut.''')
404
403
405 def inputhook_glut():
404 def inputhook_glut():
406 """ Process pending GLUT events only. """
405 """ Process pending GLUT events only. """
407 # We need to protect against a user pressing Control-C when IPython is
406
408 # idle and this is running. We trap KeyboardInterrupt and pass.
407 # We need to protect against a user pressing Control-C when IPython
408 # is idle and this is running. We should trap KeyboardInterrupt and
409 # pass but it does not seem to work with glutMainLoopEvent.
410 # Instead, we setup a signal handler on SIGINT and returns after
411 # having restored the default python SIGINT handler.
412 import signal
413 def handler(signum, frame):
414 signal.signal(signal.SIGINT, signal.default_int_handler)
415 print '\nKeyboardInterrupt'
416 # Need to reprint the prompt at this stage
417
418 signal.signal(signal.SIGINT, handler)
419
409 try:
420 try:
410 glutMainLoopEvent()
421 glutMainLoopEvent()
411 except KeyboardInterrupt:
422 except KeyboardInterrupt: # this catch doesn't work for some reasons...
412 pass
423 pass
413 return 0
414
424
415 # Frame per second : 60
425 return 0
416 # Should be probably an IPython option
426
417 fps = 60
418 if not self._apps.has_key(GUI_GLUT):
427 if not self._apps.has_key(GUI_GLUT):
419 glut.glutInit(sys.argv)
428 glut.glutInit(sys.argv)
420
429 # Display mode should be also an Ipython option since user won't be able
421 # Display mode shoudl be also an Ipython option since user won't be able
422 # to change it later
430 # to change it later
423 glut.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH)
431 glut.glutInitDisplayMode(glut_display_mode)
424 glut.glutCreateWindow(sys.argv[0])
432 glut.glutCreateWindow(sys.argv[0])
425 # glut.glutReshapeWindow(1,1)
426 glut.glutHideWindow()
433 glut.glutHideWindow()
434 glut.glutWMCloseFunc(close)
427 glut.glutDisplayFunc(display)
435 glut.glutDisplayFunc(display)
428 glut.glutTimerFunc( int(1000.0/fps), timer, fps)
436 glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
429 else:
437 else:
438 glut.glutWMCloseFunc(close)
430 glut.glutDisplayFunc(display)
439 glut.glutDisplayFunc(display)
431 glut.glutTimerFunc( int(1000.0/fps), timer, fps)
440 glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
432
441
433 self.set_inputhook(inputhook_glut)
442 self.set_inputhook(inputhook_glut)
434 self._current_gui = GUI_GLUT
443 self._current_gui = GUI_GLUT
435 self._apps[GUI_GLUT] = True
444 self._apps[GUI_GLUT] = True
436
445
437
438 def disable_glut(self):
446 def disable_glut(self):
439 """Disable event loop integration with glut.
447 """Disable event loop integration with glut.
440
448
441 This sets PyOS_InputHook to NULL and set the display function to a
449 This sets PyOS_InputHook to NULL and set the display function to a
442 dummy one and set the timer to a dummy timer that will be triggered
450 dummy one and set the timer to a dummy timer that will be triggered
443 very far in the future.
451 very far in the future.
444 """
452 """
445 glut.HideWindow()
453 import signal
446 glut.glutTimerFunc( sys.maxint-1, null_timer_none, 0)
454 import OpenGL
447 >>>>>>> Added code for the GLUT interactive session
455 OpenGL.ERROR_CHECKING = False
456 import OpenGL.GLUT as glut
457 import OpenGL.platform as platform
458
459 def timer_none(fps):
460 ''' Dummy timer function '''
461 pass
462
463 glutMainLoopEvent = None
464 if sys.platform == 'darwin':
465 try:
466 glutCheckLoop = platform.createBaseFunction(
467 'glutCheckLoop', dll=platform.GLUT, resultType=None,
468 argTypes=[],
469 doc='glutCheckLoop( ) -> None',
470 argNames=(),
471 )
472 except AttributeError:
473 raise RuntimeError(
474 '''Your glut implementation does not allow interactive sessions'''
475 '''Consider installing freeglut.''')
476 glutMainLoopEvent = glutCheckLoop
477 elif glut.HAVE_FREEGLUT:
478 glutMainLoopEvent = glut.glutMainLoopEvent
479 else:
480 raise RuntimeError(
481 '''Your glut implementation does not allow interactive sessions. '''
482 '''Consider installing freeglut.''')
483
484 glut.glutHideWindow() # This is an event to be processed below
485 glutMainLoopEvent()
486 #glut.glutTimerFunc( sys.maxint-1, timer_none, 0)
448 self.clear_inputhook()
487 self.clear_inputhook()
488 #signal.signal(signal.SIGINT, signal.default_int_handler)
489
490 def enable_pyglet(self, app=None):
491 """Enable event loop integration with pyglet.
492
493 Parameters
494 ----------
495 app : ignored
496 Ignored, it's only a placeholder to keep the call signature of all
497 gui activation methods consistent, which simplifies the logic of
498 supporting magics.
449
499
500 Notes
501 -----
502 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
503 pyglet to integrate with terminal based applications like
504 IPython.
505
506 """
507 import pyglet
508 from IPython.lib.inputhookpyglet import inputhook_pyglet
509 self.set_inputhook(inputhook_pyglet)
510 self._current_gui = GUI_PYGLET
511 return app
512
513 def disable_pyglet(self):
514 """Disable event loop integration with pyglet.
515
516 This merely sets PyOS_InputHook to NULL.
517 """
518 self.clear_inputhook()
450
519
451 def current_gui(self):
520 def current_gui(self):
452 """Return a string indicating the currently active GUI or None."""
521 """Return a string indicating the currently active GUI or None."""
453 return self._current_gui
522 return self._current_gui
454
523
455 inputhook_manager = InputHookManager()
524 inputhook_manager = InputHookManager()
456
525
457 enable_wx = inputhook_manager.enable_wx
526 enable_wx = inputhook_manager.enable_wx
458 disable_wx = inputhook_manager.disable_wx
527 disable_wx = inputhook_manager.disable_wx
459 enable_qt4 = inputhook_manager.enable_qt4
528 enable_qt4 = inputhook_manager.enable_qt4
460 disable_qt4 = inputhook_manager.disable_qt4
529 disable_qt4 = inputhook_manager.disable_qt4
461 enable_gtk = inputhook_manager.enable_gtk
530 enable_gtk = inputhook_manager.enable_gtk
462 disable_gtk = inputhook_manager.disable_gtk
531 disable_gtk = inputhook_manager.disable_gtk
463 enable_tk = inputhook_manager.enable_tk
532 enable_tk = inputhook_manager.enable_tk
464 disable_tk = inputhook_manager.disable_tk
533 disable_tk = inputhook_manager.disable_tk
465 <<<<<<< HEAD
466 enable_pyglet = inputhook_manager.enable_pyglet
467 disable_pyglet = inputhook_manager.disable_pyglet
468 =======
469 enable_glut = inputhook_manager.enable_glut
534 enable_glut = inputhook_manager.enable_glut
470 disable_glut = inputhook_manager.disable_glut
535 disable_glut = inputhook_manager.disable_glut
471 >>>>>>> Added code for the GLUT interactive session
536 enable_pyglet = inputhook_manager.enable_pyglet
537 disable_pyglet = inputhook_manager.disable_pyglet
472 clear_inputhook = inputhook_manager.clear_inputhook
538 clear_inputhook = inputhook_manager.clear_inputhook
473 set_inputhook = inputhook_manager.set_inputhook
539 set_inputhook = inputhook_manager.set_inputhook
474 current_gui = inputhook_manager.current_gui
540 current_gui = inputhook_manager.current_gui
475 clear_app_refs = inputhook_manager.clear_app_refs
541 clear_app_refs = inputhook_manager.clear_app_refs
476
542
477
543
478 # Convenience function to switch amongst them
544 # Convenience function to switch amongst them
479 def enable_gui(gui=None, app=None):
545 def enable_gui(gui=None, app=None):
480 """Switch amongst GUI input hooks by name.
546 """Switch amongst GUI input hooks by name.
481
547
482 This is just a utility wrapper around the methods of the InputHookManager
548 This is just a utility wrapper around the methods of the InputHookManager
483 object.
549 object.
484
550
485 Parameters
551 Parameters
486 ----------
552 ----------
487 gui : optional, string or None
553 gui : optional, string or None
488 If None, clears input hook, otherwise it must be one of the recognized
554 If None, clears input hook, otherwise it must be one of the recognized
489 GUI names (see ``GUI_*`` constants in module).
555 GUI names (see ``GUI_*`` constants in module).
490
556
491 app : optional, existing application object.
557 app : optional, existing application object.
492 For toolkits that have the concept of a global app, you can supply an
558 For toolkits that have the concept of a global app, you can supply an
493 existing one. If not given, the toolkit will be probed for one, and if
559 existing one. If not given, the toolkit will be probed for one, and if
494 none is found, a new one will be created. Note that GTK does not have
560 none is found, a new one will be created. Note that GTK does not have
495 this concept, and passing an app if `gui`=="GTK" will raise an error.
561 this concept, and passing an app if `gui`=="GTK" will raise an error.
496
562
497 Returns
563 Returns
498 -------
564 -------
499 The output of the underlying gui switch routine, typically the actual
565 The output of the underlying gui switch routine, typically the actual
500 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
566 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
501 one.
567 one.
502 """
568 """
503 guis = {None: clear_inputhook,
569 guis = {None: clear_inputhook,
504 GUI_OSX: lambda app=False: None,
570 GUI_OSX: lambda app=False: None,
505 GUI_TK: enable_tk,
571 GUI_TK: enable_tk,
506 GUI_GTK: enable_gtk,
572 GUI_GTK: enable_gtk,
507 GUI_WX: enable_wx,
573 GUI_WX: enable_wx,
508 GUI_QT: enable_qt4, # qt3 not supported
574 GUI_QT: enable_qt4, # qt3 not supported
509 GUI_QT4: enable_qt4,
575 GUI_QT4: enable_qt4,
510 <<<<<<< HEAD
576 GUI_GLUT: enable_glut,
511 GUI_PYGLET: enable_pyglet,
577 GUI_PYGLET: enable_pyglet,
512 }
578 }
513 =======
514 GUI_GLUT: enable_glut}
515 >>>>>>> Added code for the GLUT interactive session
516 try:
579 try:
517 gui_hook = guis[gui]
580 gui_hook = guis[gui]
518 except KeyError:
581 except KeyError:
519 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
582 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
520 raise ValueError(e)
583 raise ValueError(e)
521 return gui_hook(app)
584 return gui_hook(app)
522
585
General Comments 0
You need to be logged in to leave comments. Login now