Hi,
I am one of the core developers of IPython. One of IPython’s most popular features is that GUI toolkits (like wxPython) can be used interactively from it. This is used by packages like matplotlib and Mayavi to provide (extremely popular) interactive plotting. However, our current implementation uses threads in a manner that is very subtle, easy to break and difficult to maintain. We are currently exploring alternatives that would be more robust.
The core issue that needs to be solved is that the GUI event loop needs to continue to run when IPython’s input prompt is waiting for user input.
A number of the other GUI toolkits that IPython supports (pyqt, pygtk, tk, cocoa/pyobjc) have recently added features that make their interactive use much more straightforward. All of these toolkits are now using Python’s somewhat poorly documented PyOS_InputHook hook. This makes it possible to use these toolkits interactive from both IPython and the regular Python prompt with no threading.
I am writing for two reasons:
-
I have a Cython based prototype that implements the PyOS_InputHook for wxPython. I don’t know much about wxPython and I need some help getting it to work in a better manner.
-
This code should really be in wxPython in the first place. If we can get the code figured out and in wxPython, all wxPython users could use/dev wxPython apps interactively.
Here is a link to a post on the ipython-dev list describing more of the details of this:
http://mail.scipy.org/pipermail/ipython-dev/2009-February/004856.html
Most relevant, is Michiel de Hoon’s post about the implementation of this in the other GUI toolkits:
I wrote the code in PyGTK that uses PyOS_InputHook for interactivity, as well as the Mac OS X native backend for matplotlib that uses PyOS_InputHook in exactly the same way. PyQT and Tkinter also use PyOS_InputHook, though the code is a bit kludgy on Windows. So I definitely agree that PyOS_InputHook is the right way to go.
Your current code should work, but there’s a better way to do it. If I understand the code correctly, you rely on the fact that PyOS_InputHook is called repeatedly by readline, and you use PyOS_InputHook to process wx events that need to be processed at that time. A better way is to use PyOS_InputHook to start the wx event loop, but have this event loop check stdin. As soon as some input is available on stdin, you exit the event loop, which means that PyOS_InputHook returns, and Python can proceed to handle the command that was just entered by the user on stdin.
Essentially, think of wx’s event loop as sitting in a call to select(), waiting for the next wx event to arrive. You want to add fileno(stdin) to the set of file descriptors watched by select().
There are two advantages to this approach. First, it does not rely on readline calling PyOS_InputHook repeatedly. This is important, since Python may not be using readline at all, and if it is, depending on the Python version and how readline was installed it may call PyOS_InputHook only once. Second, this approach is more efficient (not wasting processor cycles going back and forth between readline and PyOS_InputHook), and gives a better response time (essentially immediate).
The best place to put this code is in wxPython. Hopefully (I haven’t checked this), wx exposes enough of the event loop to allow you to have it watch stdin. This may be an issue, since for example qt4 does not on Windows, which is why the event loop is kludgy with PyQT on Windows. You could have a look at the PyOS_InputHook code in PyGTK (you’ll need to get the developer’s version of PyGTK, since this code is not yet in an official release). It’s actually quite straightforward and you may be able to modify it directly for wx.
My current cython based implementation (attached to this post) doesn’t do what Michiel recommends, but it is a step in the right direction.
Is there interest amongst the wxPython devs in exploring this issue with me?
Cheers,
Brian
inputhook.h (246 Bytes)
inputhook.pyx (2.33 KB)