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 | ||||
|
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, |
|
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, |
|
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`` |
|
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, |
|
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 componen |
|
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 componen |
|
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 = |
|
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 |
|
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( |
|
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 |
|
|
373 | import wx | |
91 |
|
|
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 = |
|
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 |
|
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 = |
|
431 | self._current_gui = GUI_QT4 | |
142 | if app: |
|
432 | if app: | |
143 | from PyQt4 import QtGui |
|
433 | from PyQt4 import QtGui | |
144 |
app = Qt |
|
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 = |
|
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 |
|
|
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 = |
|
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