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