##// END OF EJS Templates
Merging upstream changes from inputhook and config-refactor....
Brian Granger -
r2224:11a6689b merge
parent child Browse files
Show More
@@ -0,0 +1,35 b''
1 #!/usr/bin/env python
2 """Simple GTK example to manually test event loop integration.
3
4 This is meant to run tests manually in ipython as:
5
6 In [5]: %gui gtk
7
8 In [6]: %run gui-gtk.py
9 """
10
11
12 import pygtk
13 pygtk.require('2.0')
14 import gtk
15
16
17 def hello_world(wigdet, data=None):
18 print "Hello World"
19
20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
21 button = gtk.Button("Hello World")
22 button.connect("clicked", hello_world, None)
23
24 window.add(self.button)
25 button.show()
26 window.show()
27
28 try:
29 from IPython.lib.inputhook import appstart_gtk
30 appstart_gtk()
31 except ImportError:
32 gtk.main()
33
34
35
@@ -0,0 +1,54 b''
1 """Test the new %gui command. Run this in ipython as
2
3 In [1]: %gui [backend]
4
5 In [2]: %run switchgui [backend]
6
7 where the optional backend can be one of: qt4, gtk, tk, wx.
8
9 Because of subtle difference in how Matplotlib handles the different GUI
10 toolkits (in things like draw and show), minor modifications to this script
11 have to be made for Tk to get it to work with the 0.99 and below releases
12 of Matplotlib. However, in the future, Matplotlib should be able to have
13 similar logic for all the toolkits, as they are all now using PyOS_InputHook.
14 """
15
16 import sys
17 import time
18
19 from IPython.lib import inputhook
20
21 gui = inputhook.current_gui()
22 if gui is None:
23 gui = 'qt4'
24 inputhook.enable_qt4(app=True)
25
26 backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
27
28 import matplotlib
29 matplotlib.use(backends[gui])
30 matplotlib.interactive(True)
31
32 import matplotlib
33 from matplotlib import pyplot as plt, mlab, pylab
34 import numpy as np
35
36 from numpy import *
37 from matplotlib.pyplot import *
38
39 x = np.linspace(0,pi,500)
40
41 print "A plot has been created"
42 line, = plot(x,sin(2*x))
43 inputhook.spin() # This has to be removed for Tk
44
45
46 print "Now, we will update the plot..."
47 print
48 for i in range(1,51):
49 print i,
50 sys.stdout.flush()
51 line.set_data(x,sin(x*i))
52 plt.title('i=%d' % i)
53 plt.draw()
54 inputhook.spin() # This has to be removed for Tk
@@ -0,0 +1,40 b''
1 #!/usr/bin/env python
2 """Simple Qt4 example to manually test event loop integration.
3
4 This is meant to run tests manually in ipython as:
5
6 In [5]: %gui qt
7
8 In [6]: %run gui-qt.py
9
10 Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/
11 """
12
13 import sys
14 from PyQt4 import QtGui, QtCore
15
16 class SimpleWindow(QtGui.QWidget):
17 def __init__(self, parent=None):
18 QtGui.QWidget.__init__(self, parent)
19
20 self.setGeometry(300, 300, 200, 80)
21 self.setWindowTitle('Hello World')
22
23 quit = QtGui.QPushButton('Close', self)
24 quit.setGeometry(10, 10, 60, 35)
25
26 self.connect(quit, QtCore.SIGNAL('clicked()'),
27 self, QtCore.SLOT('close()'))
28
29 if __name__ == '__main__':
30 app = QtCore.QCoreApplication.instance()
31 if app is None:
32 app = QtGui.QApplication([])
33
34 sw = SimpleWindow()
35 sw.show()
36
37 try:
38 from IPython import appstart_qt4; appstart_qt4(app)
39 except ImportError:
40 app.exec_()
@@ -0,0 +1,32 b''
1 #!/usr/bin/env python
2 """Simple Tk example to manually test event loop integration.
3
4 This is meant to run tests manually in ipython as:
5
6 In [5]: %gui tk
7
8 In [6]: %run gui-tk.py
9 """
10
11 from Tkinter import *
12
13 class MyApp:
14
15 def __init__(self, root):
16 frame = Frame(root)
17 frame.pack()
18
19 self.button = Button(frame, text="Hello", command=self.hello_world)
20 self.button.pack(side=LEFT)
21
22 def hello_world(self):
23 print "Hello World!"
24
25 root = Tk()
26
27 app = MyApp(root)
28
29 try:
30 from IPython import appstart_tk; appstart_tk(root)
31 except ImportError:
32 root.mainloop()
@@ -0,0 +1,99 b''
1 """A Simple wx example to test IPython's event loop integration.
2
3 To run this do:
4
5 In [5]: %gui wx
6
7 In [6]: %run gui-wx.py
8
9 Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
10
11 This example can only be run once in a given IPython session.
12 """
13
14 import wx
15
16
17 class MyFrame(wx.Frame):
18 """
19 This is MyFrame. It just shows a few controls on a wxPanel,
20 and has a simple menu.
21 """
22 def __init__(self, parent, title):
23 wx.Frame.__init__(self, parent, -1, title,
24 pos=(150, 150), size=(350, 200))
25
26 # Create the menubar
27 menuBar = wx.MenuBar()
28
29 # and a menu
30 menu = wx.Menu()
31
32 # add an item to the menu, using \tKeyName automatically
33 # creates an accelerator, the third param is some help text
34 # that will show up in the statusbar
35 menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample")
36
37 # bind the menu event to an event handler
38 self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT)
39
40 # and put the menu on the menubar
41 menuBar.Append(menu, "&File")
42 self.SetMenuBar(menuBar)
43
44 self.CreateStatusBar()
45
46 # Now create the Panel to put the other controls on.
47 panel = wx.Panel(self)
48
49 # and a few controls
50 text = wx.StaticText(panel, -1, "Hello World!")
51 text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
52 text.SetSize(text.GetBestSize())
53 btn = wx.Button(panel, -1, "Close")
54 funbtn = wx.Button(panel, -1, "Just for fun...")
55
56 # bind the button events to handlers
57 self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn)
58 self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn)
59
60 # Use a sizer to layout the controls, stacked vertically and with
61 # a 10 pixel border around each
62 sizer = wx.BoxSizer(wx.VERTICAL)
63 sizer.Add(text, 0, wx.ALL, 10)
64 sizer.Add(btn, 0, wx.ALL, 10)
65 sizer.Add(funbtn, 0, wx.ALL, 10)
66 panel.SetSizer(sizer)
67 panel.Layout()
68
69
70 def OnTimeToClose(self, evt):
71 """Event handler for the button click."""
72 print "See ya later!"
73 self.Close()
74
75 def OnFunButton(self, evt):
76 """Event handler for the button click."""
77 print "Having fun yet?"
78
79
80 class MyApp(wx.App):
81 def OnInit(self):
82 frame = MyFrame(None, "Simple wxPython App")
83 self.SetTopWindow(frame)
84
85 print "Print statements go to this stdout window by default."
86
87 frame.Show(True)
88 return True
89
90 app = wx.GetApp()
91 if app is None:
92 app = MyApp(redirect=False, clearSigInt=False)
93
94 try:
95 from IPython.lib.inputhook import appstart_wx
96 appstart_wx(app)
97 except ImportError:
98 app.MainLoop()
99
@@ -34,8 +34,23 b" if sys.version[0:3] < '2.4':"
34 34 # Therefore, non-IPython modules can be added to extensions directory
35 35 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
36 36
37 from IPython.core import iplib
37 #-----------------------------------------------------------------------------
38 # Setup the top level names
39 #-----------------------------------------------------------------------------
38 40
41 from IPython.core.iplib import InteractiveShell
42 from IPython.core.error import TryNext
43
44 from IPython.lib import (
45 enable_wx, disable_wx,
46 enable_gtk, disable_gtk,
47 enable_qt4, disable_qt4,
48 enable_tk, disable_tk,
49 set_inputhook, clear_inputhook,
50 current_gui, spin,
51 appstart_qt4, appstart_wx,
52 appstart_gtk, appstart_tk
53 )
39 54
40 55 # Release data
41 56 __author__ = ''
@@ -60,34 +60,30 b' class MetaComponentTracker(type):'
60 60 c.__instance_refs[c.__numcreated] = instance
61 61 return instance
62 62
63 def get_instances(cls, name=None, klass=None, root=None):
63 def get_instances(cls, name=None, root=None):
64 64 """Get all instances of cls and its subclasses.
65 65
66 66 Parameters
67 67 ----------
68 68 name : str
69 69 Limit to components with this name.
70 klass : class
71 Limit to components having isinstance(component, klass)
72 70 root : Component or subclass
73 71 Limit to components having this root.
74 72 """
75 73 instances = cls.__instance_refs.values()
76 74 if name is not None:
77 75 instances = [i for i in instances if i.name == name]
78 if klass is not None:
79 instances = [i for i in instances if isinstance(i, klass)]
80 76 if root is not None:
81 77 instances = [i for i in instances if i.root == root]
82 78 return instances
83 79
84 def get_instances_by_condition(cls, call, name=None, klass=None, root=None):
80 def get_instances_by_condition(cls, call, name=None, root=None):
85 81 """Get all instances of cls, i such that call(i)==True.
86 82
87 This also takes the ``name``, ``klass`` and ``root`` arguments of
83 This also takes the ``name`` and ``root`` arguments of
88 84 :meth:`get_instance`
89 85 """
90 return [i for i in cls.get_instances(name,klass,root) if call(i)]
86 return [i for i in cls.get_instances(name, root) if call(i)]
91 87
92 88
93 89 class ComponentNameGenerator(object):
@@ -199,6 +195,14 b' class Component(HasTraitlets):'
199 195 "root != parent.root")
200 196
201 197 def _config_changed(self, name, old, new):
198 """Update all the class traits having a config_key with the config.
199
200 For any class traitlet with a ``config_key`` metadata attribute, we
201 update the traitlet with the value of the corresponding config entry.
202
203 In the future, we might want to do a pop here so stale config info
204 is not passed onto children.
205 """
202 206 # Get all traitlets with a config_key metadata entry
203 207 traitlets = self.traitlets('config_key')
204 208 for k, v in traitlets.items():
@@ -215,13 +219,13 b' class Component(HasTraitlets):'
215 219 return self._children
216 220
217 221 def _remove_child(self, child):
218 """A private method for removing children componenets."""
222 """A private method for removing children components."""
219 223 if child in self._children:
220 224 index = self._children.index(child)
221 225 del self._children[index]
222 226
223 227 def _add_child(self, child):
224 """A private method for adding children componenets."""
228 """A private method for adding children components."""
225 229 if child not in self._children:
226 230 self._children.append(child)
227 231
@@ -29,6 +29,7 b' Authors:'
29 29
30 30 from IPython.core.error import TryNext, UsageError
31 31 from IPython.core.component import Component
32 from IPython.core.iplib import InteractiveShell
32 33
33 34 #-----------------------------------------------------------------------------
34 35 # Classes and functions
@@ -36,7 +37,7 b' from IPython.core.component import Component'
36 37
37 38 def get():
38 39 """Get the most recently created InteractiveShell instance."""
39 insts = Component.get_instances(name='__IP')
40 insts = InteractiveShell.get_instances()
40 41 most_recent = insts[0]
41 42 for inst in insts[1:]:
42 43 if inst.created > most_recent.created:
@@ -3548,11 +3548,11 b' Defaulting color scheme to \'NoColor\'"""'
3548 3548 interrupts should work without any problems. The following toolkits
3549 3549 are supports: wxPython, PyQt4, PyGTK, and Tk::
3550 3550
3551 %gui wx # enable wxPython event loop integration
3552 %gui qt4 # enable PyQt4 event loop integration
3553 %gui gtk # enable PyGTK event loop integration
3554 %gui tk # enable Tk event loop integration
3555 %gui # disable all event loop integration
3551 %gui wx # enable wxPython event loop integration
3552 %gui qt4|qt # enable PyQt4 event loop integration
3553 %gui gtk # enable PyGTK event loop integration
3554 %gui tk # enable Tk event loop integration
3555 %gui # disable all event loop integration
3556 3556
3557 3557 WARNING: after any of these has been called you can simply create
3558 3558 an application object, but DO NOT start the event loop yourself, as
@@ -3574,7 +3574,7 b' Defaulting color scheme to \'NoColor\'"""'
3574 3574 inputhook.clear_inputhook()
3575 3575 elif 'wx' in parameter_s:
3576 3576 return inputhook.enable_wx(app)
3577 elif 'qt4' in parameter_s:
3577 elif ('qt4' in parameter_s) or ('qt' in parameter_s):
3578 3578 return inputhook.enable_qt4(app)
3579 3579 elif 'gtk' in parameter_s:
3580 3580 return inputhook.enable_gtk(app)
@@ -52,7 +52,7 b' class TestComponentMeta(TestCase):'
52 52 c2 = MyOtherComponent(c1)
53 53 c3 = MyOtherComponent(c2)
54 54 self.assertEquals(MyComponent.get_instances(), [c1, c2, c3])
55 self.assertEquals(MyComponent.get_instances(klass=MyOtherComponent), [c2, c3])
55 self.assertEquals(MyOtherComponent.get_instances(), [c2, c3])
56 56
57 57 def test_get_instances_root(self):
58 58 class MyComponent(Component):
@@ -20,7 +20,10 b' from IPython.lib.inputhook import ('
20 20 enable_gtk, disable_gtk,
21 21 enable_qt4, disable_qt4,
22 22 enable_tk, disable_tk,
23 set_inputhook, clear_inputhook
23 set_inputhook, clear_inputhook,
24 current_gui, spin,
25 appstart_qt4, appstart_wx,
26 appstart_gtk, appstart_tk
24 27 )
25 28
26 29 #-----------------------------------------------------------------------------
@@ -19,7 +19,153 b' import ctypes'
19 19 import sys
20 20
21 21 #-----------------------------------------------------------------------------
22 # Code
22 # Constants
23 #-----------------------------------------------------------------------------
24
25 # Constants for identifying the GUI toolkits.
26 GUI_WX = 'wx'
27 GUI_QT4 = 'qt4'
28 GUI_GTK = 'gtk'
29 GUI_TK = 'tk'
30
31 #-----------------------------------------------------------------------------
32 # Utility classes
33 #-----------------------------------------------------------------------------
34
35
36 class _DummyMainloop(object):
37 """A special manager to hijack GUI mainloops that is mostly a no-op.
38
39 We are not using this class currently as it breaks GUI code that calls
40 a mainloop function after the app has started to process pending events.
41 """
42 def __init__(self, ml, ihm, gui_type):
43 self.ml = ml
44 self.ihm = ihm
45 self.gui_type = gui_type
46
47 def __call__(self, *args, **kw):
48 if self.ihm.current_gui() == self.gui_type:
49 pass
50 else:
51 self.ml(*args, **kw)
52
53
54 #-----------------------------------------------------------------------------
55 # Appstart and spin functions
56 #-----------------------------------------------------------------------------
57
58
59 def appstart_qt4(app):
60 """Start the qt4 event loop in a way that plays with IPython.
61
62 When a qt4 app is run interactively in IPython, the event loop should
63 not be started. This function checks to see if IPython's qt4 integration
64 is activated and if so, it passes. If not, it will call the :meth:`exec_`
65 method of the main qt4 app.
66
67 This function should be used by users who want their qt4 scripts to work
68 both at the command line and in IPython. These users should put the
69 following logic at the bottom on their script, after they create a
70 :class:`QApplication` instance (called ``app`` here)::
71
72 try:
73 from IPython.lib.inputhook import appstart_qt4
74 appstart_qt4(app)
75 except ImportError:
76 app.exec_()
77 """
78 from PyQt4 import QtCore, QtGui
79
80 assert isinstance(app, QtCore.QCoreApplication)
81 if app is not None:
82 if current_gui() == GUI_QT4:
83 pass
84 else:
85 app.exec_()
86
87
88 def appstart_wx(app):
89 """Start the wx event loop in a way that plays with IPython.
90
91 When a wx app is run interactively in IPython, the event loop should
92 not be started. This function checks to see if IPython's wx integration
93 is activated and if so, it passes. If not, it will call the
94 :meth:`MainLoop` method of the main qt4 app.
95
96 This function should be used by users who want their wx scripts to work
97 both at the command line and in IPython. These users should put the
98 following logic at the bottom on their script, after they create a
99 :class:`App` instance (called ``app`` here)::
100
101 try:
102 from IPython.lib.inputhook import appstart_wx
103 appstart_wx(app)
104 except ImportError:
105 app.MainLoop()
106 """
107 import wx
108
109 assert isinstance(app, wx.App)
110 if app is not None:
111 if current_gui() == GUI_WX:
112 pass
113 else:
114 app.MainLoop()
115
116
117 def appstart_tk(app):
118 """Start the tk event loop in a way that plays with IPython.
119
120 When a tk app is run interactively in IPython, the event loop should
121 not be started. This function checks to see if IPython's tk integration
122 is activated and if so, it passes. If not, it will call the
123 :meth:`mainloop` method of the tk object passed to this method.
124
125 This function should be used by users who want their tk scripts to work
126 both at the command line and in IPython. These users should put the
127 following logic at the bottom on their script, after they create a
128 :class:`Tk` instance (called ``app`` here)::
129
130 try:
131 from IPython.lib.inputhook import appstart_tk
132 appstart_tk(app)
133 except ImportError:
134 app.mainloop()
135 """
136 if app is not None:
137 if current_gui() == GUI_TK:
138 pass
139 else:
140 app.mainloop()
141
142 def appstart_gtk():
143 """Start the gtk event loop in a way that plays with IPython.
144
145 When a gtk app is run interactively in IPython, the event loop should
146 not be started. This function checks to see if IPython's gtk integration
147 is activated and if so, it passes. If not, it will call
148 :func:`gtk.main`. Unlike the other appstart implementations, this does
149 not take an ``app`` argument.
150
151 This function should be used by users who want their gtk scripts to work
152 both at the command line and in IPython. These users should put the
153 following logic at the bottom on their script::
154
155 try:
156 from IPython.lib.inputhook import appstart_gtk
157 appstart_gtk()
158 except ImportError:
159 gtk.main()
160 """
161 import gtk
162 if current_gui() == GUI_GTK:
163 pass
164 else:
165 gtk.main()
166
167 #-----------------------------------------------------------------------------
168 # Main InputHookManager class
23 169 #-----------------------------------------------------------------------------
24 170
25 171
@@ -32,6 +178,12 b' class InputHookManager(object):'
32 178
33 179 def __init__(self):
34 180 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
181 self._apps = {}
182 self._spinner_dict = {
183 GUI_QT4 : self._spin_qt4,
184 GUI_WX : self._spin_wx,
185 GUI_GTK : self._spin_gtk,
186 GUI_TK : self._spin_tk}
35 187 self._reset()
36 188
37 189 def _reset(self):
@@ -40,19 +192,131 b' class InputHookManager(object):'
40 192 self._installed = False
41 193 self._current_gui = None
42 194
43 def get_pyos_inputhook(self):
44 """Return the current PyOS_InputHook as a ctypes.c_void_p.
195 def _hijack_wx(self):
196 """Hijack the wx mainloop so a user calling it won't cause badness.
197
198 We are not currently using this as it breaks GUI code that calls a
199 mainloop at anytime but startup.
200 """
201 import wx
202 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
203 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
204 else: raise AttributeError('Could not find wx core module')
205 orig_mainloop = core.PyApp_MainLoop
206 core.PyApp_MainLoop = _DummyMainloop
207 return orig_mainloop
208
209 def _hijack_qt4(self):
210 """Hijack the qt4 mainloop so a user calling it won't cause badness.
211
212 We are not currently using this as it breaks GUI code that calls a
213 mainloop at anytime but startup.
214 """
215 from PyQt4 import QtGui, QtCore
216 orig_mainloop = QtGui.qApp.exec_
217 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_QT4)
218 QtGui.qApp.exec_ = dumb_ml
219 QtGui.QApplication.exec_ = dumb_ml
220 QtCore.QCoreApplication.exec_ = dumb_ml
221 return orig_mainloop
222
223 def _hijack_gtk(self):
224 """Hijack the gtk mainloop so a user calling it won't cause badness.
225
226 We are not currently using this as it breaks GUI code that calls a
227 mainloop at anytime but startup.
228 """
229 import gtk
230 orig_mainloop = gtk.main
231 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_GTK)
232 gtk.mainloop = dumb_ml
233 gtk.main = dumb_ml
234 return orig_mainloop
235
236 def _hijack_tk(self):
237 """Hijack the tk mainloop so a user calling it won't cause badness.
238
239 We are not currently using this as it breaks GUI code that calls a
240 mainloop at anytime but startup.
241 """
242 import Tkinter
243 orig_mainloop = gtk.main
244 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_TK)
245 Tkinter.Misc.mainloop = dumb_ml
246 Tkinter.mainloop = dumb_ml
247
248 def _spin_qt4(self):
249 """Process all pending events in the qt4 event loop.
250
251 This is for internal IPython use only and user code should not call this.
252 Instead, they should issue the raw GUI calls themselves.
253 """
254 from PyQt4 import QtCore, QtGui
255
256 app = QtCore.QCoreApplication.instance()
257 if app is not None:
258 QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)
259
260 def _spin_wx(self):
261 """Process all pending events in the wx event loop.
262
263 This is for internal IPython use only and user code should not call this.
264 Instead, they should issue the raw GUI calls themselves.
265 """
266 import wx
267 app = wx.GetApp()
268 if app is not None and wx.Thread_IsMain():
269 evtloop = wx.EventLoop()
270 ea = wx.EventLoopActivator(evtloop)
271 while evtloop.Pending():
272 evtloop.Dispatch()
273 app.ProcessIdle()
274 del ea
275
276 def _spin_gtk(self):
277 """Process all pending events in the gtk event loop.
278
279 This is for internal IPython use only and user code should not call this.
280 Instead, they should issue the raw GUI calls themselves.
281 """
282 import gtk
283 gtk.gdk.threads_enter()
284 while gtk.events_pending():
285 gtk.main_iteration(False)
286 gtk.gdk.flush()
287 gtk.gdk.threads_leave()
288
289 def _spin_tk(self):
290 """Process all pending events in the tk event loop.
291
292 This is for internal IPython use only and user code should not call this.
293 Instead, they should issue the raw GUI calls themselves.
45 294 """
295 app = self._apps.get(GUI_TK)
296 if app is not None:
297 app.update()
298
299 def spin(self):
300 """Process pending events in the current gui.
301
302 This method is just provided for IPython to use internally if needed
303 for things like testing. Third party projects should not call this
304 method, but instead should call the underlying GUI toolkit methods
305 that we are calling.
306 """
307 spinner = self._spinner_dict.get(self._current_gui, lambda: None)
308 spinner()
309
310 def get_pyos_inputhook(self):
311 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
46 312 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
47 313
48 314 def get_pyos_inputhook_as_func(self):
49 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.
50 """
315 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
51 316 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
52 317
53 318 def set_inputhook(self, callback):
54 """Set PyOS_InputHook to callback and return the previous one.
55 """
319 """Set PyOS_InputHook to callback and return the previous one."""
56 320 self._callback = callback
57 321 self._callback_pyfunctype = self.PYFUNC(callback)
58 322 pyos_inputhook_ptr = self.get_pyos_inputhook()
@@ -63,14 +327,33 b' class InputHookManager(object):'
63 327 return original
64 328
65 329 def clear_inputhook(self):
66 """Set PyOS_InputHook to NULL and return the previous one.
67 """
330 """Set PyOS_InputHook to NULL and return the previous one."""
68 331 pyos_inputhook_ptr = self.get_pyos_inputhook()
69 332 original = self.get_pyos_inputhook_as_func()
70 333 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
71 334 self._reset()
72 335 return original
73 336
337 def clear_app_refs(self, gui=None):
338 """Clear IPython's internal reference to an application instance.
339
340 Whenever we create an app for a user on qt4 or wx, we hold a
341 reference to the app. This is needed because in some cases bad things
342 can happen if a user doesn't hold a reference themselves. This
343 method is provided to clear the references we are holding.
344
345 Parameters
346 ----------
347 gui : None or str
348 If None, clear all app references. If ('wx', 'qt4') clear
349 the app for that toolkit. References are not held for gtk or tk
350 as those toolkits don't have the notion of an app.
351 """
352 if gui is None:
353 self._apps = {}
354 elif self._apps.has_key(gui):
355 del self._apps[gui]
356
74 357 def enable_wx(self, app=False):
75 358 """Enable event loop integration with wxPython.
76 359
@@ -81,32 +364,35 b' class InputHookManager(object):'
81 364
82 365 Notes
83 366 -----
84 This methods sets the PyOS_InputHook for wxPython, which allows
367 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
85 368 the wxPython to integrate with terminal based applications like
86 369 IPython.
87
88 Once this has been called, you can use wx interactively by doing::
89
90 >>> import wx
91 >>> app = wx.App(redirect=False, clearSigInt=False)
92
370
371 If ``app`` is True, we create an :class:`wx.App` as follows::
372
373 import wx
374 app = wx.App(redirect=False, clearSigInt=False)
375
93 376 Both options this constructor are important for things to work
94 377 properly in an interactive context.
95
96 But, *don't start the event loop*. That is handled automatically by
97 PyOS_InputHook.
378
379 But, we first check to see if an application has already been
380 created. If so, we simply return that instance.
98 381 """
99 382 from IPython.lib.inputhookwx import inputhook_wx
100 383 self.set_inputhook(inputhook_wx)
101 self._current_gui = 'wx'
384 self._current_gui = GUI_WX
102 385 if app:
103 386 import wx
104 app = wx.App(redirect=False, clearSigInt=False)
387 app = wx.GetApp()
388 if app is None:
389 app = wx.App(redirect=False, clearSigInt=False)
390 self._apps[GUI_WX] = app
105 391 return app
106 392
107 393 def disable_wx(self):
108 394 """Disable event loop integration with wxPython.
109
395
110 396 This merely sets PyOS_InputHook to NULL.
111 397 """
112 398 self.clear_inputhook()
@@ -121,13 +407,17 b' class InputHookManager(object):'
121 407
122 408 Notes
123 409 -----
124 This methods sets the PyOS_InputHook for wxPython, which allows
410 This methods sets the PyOS_InputHook for PyQt4, which allows
125 411 the PyQt4 to integrate with terminal based applications like
126 412 IPython.
127
128 Once this has been called, you can simply create a QApplication and
129 use it. But, *don't start the event loop*. That is handled
130 automatically by PyOS_InputHook.
413
414 If ``app`` is True, we create an :class:`QApplication` as follows::
415
416 from PyQt4 import QtCore
417 app = QtGui.QApplication(sys.argv)
418
419 But, we first check to see if an application has already been
420 created. If so, we simply return that instance.
131 421 """
132 422 from PyQt4 import QtCore
133 423 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
@@ -138,15 +428,18 b' class InputHookManager(object):'
138 428 QtCore.pyqtRestoreInputHook()
139 429 except AttributeError:
140 430 pass
141 self._current_gui = 'qt4'
431 self._current_gui = GUI_QT4
142 432 if app:
143 433 from PyQt4 import QtGui
144 app = QtGui.QApplication(sys.argv)
434 app = QtCore.QCoreApplication.instance()
435 if app is None:
436 app = QtGui.QApplication(sys.argv)
437 self._apps[GUI_QT4] = app
145 438 return app
146 439
147 440 def disable_qt4(self):
148 441 """Disable event loop integration with PyQt4.
149
442
150 443 This merely sets PyOS_InputHook to NULL.
151 444 """
152 445 self.clear_inputhook()
@@ -157,26 +450,24 b' class InputHookManager(object):'
157 450 Parameters
158 451 ----------
159 452 app : bool
160 Create a running application object or not.
453 Create a running application object or not. Because gtk does't
454 have an app class, this does nothing.
161 455
162 456 Notes
163 457 -----
164 458 This methods sets the PyOS_InputHook for PyGTK, which allows
165 459 the PyGTK to integrate with terminal based applications like
166 460 IPython.
167
168 Once this has been called, you can simple create PyGTK objects and
169 use them. But, *don't start the event loop*. That is handled
170 automatically by PyOS_InputHook.
171 461 """
172 462 import gtk
173 463 try:
174 464 gtk.set_interactive(True)
175 self._current_gui = 'gtk'
465 self._current_gui = GUI_GTK
176 466 except AttributeError:
177 467 # For older versions of gtk, use our own ctypes version
178 468 from IPython.lib.inputhookgtk import inputhook_gtk
179 add_inputhook(inputhook_gtk)
469 self.set_inputhook(inputhook_gtk)
470 self._current_gui = GUI_GTK
180 471
181 472 def disable_gtk(self):
182 473 """Disable event loop integration with PyGTK.
@@ -198,7 +489,13 b' class InputHookManager(object):'
198 489 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
199 490 sets ``PyOS_InputHook``.
200 491 """
201 self._current_gui = 'tk'
492 self._current_gui = GUI_TK
493 if app:
494 import Tkinter
495 app = Tkinter.Tk()
496 app.withdraw()
497 self._apps[GUI_TK] = app
498 return app
202 499
203 500 def disable_tk(self):
204 501 """Disable event loop integration with Tkinter.
@@ -223,4 +520,6 b' enable_tk = inputhook_manager.enable_tk'
223 520 disable_tk = inputhook_manager.disable_tk
224 521 clear_inputhook = inputhook_manager.clear_inputhook
225 522 set_inputhook = inputhook_manager.set_inputhook
226 current_gui = inputhook_manager.current_gui No newline at end of file
523 current_gui = inputhook_manager.current_gui
524 clear_app_refs = inputhook_manager.clear_app_refs
525 spin = inputhook_manager.spin
@@ -19,6 +19,7 b' Authors: Robin Dunn, Brian Granger, Ondrej Certik'
19 19 #-----------------------------------------------------------------------------
20 20
21 21 import os
22 import signal
22 23 import sys
23 24 import time
24 25 from timeit import default_timer as clock
@@ -122,6 +123,12 b' def inputhook_wx3():'
122 123 if app is not None:
123 124 assert wx.Thread_IsMain()
124 125
126 # The import of wx on Linux sets the handler for signal.SIGINT
127 # to 0. This is a bug in wx or gtk. We fix by just setting it
128 # back to the Python default.
129 if not callable(signal.getsignal(signal.SIGINT)):
130 signal.signal(signal.SIGINT, signal.default_int_handler)
131
125 132 evtloop = wx.EventLoop()
126 133 ea = wx.EventLoopActivator(evtloop)
127 134 t = clock()
@@ -139,7 +146,9 b' def inputhook_wx3():'
139 146 # 0.001 13%
140 147 # 0.005 3%
141 148 # 0.01 1.5%
142 # 0.05 0.5%
149 # 0.05 0.5%
150 if clock()-t > 1.0:
151 time.sleep(1.0)
143 152 if clock()-t > 0.1:
144 153 # Few GUI events coming in, so we can sleep longer
145 154 time.sleep(0.05)
@@ -1412,6 +1412,9 b' the form of a library, the capabilities are exposed in library form'
1412 1412 in the :mod:`IPython.lib.inputhook`. Interested developers should see the
1413 1413 module docstrings for more information.
1414 1414
1415 In addition, we also have a number of examples in our source directory
1416 :file:`docs/examples/lib` that demonstrate these capabilities.
1417
1415 1418 .. _matplotlib_support:
1416 1419
1417 1420 Plotting with matplotlib
General Comments 0
You need to be logged in to leave comments. Login now