Running MainLoop in its own thread

I've been trying to run the wxWindows event loop in a separate thread. What I want to be able to do is start an interactive python session, open up a window from it without loosing the prompt and be able to send drawing commands to the window. The following will allow me to open a window using a new thread so I don't lose my prompt:

from threading import *
class MyThread(Thread):
    def __init__(self):
        Thread.__init__(self)
    def run(self):
        app = MyApp(0)
        app.cube_canvas.newCube()
        app.MainLoop()
   t = MyThread()
t.start()

That's great, but I don't have any way to call methods on my app so I can ask it to do things. MyApp actually has a wxGLcanvas in it (called cube_canvas) and I want to be able to give it methods so I can add 3D objects to the canvas (see the newCube() method call above). I can't do that because I have to reference to the app. (not to mention thread synchronization issues). So the obvious thing to do was this (see code below): make the app global and just call its MainLoop() method in the thread. This causes a segmentation fault on my Linux system.

···

#-----------------------------------------------------------
app = MyApp(0)

from threading import *
class MyThread(Thread):

    def __init__(self):
        Thread.__init__(self)
        self.app = 0

    def run(self):
        global app
        app.cube_canvas.newCube()
        app.MainLoop()

   t = MyThread()
t.start()

Then I moved the line contructing a MyApp back into the thread's run method and tried running it interactively. To my surprise, the 'app' variable is accessible from the prompt, however, if I try to do the same thing in a script, it tells me 'app' is not

    def run(self):
        global app
        app = MyApp(0)
        app.cube_canvas.newCube()
        app.MainLoop()

At a shell prompt:
$ python -i cube3.py
>>> app
<__main__.MyApp instance; proxy of C++ wxPyApp instance at _82fa058_wxPyApp_p>
>>> app.cube_canvas.rotx = 0
>>> app.cube_canvas.AddPendingEvent(wxPaintEvent())
>>>

Sure enough this works. rotx is the rotation of a cube around the X axis. It rotates and repaints as expected. Cool!

However, trying to put the same code in a script fails:

#!/usr/bin/python
from threading import *
.
. (definition of MyApp deleted)
.

class MyThread(Thread):
    def __init__(self):
        Thread.__init__(self)
    def run(self):
        global app
        app = MyApp(0)
        app.cube_canvas.newCube()
        app.MainLoop()

t = MyThread()
t.start()

cube = app.cube_canvas
cube.rotx = 10
cube.AddPendingEvent(wxPaintEvent())

$ cube3.py
Traceback (most recent call last):
  File "<stdin>", line 177, in ?
NameError: global name 'app' is not defined

I tried various other things like giving the Thread a method that returns a reference to MyApp, but it doesn't work except in an interactive session. It seems like it shouldn't work at all to me, but that the first code example I gave above should work. Doesn't anybody understand the thread model better and can explain the behavior documented above?

Thanks, oh wise ones,
Barry Tolnas
tolnasb@evergreen.edu

Barry Tolnas wrote:

I've been trying to run the wxWindows event loop in a separate thread. What I want to be able to do is start an interactive python session, open up a window from it without loosing the prompt and be able to send drawing commands to the window.

Why not use a PyCrust window? There would be no need to run an alternate thread at all in that case. The app is already created and the MainLoop is already running when you get to the Python prompt, and as long as you don't do anything that totally blocks the gui you can do whatever you want with GUI objects and still keep your Python prompt. For example: http://wxpython.org/ss/pyshell.png

The following will allow me to open a window using a new thread so I don't lose my prompt:

from threading import *
class MyThread(Thread):
   def __init__(self):
       Thread.__init__(self)
   def run(self):
       app = MyApp(0)
       app.cube_canvas.newCube()
       app.MainLoop()
  t = MyThread()
t.start()

First a little background info on wxPython and threads in case you still want to try doing it this way: wxWindows expects that GUI operations and event dispatching can only take place in the main thread, (because of no thread safty in most GUI libs) and it takes some precautions to ensure that things work this way, and problems will probably happen eventually if the MainLoop thread is different than wxWindows "main thread." Whatever thread is the current one when wxWindows is initialized is what it will consider the "main thread." For wxPython 2.4 that happens when wxPython.wx is inported the first time. For 2.5 it will be When the wx.App object is created.

So for things like the above you need to ensure that wxPython is not imported at all until you are within the thread's run(), but since you need to derive classes and such that can be very tricky. One way to do it is to move all the wx stuff to another module, and then import that module in the run(). The other module imports wxPython and creates your classes, makes the app and runs MainLoop. You can then interact with it by accessing attributes of the module object.

However, trying to put the same code in a script fails:

#!/usr/bin/python
from threading import *
.
. (definition of MyApp deleted)
.

class MyThread(Thread):
   def __init__(self):
       Thread.__init__(self)
   def run(self):
       global app
       app = MyApp(0)
       app.cube_canvas.newCube()
       app.MainLoop()

t = MyThread()
t.start()

cube = app.cube_canvas
cube.rotx = 10
cube.AddPendingEvent(wxPaintEvent())

$ cube3.py
Traceback (most recent call last):
File "<stdin>", line 177, in ?
NameError: global name 'app' is not defined

This is probably because by the time you get to "cube = app.cube_canvas" the "app = MyApp(0)" in the thread hasn't executed yet.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Don't know if it will solve you problem, but did you try the module
gui_thread from scipy:

   http://www.scipy.org/site_content/tutorials/gui_thread

···

On Friday 24 October 2003 00:39, Barry Tolnas wrote:

I've been trying to run the wxWindows event loop in a separate thread.

--
   Frederic

   http://linux.gbiloba.org