##// 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 # Therefore, non-IPython modules can be added to extensions directory
34 # Therefore, non-IPython modules can be added to extensions directory
35 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
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 # Release data
55 # Release data
41 __author__ = ''
56 __author__ = ''
@@ -60,34 +60,30 b' class MetaComponentTracker(type):'
60 c.__instance_refs[c.__numcreated] = instance
60 c.__instance_refs[c.__numcreated] = instance
61 return instance
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 """Get all instances of cls and its subclasses.
64 """Get all instances of cls and its subclasses.
65
65
66 Parameters
66 Parameters
67 ----------
67 ----------
68 name : str
68 name : str
69 Limit to components with this name.
69 Limit to components with this name.
70 klass : class
71 Limit to components having isinstance(component, klass)
72 root : Component or subclass
70 root : Component or subclass
73 Limit to components having this root.
71 Limit to components having this root.
74 """
72 """
75 instances = cls.__instance_refs.values()
73 instances = cls.__instance_refs.values()
76 if name is not None:
74 if name is not None:
77 instances = [i for i in instances if i.name == name]
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 if root is not None:
76 if root is not None:
81 instances = [i for i in instances if i.root == root]
77 instances = [i for i in instances if i.root == root]
82 return instances
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 """Get all instances of cls, i such that call(i)==True.
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 :meth:`get_instance`
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 class ComponentNameGenerator(object):
89 class ComponentNameGenerator(object):
@@ -199,6 +195,14 b' class Component(HasTraitlets):'
199 "root != parent.root")
195 "root != parent.root")
200
196
201 def _config_changed(self, name, old, new):
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 # Get all traitlets with a config_key metadata entry
206 # Get all traitlets with a config_key metadata entry
203 traitlets = self.traitlets('config_key')
207 traitlets = self.traitlets('config_key')
204 for k, v in traitlets.items():
208 for k, v in traitlets.items():
@@ -215,13 +219,13 b' class Component(HasTraitlets):'
215 return self._children
219 return self._children
216
220
217 def _remove_child(self, child):
221 def _remove_child(self, child):
218 """A private method for removing children componenets."""
222 """A private method for removing children components."""
219 if child in self._children:
223 if child in self._children:
220 index = self._children.index(child)
224 index = self._children.index(child)
221 del self._children[index]
225 del self._children[index]
222
226
223 def _add_child(self, child):
227 def _add_child(self, child):
224 """A private method for adding children componenets."""
228 """A private method for adding children components."""
225 if child not in self._children:
229 if child not in self._children:
226 self._children.append(child)
230 self._children.append(child)
227
231
@@ -29,6 +29,7 b' Authors:'
29
29
30 from IPython.core.error import TryNext, UsageError
30 from IPython.core.error import TryNext, UsageError
31 from IPython.core.component import Component
31 from IPython.core.component import Component
32 from IPython.core.iplib import InteractiveShell
32
33
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Classes and functions
35 # Classes and functions
@@ -36,7 +37,7 b' from IPython.core.component import Component'
36
37
37 def get():
38 def get():
38 """Get the most recently created InteractiveShell instance."""
39 """Get the most recently created InteractiveShell instance."""
39 insts = Component.get_instances(name='__IP')
40 insts = InteractiveShell.get_instances()
40 most_recent = insts[0]
41 most_recent = insts[0]
41 for inst in insts[1:]:
42 for inst in insts[1:]:
42 if inst.created > most_recent.created:
43 if inst.created > most_recent.created:
@@ -3548,11 +3548,11 b' Defaulting color scheme to \'NoColor\'"""'
3548 interrupts should work without any problems. The following toolkits
3548 interrupts should work without any problems. The following toolkits
3549 are supports: wxPython, PyQt4, PyGTK, and Tk::
3549 are supports: wxPython, PyQt4, PyGTK, and Tk::
3550
3550
3551 %gui wx # enable wxPython event loop integration
3551 %gui wx # enable wxPython event loop integration
3552 %gui qt4 # enable PyQt4 event loop integration
3552 %gui qt4|qt # enable PyQt4 event loop integration
3553 %gui gtk # enable PyGTK event loop integration
3553 %gui gtk # enable PyGTK event loop integration
3554 %gui tk # enable Tk event loop integration
3554 %gui tk # enable Tk event loop integration
3555 %gui # disable all event loop integration
3555 %gui # disable all event loop integration
3556
3556
3557 WARNING: after any of these has been called you can simply create
3557 WARNING: after any of these has been called you can simply create
3558 an application object, but DO NOT start the event loop yourself, as
3558 an application object, but DO NOT start the event loop yourself, as
@@ -3574,7 +3574,7 b' Defaulting color scheme to \'NoColor\'"""'
3574 inputhook.clear_inputhook()
3574 inputhook.clear_inputhook()
3575 elif 'wx' in parameter_s:
3575 elif 'wx' in parameter_s:
3576 return inputhook.enable_wx(app)
3576 return inputhook.enable_wx(app)
3577 elif 'qt4' in parameter_s:
3577 elif ('qt4' in parameter_s) or ('qt' in parameter_s):
3578 return inputhook.enable_qt4(app)
3578 return inputhook.enable_qt4(app)
3579 elif 'gtk' in parameter_s:
3579 elif 'gtk' in parameter_s:
3580 return inputhook.enable_gtk(app)
3580 return inputhook.enable_gtk(app)
@@ -52,7 +52,7 b' class TestComponentMeta(TestCase):'
52 c2 = MyOtherComponent(c1)
52 c2 = MyOtherComponent(c1)
53 c3 = MyOtherComponent(c2)
53 c3 = MyOtherComponent(c2)
54 self.assertEquals(MyComponent.get_instances(), [c1, c2, c3])
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 def test_get_instances_root(self):
57 def test_get_instances_root(self):
58 class MyComponent(Component):
58 class MyComponent(Component):
@@ -20,7 +20,10 b' from IPython.lib.inputhook import ('
20 enable_gtk, disable_gtk,
20 enable_gtk, disable_gtk,
21 enable_qt4, disable_qt4,
21 enable_qt4, disable_qt4,
22 enable_tk, disable_tk,
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 import sys
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 def __init__(self):
179 def __init__(self):
34 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
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 self._reset()
187 self._reset()
36
188
37 def _reset(self):
189 def _reset(self):
@@ -40,19 +192,131 b' class InputHookManager(object):'
40 self._installed = False
192 self._installed = False
41 self._current_gui = None
193 self._current_gui = None
42
194
43 def get_pyos_inputhook(self):
195 def _hijack_wx(self):
44 """Return the current PyOS_InputHook as a ctypes.c_void_p.
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 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
312 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
47
313
48 def get_pyos_inputhook_as_func(self):
314 def get_pyos_inputhook_as_func(self):
49 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.
315 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
50 """
51 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
316 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
52
317
53 def set_inputhook(self, callback):
318 def set_inputhook(self, callback):
54 """Set PyOS_InputHook to callback and return the previous one.
319 """Set PyOS_InputHook to callback and return the previous one."""
55 """
56 self._callback = callback
320 self._callback = callback
57 self._callback_pyfunctype = self.PYFUNC(callback)
321 self._callback_pyfunctype = self.PYFUNC(callback)
58 pyos_inputhook_ptr = self.get_pyos_inputhook()
322 pyos_inputhook_ptr = self.get_pyos_inputhook()
@@ -63,14 +327,33 b' class InputHookManager(object):'
63 return original
327 return original
64
328
65 def clear_inputhook(self):
329 def clear_inputhook(self):
66 """Set PyOS_InputHook to NULL and return the previous one.
330 """Set PyOS_InputHook to NULL and return the previous one."""
67 """
68 pyos_inputhook_ptr = self.get_pyos_inputhook()
331 pyos_inputhook_ptr = self.get_pyos_inputhook()
69 original = self.get_pyos_inputhook_as_func()
332 original = self.get_pyos_inputhook_as_func()
70 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
333 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
71 self._reset()
334 self._reset()
72 return original
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 def enable_wx(self, app=False):
357 def enable_wx(self, app=False):
75 """Enable event loop integration with wxPython.
358 """Enable event loop integration with wxPython.
76
359
@@ -81,32 +364,35 b' class InputHookManager(object):'
81
364
82 Notes
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 the wxPython to integrate with terminal based applications like
368 the wxPython to integrate with terminal based applications like
86 IPython.
369 IPython.
87
370
88 Once this has been called, you can use wx interactively by doing::
371 If ``app`` is True, we create an :class:`wx.App` as follows::
89
372
90 >>> import wx
373 import wx
91 >>> app = wx.App(redirect=False, clearSigInt=False)
374 app = wx.App(redirect=False, clearSigInt=False)
92
375
93 Both options this constructor are important for things to work
376 Both options this constructor are important for things to work
94 properly in an interactive context.
377 properly in an interactive context.
95
378
96 But, *don't start the event loop*. That is handled automatically by
379 But, we first check to see if an application has already been
97 PyOS_InputHook.
380 created. If so, we simply return that instance.
98 """
381 """
99 from IPython.lib.inputhookwx import inputhook_wx
382 from IPython.lib.inputhookwx import inputhook_wx
100 self.set_inputhook(inputhook_wx)
383 self.set_inputhook(inputhook_wx)
101 self._current_gui = 'wx'
384 self._current_gui = GUI_WX
102 if app:
385 if app:
103 import wx
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 return app
391 return app
106
392
107 def disable_wx(self):
393 def disable_wx(self):
108 """Disable event loop integration with wxPython.
394 """Disable event loop integration with wxPython.
109
395
110 This merely sets PyOS_InputHook to NULL.
396 This merely sets PyOS_InputHook to NULL.
111 """
397 """
112 self.clear_inputhook()
398 self.clear_inputhook()
@@ -121,13 +407,17 b' class InputHookManager(object):'
121
407
122 Notes
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 the PyQt4 to integrate with terminal based applications like
411 the PyQt4 to integrate with terminal based applications like
126 IPython.
412 IPython.
127
413
128 Once this has been called, you can simply create a QApplication and
414 If ``app`` is True, we create an :class:`QApplication` as follows::
129 use it. But, *don't start the event loop*. That is handled
415
130 automatically by PyOS_InputHook.
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 from PyQt4 import QtCore
422 from PyQt4 import QtCore
133 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
423 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
@@ -138,15 +428,18 b' class InputHookManager(object):'
138 QtCore.pyqtRestoreInputHook()
428 QtCore.pyqtRestoreInputHook()
139 except AttributeError:
429 except AttributeError:
140 pass
430 pass
141 self._current_gui = 'qt4'
431 self._current_gui = GUI_QT4
142 if app:
432 if app:
143 from PyQt4 import QtGui
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 return app
438 return app
146
439
147 def disable_qt4(self):
440 def disable_qt4(self):
148 """Disable event loop integration with PyQt4.
441 """Disable event loop integration with PyQt4.
149
442
150 This merely sets PyOS_InputHook to NULL.
443 This merely sets PyOS_InputHook to NULL.
151 """
444 """
152 self.clear_inputhook()
445 self.clear_inputhook()
@@ -157,26 +450,24 b' class InputHookManager(object):'
157 Parameters
450 Parameters
158 ----------
451 ----------
159 app : bool
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 Notes
456 Notes
163 -----
457 -----
164 This methods sets the PyOS_InputHook for PyGTK, which allows
458 This methods sets the PyOS_InputHook for PyGTK, which allows
165 the PyGTK to integrate with terminal based applications like
459 the PyGTK to integrate with terminal based applications like
166 IPython.
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 import gtk
462 import gtk
173 try:
463 try:
174 gtk.set_interactive(True)
464 gtk.set_interactive(True)
175 self._current_gui = 'gtk'
465 self._current_gui = GUI_GTK
176 except AttributeError:
466 except AttributeError:
177 # For older versions of gtk, use our own ctypes version
467 # For older versions of gtk, use our own ctypes version
178 from IPython.lib.inputhookgtk import inputhook_gtk
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 def disable_gtk(self):
472 def disable_gtk(self):
182 """Disable event loop integration with PyGTK.
473 """Disable event loop integration with PyGTK.
@@ -198,7 +489,13 b' class InputHookManager(object):'
198 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
489 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
199 sets ``PyOS_InputHook``.
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 def disable_tk(self):
500 def disable_tk(self):
204 """Disable event loop integration with Tkinter.
501 """Disable event loop integration with Tkinter.
@@ -223,4 +520,6 b' enable_tk = inputhook_manager.enable_tk'
223 disable_tk = inputhook_manager.disable_tk
520 disable_tk = inputhook_manager.disable_tk
224 clear_inputhook = inputhook_manager.clear_inputhook
521 clear_inputhook = inputhook_manager.clear_inputhook
225 set_inputhook = inputhook_manager.set_inputhook
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 import os
21 import os
22 import signal
22 import sys
23 import sys
23 import time
24 import time
24 from timeit import default_timer as clock
25 from timeit import default_timer as clock
@@ -122,6 +123,12 b' def inputhook_wx3():'
122 if app is not None:
123 if app is not None:
123 assert wx.Thread_IsMain()
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 evtloop = wx.EventLoop()
132 evtloop = wx.EventLoop()
126 ea = wx.EventLoopActivator(evtloop)
133 ea = wx.EventLoopActivator(evtloop)
127 t = clock()
134 t = clock()
@@ -139,7 +146,9 b' def inputhook_wx3():'
139 # 0.001 13%
146 # 0.001 13%
140 # 0.005 3%
147 # 0.005 3%
141 # 0.01 1.5%
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 if clock()-t > 0.1:
152 if clock()-t > 0.1:
144 # Few GUI events coming in, so we can sleep longer
153 # Few GUI events coming in, so we can sleep longer
145 time.sleep(0.05)
154 time.sleep(0.05)
@@ -1412,6 +1412,9 b' the form of a library, the capabilities are exposed in library form'
1412 in the :mod:`IPython.lib.inputhook`. Interested developers should see the
1412 in the :mod:`IPython.lib.inputhook`. Interested developers should see the
1413 module docstrings for more information.
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 .. _matplotlib_support:
1418 .. _matplotlib_support:
1416
1419
1417 Plotting with matplotlib
1420 Plotting with matplotlib
General Comments 0
You need to be logged in to leave comments. Login now