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 | 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, |
|
|
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, |
|
|
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`` |
|
|
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, |
|
|
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 componen |
|
|
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 componen |
|
|
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 = |
|
|
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: |
@@ -3549,7 +3549,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3549 | 3549 | are supports: wxPython, PyQt4, PyGTK, and Tk:: |
|
3550 | 3550 | |
|
3551 | 3551 | %gui wx # enable wxPython event loop integration |
|
3552 |
%gui qt4 |
|
|
3552 | %gui qt4|qt # enable PyQt4 event loop integration | |
|
3553 | 3553 | %gui gtk # enable PyGTK event loop integration |
|
3554 | 3554 | %gui tk # enable Tk event loop integration |
|
3555 | 3555 | %gui # disable all event loop integration |
@@ -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( |
|
|
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,27 +364,30 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 | 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 |
|
|
|
91 |
|
|
|
373 | import wx | |
|
374 | app = wx.App(redirect=False, clearSigInt=False) | |
|
92 | 375 | |
|
93 | 376 | Both options this constructor are important for things to work |
|
94 | 377 | properly in an interactive context. |
|
95 | 378 | |
|
96 | But, *don't start the event loop*. That is handled automatically by | |
|
97 | PyOS_InputHook. | |
|
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 = |
|
|
384 | self._current_gui = GUI_WX | |
|
102 | 385 | if app: |
|
103 | 386 | import wx |
|
387 | app = wx.GetApp() | |
|
388 | if app is None: | |
|
104 | 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): |
@@ -121,13 +407,17 b' class InputHookManager(object):' | |||
|
121 | 407 | |
|
122 | 408 | Notes |
|
123 | 409 | ----- |
|
124 |
This methods sets the PyOS_InputHook for |
|
|
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 | 413 | |
|
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. | |
|
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,10 +428,13 b' class InputHookManager(object):' | |||
|
138 | 428 | QtCore.pyqtRestoreInputHook() |
|
139 | 429 | except AttributeError: |
|
140 | 430 | pass |
|
141 |
self._current_gui = |
|
|
431 | self._current_gui = GUI_QT4 | |
|
142 | 432 | if app: |
|
143 | 433 | from PyQt4 import QtGui |
|
434 | app = QtCore.QCoreApplication.instance() | |
|
435 | if app is None: | |
|
144 | 436 | app = QtGui.QApplication(sys.argv) |
|
437 | self._apps[GUI_QT4] = app | |
|
145 | 438 | return app |
|
146 | 439 | |
|
147 | 440 | def disable_qt4(self): |
@@ -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 = |
|
|
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 |
|
|
|
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 = |
|
|
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. |
@@ -224,3 +521,5 b' disable_tk = inputhook_manager.disable_tk' | |||
|
224 | 521 | clear_inputhook = inputhook_manager.clear_inputhook |
|
225 | 522 | set_inputhook = inputhook_manager.set_inputhook |
|
226 | 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() |
@@ -140,6 +147,8 b' def inputhook_wx3():' | |||
|
140 | 147 | # 0.005 3% |
|
141 | 148 | # 0.01 1.5% |
|
142 | 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