Hey crew,
We’re trying to devise safe ways for users to interact with wxPython from the command line – either from a standard Python shell, or from a wxPython shell such as PyCrust. I realize that this is not how wxPython was designed to be used, but it has significant benfits to a number of applications. As you are aware, there are some tricky threading issues to deal with having wxPython co-exist with a standard shell. In PyCrust, the issues are less difficult, but it is still very easy to cause a segmentation fault in the interpreter. Things like trying to show an already closed window, etc. will do this because wxPython calls are made on a window pointer that has already been destroyed. As an example, the SciPy plt module will plot a list of values handed to it:
from scipy import plt
a = plt.plot([1,2,3]) # create a plot
a.Close() # close the plot (or closing with the mouse is just as good)
a.Show(1)
A while back, I posted a package called gui_thread to at least partially solves these problems (both in PyCrust and in a standard shell) by wrapping wxPython classes with proxy classes. The package has had significant work done on it by Prabhu Ramachandran since then to improve its safety. It works reasonably well for many situations. However, we are both frustrated with some of the limitations that result from the proxy approach. And, it appears that gui_thread will never be completely transparent to the user nor capable of handling all situations because of this proxy approach.
So, I’ve been playing with a different approach that holds more promise – namely generating special versions of all wxPython shadow classes that are safer to use in PyCrust (and later with the threading support). After all, the shadow classes are already proxies to the underlying “this” pointer. It is never quite as easy as it seems though…
The current (broken) approach invalidates the “this” pointer whenever the window is destroyed. Every other shadow class method checks the “this” pointer before calling the underlying wxPython method. So, the resulting code is something like:
def Show(self, *_args, **_kwargs):
if not self.pointer_is_valid(): invalid_pointer_error()
val = apply(windowsc.wxWindow_Show,(self,) + _args, _kwargs)
return val
...
def Destroy(self, *_args, **_kwargs):
if not self.pointer_is_valid(): invalid_pointer_error()
val = apply(windowsc.wxWindow_Destroy,(self,) + _args, _kwargs)
self.invalidate_pointer()
return val
Currently, invalidate_pointer() just sets self.this = None and pointer_is_valid() checks for a non-None value.
Adding this kind of code, throughout the wxPython code base does not seem to break the wxPython demo. However, it also hasn’t proven to make PyCrust safe in many cases. For example, when the user closes a window with mouse clicks, the pointer Python Destroy() method is called and the pointer isn’t invalidated(). I’ve tried registering some Close handlers in the shadow class init functions that invalidate pointers. This caused problems with the SplashScreen when running the demo, so it obviously not a general solution.
Step two has been to look for a way to ask the wxWindows framework is a “this” pointer is indeed a valid windows pointer. I’ve been looking through the wxWindows codebase (msw/windows.cpp primarily), and it looks there is a “wxList *wxWinHandleList” object that keeps track of this information, but I didn’t see a public (or local for that matter) interface to check whether a window pointer.
So now I’m wondering:
(1) Is there a way to ask wxWindows whether a window pointer is still valid or not? If not, is there any chance of getting it added?
(2) Has anyone else thought through these issues already and come up with a better solution?
It’s pretty important for us to have a GUI library that is seg-fault proof from the command line. Its also important for it to run from the command line without interfering with the interpreter. I know TkInter already has these features, but wxPython is better in so many other ways that we’d rather find a solution for it. I’m hoping that this is technically feasible.
thanks,
eric