##// END OF EJS Templates
Finishing up the wx, qt4 and tk support. Still have to do gtk.
Brian Granger -
Show More
@@ -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
@@ -63,6 +63,16 b' Shell = shell'
63 from IPython.core import ipapi
63 from IPython.core import ipapi
64 from IPython.core import iplib
64 from IPython.core import iplib
65
65
66 from IPython.lib import (
67 enable_wx, disable_wx,
68 enable_gtk, disable_gtk,
69 enable_qt4, disable_qt4,
70 enable_tk, disable_tk,
71 set_inputhook, clear_inputhook,
72 current_gui, spin,
73 appstart_qt4, appstart_wx
74 )
75
66 # Release data
76 # Release data
67 from IPython.core import release # do it explicitly so pydoc can see it - pydoc bug
77 from IPython.core import release # do it explicitly so pydoc can see it - pydoc bug
68 __author__ = '%s <%s>\n%s <%s>\n%s <%s>' % \
78 __author__ = '%s <%s>\n%s <%s>\n%s <%s>' % \
@@ -20,7 +20,9 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
24 )
26 )
25
27
26 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
@@ -19,70 +19,104 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
23 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
24
34
25 def appstart_qt4():
26 from PyQt4 import QtCore, QtGui
27
28 app = QtCore.QCoreApplication.instance()
29 print 'qtapp:', app
30 if app is not None:
31 if current_gui() == 'qt4':
32 pass
33 else:
34 app.exec_()
35
36
35
37 class _DummyMainloop(object):
36 class _DummyMainloop(object):
38 """A special manager to hijack GUI mainloops that is mostly a no-op.
37 """A special manager to hijack GUI mainloops that is mostly a no-op.
39
38
40 This does have, however, special logic.
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 """
41 """
42 def __init__(self, ml, ihm, gui_type):
42 def __init__(self, ml, ihm, gui_type):
43 self.ml = ml
43 self.ml = ml
44 self.ihm = ihm
44 self.ihm = ihm
45 self.gui_type = gui_type
45 self.gui_type = gui_type
46
46
47
48 def __call__(self, *args, **kw):
47 def __call__(self, *args, **kw):
49 force = kw.pop('force', False)
50 force = False
51 if force:
52 #print 'forced spin' # dbg
53 self.ml(*args, **kw)
54
55 if self.ihm.current_gui() == self.gui_type:
48 if self.ihm.current_gui() == self.gui_type:
56 pass
49 pass
57 else:
50 else:
58 self.ml(*args, **kw)
51 self.ml(*args, **kw)
59
52
60
53
61 def spin_qt4():
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 """
62 from PyQt4 import QtCore, QtGui
78 from PyQt4 import QtCore, QtGui
63
79
64 app = QtCore.QCoreApplication.instance()
80 assert isinstance(app, QtCore.QCoreApplication)
65 if (app is not None) and (app.thread() == QtCore.QThread.currentThread()):
81 if app is not None:
66 ## timer = QtCore.QTimer()
82 if current_gui() == GUI_QT4:
67 ## QtCore.QObject.connect(timer,
83 pass
68 ## QtCore.SIGNAL('timeout()'),
84 else:
69 ## app,
85 app.exec_()
70 ## QtCore.SLOT('quit()'))
86
71 ## timer.start(100)
87
72 #QtCore.QCoreApplication.exec_(force=True)
88 def appstart_wx(app):
73 QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)
89 """Start the wx event loop in a way that plays with IPython.
74 ##timer.stop()
90
75
91 When a wx app is run interactively in IPython, the event loop should
76
92 not be started. This function checks to see if IPython's wx integration
77 def spin_wx():
93 is activated and if so, it passes. If not, it will call the
78 app = wx.GetApp()
94 :meth:`MainLoop` method of the main qt4 app.
79 if app is not None and wx.Thread_IsMain():
95
80 evtloop = wx.EventLoop()
96 This function should be used by users who want their wx scripts to work
81 ea = wx.EventLoopActivator(evtloop)
97 both at the command line and in IPython. These users should put the
82 while evtloop.Pending():
98 following logic at the bottom on their script, after they create a
83 evtloop.Dispatch()
99 :class:`App` instance (called ``app`` here)::
84 app.ProcessIdle()
100
85 del ea
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 #-----------------------------------------------------------------------------
118 # Main InputHookManager class
119 #-----------------------------------------------------------------------------
86
120
87
121
88 class InputHookManager(object):
122 class InputHookManager(object):
@@ -95,6 +129,11 b' class InputHookManager(object):'
95 def __init__(self):
129 def __init__(self):
96 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
130 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
97 self._apps = {}
131 self._apps = {}
132 self._spinner_dict = {
133 GUI_QT4 : self._spin_qt4,
134 GUI_WX : self._spin_wx,
135 GUI_GTK : self._spin_gtk,
136 GUI_TK : self._spin_tk}
98 self._reset()
137 self._reset()
99
138
100 def _reset(self):
139 def _reset(self):
@@ -104,7 +143,11 b' class InputHookManager(object):'
104 self._current_gui = None
143 self._current_gui = None
105
144
106 def _hijack_wx(self):
145 def _hijack_wx(self):
107 """Hijack the wx mainloop so a user calling it won't cause badness."""
146 """Hijack the wx mainloop so a user calling it won't cause badness.
147
148 We are not currently using this as it breaks GUI code that calls a
149 mainloop at anytime but startup.
150 """
108 import wx
151 import wx
109 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
152 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
110 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
153 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
@@ -114,28 +157,100 b' class InputHookManager(object):'
114 return orig_mainloop
157 return orig_mainloop
115
158
116 def _hijack_qt4(self):
159 def _hijack_qt4(self):
117 """Hijack the qt4 mainloop so a user calling it won't cause badness."""
160 """Hijack the qt4 mainloop so a user calling it won't cause badness.
161
162 We are not currently using this as it breaks GUI code that calls a
163 mainloop at anytime but startup.
164 """
118 from PyQt4 import QtGui, QtCore
165 from PyQt4 import QtGui, QtCore
119 orig_mainloop = QtGui.qApp.exec_
166 orig_mainloop = QtGui.qApp.exec_
120 dumb_ml = _DummyMainloop(orig_mainloop, self, 'qt4')
167 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_QT4)
121 QtGui.qApp.exec_ = dumb_ml
168 QtGui.qApp.exec_ = dumb_ml
122 QtGui.QApplication.exec_ = dumb_ml
169 QtGui.QApplication.exec_ = dumb_ml
123 QtCore.QCoreApplication.exec_ = dumb_ml
170 QtCore.QCoreApplication.exec_ = dumb_ml
124 return orig_mainloop
171 return orig_mainloop
125
172
126 def _hijack_gtk(self):
173 def _hijack_gtk(self):
127 """Hijack the gtk mainloop so a user calling it won't cause badness."""
174 """Hijack the gtk mainloop so a user calling it won't cause badness.
175
176 We are not currently using this as it breaks GUI code that calls a
177 mainloop at anytime but startup.
178 """
128 import gtk
179 import gtk
129 orig_mainloop = gtk.main
180 orig_mainloop = gtk.main
130 gtk.mainloop = _DummyMainloop
181 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_GTK)
131 gtk.main = _DummyMainloop
182 gtk.mainloop = dumb_ml
183 gtk.main = dumb_ml
132 return orig_mainloop
184 return orig_mainloop
133
185
134 def _hijack_tk(self):
186 def _hijack_tk(self):
135 """Hijack the tk mainloop so a user calling it won't cause badness."""
187 """Hijack the tk mainloop so a user calling it won't cause badness.
188
189 We are not currently using this as it breaks GUI code that calls a
190 mainloop at anytime but startup.
191 """
136 import Tkinter
192 import Tkinter
137 Tkinter.Misc.mainloop = _DummyMainloop
193 orig_mainloop = gtk.main
138 Tkinter.mainloop = _DummyMainloop
194 dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_TK)
195 Tkinter.Misc.mainloop = dumb_ml
196 Tkinter.mainloop = dumb_ml
197
198 def _spin_qt4(self):
199 """Process all pending events in the qt4 event loop.
200
201 This is for internal IPython use only and user code should not call this.
202 Instead, they should issue the raw GUI calls themselves.
203 """
204 from PyQt4 import QtCore, QtGui
205
206 app = QtCore.QCoreApplication.instance()
207 if app is not None:
208 QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)
209
210 def _spin_wx(self):
211 """Process all pending events in the wx event loop.
212
213 This is for internal IPython use only and user code should not call this.
214 Instead, they should issue the raw GUI calls themselves.
215 """
216 import wx
217 app = wx.GetApp()
218 if app is not None and wx.Thread_IsMain():
219 evtloop = wx.EventLoop()
220 ea = wx.EventLoopActivator(evtloop)
221 while evtloop.Pending():
222 evtloop.Dispatch()
223 app.ProcessIdle()
224 del ea
225
226 def _spin_gtk(self):
227 """Process all pending events in the gtk event loop.
228
229 This is for internal IPython use only and user code should not call this.
230 Instead, they should issue the raw GUI calls themselves.
231 """
232 pass
233
234 def _spin_tk(self):
235 """Process all pending events in the tk event loop.
236
237 This is for internal IPython use only and user code should not call this.
238 Instead, they should issue the raw GUI calls themselves.
239 """
240 app = self._apps.get(GUI_TK)
241 if app is not None:
242 app.update()
243
244 def spin(self):
245 """Process pending events in the current gui.
246
247 This method is just provided for IPython to use internally if needed
248 for things like testing. Third party projects should not call this
249 method, but instead should call the underlying GUI toolkit methods
250 that we are calling.
251 """
252 spinner = self._spinner_dict.get(self._current_gui, lambda: None)
253 spinner()
139
254
140 def get_pyos_inputhook(self):
255 def get_pyos_inputhook(self):
141 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
256 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
@@ -211,14 +326,13 b' class InputHookManager(object):'
211 """
326 """
212 from IPython.lib.inputhookwx import inputhook_wx
327 from IPython.lib.inputhookwx import inputhook_wx
213 self.set_inputhook(inputhook_wx)
328 self.set_inputhook(inputhook_wx)
214 self._current_gui = 'wx'
329 self._current_gui = GUI_WX
215 self._hijack_wx()
216 if app:
330 if app:
217 import wx
331 import wx
218 app = wx.GetApp()
332 app = wx.GetApp()
219 if app is None:
333 if app is None:
220 app = wx.App(redirect=False, clearSigInt=False)
334 app = wx.App(redirect=False, clearSigInt=False)
221 self._apps['wx'] = app
335 self._apps[GUI_WX] = app
222 return app
336 return app
223
337
224 def disable_wx(self):
338 def disable_wx(self):
@@ -259,14 +373,13 b' class InputHookManager(object):'
259 QtCore.pyqtRestoreInputHook()
373 QtCore.pyqtRestoreInputHook()
260 except AttributeError:
374 except AttributeError:
261 pass
375 pass
262 self._current_gui = 'qt4'
376 self._current_gui = GUI_QT4
263 #self._hijack_qt4()
264 if app:
377 if app:
265 from PyQt4 import QtGui
378 from PyQt4 import QtGui
266 app = QtCore.QCoreApplication.instance()
379 app = QtCore.QCoreApplication.instance()
267 if app is None:
380 if app is None:
268 app = QtGui.QApplication(sys.argv)
381 app = QtGui.QApplication(sys.argv)
269 self._apps['qt4'] = app
382 self._apps[GUI_QT4] = app
270 return app
383 return app
271
384
272 def disable_qt4(self):
385 def disable_qt4(self):
@@ -294,13 +407,12 b' class InputHookManager(object):'
294 import gtk
407 import gtk
295 try:
408 try:
296 gtk.set_interactive(True)
409 gtk.set_interactive(True)
297 self._current_gui = 'gtk'
410 self._current_gui = GUI_GTK
298 except AttributeError:
411 except AttributeError:
299 # For older versions of gtk, use our own ctypes version
412 # For older versions of gtk, use our own ctypes version
300 from IPython.lib.inputhookgtk import inputhook_gtk
413 from IPython.lib.inputhookgtk import inputhook_gtk
301 self.set_inputhook(inputhook_gtk)
414 self.set_inputhook(inputhook_gtk)
302 self._current_gui = 'gtk'
415 self._current_gui = GUI_GTK
303 self._hijack_gtk()
304
416
305 def disable_gtk(self):
417 def disable_gtk(self):
306 """Disable event loop integration with PyGTK.
418 """Disable event loop integration with PyGTK.
@@ -322,8 +434,13 b' class InputHookManager(object):'
322 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
434 Currently this is a no-op as creating a :class:`Tkinter.Tk` object
323 sets ``PyOS_InputHook``.
435 sets ``PyOS_InputHook``.
324 """
436 """
325 self._current_gui = 'tk'
437 self._current_gui = GUI_TK
326 self._hijack_tk()
438 if app:
439 import Tkinter
440 app = Tkinter.Tk()
441 app.withdraw()
442 self._apps[GUI_TK] = app
443 return app
327
444
328 def disable_tk(self):
445 def disable_tk(self):
329 """Disable event loop integration with Tkinter.
446 """Disable event loop integration with Tkinter.
@@ -350,3 +467,4 b' clear_inputhook = inputhook_manager.clear_inputhook'
350 set_inputhook = inputhook_manager.set_inputhook
467 set_inputhook = inputhook_manager.set_inputhook
351 current_gui = inputhook_manager.current_gui
468 current_gui = inputhook_manager.current_gui
352 clear_app_refs = inputhook_manager.clear_app_refs
469 clear_app_refs = inputhook_manager.clear_app_refs
470 spin = inputhook_manager.spin
@@ -35,8 +35,6 b" if __name__ == '__main__':"
35 sw.show()
35 sw.show()
36
36
37 try:
37 try:
38 import IPython.lib.inputhook as i; i.appstart_qt4()
38 from IPython import appstart_qt4; appstart_qt4(app)
39 except ImportError:
39 except ImportError:
40 app.exec_()
40 app.exec_()
41
42 #import time; time.sleep(10)
@@ -1,6 +1,8 b''
1 """Test the new %gui command. Run this in ipython as
1 """Test the new %gui command. Run this in ipython as
2
2
3 %run switchgui [backend]
3 In [1]: %gui [backend]
4
5 In [2]: %run switchgui [backend]
4
6
5 where the optional backend can be one of: qt4, gtk, tk, wx.
7 where the optional backend can be one of: qt4, gtk, tk, wx.
6 """
8 """
@@ -8,23 +10,18 b' where the optional backend can be one of: qt4, gtk, tk, wx.'
8 import sys
10 import sys
9 import time
11 import time
10
12
11 import IPython.core.ipapi as ipapi
12 ip = ipapi.get()
13
14 from IPython.lib import inputhook
13 from IPython.lib import inputhook
15
14
16 try:
15 gui = inputhook.current_gui()
17 backend = sys.argv[1]
16 if gui is None:
18 #a = ip.magic('gui -a %s' % backend)
17 gui = 'qt4'
19 #a = ip.magic('gui %s' % backend)
18 inputhook.enable_qt4(app=True)
20 except IndexError:
21 backend = 'qt'
22
19
23 backends = dict(wx='wxagg', qt='qt4agg', gtk='gtkagg', tk='tkagg')
20 backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
24
21
25 import matplotlib
22 import matplotlib
26 matplotlib.use(backends[backend])
23 matplotlib.use(backends[gui])
27 #matplotlib.interactive(True)
24 matplotlib.interactive(True)
28
25
29 import matplotlib
26 import matplotlib
30 from matplotlib import pyplot as plt, mlab, pylab
27 from matplotlib import pyplot as plt, mlab, pylab
@@ -33,25 +30,19 b' import numpy as np'
33 from numpy import *
30 from numpy import *
34 from matplotlib.pyplot import *
31 from matplotlib.pyplot import *
35
32
36 x = np.linspace(0,pi,100)
33 x = np.linspace(0,pi,500)
37
34
38 print "A plot has been created"
35 print "A plot has been created"
39 line, = plot(x,sin(2*x))
36 line, = plot(x,sin(2*x))
40 plt.show()
37 inputhook.spin()
41 inputhook.spin_qt4()
42
38
43 #raw_input("Press Enter to continue")
44
39
45 print "I will now count until 10, please hit Ctrl-C before I'm done..."
40 print "Now, we will update the plot..."
46 print "IPython should stop counting and return to the prompt without crashing."
47 print
41 print
48 line_x = line.get_data()[0]
49 for i in range(1,51):
42 for i in range(1,51):
50 print i,
43 print i,
51 sys.stdout.flush()
44 sys.stdout.flush()
52 line.set_data(line_x,sin(x*i))
45 line.set_data(x,sin(x*i))
53 plt.title('i=%d' % i)
46 plt.title('i=%d' % i)
54 #plt.show()
55 plt.draw()
47 plt.draw()
56 inputhook.spin_qt4()
48 inputhook.spin()
57 #time.sleep(0.04)
General Comments 0
You need to be logged in to leave comments. Login now