##// END OF EJS Templates
Missing files added
Nicolas Rougier -
Show More
@@ -1,344 +1,382 b''
1 1 # coding: utf-8
2 2 """
3 3 Inputhook management for GUI event loop integration.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import ctypes
18 18 import sys
19 19 import warnings
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Constants
23 23 #-----------------------------------------------------------------------------
24 24
25 25 # Constants for identifying the GUI toolkits.
26 26 GUI_WX = 'wx'
27 27 GUI_QT = 'qt'
28 28 GUI_QT4 = 'qt4'
29 29 GUI_GTK = 'gtk'
30 30 GUI_TK = 'tk'
31 31 GUI_OSX = 'osx'
32 GUI_PYGLET = 'pyglet'
32 33
33 34 #-----------------------------------------------------------------------------
34 35 # Utility classes
35 36 #-----------------------------------------------------------------------------
36 37
37 38
38 39 #-----------------------------------------------------------------------------
39 40 # Main InputHookManager class
40 41 #-----------------------------------------------------------------------------
41 42
42 43
43 44 class InputHookManager(object):
44 45 """Manage PyOS_InputHook for different GUI toolkits.
45 46
46 47 This class installs various hooks under ``PyOSInputHook`` to handle
47 48 GUI event loop integration.
48 49 """
49 50
50 51 def __init__(self):
51 52 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
52 53 self._apps = {}
53 54 self._reset()
54 55
55 56 def _reset(self):
56 57 self._callback_pyfunctype = None
57 58 self._callback = None
58 59 self._installed = False
59 60 self._current_gui = None
60 61
61 62 def get_pyos_inputhook(self):
62 63 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
63 64 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
64 65
65 66 def get_pyos_inputhook_as_func(self):
66 67 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
67 68 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
68 69
69 70 def set_inputhook(self, callback):
70 71 """Set PyOS_InputHook to callback and return the previous one."""
71 72 self._callback = callback
72 73 self._callback_pyfunctype = self.PYFUNC(callback)
73 74 pyos_inputhook_ptr = self.get_pyos_inputhook()
74 75 original = self.get_pyos_inputhook_as_func()
75 76 pyos_inputhook_ptr.value = \
76 77 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
77 78 self._installed = True
78 79 return original
79 80
80 81 def clear_inputhook(self, app=None):
81 82 """Set PyOS_InputHook to NULL and return the previous one.
82 83
83 84 Parameters
84 85 ----------
85 86 app : optional, ignored
86 87 This parameter is allowed only so that clear_inputhook() can be
87 88 called with a similar interface as all the ``enable_*`` methods. But
88 89 the actual value of the parameter is ignored. This uniform interface
89 90 makes it easier to have user-level entry points in the main IPython
90 91 app like :meth:`enable_gui`."""
91 92 pyos_inputhook_ptr = self.get_pyos_inputhook()
92 93 original = self.get_pyos_inputhook_as_func()
93 94 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
94 95 self._reset()
95 96 return original
96 97
97 98 def clear_app_refs(self, gui=None):
98 99 """Clear IPython's internal reference to an application instance.
99 100
100 101 Whenever we create an app for a user on qt4 or wx, we hold a
101 102 reference to the app. This is needed because in some cases bad things
102 103 can happen if a user doesn't hold a reference themselves. This
103 104 method is provided to clear the references we are holding.
104 105
105 106 Parameters
106 107 ----------
107 108 gui : None or str
108 109 If None, clear all app references. If ('wx', 'qt4') clear
109 110 the app for that toolkit. References are not held for gtk or tk
110 111 as those toolkits don't have the notion of an app.
111 112 """
112 113 if gui is None:
113 114 self._apps = {}
114 115 elif self._apps.has_key(gui):
115 116 del self._apps[gui]
116 117
117 118 def enable_wx(self, app=None):
118 119 """Enable event loop integration with wxPython.
119 120
120 121 Parameters
121 122 ----------
122 123 app : WX Application, optional.
123 124 Running application to use. If not given, we probe WX for an
124 125 existing application object, and create a new one if none is found.
125 126
126 127 Notes
127 128 -----
128 129 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
129 130 the wxPython to integrate with terminal based applications like
130 131 IPython.
131 132
132 133 If ``app`` is not given we probe for an existing one, and return it if
133 134 found. If no existing app is found, we create an :class:`wx.App` as
134 135 follows::
135 136
136 137 import wx
137 138 app = wx.App(redirect=False, clearSigInt=False)
138 139 """
139 140 from IPython.lib.inputhookwx import inputhook_wx
140 141 self.set_inputhook(inputhook_wx)
141 142 self._current_gui = GUI_WX
142 143 import wx
143 144 if app is None:
144 145 app = wx.GetApp()
145 146 if app is None:
146 147 app = wx.App(redirect=False, clearSigInt=False)
147 148 app._in_event_loop = True
148 149 self._apps[GUI_WX] = app
149 150 return app
150 151
151 152 def disable_wx(self):
152 153 """Disable event loop integration with wxPython.
153 154
154 155 This merely sets PyOS_InputHook to NULL.
155 156 """
156 157 if self._apps.has_key(GUI_WX):
157 158 self._apps[GUI_WX]._in_event_loop = False
158 159 self.clear_inputhook()
159 160
160 161 def enable_qt4(self, app=None):
161 162 """Enable event loop integration with PyQt4.
162 163
163 164 Parameters
164 165 ----------
165 166 app : Qt Application, optional.
166 167 Running application to use. If not given, we probe Qt for an
167 168 existing application object, and create a new one if none is found.
168 169
169 170 Notes
170 171 -----
171 172 This methods sets the PyOS_InputHook for PyQt4, which allows
172 173 the PyQt4 to integrate with terminal based applications like
173 174 IPython.
174 175
175 176 If ``app`` is not given we probe for an existing one, and return it if
176 177 found. If no existing app is found, we create an :class:`QApplication`
177 178 as follows::
178 179
179 180 from PyQt4 import QtCore
180 181 app = QtGui.QApplication(sys.argv)
181 182 """
182 183 from IPython.external.qt_for_kernel import QtCore, QtGui
183 184
184 185 if 'pyreadline' in sys.modules:
185 186 # see IPython GitHub Issue #281 for more info on this issue
186 187 # Similar intermittent behavior has been reported on OSX,
187 188 # but not consistently reproducible
188 189 warnings.warn("""PyReadline's inputhook can conflict with Qt, causing delays
189 190 in interactive input. If you do see this issue, we recommend using another GUI
190 191 toolkit if you can, or disable readline with the configuration option
191 192 'TerminalInteractiveShell.readline_use=False', specified in a config file or
192 193 at the command-line""",
193 194 RuntimeWarning)
194 195
195 196 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
196 197 # was set when QtCore was imported, but if it ever got removed,
197 198 # you couldn't reset it. For earlier versions we can
198 199 # probably implement a ctypes version.
199 200 try:
200 201 QtCore.pyqtRestoreInputHook()
201 202 except AttributeError:
202 203 pass
203 204
204 205 self._current_gui = GUI_QT4
205 206 if app is None:
206 207 app = QtCore.QCoreApplication.instance()
207 208 if app is None:
208 209 app = QtGui.QApplication([" "])
209 210 app._in_event_loop = True
210 211 self._apps[GUI_QT4] = app
211 212 return app
212 213
213 214 def disable_qt4(self):
214 215 """Disable event loop integration with PyQt4.
215 216
216 217 This merely sets PyOS_InputHook to NULL.
217 218 """
218 219 if self._apps.has_key(GUI_QT4):
219 220 self._apps[GUI_QT4]._in_event_loop = False
220 221 self.clear_inputhook()
221 222
222 223 def enable_gtk(self, app=None):
223 224 """Enable event loop integration with PyGTK.
224 225
225 226 Parameters
226 227 ----------
227 228 app : ignored
228 229 Ignored, it's only a placeholder to keep the call signature of all
229 230 gui activation methods consistent, which simplifies the logic of
230 231 supporting magics.
231 232
232 233 Notes
233 234 -----
234 235 This methods sets the PyOS_InputHook for PyGTK, which allows
235 236 the PyGTK to integrate with terminal based applications like
236 237 IPython.
237 238 """
238 239 import gtk
239 240 try:
240 241 gtk.set_interactive(True)
241 242 self._current_gui = GUI_GTK
242 243 except AttributeError:
243 244 # For older versions of gtk, use our own ctypes version
244 245 from IPython.lib.inputhookgtk import inputhook_gtk
245 246 self.set_inputhook(inputhook_gtk)
246 247 self._current_gui = GUI_GTK
247 248
248 249 def disable_gtk(self):
249 250 """Disable event loop integration with PyGTK.
250 251
251 252 This merely sets PyOS_InputHook to NULL.
252 253 """
253 254 self.clear_inputhook()
254 255
255 256 def enable_tk(self, app=None):
256 257 """Enable event loop integration with Tk.
257 258
258 259 Parameters
259 260 ----------
260 261 app : toplevel :class:`Tkinter.Tk` widget, optional.
261 262 Running toplevel widget to use. If not given, we probe Tk for an
262 263 existing one, and create a new one if none is found.
263 264
264 265 Notes
265 266 -----
266 267 If you have already created a :class:`Tkinter.Tk` object, the only
267 268 thing done by this method is to register with the
268 269 :class:`InputHookManager`, since creating that object automatically
269 270 sets ``PyOS_InputHook``.
270 271 """
271 272 self._current_gui = GUI_TK
272 273 if app is None:
273 274 import Tkinter
274 275 app = Tkinter.Tk()
275 276 app.withdraw()
276 277 self._apps[GUI_TK] = app
277 278 return app
278 279
279 280 def disable_tk(self):
280 281 """Disable event loop integration with Tkinter.
281 282
282 283 This merely sets PyOS_InputHook to NULL.
283 284 """
284 285 self.clear_inputhook()
285 286
287
288
289 def enable_pyglet(self, app=None):
290 """Enable event loop integration with pyglet.
291
292 Parameters
293 ----------
294 app : ignored
295 Ignored, it's only a placeholder to keep the call signature of all
296 gui activation methods consistent, which simplifies the logic of
297 supporting magics.
298
299 Notes
300 -----
301 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
302 pyglet to integrate with terminal based applications like
303 IPython.
304
305 """
306 import pyglet
307 from IPython.lib.inputhookpyglet import inputhook_pyglet
308 self.set_inputhook(inputhook_pyglet)
309 self._current_gui = GUI_PYGLET
310 return app
311
312 def disable_pyglet(self):
313 """Disable event loop integration with pyglet.
314
315 This merely sets PyOS_InputHook to NULL.
316 """
317 self.clear_inputhook()
318
319
286 320 def current_gui(self):
287 321 """Return a string indicating the currently active GUI or None."""
288 322 return self._current_gui
289 323
290 324 inputhook_manager = InputHookManager()
291 325
292 326 enable_wx = inputhook_manager.enable_wx
293 327 disable_wx = inputhook_manager.disable_wx
294 328 enable_qt4 = inputhook_manager.enable_qt4
295 329 disable_qt4 = inputhook_manager.disable_qt4
296 330 enable_gtk = inputhook_manager.enable_gtk
297 331 disable_gtk = inputhook_manager.disable_gtk
298 332 enable_tk = inputhook_manager.enable_tk
299 333 disable_tk = inputhook_manager.disable_tk
334 enable_pyglet = inputhook_manager.enable_pyglet
335 disable_pyglet = inputhook_manager.disable_pyglet
300 336 clear_inputhook = inputhook_manager.clear_inputhook
301 337 set_inputhook = inputhook_manager.set_inputhook
302 338 current_gui = inputhook_manager.current_gui
303 339 clear_app_refs = inputhook_manager.clear_app_refs
304 340
305 341
306 342 # Convenience function to switch amongst them
307 343 def enable_gui(gui=None, app=None):
308 344 """Switch amongst GUI input hooks by name.
309 345
310 346 This is just a utility wrapper around the methods of the InputHookManager
311 347 object.
312 348
313 349 Parameters
314 350 ----------
315 351 gui : optional, string or None
316 352 If None, clears input hook, otherwise it must be one of the recognized
317 353 GUI names (see ``GUI_*`` constants in module).
318 354
319 355 app : optional, existing application object.
320 356 For toolkits that have the concept of a global app, you can supply an
321 357 existing one. If not given, the toolkit will be probed for one, and if
322 358 none is found, a new one will be created. Note that GTK does not have
323 359 this concept, and passing an app if `gui`=="GTK" will raise an error.
324 360
325 361 Returns
326 362 -------
327 363 The output of the underlying gui switch routine, typically the actual
328 364 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
329 365 one.
330 366 """
331 367 guis = {None: clear_inputhook,
332 368 GUI_OSX: lambda app=False: None,
333 369 GUI_TK: enable_tk,
334 370 GUI_GTK: enable_gtk,
335 371 GUI_WX: enable_wx,
336 372 GUI_QT: enable_qt4, # qt3 not supported
337 GUI_QT4: enable_qt4 }
373 GUI_QT4: enable_qt4,
374 GUI_PYGLET: enable_pyglet,
375 }
338 376 try:
339 377 gui_hook = guis[gui]
340 378 except KeyError:
341 379 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
342 380 raise ValueError(e)
343 381 return gui_hook(app)
344 382
General Comments 0
You need to be logged in to leave comments. Login now