##// END OF EJS Templates
inputhook: use '%gui none' for disabling the input hook.
Christian Boos -
Show More
@@ -1,451 +1,453 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 os
18 import os
19 import sys
19 import sys
20 import warnings
20 import warnings
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Constants
23 # Constants
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 # Constants for identifying the GUI toolkits.
26 # Constants for identifying the GUI toolkits.
27 GUI_WX = 'wx'
27 GUI_WX = 'wx'
28 GUI_QT = 'qt'
28 GUI_QT = 'qt'
29 GUI_QT4 = 'qt4'
29 GUI_QT4 = 'qt4'
30 GUI_GTK = 'gtk'
30 GUI_GTK = 'gtk'
31 GUI_TK = 'tk'
31 GUI_TK = 'tk'
32 GUI_OSX = 'osx'
32 GUI_OSX = 'osx'
33 GUI_GLUT = 'glut'
33 GUI_GLUT = 'glut'
34 GUI_PYGLET = 'pyglet'
34 GUI_PYGLET = 'pyglet'
35 GUI_NONE = 'none' # i.e. disable
35
36
36 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
37 # Utilities
38 # Utilities
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
40 def _stdin_ready_posix():
41 def _stdin_ready_posix():
41 """Return True if there's something to read on stdin (posix version)."""
42 """Return True if there's something to read on stdin (posix version)."""
42 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
43 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
43 return bool(infds)
44 return bool(infds)
44
45
45 def _stdin_ready_nt():
46 def _stdin_ready_nt():
46 """Return True if there's something to read on stdin (nt version)."""
47 """Return True if there's something to read on stdin (nt version)."""
47 return msvcrt.kbhit()
48 return msvcrt.kbhit()
48
49
49 def _stdin_ready_other():
50 def _stdin_ready_other():
50 """Return True, assuming there's something to read on stdin."""
51 """Return True, assuming there's something to read on stdin."""
51 return True #
52 return True #
52
53
53 if os.name == 'posix':
54 if os.name == 'posix':
54 import select
55 import select
55 stdin_ready = _stdin_ready_posix
56 stdin_ready = _stdin_ready_posix
56 elif os.name == 'nt':
57 elif os.name == 'nt':
57 import msvcrt
58 import msvcrt
58 stdin_ready = _stdin_ready_nt
59 stdin_ready = _stdin_ready_nt
59 else:
60 else:
60 stdin_ready = _stdin_ready_other
61 stdin_ready = _stdin_ready_other
61
62
62
63
63 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
64 # Main InputHookManager class
65 # Main InputHookManager class
65 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
66
67
67
68
68 class InputHookManager(object):
69 class InputHookManager(object):
69 """Manage PyOS_InputHook for different GUI toolkits.
70 """Manage PyOS_InputHook for different GUI toolkits.
70
71
71 This class installs various hooks under ``PyOSInputHook`` to handle
72 This class installs various hooks under ``PyOSInputHook`` to handle
72 GUI event loop integration.
73 GUI event loop integration.
73 """
74 """
74
75
75 def __init__(self):
76 def __init__(self):
76 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
77 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
77 self._apps = {}
78 self._apps = {}
78 self._reset()
79 self._reset()
79
80
80 def _reset(self):
81 def _reset(self):
81 self._callback_pyfunctype = None
82 self._callback_pyfunctype = None
82 self._callback = None
83 self._callback = None
83 self._installed = False
84 self._installed = False
84 self._current_gui = None
85 self._current_gui = None
85
86
86 def get_pyos_inputhook(self):
87 def get_pyos_inputhook(self):
87 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
88 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
88 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
89 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
89
90
90 def get_pyos_inputhook_as_func(self):
91 def get_pyos_inputhook_as_func(self):
91 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
92 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
92 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
93 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
93
94
94 def set_inputhook(self, callback):
95 def set_inputhook(self, callback):
95 """Set PyOS_InputHook to callback and return the previous one."""
96 """Set PyOS_InputHook to callback and return the previous one."""
96 self._callback = callback
97 self._callback = callback
97 self._callback_pyfunctype = self.PYFUNC(callback)
98 self._callback_pyfunctype = self.PYFUNC(callback)
98 pyos_inputhook_ptr = self.get_pyos_inputhook()
99 pyos_inputhook_ptr = self.get_pyos_inputhook()
99 original = self.get_pyos_inputhook_as_func()
100 original = self.get_pyos_inputhook_as_func()
100 pyos_inputhook_ptr.value = \
101 pyos_inputhook_ptr.value = \
101 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
102 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
102 self._installed = True
103 self._installed = True
103 return original
104 return original
104
105
105 def clear_inputhook(self, app=None):
106 def clear_inputhook(self, app=None):
106 """Set PyOS_InputHook to NULL and return the previous one.
107 """Set PyOS_InputHook to NULL and return the previous one.
107
108
108 Parameters
109 Parameters
109 ----------
110 ----------
110 app : optional, ignored
111 app : optional, ignored
111 This parameter is allowed only so that clear_inputhook() can be
112 This parameter is allowed only so that clear_inputhook() can be
112 called with a similar interface as all the ``enable_*`` methods. But
113 called with a similar interface as all the ``enable_*`` methods. But
113 the actual value of the parameter is ignored. This uniform interface
114 the actual value of the parameter is ignored. This uniform interface
114 makes it easier to have user-level entry points in the main IPython
115 makes it easier to have user-level entry points in the main IPython
115 app like :meth:`enable_gui`."""
116 app like :meth:`enable_gui`."""
116 pyos_inputhook_ptr = self.get_pyos_inputhook()
117 pyos_inputhook_ptr = self.get_pyos_inputhook()
117 original = self.get_pyos_inputhook_as_func()
118 original = self.get_pyos_inputhook_as_func()
118 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
119 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
119 self._reset()
120 self._reset()
120 return original
121 return original
121
122
122 def clear_app_refs(self, gui=None):
123 def clear_app_refs(self, gui=None):
123 """Clear IPython's internal reference to an application instance.
124 """Clear IPython's internal reference to an application instance.
124
125
125 Whenever we create an app for a user on qt4 or wx, we hold a
126 Whenever we create an app for a user on qt4 or wx, we hold a
126 reference to the app. This is needed because in some cases bad things
127 reference to the app. This is needed because in some cases bad things
127 can happen if a user doesn't hold a reference themselves. This
128 can happen if a user doesn't hold a reference themselves. This
128 method is provided to clear the references we are holding.
129 method is provided to clear the references we are holding.
129
130
130 Parameters
131 Parameters
131 ----------
132 ----------
132 gui : None or str
133 gui : None or str
133 If None, clear all app references. If ('wx', 'qt4') clear
134 If None, clear all app references. If ('wx', 'qt4') clear
134 the app for that toolkit. References are not held for gtk or tk
135 the app for that toolkit. References are not held for gtk or tk
135 as those toolkits don't have the notion of an app.
136 as those toolkits don't have the notion of an app.
136 """
137 """
137 if gui is None:
138 if gui is None:
138 self._apps = {}
139 self._apps = {}
139 elif self._apps.has_key(gui):
140 elif self._apps.has_key(gui):
140 del self._apps[gui]
141 del self._apps[gui]
141
142
142 def enable_wx(self, app=None):
143 def enable_wx(self, app=None):
143 """Enable event loop integration with wxPython.
144 """Enable event loop integration with wxPython.
144
145
145 Parameters
146 Parameters
146 ----------
147 ----------
147 app : WX Application, optional.
148 app : WX Application, optional.
148 Running application to use. If not given, we probe WX for an
149 Running application to use. If not given, we probe WX for an
149 existing application object, and create a new one if none is found.
150 existing application object, and create a new one if none is found.
150
151
151 Notes
152 Notes
152 -----
153 -----
153 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
154 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
154 the wxPython to integrate with terminal based applications like
155 the wxPython to integrate with terminal based applications like
155 IPython.
156 IPython.
156
157
157 If ``app`` is not given we probe for an existing one, and return it if
158 If ``app`` is not given we probe for an existing one, and return it if
158 found. If no existing app is found, we create an :class:`wx.App` as
159 found. If no existing app is found, we create an :class:`wx.App` as
159 follows::
160 follows::
160
161
161 import wx
162 import wx
162 app = wx.App(redirect=False, clearSigInt=False)
163 app = wx.App(redirect=False, clearSigInt=False)
163 """
164 """
164 from IPython.lib.inputhookwx import inputhook_wx
165 from IPython.lib.inputhookwx import inputhook_wx
165 self.set_inputhook(inputhook_wx)
166 self.set_inputhook(inputhook_wx)
166 self._current_gui = GUI_WX
167 self._current_gui = GUI_WX
167 import wx
168 import wx
168 if app is None:
169 if app is None:
169 app = wx.GetApp()
170 app = wx.GetApp()
170 if app is None:
171 if app is None:
171 app = wx.App(redirect=False, clearSigInt=False)
172 app = wx.App(redirect=False, clearSigInt=False)
172 app._in_event_loop = True
173 app._in_event_loop = True
173 self._apps[GUI_WX] = app
174 self._apps[GUI_WX] = app
174 return app
175 return app
175
176
176 def disable_wx(self):
177 def disable_wx(self):
177 """Disable event loop integration with wxPython.
178 """Disable event loop integration with wxPython.
178
179
179 This merely sets PyOS_InputHook to NULL.
180 This merely sets PyOS_InputHook to NULL.
180 """
181 """
181 if self._apps.has_key(GUI_WX):
182 if self._apps.has_key(GUI_WX):
182 self._apps[GUI_WX]._in_event_loop = False
183 self._apps[GUI_WX]._in_event_loop = False
183 self.clear_inputhook()
184 self.clear_inputhook()
184
185
185 def enable_qt4(self, app=None):
186 def enable_qt4(self, app=None):
186 """Enable event loop integration with PyQt4.
187 """Enable event loop integration with PyQt4.
187
188
188 Parameters
189 Parameters
189 ----------
190 ----------
190 app : Qt Application, optional.
191 app : Qt Application, optional.
191 Running application to use. If not given, we probe Qt for an
192 Running application to use. If not given, we probe Qt for an
192 existing application object, and create a new one if none is found.
193 existing application object, and create a new one if none is found.
193
194
194 Notes
195 Notes
195 -----
196 -----
196 This methods sets the PyOS_InputHook for PyQt4, which allows
197 This methods sets the PyOS_InputHook for PyQt4, which allows
197 the PyQt4 to integrate with terminal based applications like
198 the PyQt4 to integrate with terminal based applications like
198 IPython.
199 IPython.
199
200
200 If ``app`` is not given we probe for an existing one, and return it if
201 If ``app`` is not given we probe for an existing one, and return it if
201 found. If no existing app is found, we create an :class:`QApplication`
202 found. If no existing app is found, we create an :class:`QApplication`
202 as follows::
203 as follows::
203
204
204 from PyQt4 import QtCore
205 from PyQt4 import QtCore
205 app = QtGui.QApplication(sys.argv)
206 app = QtGui.QApplication(sys.argv)
206 """
207 """
207 from IPython.lib.inputhookqt4 import create_inputhook_qt4
208 from IPython.lib.inputhookqt4 import create_inputhook_qt4
208 app, inputhook_qt4 = create_inputhook_qt4(self, app)
209 app, inputhook_qt4 = create_inputhook_qt4(self, app)
209 self.set_inputhook(inputhook_qt4)
210 self.set_inputhook(inputhook_qt4)
210
211
211 self._current_gui = GUI_QT4
212 self._current_gui = GUI_QT4
212 app._in_event_loop = True
213 app._in_event_loop = True
213 self._apps[GUI_QT4] = app
214 self._apps[GUI_QT4] = app
214 return app
215 return app
215
216
216 def disable_qt4(self):
217 def disable_qt4(self):
217 """Disable event loop integration with PyQt4.
218 """Disable event loop integration with PyQt4.
218
219
219 This merely sets PyOS_InputHook to NULL.
220 This merely sets PyOS_InputHook to NULL.
220 """
221 """
221 if self._apps.has_key(GUI_QT4):
222 if self._apps.has_key(GUI_QT4):
222 self._apps[GUI_QT4]._in_event_loop = False
223 self._apps[GUI_QT4]._in_event_loop = False
223 self.clear_inputhook()
224 self.clear_inputhook()
224
225
225 def enable_gtk(self, app=None):
226 def enable_gtk(self, app=None):
226 """Enable event loop integration with PyGTK.
227 """Enable event loop integration with PyGTK.
227
228
228 Parameters
229 Parameters
229 ----------
230 ----------
230 app : ignored
231 app : ignored
231 Ignored, it's only a placeholder to keep the call signature of all
232 Ignored, it's only a placeholder to keep the call signature of all
232 gui activation methods consistent, which simplifies the logic of
233 gui activation methods consistent, which simplifies the logic of
233 supporting magics.
234 supporting magics.
234
235
235 Notes
236 Notes
236 -----
237 -----
237 This methods sets the PyOS_InputHook for PyGTK, which allows
238 This methods sets the PyOS_InputHook for PyGTK, which allows
238 the PyGTK to integrate with terminal based applications like
239 the PyGTK to integrate with terminal based applications like
239 IPython.
240 IPython.
240 """
241 """
241 import gtk
242 import gtk
242 try:
243 try:
243 gtk.set_interactive(True)
244 gtk.set_interactive(True)
244 self._current_gui = GUI_GTK
245 self._current_gui = GUI_GTK
245 except AttributeError:
246 except AttributeError:
246 # For older versions of gtk, use our own ctypes version
247 # For older versions of gtk, use our own ctypes version
247 from IPython.lib.inputhookgtk import inputhook_gtk
248 from IPython.lib.inputhookgtk import inputhook_gtk
248 self.set_inputhook(inputhook_gtk)
249 self.set_inputhook(inputhook_gtk)
249 self._current_gui = GUI_GTK
250 self._current_gui = GUI_GTK
250
251
251 def disable_gtk(self):
252 def disable_gtk(self):
252 """Disable event loop integration with PyGTK.
253 """Disable event loop integration with PyGTK.
253
254
254 This merely sets PyOS_InputHook to NULL.
255 This merely sets PyOS_InputHook to NULL.
255 """
256 """
256 self.clear_inputhook()
257 self.clear_inputhook()
257
258
258 def enable_tk(self, app=None):
259 def enable_tk(self, app=None):
259 """Enable event loop integration with Tk.
260 """Enable event loop integration with Tk.
260
261
261 Parameters
262 Parameters
262 ----------
263 ----------
263 app : toplevel :class:`Tkinter.Tk` widget, optional.
264 app : toplevel :class:`Tkinter.Tk` widget, optional.
264 Running toplevel widget to use. If not given, we probe Tk for an
265 Running toplevel widget to use. If not given, we probe Tk for an
265 existing one, and create a new one if none is found.
266 existing one, and create a new one if none is found.
266
267
267 Notes
268 Notes
268 -----
269 -----
269 If you have already created a :class:`Tkinter.Tk` object, the only
270 If you have already created a :class:`Tkinter.Tk` object, the only
270 thing done by this method is to register with the
271 thing done by this method is to register with the
271 :class:`InputHookManager`, since creating that object automatically
272 :class:`InputHookManager`, since creating that object automatically
272 sets ``PyOS_InputHook``.
273 sets ``PyOS_InputHook``.
273 """
274 """
274 self._current_gui = GUI_TK
275 self._current_gui = GUI_TK
275 if app is None:
276 if app is None:
276 import Tkinter
277 import Tkinter
277 app = Tkinter.Tk()
278 app = Tkinter.Tk()
278 app.withdraw()
279 app.withdraw()
279 self._apps[GUI_TK] = app
280 self._apps[GUI_TK] = app
280 return app
281 return app
281
282
282 def disable_tk(self):
283 def disable_tk(self):
283 """Disable event loop integration with Tkinter.
284 """Disable event loop integration with Tkinter.
284
285
285 This merely sets PyOS_InputHook to NULL.
286 This merely sets PyOS_InputHook to NULL.
286 """
287 """
287 self.clear_inputhook()
288 self.clear_inputhook()
288
289
289
290
290 def enable_glut(self, app=None):
291 def enable_glut(self, app=None):
291 """ Enable event loop integration with GLUT.
292 """ Enable event loop integration with GLUT.
292
293
293 Parameters
294 Parameters
294 ----------
295 ----------
295
296
296 app : ignored
297 app : ignored
297 Ignored, it's only a placeholder to keep the call signature of all
298 Ignored, it's only a placeholder to keep the call signature of all
298 gui activation methods consistent, which simplifies the logic of
299 gui activation methods consistent, which simplifies the logic of
299 supporting magics.
300 supporting magics.
300
301
301 Notes
302 Notes
302 -----
303 -----
303
304
304 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
305 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
305 integrate with terminal based applications like IPython. Due to GLUT
306 integrate with terminal based applications like IPython. Due to GLUT
306 limitations, it is currently not possible to start the event loop
307 limitations, it is currently not possible to start the event loop
307 without first creating a window. You should thus not create another
308 without first creating a window. You should thus not create another
308 window but use instead the created one. See 'gui-glut.py' in the
309 window but use instead the created one. See 'gui-glut.py' in the
309 docs/examples/lib directory.
310 docs/examples/lib directory.
310
311
311 The default screen mode is set to:
312 The default screen mode is set to:
312 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
313 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
313 """
314 """
314
315
315 import OpenGL.GLUT as glut
316 import OpenGL.GLUT as glut
316 from IPython.lib.inputhookglut import glut_display_mode, \
317 from IPython.lib.inputhookglut import glut_display_mode, \
317 glut_close, glut_display, \
318 glut_close, glut_display, \
318 glut_idle, inputhook_glut
319 glut_idle, inputhook_glut
319
320
320 if not self._apps.has_key( GUI_GLUT ):
321 if not self._apps.has_key( GUI_GLUT ):
321 glut.glutInit( sys.argv )
322 glut.glutInit( sys.argv )
322 glut.glutInitDisplayMode( glut_display_mode )
323 glut.glutInitDisplayMode( glut_display_mode )
323 # This is specific to freeglut
324 # This is specific to freeglut
324 if bool(glut.glutSetOption):
325 if bool(glut.glutSetOption):
325 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
326 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
326 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
327 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
327 glut.glutCreateWindow( sys.argv[0] )
328 glut.glutCreateWindow( sys.argv[0] )
328 glut.glutReshapeWindow( 1, 1 )
329 glut.glutReshapeWindow( 1, 1 )
329 glut.glutHideWindow( )
330 glut.glutHideWindow( )
330 glut.glutWMCloseFunc( glut_close )
331 glut.glutWMCloseFunc( glut_close )
331 glut.glutDisplayFunc( glut_display )
332 glut.glutDisplayFunc( glut_display )
332 glut.glutIdleFunc( glut_idle )
333 glut.glutIdleFunc( glut_idle )
333 else:
334 else:
334 glut.glutWMCloseFunc( glut_close )
335 glut.glutWMCloseFunc( glut_close )
335 glut.glutDisplayFunc( glut_display )
336 glut.glutDisplayFunc( glut_display )
336 glut.glutIdleFunc( glut_idle)
337 glut.glutIdleFunc( glut_idle)
337 self.set_inputhook( inputhook_glut )
338 self.set_inputhook( inputhook_glut )
338 self._current_gui = GUI_GLUT
339 self._current_gui = GUI_GLUT
339 self._apps[GUI_GLUT] = True
340 self._apps[GUI_GLUT] = True
340
341
341
342
342 def disable_glut(self):
343 def disable_glut(self):
343 """Disable event loop integration with glut.
344 """Disable event loop integration with glut.
344
345
345 This sets PyOS_InputHook to NULL and set the display function to a
346 This sets PyOS_InputHook to NULL and set the display function to a
346 dummy one and set the timer to a dummy timer that will be triggered
347 dummy one and set the timer to a dummy timer that will be triggered
347 very far in the future.
348 very far in the future.
348 """
349 """
349 import OpenGL.GLUT as glut
350 import OpenGL.GLUT as glut
350 from glut_support import glutMainLoopEvent
351 from glut_support import glutMainLoopEvent
351
352
352 glut.glutHideWindow() # This is an event to be processed below
353 glut.glutHideWindow() # This is an event to be processed below
353 glutMainLoopEvent()
354 glutMainLoopEvent()
354 self.clear_inputhook()
355 self.clear_inputhook()
355
356
356 def enable_pyglet(self, app=None):
357 def enable_pyglet(self, app=None):
357 """Enable event loop integration with pyglet.
358 """Enable event loop integration with pyglet.
358
359
359 Parameters
360 Parameters
360 ----------
361 ----------
361 app : ignored
362 app : ignored
362 Ignored, it's only a placeholder to keep the call signature of all
363 Ignored, it's only a placeholder to keep the call signature of all
363 gui activation methods consistent, which simplifies the logic of
364 gui activation methods consistent, which simplifies the logic of
364 supporting magics.
365 supporting magics.
365
366
366 Notes
367 Notes
367 -----
368 -----
368 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
369 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
369 pyglet to integrate with terminal based applications like
370 pyglet to integrate with terminal based applications like
370 IPython.
371 IPython.
371
372
372 """
373 """
373 import pyglet
374 import pyglet
374 from IPython.lib.inputhookpyglet import inputhook_pyglet
375 from IPython.lib.inputhookpyglet import inputhook_pyglet
375 self.set_inputhook(inputhook_pyglet)
376 self.set_inputhook(inputhook_pyglet)
376 self._current_gui = GUI_PYGLET
377 self._current_gui = GUI_PYGLET
377 return app
378 return app
378
379
379 def disable_pyglet(self):
380 def disable_pyglet(self):
380 """Disable event loop integration with pyglet.
381 """Disable event loop integration with pyglet.
381
382
382 This merely sets PyOS_InputHook to NULL.
383 This merely sets PyOS_InputHook to NULL.
383 """
384 """
384 self.clear_inputhook()
385 self.clear_inputhook()
385
386
386 def current_gui(self):
387 def current_gui(self):
387 """Return a string indicating the currently active GUI or None."""
388 """Return a string indicating the currently active GUI or None."""
388 return self._current_gui
389 return self._current_gui
389
390
390 inputhook_manager = InputHookManager()
391 inputhook_manager = InputHookManager()
391
392
392 enable_wx = inputhook_manager.enable_wx
393 enable_wx = inputhook_manager.enable_wx
393 disable_wx = inputhook_manager.disable_wx
394 disable_wx = inputhook_manager.disable_wx
394 enable_qt4 = inputhook_manager.enable_qt4
395 enable_qt4 = inputhook_manager.enable_qt4
395 disable_qt4 = inputhook_manager.disable_qt4
396 disable_qt4 = inputhook_manager.disable_qt4
396 enable_gtk = inputhook_manager.enable_gtk
397 enable_gtk = inputhook_manager.enable_gtk
397 disable_gtk = inputhook_manager.disable_gtk
398 disable_gtk = inputhook_manager.disable_gtk
398 enable_tk = inputhook_manager.enable_tk
399 enable_tk = inputhook_manager.enable_tk
399 disable_tk = inputhook_manager.disable_tk
400 disable_tk = inputhook_manager.disable_tk
400 enable_glut = inputhook_manager.enable_glut
401 enable_glut = inputhook_manager.enable_glut
401 disable_glut = inputhook_manager.disable_glut
402 disable_glut = inputhook_manager.disable_glut
402 enable_pyglet = inputhook_manager.enable_pyglet
403 enable_pyglet = inputhook_manager.enable_pyglet
403 disable_pyglet = inputhook_manager.disable_pyglet
404 disable_pyglet = inputhook_manager.disable_pyglet
404 clear_inputhook = inputhook_manager.clear_inputhook
405 clear_inputhook = inputhook_manager.clear_inputhook
405 set_inputhook = inputhook_manager.set_inputhook
406 set_inputhook = inputhook_manager.set_inputhook
406 current_gui = inputhook_manager.current_gui
407 current_gui = inputhook_manager.current_gui
407 clear_app_refs = inputhook_manager.clear_app_refs
408 clear_app_refs = inputhook_manager.clear_app_refs
408
409
409
410
410 # Convenience function to switch amongst them
411 # Convenience function to switch amongst them
411 def enable_gui(gui=None, app=None):
412 def enable_gui(gui=None, app=None):
412 """Switch amongst GUI input hooks by name.
413 """Switch amongst GUI input hooks by name.
413
414
414 This is just a utility wrapper around the methods of the InputHookManager
415 This is just a utility wrapper around the methods of the InputHookManager
415 object.
416 object.
416
417
417 Parameters
418 Parameters
418 ----------
419 ----------
419 gui : optional, string or None
420 gui : optional, string or None
420 If None, clears input hook, otherwise it must be one of the recognized
421 If None (or 'none'), clears input hook, otherwise it must be one
421 GUI names (see ``GUI_*`` constants in module).
422 of the recognized GUI names (see ``GUI_*`` constants in module).
422
423
423 app : optional, existing application object.
424 app : optional, existing application object.
424 For toolkits that have the concept of a global app, you can supply an
425 For toolkits that have the concept of a global app, you can supply an
425 existing one. If not given, the toolkit will be probed for one, and if
426 existing one. If not given, the toolkit will be probed for one, and if
426 none is found, a new one will be created. Note that GTK does not have
427 none is found, a new one will be created. Note that GTK does not have
427 this concept, and passing an app if `gui`=="GTK" will raise an error.
428 this concept, and passing an app if `gui`=="GTK" will raise an error.
428
429
429 Returns
430 Returns
430 -------
431 -------
431 The output of the underlying gui switch routine, typically the actual
432 The output of the underlying gui switch routine, typically the actual
432 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
433 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
433 one.
434 one.
434 """
435 """
435 guis = {None: clear_inputhook,
436 guis = {None: clear_inputhook,
437 GUI_NONE: clear_inputhook,
436 GUI_OSX: lambda app=False: None,
438 GUI_OSX: lambda app=False: None,
437 GUI_TK: enable_tk,
439 GUI_TK: enable_tk,
438 GUI_GTK: enable_gtk,
440 GUI_GTK: enable_gtk,
439 GUI_WX: enable_wx,
441 GUI_WX: enable_wx,
440 GUI_QT: enable_qt4, # qt3 not supported
442 GUI_QT: enable_qt4, # qt3 not supported
441 GUI_QT4: enable_qt4,
443 GUI_QT4: enable_qt4,
442 GUI_GLUT: enable_glut,
444 GUI_GLUT: enable_glut,
443 GUI_PYGLET: enable_pyglet,
445 GUI_PYGLET: enable_pyglet,
444 }
446 }
445 try:
447 try:
446 gui_hook = guis[gui]
448 gui_hook = guis[gui]
447 except KeyError:
449 except KeyError:
448 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
450 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
449 raise ValueError(e)
451 raise ValueError(e)
450 return gui_hook(app)
452 return gui_hook(app)
451
453
@@ -1,110 +1,113 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Qt4's inputhook support function
3 Qt4's inputhook support function
4
4
5 Author: Christian Boos
5 Author: Christian Boos
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from IPython.external.qt_for_kernel import QtCore, QtGui
19 from IPython.external.qt_for_kernel import QtCore, QtGui
20 from IPython.lib.inputhook import stdin_ready
20 from IPython.lib.inputhook import stdin_ready
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Code
23 # Code
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 def create_inputhook_qt4(mgr, app=None):
26 def create_inputhook_qt4(mgr, app=None):
27 """Create an input hook for running the Qt4 application event loop.
27 """Create an input hook for running the Qt4 application event loop.
28
28
29 Parameters
29 Parameters
30 ----------
30 ----------
31 mgr : an InputHookManager
31 mgr : an InputHookManager
32
32
33 app : Qt Application, optional.
33 app : Qt Application, optional.
34 Running application to use. If not given, we probe Qt for an
34 Running application to use. If not given, we probe Qt for an
35 existing application object, and create a new one if none is found.
35 existing application object, and create a new one if none is found.
36
36
37 Returns
37 Returns
38 -------
38 -------
39 A pair consisting of a Qt Application (either the one given or the
39 A pair consisting of a Qt Application (either the one given or the
40 one found or created) and a inputhook.
40 one found or created) and a inputhook.
41
41
42 Notes
42 Notes
43 -----
43 -----
44 We use a custom input hook instead of PyQt4's default one, as it
44 We use a custom input hook instead of PyQt4's default one, as it
45 interacts better with the readline packages (issue #481).
45 interacts better with the readline packages (issue #481).
46
46
47 The inputhook function works in tandem with a 'pre_prompt_hook'
47 The inputhook function works in tandem with a 'pre_prompt_hook'
48 which automatically restores the hook as an inputhook in case the
48 which automatically restores the hook as an inputhook in case the
49 latter has been temporarily disabled after having intercepted a
49 latter has been temporarily disabled after having intercepted a
50 KeyboardInterrupt.
50 KeyboardInterrupt.
51 """
51 """
52
52
53 if app is None:
53 if app is None:
54 app = QtCore.QCoreApplication.instance()
54 app = QtCore.QCoreApplication.instance()
55 if app is None:
55 if app is None:
56 app = QtGui.QApplication([" "])
56 app = QtGui.QApplication([" "])
57
57
58 # Re-use previously created inputhook if any
58 # Re-use previously created inputhook if any
59 ip = get_ipython()
59 ip = get_ipython()
60 if hasattr(ip, '_inputhook_qt4'):
60 if hasattr(ip, '_inputhook_qt4'):
61 return app, ip._inputhook_qt4
61 return app, ip._inputhook_qt4
62
62
63 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
63 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
64 # hooks (they both share the got_kbdint flag)
64 # hooks (they both share the got_kbdint flag)
65
65
66 got_kbdint = [False]
66 got_kbdint = [False]
67
67
68 def inputhook_qt4():
68 def inputhook_qt4():
69 """PyOS_InputHook python hook for Qt4.
69 """PyOS_InputHook python hook for Qt4.
70
70
71 Process pending Qt events and if there's no pending keyboard
71 Process pending Qt events and if there's no pending keyboard
72 input, spend a short slice of time (50ms) running the Qt event
72 input, spend a short slice of time (50ms) running the Qt event
73 loop.
73 loop.
74
74
75 As a Python ctypes callback can't raise an exception, we catch
75 As a Python ctypes callback can't raise an exception, we catch
76 the KeyboardInterrupt and temporarily deactivate the hook,
76 the KeyboardInterrupt and temporarily deactivate the hook,
77 which will let a *second* CTRL+C be processed normally and go
77 which will let a *second* CTRL+C be processed normally and go
78 back to a clean prompt line.
78 back to a clean prompt line.
79 """
79 """
80 try:
80 try:
81 app = QtCore.QCoreApplication.instance()
81 app = QtCore.QCoreApplication.instance()
82 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
82 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
83 if not stdin_ready():
83 if not stdin_ready():
84 timer = QtCore.QTimer()
84 timer = QtCore.QTimer()
85 timer.timeout.connect(app.quit)
85 timer.timeout.connect(app.quit)
86 while not stdin_ready():
86 while not stdin_ready():
87 timer.start(50)
87 timer.start(50)
88 app.exec_()
88 app.exec_()
89 timer.stop()
89 timer.stop()
90 except KeyboardInterrupt:
90 except KeyboardInterrupt:
91 got_kbdint[0] = True
91 got_kbdint[0] = True
92 mgr.clear_inputhook()
92 mgr.clear_inputhook()
93 print("\n(event loop interrupted - "
93 print("\nKeyboardInterrupt - qt4 event loop interrupted!"
94 "hit CTRL+C again to clear the prompt)")
94 "\n * hit CTRL+C again to clear the prompt"
95 "\n * use '%gui none' to disable the event loop"
96 " permanently"
97 "\n and '%gui qt4' to re-enable it later")
95 return 0
98 return 0
96
99
97 def preprompthook_qt4(ishell):
100 def preprompthook_qt4(ishell):
98 """'pre_prompt_hook' used to restore the Qt4 input hook
101 """'pre_prompt_hook' used to restore the Qt4 input hook
99
102
100 (in case the latter was temporarily deactivated after a
103 (in case the latter was temporarily deactivated after a
101 CTRL+C)
104 CTRL+C)
102 """
105 """
103 if got_kbdint[0]:
106 if got_kbdint[0]:
104 mgr.set_inputhook(inputhook_qt4)
107 mgr.set_inputhook(inputhook_qt4)
105 got_kbdint[0] = False
108 got_kbdint[0] = False
106
109
107 ip._inputhook_qt4 = inputhook_qt4
110 ip._inputhook_qt4 = inputhook_qt4
108 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
111 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
109
112
110 return app, inputhook_qt4
113 return app, inputhook_qt4
General Comments 0
You need to be logged in to leave comments. Login now