##// END OF EJS Templates
Adding more documentation of inputhook.py
Brian Granger -
Show More
@@ -1,286 +1,292 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Inputhook management for GUI event loop integration.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import ctypes
19 19 import sys
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Code
23 23 #-----------------------------------------------------------------------------
24 24
25 25 def _dummy_mainloop(*args, **kw):
26 26 pass
27 27
28 28
29 29 class InputHookManager(object):
30 30 """Manage PyOS_InputHook for different GUI toolkits.
31 31
32 32 This class installs various hooks under ``PyOSInputHook`` to handle
33 33 GUI event loop integration.
34 34 """
35 35
36 36 def __init__(self):
37 37 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
38 38 self._apps = {}
39 39 self._reset()
40 40
41 41 def _reset(self):
42 42 self._callback_pyfunctype = None
43 43 self._callback = None
44 44 self._installed = False
45 45 self._current_gui = None
46 46
47 47 def _hijack_wx(self):
48 """Hijack the wx mainloop so a user calling it won't cause badness."""
48 49 import wx
49 50 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
50 51 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
51 52 else: raise AttributeError('Could not find wx core module')
52 53 orig_mainloop = core.PyApp_MainLoop
53 54 core.PyApp_MainLoop = _dummy_mainloop
54 55 return orig_mainloop
55 56
56 57 def _hijack_qt4(self):
58 """Hijack the qt4 mainloop so a user calling it won't cause badness."""
57 59 from PyQt4 import QtGui, QtCore
58 60 orig_mainloop = QtGui.qApp.exec_
59 61 QtGui.qApp.exec_ = _dummy_mainloop
60 62 QtGui.QApplication.exec_ = _dummy_mainloop
61 63 QtCore.QCoreApplication.exec_ = _dummy_mainloop
62 64 return orig_mainloop
63 65
64 66 def _hijack_gtk(self):
67 """Hijack the gtk mainloop so a user calling it won't cause badness."""
65 68 import gtk
66 69 orig_mainloop = gtk.main
67 70 gtk.mainloop = _dummy_mainloop
68 71 gtk.main = _dummy_mainloop
69 72 return orig_mainloop
70 73
71 74 def _hijack_tk(self):
75 """Hijack the tk mainloop so a user calling it won't cause badness."""
72 76 import Tkinter
73 77 Tkinter.Misc.mainloop = _dummy_mainloop
74 78 Tkinter.mainloop = _dummy_mainloop
75 79
76 80 def get_pyos_inputhook(self):
77 """Return the current PyOS_InputHook as a ctypes.c_void_p.
78 """
81 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
79 82 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
80 83
81 84 def get_pyos_inputhook_as_func(self):
82 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.
83 """
85 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
84 86 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
85 87
86 88 def set_inputhook(self, callback):
87 """Set PyOS_InputHook to callback and return the previous one.
88 """
89 """Set PyOS_InputHook to callback and return the previous one."""
89 90 self._callback = callback
90 91 self._callback_pyfunctype = self.PYFUNC(callback)
91 92 pyos_inputhook_ptr = self.get_pyos_inputhook()
92 93 original = self.get_pyos_inputhook_as_func()
93 94 pyos_inputhook_ptr.value = \
94 95 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
95 96 self._installed = True
96 97 return original
97 98
98 99 def clear_inputhook(self):
99 """Set PyOS_InputHook to NULL and return the previous one.
100 """
100 """Set PyOS_InputHook to NULL and return the previous one."""
101 101 pyos_inputhook_ptr = self.get_pyos_inputhook()
102 102 original = self.get_pyos_inputhook_as_func()
103 103 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
104 104 self._reset()
105 105 return original
106 106
107 107 def clear_app_refs(self, gui=None):
108 108 """Clear IPython's internal reference to an application instance.
109 109
110 Whenever we create an app for a user on qt4 or wx, we hold a
111 reference to the app. This is needed because in some cases bad things
112 can happen if a user doesn't hold a reference themselves. This
113 method is provided to clear the references we are holding.
114
110 115 Parameters
111 116 ----------
112 117 gui : None or str
113 118 If None, clear all app references. If ('wx', 'qt4') clear
114 119 the app for that toolkit. References are not held for gtk or tk
115 120 as those toolkits don't have the notion of an app.
116 121 """
117 122 if gui is None:
118 123 self._apps = {}
119 124 elif self._apps.has_key(gui):
120 125 del self._apps[gui]
121 126
122 127 def enable_wx(self, app=False):
123 128 """Enable event loop integration with wxPython.
124 129
125 130 Parameters
126 131 ----------
127 132 app : bool
128 133 Create a running application object or not.
129 134
130 135 Notes
131 136 -----
132 This methods sets the PyOS_InputHook for wxPython, which allows
137 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
133 138 the wxPython to integrate with terminal based applications like
134 139 IPython.
135
136 Once this has been called, you can use wx interactively by doing::
137
138 >>> import wx
139 >>> app = wx.App(redirect=False, clearSigInt=False)
140
140
141 If ``app`` is True, we create an :class:`wx.App` as follows::
142
143 import wx
144 app = wx.App(redirect=False, clearSigInt=False)
145
141 146 Both options this constructor are important for things to work
142 147 properly in an interactive context.
143
144 But, *don't start the event loop*. That is handled automatically by
145 PyOS_InputHook.
148
149 But, we first check to see if an application has already been
150 created. If so, we simply return that instance.
146 151 """
147 152 from IPython.lib.inputhookwx import inputhook_wx
148 153 self.set_inputhook(inputhook_wx)
149 154 self._current_gui = 'wx'
150 155 self._hijack_wx()
151 156 if app:
152 157 import wx
153 158 app = wx.GetApp()
154 159 if app is None:
155 160 app = wx.App(redirect=False, clearSigInt=False)
156 161 self._apps['wx'] = app
157 162 return app
158 163
159 164 def disable_wx(self):
160 165 """Disable event loop integration with wxPython.
161
166
162 167 This merely sets PyOS_InputHook to NULL.
163 168 """
164 169 self.clear_inputhook()
165 170
166 171 def enable_qt4(self, app=False):
167 172 """Enable event loop integration with PyQt4.
168 173
169 174 Parameters
170 175 ----------
171 176 app : bool
172 177 Create a running application object or not.
173 178
174 179 Notes
175 180 -----
176 This methods sets the PyOS_InputHook for wxPython, which allows
181 This methods sets the PyOS_InputHook for PyQt4, which allows
177 182 the PyQt4 to integrate with terminal based applications like
178 183 IPython.
179
180 Once this has been called, you can simply create a QApplication and
181 use it. But, *don't start the event loop*. That is handled
182 automatically by PyOS_InputHook.
184
185 If ``app`` is True, we create an :class:`QApplication` as follows::
186
187 from PyQt4 import QtCore
188 app = QtGui.QApplication(sys.argv)
189
190 But, we first check to see if an application has already been
191 created. If so, we simply return that instance.
183 192 """
184 193 from PyQt4 import QtCore
185 194 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
186 195 # was set when QtCore was imported, but if it ever got removed,
187 196 # you couldn't reset it. For earlier versions we can
188 197 # probably implement a ctypes version.
189 198 try:
190 199 QtCore.pyqtRestoreInputHook()
191 200 except AttributeError:
192 201 pass
193 202 self._current_gui = 'qt4'
194 203 self._hijack_qt4()
195 204 if app:
196 205 from PyQt4 import QtGui
197 206 app = QtGui.QApplication.instance()
198 207 if app is None:
199 208 app = QtGui.QApplication(sys.argv)
200 209 self._apps['qt4'] = app
201 210 return app
202 211
203 212 def disable_qt4(self):
204 213 """Disable event loop integration with PyQt4.
205
214
206 215 This merely sets PyOS_InputHook to NULL.
207 216 """
208 217 self.clear_inputhook()
209 218
210 219 def enable_gtk(self, app=False):
211 220 """Enable event loop integration with PyGTK.
212 221
213 222 Parameters
214 223 ----------
215 224 app : bool
216 Create a running application object or not.
225 Create a running application object or not. Because gtk does't
226 have an app class, this does nothing.
217 227
218 228 Notes
219 229 -----
220 230 This methods sets the PyOS_InputHook for PyGTK, which allows
221 231 the PyGTK to integrate with terminal based applications like
222 232 IPython.
223
224 Once this has been called, you can simple create PyGTK objects and
225 use them. But, *don't start the event loop*. That is handled
226 automatically by PyOS_InputHook.
227 233 """
228 234 import gtk
229 235 try:
230 236 gtk.set_interactive(True)
231 237 self._current_gui = 'gtk'
232 238 except AttributeError:
233 239 # For older versions of gtk, use our own ctypes version
234 240 from IPython.lib.inputhookgtk import inputhook_gtk
235 241 self.set_inputhook(inputhook_gtk)
236 242 self._current_gui = 'gtk'
237 243 self._hijack_gtk()
238 244
239 245 def disable_gtk(self):
240 246 """Disable event loop integration with PyGTK.
241 247
242 248 This merely sets PyOS_InputHook to NULL.
243 249 """
244 250 self.clear_inputhook()
245 251
246 252 def enable_tk(self, app=False):
247 253 """Enable event loop integration with Tk.
248 254
249 255 Parameters
250 256 ----------
251 257 app : bool
252 258 Create a running application object or not.
253 259
254 260 Notes
255 261 -----
256 262 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
257 263 sets ``PyOS_InputHook``.
258 264 """
259 265 self._current_gui = 'tk'
260 266 self._hijack_tk()
261 267
262 268 def disable_tk(self):
263 269 """Disable event loop integration with Tkinter.
264 270
265 271 This merely sets PyOS_InputHook to NULL.
266 272 """
267 273 self.clear_inputhook()
268 274
269 275 def current_gui(self):
270 276 """Return a string indicating the currently active GUI or None."""
271 277 return self._current_gui
272 278
273 279 inputhook_manager = InputHookManager()
274 280
275 281 enable_wx = inputhook_manager.enable_wx
276 282 disable_wx = inputhook_manager.disable_wx
277 283 enable_qt4 = inputhook_manager.enable_qt4
278 284 disable_qt4 = inputhook_manager.disable_qt4
279 285 enable_gtk = inputhook_manager.enable_gtk
280 286 disable_gtk = inputhook_manager.disable_gtk
281 287 enable_tk = inputhook_manager.enable_tk
282 288 disable_tk = inputhook_manager.disable_tk
283 289 clear_inputhook = inputhook_manager.clear_inputhook
284 290 set_inputhook = inputhook_manager.set_inputhook
285 291 current_gui = inputhook_manager.current_gui
286 292 clear_app_refs = inputhook_manager.clear_app_refs
General Comments 0
You need to be logged in to leave comments. Login now