##// 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
@@ -1,76 +1,86 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 IPython -- An enhanced Interactive Python
3 IPython -- An enhanced Interactive Python
4
4
5 One of Python's nicest features is its interactive interpreter. This allows
5 One of Python's nicest features is its interactive interpreter. This allows
6 very fast testing of ideas without the overhead of creating test files as is
6 very fast testing of ideas without the overhead of creating test files as is
7 typical in most programming languages. However, the interpreter supplied with
7 typical in most programming languages. However, the interpreter supplied with
8 the standard Python distribution is fairly primitive (and IDLE isn't really
8 the standard Python distribution is fairly primitive (and IDLE isn't really
9 much better).
9 much better).
10
10
11 IPython tries to:
11 IPython tries to:
12
12
13 i - provide an efficient environment for interactive work in Python
13 i - provide an efficient environment for interactive work in Python
14 programming. It tries to address what we see as shortcomings of the standard
14 programming. It tries to address what we see as shortcomings of the standard
15 Python prompt, and adds many features to make interactive work much more
15 Python prompt, and adds many features to make interactive work much more
16 efficient.
16 efficient.
17
17
18 ii - offer a flexible framework so that it can be used as the base
18 ii - offer a flexible framework so that it can be used as the base
19 environment for other projects and problems where Python can be the
19 environment for other projects and problems where Python can be the
20 underlying language. Specifically scientific environments like Mathematica,
20 underlying language. Specifically scientific environments like Mathematica,
21 IDL and Mathcad inspired its design, but similar ideas can be useful in many
21 IDL and Mathcad inspired its design, but similar ideas can be useful in many
22 fields. Python is a fabulous language for implementing this kind of system
22 fields. Python is a fabulous language for implementing this kind of system
23 (due to its dynamic and introspective features), and with suitable libraries
23 (due to its dynamic and introspective features), and with suitable libraries
24 entire systems could be built leveraging Python's power.
24 entire systems could be built leveraging Python's power.
25
25
26 iii - serve as an embeddable, ready to go interpreter for your own programs.
26 iii - serve as an embeddable, ready to go interpreter for your own programs.
27
27
28 IPython requires Python 2.4 or newer.
28 IPython requires Python 2.4 or newer.
29 """
29 """
30
30
31 #*****************************************************************************
31 #*****************************************************************************
32 # Copyright (C) 2008-2009 The IPython Development Team
32 # Copyright (C) 2008-2009 The IPython Development Team
33 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
33 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
34 #
34 #
35 # Distributed under the terms of the BSD License. The full license is in
35 # Distributed under the terms of the BSD License. The full license is in
36 # the file COPYING, distributed as part of this software.
36 # the file COPYING, distributed as part of this software.
37 #*****************************************************************************
37 #*****************************************************************************
38
38
39 # Enforce proper version requirements
39 # Enforce proper version requirements
40 import sys
40 import sys
41
41
42 if sys.version[0:3] < '2.4':
42 if sys.version[0:3] < '2.4':
43 raise ImportError('Python Version 2.4 or above is required for IPython.')
43 raise ImportError('Python Version 2.4 or above is required for IPython.')
44
44
45 # Make it easy to import extensions - they are always directly on pythonpath.
45 # Make it easy to import extensions - they are always directly on pythonpath.
46 # Therefore, non-IPython modules can be added to extensions directory
46 # Therefore, non-IPython modules can be added to extensions directory
47 import os
47 import os
48 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
48 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
49
49
50 # Define what gets imported with a 'from IPython import *'
50 # Define what gets imported with a 'from IPython import *'
51 __all__ = ['IPython.core.ipapi','utils.generics','utils.ipstruct',
51 __all__ = ['IPython.core.ipapi','utils.generics','utils.ipstruct',
52 'core.release','core.shell']
52 'core.release','core.shell']
53
53
54 # Load __all__ in IPython namespace so that a simple 'import IPython' gives
54 # Load __all__ in IPython namespace so that a simple 'import IPython' gives
55 # access to them via IPython.<name>
55 # access to them via IPython.<name>
56 glob,loc = globals(),locals()
56 glob,loc = globals(),locals()
57 for name in __all__:
57 for name in __all__:
58 #print 'Importing: ',name # dbg
58 #print 'Importing: ',name # dbg
59 __import__(name,glob,loc,[])
59 __import__(name,glob,loc,[])
60
60
61 from IPython.core import shell
61 from IPython.core import shell
62 Shell = shell
62 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>' % \
69 ( release.authors['Fernando'] + release.authors['Janko'] + \
79 ( release.authors['Fernando'] + release.authors['Janko'] + \
70 release.authors['Nathan'] )
80 release.authors['Nathan'] )
71 __license__ = release.license
81 __license__ = release.license
72 __version__ = release.version
82 __version__ = release.version
73 __revision__ = release.revision
83 __revision__ = release.revision
74
84
75 # Namespace cleanup
85 # Namespace cleanup
76 del name,glob,loc
86 del name,glob,loc
@@ -1,28 +1,30 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Extra capabilities for IPython
4 Extra capabilities for IPython
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from IPython.lib.inputhook import (
18 from IPython.lib.inputhook import (
19 enable_wx, disable_wx,
19 enable_wx, disable_wx,
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 #-----------------------------------------------------------------------------
27 # Code
29 # Code
28 #----------------------------------------------------------------------------- No newline at end of file
30 #-----------------------------------------------------------------------------
@@ -1,352 +1,470 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Inputhook management for GUI event loop integration.
4 Inputhook management for GUI event loop integration.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import ctypes
18 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):
89 """Manage PyOS_InputHook for different GUI toolkits.
123 """Manage PyOS_InputHook for different GUI toolkits.
90
124
91 This class installs various hooks under ``PyOSInputHook`` to handle
125 This class installs various hooks under ``PyOSInputHook`` to handle
92 GUI event loop integration.
126 GUI event loop integration.
93 """
127 """
94
128
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):
101 self._callback_pyfunctype = None
140 self._callback_pyfunctype = None
102 self._callback = None
141 self._callback = None
103 self._installed = False
142 self._installed = False
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')
111 else: raise AttributeError('Could not find wx core module')
154 else: raise AttributeError('Could not find wx core module')
112 orig_mainloop = core.PyApp_MainLoop
155 orig_mainloop = core.PyApp_MainLoop
113 core.PyApp_MainLoop = _DummyMainloop
156 core.PyApp_MainLoop = _DummyMainloop
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."""
142 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
257 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
143
258
144 def get_pyos_inputhook_as_func(self):
259 def get_pyos_inputhook_as_func(self):
145 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
260 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
146 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
261 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
147
262
148 def set_inputhook(self, callback):
263 def set_inputhook(self, callback):
149 """Set PyOS_InputHook to callback and return the previous one."""
264 """Set PyOS_InputHook to callback and return the previous one."""
150 self._callback = callback
265 self._callback = callback
151 self._callback_pyfunctype = self.PYFUNC(callback)
266 self._callback_pyfunctype = self.PYFUNC(callback)
152 pyos_inputhook_ptr = self.get_pyos_inputhook()
267 pyos_inputhook_ptr = self.get_pyos_inputhook()
153 original = self.get_pyos_inputhook_as_func()
268 original = self.get_pyos_inputhook_as_func()
154 pyos_inputhook_ptr.value = \
269 pyos_inputhook_ptr.value = \
155 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
270 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
156 self._installed = True
271 self._installed = True
157 return original
272 return original
158
273
159 def clear_inputhook(self):
274 def clear_inputhook(self):
160 """Set PyOS_InputHook to NULL and return the previous one."""
275 """Set PyOS_InputHook to NULL and return the previous one."""
161 pyos_inputhook_ptr = self.get_pyos_inputhook()
276 pyos_inputhook_ptr = self.get_pyos_inputhook()
162 original = self.get_pyos_inputhook_as_func()
277 original = self.get_pyos_inputhook_as_func()
163 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
278 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
164 self._reset()
279 self._reset()
165 return original
280 return original
166
281
167 def clear_app_refs(self, gui=None):
282 def clear_app_refs(self, gui=None):
168 """Clear IPython's internal reference to an application instance.
283 """Clear IPython's internal reference to an application instance.
169
284
170 Whenever we create an app for a user on qt4 or wx, we hold a
285 Whenever we create an app for a user on qt4 or wx, we hold a
171 reference to the app. This is needed because in some cases bad things
286 reference to the app. This is needed because in some cases bad things
172 can happen if a user doesn't hold a reference themselves. This
287 can happen if a user doesn't hold a reference themselves. This
173 method is provided to clear the references we are holding.
288 method is provided to clear the references we are holding.
174
289
175 Parameters
290 Parameters
176 ----------
291 ----------
177 gui : None or str
292 gui : None or str
178 If None, clear all app references. If ('wx', 'qt4') clear
293 If None, clear all app references. If ('wx', 'qt4') clear
179 the app for that toolkit. References are not held for gtk or tk
294 the app for that toolkit. References are not held for gtk or tk
180 as those toolkits don't have the notion of an app.
295 as those toolkits don't have the notion of an app.
181 """
296 """
182 if gui is None:
297 if gui is None:
183 self._apps = {}
298 self._apps = {}
184 elif self._apps.has_key(gui):
299 elif self._apps.has_key(gui):
185 del self._apps[gui]
300 del self._apps[gui]
186
301
187 def enable_wx(self, app=False):
302 def enable_wx(self, app=False):
188 """Enable event loop integration with wxPython.
303 """Enable event loop integration with wxPython.
189
304
190 Parameters
305 Parameters
191 ----------
306 ----------
192 app : bool
307 app : bool
193 Create a running application object or not.
308 Create a running application object or not.
194
309
195 Notes
310 Notes
196 -----
311 -----
197 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
312 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
198 the wxPython to integrate with terminal based applications like
313 the wxPython to integrate with terminal based applications like
199 IPython.
314 IPython.
200
315
201 If ``app`` is True, we create an :class:`wx.App` as follows::
316 If ``app`` is True, we create an :class:`wx.App` as follows::
202
317
203 import wx
318 import wx
204 app = wx.App(redirect=False, clearSigInt=False)
319 app = wx.App(redirect=False, clearSigInt=False)
205
320
206 Both options this constructor are important for things to work
321 Both options this constructor are important for things to work
207 properly in an interactive context.
322 properly in an interactive context.
208
323
209 But, we first check to see if an application has already been
324 But, we first check to see if an application has already been
210 created. If so, we simply return that instance.
325 created. If so, we simply return that instance.
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):
225 """Disable event loop integration with wxPython.
339 """Disable event loop integration with wxPython.
226
340
227 This merely sets PyOS_InputHook to NULL.
341 This merely sets PyOS_InputHook to NULL.
228 """
342 """
229 self.clear_inputhook()
343 self.clear_inputhook()
230
344
231 def enable_qt4(self, app=False):
345 def enable_qt4(self, app=False):
232 """Enable event loop integration with PyQt4.
346 """Enable event loop integration with PyQt4.
233
347
234 Parameters
348 Parameters
235 ----------
349 ----------
236 app : bool
350 app : bool
237 Create a running application object or not.
351 Create a running application object or not.
238
352
239 Notes
353 Notes
240 -----
354 -----
241 This methods sets the PyOS_InputHook for PyQt4, which allows
355 This methods sets the PyOS_InputHook for PyQt4, which allows
242 the PyQt4 to integrate with terminal based applications like
356 the PyQt4 to integrate with terminal based applications like
243 IPython.
357 IPython.
244
358
245 If ``app`` is True, we create an :class:`QApplication` as follows::
359 If ``app`` is True, we create an :class:`QApplication` as follows::
246
360
247 from PyQt4 import QtCore
361 from PyQt4 import QtCore
248 app = QtGui.QApplication(sys.argv)
362 app = QtGui.QApplication(sys.argv)
249
363
250 But, we first check to see if an application has already been
364 But, we first check to see if an application has already been
251 created. If so, we simply return that instance.
365 created. If so, we simply return that instance.
252 """
366 """
253 from PyQt4 import QtCore
367 from PyQt4 import QtCore
254 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
368 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
255 # was set when QtCore was imported, but if it ever got removed,
369 # was set when QtCore was imported, but if it ever got removed,
256 # you couldn't reset it. For earlier versions we can
370 # you couldn't reset it. For earlier versions we can
257 # probably implement a ctypes version.
371 # probably implement a ctypes version.
258 try:
372 try:
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):
273 """Disable event loop integration with PyQt4.
386 """Disable event loop integration with PyQt4.
274
387
275 This merely sets PyOS_InputHook to NULL.
388 This merely sets PyOS_InputHook to NULL.
276 """
389 """
277 self.clear_inputhook()
390 self.clear_inputhook()
278
391
279 def enable_gtk(self, app=False):
392 def enable_gtk(self, app=False):
280 """Enable event loop integration with PyGTK.
393 """Enable event loop integration with PyGTK.
281
394
282 Parameters
395 Parameters
283 ----------
396 ----------
284 app : bool
397 app : bool
285 Create a running application object or not. Because gtk does't
398 Create a running application object or not. Because gtk does't
286 have an app class, this does nothing.
399 have an app class, this does nothing.
287
400
288 Notes
401 Notes
289 -----
402 -----
290 This methods sets the PyOS_InputHook for PyGTK, which allows
403 This methods sets the PyOS_InputHook for PyGTK, which allows
291 the PyGTK to integrate with terminal based applications like
404 the PyGTK to integrate with terminal based applications like
292 IPython.
405 IPython.
293 """
406 """
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.
307
419
308 This merely sets PyOS_InputHook to NULL.
420 This merely sets PyOS_InputHook to NULL.
309 """
421 """
310 self.clear_inputhook()
422 self.clear_inputhook()
311
423
312 def enable_tk(self, app=False):
424 def enable_tk(self, app=False):
313 """Enable event loop integration with Tk.
425 """Enable event loop integration with Tk.
314
426
315 Parameters
427 Parameters
316 ----------
428 ----------
317 app : bool
429 app : bool
318 Create a running application object or not.
430 Create a running application object or not.
319
431
320 Notes
432 Notes
321 -----
433 -----
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.
330
447
331 This merely sets PyOS_InputHook to NULL.
448 This merely sets PyOS_InputHook to NULL.
332 """
449 """
333 self.clear_inputhook()
450 self.clear_inputhook()
334
451
335 def current_gui(self):
452 def current_gui(self):
336 """Return a string indicating the currently active GUI or None."""
453 """Return a string indicating the currently active GUI or None."""
337 return self._current_gui
454 return self._current_gui
338
455
339 inputhook_manager = InputHookManager()
456 inputhook_manager = InputHookManager()
340
457
341 enable_wx = inputhook_manager.enable_wx
458 enable_wx = inputhook_manager.enable_wx
342 disable_wx = inputhook_manager.disable_wx
459 disable_wx = inputhook_manager.disable_wx
343 enable_qt4 = inputhook_manager.enable_qt4
460 enable_qt4 = inputhook_manager.enable_qt4
344 disable_qt4 = inputhook_manager.disable_qt4
461 disable_qt4 = inputhook_manager.disable_qt4
345 enable_gtk = inputhook_manager.enable_gtk
462 enable_gtk = inputhook_manager.enable_gtk
346 disable_gtk = inputhook_manager.disable_gtk
463 disable_gtk = inputhook_manager.disable_gtk
347 enable_tk = inputhook_manager.enable_tk
464 enable_tk = inputhook_manager.enable_tk
348 disable_tk = inputhook_manager.disable_tk
465 disable_tk = inputhook_manager.disable_tk
349 clear_inputhook = inputhook_manager.clear_inputhook
466 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
@@ -1,42 +1,40 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Simple Qt4 example to manually test event loop integration.
2 """Simple Qt4 example to manually test event loop integration.
3
3
4 This is meant to run tests manually in ipython as:
4 This is meant to run tests manually in ipython as:
5
5
6 In [5]: %gui qt
6 In [5]: %gui qt
7
7
8 In [6]: %run gui-qt.py
8 In [6]: %run gui-qt.py
9
9
10 Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/
10 Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/
11 """
11 """
12
12
13 import sys
13 import sys
14 from PyQt4 import QtGui, QtCore
14 from PyQt4 import QtGui, QtCore
15
15
16 class SimpleWindow(QtGui.QWidget):
16 class SimpleWindow(QtGui.QWidget):
17 def __init__(self, parent=None):
17 def __init__(self, parent=None):
18 QtGui.QWidget.__init__(self, parent)
18 QtGui.QWidget.__init__(self, parent)
19
19
20 self.setGeometry(300, 300, 200, 80)
20 self.setGeometry(300, 300, 200, 80)
21 self.setWindowTitle('Hello World')
21 self.setWindowTitle('Hello World')
22
22
23 quit = QtGui.QPushButton('Close', self)
23 quit = QtGui.QPushButton('Close', self)
24 quit.setGeometry(10, 10, 60, 35)
24 quit.setGeometry(10, 10, 60, 35)
25
25
26 self.connect(quit, QtCore.SIGNAL('clicked()'),
26 self.connect(quit, QtCore.SIGNAL('clicked()'),
27 self, QtCore.SLOT('close()'))
27 self, QtCore.SLOT('close()'))
28
28
29 if __name__ == '__main__':
29 if __name__ == '__main__':
30 app = QtCore.QCoreApplication.instance()
30 app = QtCore.QCoreApplication.instance()
31 if app is None:
31 if app is None:
32 app = QtGui.QApplication([])
32 app = QtGui.QApplication([])
33
33
34 sw = SimpleWindow()
34 sw = SimpleWindow()
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,57 +1,48 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 """
7
9
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
31 import numpy as np
28 import numpy as np
32
29
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