threading weirdness

Hi,

I need to start a wx.App in a separate thread which seems to work.
However, running the attached example, I get the following traceback
after having created and run the wx.App in its own thread:

Traceback (most recent call last):
  File "wxthread.py", line 42, in OnUpdate
    self.timer.Start(3000, wx.TIMER_ONE_SHOT)
  File "c:\pythonxy\python\lib\site-packages\wx-2.8-msw-unicode\wx
\_misc.py", line 1298, in Start
    return _misc_.Timer_Start(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "wxThread::IsMain()" failed
at ..\..\src\common\timercmn.cpp(66) in wxTimerBase::Start(): timer
can only be started from the main thread
end thread

Any ideas what is happening?

Thanks in advance, Christian

import wx
import wx.lib.newevent

from threading import Thread
import time

(NextEvent, EVT_NEXT) = wx.lib.newevent.NewEvent()

class Listener(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        app = wx.PySimpleApp(0)

        self.f = wx.Frame(None)
        p = wx.Panel(self.f)
        lab = wx.StaticText(p, -1, 'tag report')

        self.txt = wx.TextCtrl(p, -1, '', size=(500,150),
                          style=wx.TE_MULTILINE|wx.TE_READONLY)

        box = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(lab, 1, wx.TOP|wx.BOTTOM|wx.EXPAND, 10)
        box.Add(hbox, 0, wx.EXPAND|wx.ALL, 2)
        box.Add(self.txt, 1, wx.ALL|wx.EXPAND, 2)
        p.SetSizer(box)
        p.Fit()
        self.f.Fit()
        self.f.Show()

        self.f.Bind(wx.EVT_CLOSE, self.OnClose)
        self.f.Bind(EVT_NEXT, self.OnUpdate)
        self.f.Bind(wx.EVT_TIMER, self.OnClose)
        app.MainLoop()
        print 'end thread'

    def OnUpdate(self, evt):
        msg = evt.msg
        if msg.find('done.') == 0:
            self.timer = wx.Timer(self.f)
            self.timer.Start(3000, wx.TIMER_ONE_SHOT)
        self.txt.SetValue(self.txt.GetValue()+msg)
        self.txt.SetInsertionPointEnd()

    def OnClose(self, evt):
        self.timer.Stop()
        del self.timer
        self.f.Bind(EVT_NEXT, None)
        self.f.Bind(wx.EVT_TIMER, None)
        self.f.Bind(wx.EVT_CLOSE, None)
        self.f.Destroy()

class Progress:
    def __init__(self):
        self.t = Listener()
        self.t.start()

    def next(self, msg='piep\n'):
        if self.alive and hasattr(self.t, 'f'):
            if msg[-1] != '\n':
                msg += '\n'
            evt = NextEvent(msg=msg)
            wx.PostEvent(self.t.f, evt)

    def isalive(self):
        return self.t.isAlive()
    alive = property(isalive)

    def end(self):
        del self.t

if __name__ == '__main__':
    for l in range(2):
        d = Progress()
        time.sleep(2)
        for k in range(10):
            d.next('number %d'%k)
            time.sleep(0.5)
        d.next('done.')
        while d.alive:
            time.sleep(0.1)
        d.end()

Christian,

indeed you have to run your other stuff in newly created threads and wx
in the main one. There is no way that I know around that.

Note that you need to communicate from the other threads with your wx
elements via wx events and return queues. Otherwise you will get weird
crashes as multithreading and memory protection create race conditions
and other errors!

Paul

ckkart wrote:

perzonae logo-tiny.JPG

···

wxPython-users@googlegroups.comwxPython-users+unsubscribe@googlegroups.comhttp://groups.google.com/group/wxPython-users?hl=en

perzonae sig



Paul
Sijben
tel:
+31 33 7114626

Perzonae
Unified Communications BV

mobile:
+31 646272086

Amersfoort,
the Netherlands

www.perzonae.com

Hi,

Hi,

I need to start a wx.App in a separate thread which seems to work.
However, running the attached example, I get the following traceback
after having created and run the wx.App in its own thread:

Traceback (most recent call last):
File "wxthread.py", line 42, in OnUpdate
self.timer.Start(3000, wx.TIMER_ONE_SHOT)
File "c:\pythonxy\python\lib\site-packages\wx-2.8-msw-unicode\wx
\_misc.py", line 1298, in Start
return _misc_.Timer_Start(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "wxThread::IsMain()" failed
at ..\..\src\common\timercmn.cpp(66) in wxTimerBase::Start(): timer
can only be started from the main thread
end thread

Any ideas what is happening?

Thanks in advance, Christian

import wx
import wx.lib.newevent

from threading import Thread
import time

(NextEvent, EVT_NEXT) = wx.lib.newevent.NewEvent()

class Listener(Thread):
def __init__(self):
Thread.__init__(self)

def run\(self\):
    app = wx\.PySimpleApp\(0\)

    self\.f = wx\.Frame\(None\)
    p  = wx\.Panel\(self\.f\)
    lab = wx\.StaticText\(p, \-1, 'tag report'\)

    self\.txt = wx\.TextCtrl\(p, \-1, '', size=\(500,150\),
                      style=wx\.TE\_MULTILINE|wx\.TE\_READONLY\)

    box = wx\.BoxSizer\(wx\.VERTICAL\)
    hbox = wx\.BoxSizer\(wx\.HORIZONTAL\)
    hbox\.Add\(lab, 1, wx\.TOP|wx\.BOTTOM|wx\.EXPAND, 10\)
    box\.Add\(hbox, 0, wx\.EXPAND|wx\.ALL, 2\)
    box\.Add\(self\.txt, 1, wx\.ALL|wx\.EXPAND, 2\)
    p\.SetSizer\(box\)
    p\.Fit\(\)
    self\.f\.Fit\(\)
    self\.f\.Show\(\)

    self\.f\.Bind\(wx\.EVT\_CLOSE, self\.OnClose\)
    self\.f\.Bind\(EVT\_NEXT, self\.OnUpdate\)
    self\.f\.Bind\(wx\.EVT\_TIMER, self\.OnClose\)
    app\.MainLoop\(\)
    print 'end thread'

def OnUpdate\(self, evt\):
    msg = evt\.msg
    if msg\.find\('done\.'\) == 0:
        self\.timer = wx\.Timer\(self\.f\)
        self\.timer\.Start\(3000, wx\.TIMER\_ONE\_SHOT\)
    self\.txt\.SetValue\(self\.txt\.GetValue\(\)\+msg\)
    self\.txt\.SetInsertionPointEnd\(\)

def OnClose\(self, evt\):
    self\.timer\.Stop\(\)
    del self\.timer
    self\.f\.Bind\(EVT\_NEXT, None\)
    self\.f\.Bind\(wx\.EVT\_TIMER, None\)
    self\.f\.Bind\(wx\.EVT\_CLOSE, None\)
    self\.f\.Destroy\(\)

class Progress:
def __init__(self):
self.t = Listener()
self.t.start()

def next\(self, msg='piep\\n'\):
    if self\.alive and hasattr\(self\.t, 'f'\):
        if msg\[\-1\] \!= '\\n':
            msg \+= '\\n'
        evt = NextEvent\(msg=msg\)
        wx\.PostEvent\(self\.t\.f, evt\)

def isalive\(self\):
    return self\.t\.isAlive\(\)
alive = property\(isalive\)

def end\(self\):
    del self\.t

if __name__ == '__main__':
for l in range(2):
d = Progress()
time.sleep(2)
for k in range(10):
d.next('number %d'%k)
time.sleep(0.5)
d.next('done.')
while d.alive:
time.sleep(0.1)
d.end()

In most GUI toolkits (and maybe ALL of them), you cannot make direct
calls to GUI elements outside of the GUI's mainloop. Each toolkit has
their own methods around this issue.

In the case of wxPython, most use the CallAfter and CallLater
functions. I think I've heard some people using Python's queue module
as well. Anyway, there are examples in the wiki here:

http://wiki.wxpython.org/LongRunningTasks

And I think there's some stuff in the demo too.

Mike

···

On Jun 17, 12:38 am, ckkart <ckk...@gmail.com> wrote:

Hi Mike, Paul,

thank you for your comments, but I think you miss the point. This is
mostly my fault as I did not explain it very well.
In my setup the wxpython app will never run in the main python thread
which is ok according to the information I got from this list (Robin
et.al.). For wx the thread it is run in will be the main thread and
gui calls from outside are not allowed. I think that this rule is
respected in the code I sent last time.
Anyway I have boiled down the problem to a second script, attached
below. It will start a wx app in a second thread, which opens a frame
and waits for it to be closed, then it will do the same again.
Surprisingly, wx.Thread_IsMain() will report True in the first run and
False in the second one. This is what I don't get.

Regards, Christian

import wx
from threading import Thread
import time

class App(wx.App):
    def __init__(self):
        wx.App.__init__(self, False)

    def OnInit(self):
        print wx.Thread_IsMain()

        self.f = wx.Frame(None, size=(200,40))

        self.f.Show()
        return True

class Listener(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        print 'starting app'
        self.app = App()
        self.app.MainLoop()
        print 'app terminated'

class Progress:
    def __init__(self):
        self.listener = Listener()
        self.listener.start()

    def isalive(self):
        return self.listener.isAlive()
    alive = property(isalive)

    def end(self):
        del self.listener.app
        del self.listener

if __name__ == '__main__':
    d = Progress()
    while d.alive:
        time.sleep(0.1)
    d.end()
    d = Progress()
    while d.alive:
        time.sleep(0.1)
    d.end()

ckkart wrote:

Hi,

I need to start a wx.App in a separate thread which seems to work.
However, running the attached example, I get the following traceback
after having created and run the wx.App in its own thread:

Traceback (most recent call last):
  File "wxthread.py", line 42, in OnUpdate
    self.timer.Start(3000, wx.TIMER_ONE_SHOT)
  File "c:\pythonxy\python\lib\site-packages\wx-2.8-msw-unicode\wx
\_misc.py", line 1298, in Start
    return _misc_.Timer_Start(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "wxThread::IsMain()" failed
at ..\..\src\common\timercmn.cpp(66) in wxTimerBase::Start(): timer
can only be started from the main thread
end thread

Any ideas what is happening?

The first Listener thread is where the wx.App is first created, and that then becomes the main thread as far as wx is concerned. The second time through you create a new Listener thread, but the first (now dead) one is still the one that wx will consider to be the main thread.

If you need to run the UI in a thread other than the main thread of the process then there are a few rules that need to be followed. There are a few platform differences here, but to be safe for all platforms you should follow all of these.

* Just like when running in the main thread, there can be no direct manipulation of the UI elements from other threads. You are handling that well with the wx.PostEvent.

* There can only be one UI thread. Not only one at a time, but only one for the whole life of the process.

* There should only be one wx.App instance created in the process. This is not as bad as it used to be, but it is still valid.

···

--
Robin Dunn
Software Craftsman

Thanks for the explanation. Unfortunately I do not see a way how to
achieve what I want. I was thinking of reusing the same thread object
and simply start the main loop again, but apparently that is not
possible. In summary, is it right, that it is impossible to run a
wxPython app in its own thread several times during the runtime of the
process?

Regards, Christian

···

On 17 Jun., 22:30, Robin Dunn <ro...@alldunn.com> wrote:

ckkart wrote:

> Hi,

> I need to start a wx.App in a separate thread which seems to work.
> However, running the attached example, I get the following traceback
> after having created and run the wx.App in its own thread:

> Traceback (most recent call last):
> File "wxthread.py", line 42, in OnUpdate
> self.timer.Start(3000, wx.TIMER_ONE_SHOT)
> File "c:\pythonxy\python\lib\site-packages\wx-2.8-msw-unicode\wx
> \_misc.py", line 1298, in Start
> return _misc_.Timer_Start(*args, **kwargs)
> wx._core.PyAssertionError: C++ assertion "wxThread::IsMain()" failed
> at ..\..\src\common\timercmn.cpp(66) in wxTimerBase::Start(): timer
> can only be started from the main thread
> end thread

> Any ideas what is happening?

The first Listener thread is where the wx.App is first created, and that
then becomes the main thread as far as wx is concerned. The second time
through you create a new Listener thread, but the first (now dead) one
is still the one that wx will consider to be the main thread.

If you need to run the UI in a thread other than the main thread of the
process then there are a few rules that need to be followed. There are
a few platform differences here, but to be safe for all platforms you
should follow all of these.

* Just like when running in the main thread, there can be no direct
manipulation of the UI elements from other threads. You are handling
that well with the wx.PostEvent.

* There can only be one UI thread. Not only one at a time, but only one
for the whole life of the process.

* There should only be one wx.App instance created in the process. This
is not as bad as it used to be, but it is still valid.

You can do this as long as you don’t kill the app. One way I think will work is to catch the close event (i.e. EVT_CLOSE) and just hide the frame. You can use pubsub to tell your app when to show the frame and when you want to terminate the application.

Here’s a sample receiver:

from wx.lib.pubsub import Publisher
Publisher().subscribe(self.winCloseListener, (‘window’, ‘closing’))

and this is the method that it calls.

def winCloseListener(self, message):
“”"
Pubsub listener object. When a sub-window / frame
closes, it publishes a message to this listener
which in turn shows the frame.
“”"

print 'in __winCloseListener...'
print '%s!' % message.data[0]
if not self.frame.IsShown():
    print 'frame show..'
    self.frame.Show(True)
    self.frame.Layout()

self.frame.Raise()

So to call this from your code, you’d do something like this in your Listener object:

wx.CallAfter(showFrame)

def showFrame(self):

Publisher().sendMessage(('window', 'closing'), ['ts_worksheet closed'])

See the following pages for more info:

http://wiki.wxpython.org/CallAfter

http://wiki.wxpython.org/Controlling%20GUI%20with%20pubsub
http://wiki.wxpython.org/PubSub

···

On Fri, Jun 19, 2009 at 5:56 AM, ckkart ckkart@gmail.com wrote:

On 17 Jun., 22:30, Robin Dunn ro...@alldunn.com wrote:

ckkart wrote:

Hi,

I need to start a wx.App in a separate thread which seems to work.

However, running the attached example, I get the following traceback

after having created and run the wx.App in its own thread:

Traceback (most recent call last):

File “wxthread.py”, line 42, in OnUpdate

self.timer.Start(3000, wx.TIMER_ONE_SHOT)

File "c:\pythonxy\python\lib\site-packages\wx-2.8-msw-unicode\wx

_misc.py", line 1298, in Start

return _misc_.Timer_Start(*args, **kwargs)

wx._core.PyAssertionError: C++ assertion “wxThread::IsMain()” failed

at …..\src\common\timercmn.cpp(66) in wxTimerBase::Start(): timer

can only be started from the main thread

end thread

Any ideas what is happening?

The first Listener thread is where the wx.App is first created, and that

then becomes the main thread as far as wx is concerned. The second time

through you create a new Listener thread, but the first (now dead) one

is still the one that wx will consider to be the main thread.

If you need to run the UI in a thread other than the main thread of the

process then there are a few rules that need to be followed. There are

a few platform differences here, but to be safe for all platforms you

should follow all of these.

  • Just like when running in the main thread, there can be no direct

manipulation of the UI elements from other threads. You are handling

that well with the wx.PostEvent.

  • There can only be one UI thread. Not only one at a time, but only one

for the whole life of the process.

  • There should only be one wx.App instance created in the process. This

is not as bad as it used to be, but it is still valid.

Thanks for the explanation. Unfortunately I do not see a way how to

achieve what I want. I was thinking of reusing the same thread object

and simply start the main loop again, but apparently that is not

possible. In summary, is it right, that it is impossible to run a

wxPython app in its own thread several times during the runtime of the

process?

Regards, Christian


Mike Driscoll

Blog: http://blog.pythonlibrary.org

ckkart wrote:

Thanks for the explanation. Unfortunately I do not see a way how to
achieve what I want. I was thinking of reusing the same thread object
and simply start the main loop again, but apparently that is not
possible. In summary, is it right, that it is impossible to run a
wxPython app in its own thread several times during the runtime of the
process?

No, but you can keep the MainLoop running in the thread and create multiple windows as you need them.

···

--
Robin Dunn
Software Craftsman

Robin Dunn <robin <at> alldunn.com> writes:

> Thanks for the explanation. Unfortunately I do not see a way how to
> achieve what I want. I was thinking of reusing the same thread object
> and simply start the main loop again, but apparently that is not
> possible. In summary, is it right, that it is impossible to run a
> wxPython app in its own thread several times during the runtime of the
> process?

No, but you can keep the MainLoop running in the thread and create
multiple windows as you need them.

Thanks, Mike and Robin. As you sugessted, I let the MainLoop run in its own
thread forever, i.e. until termination of the main thread, and just hide and
show the top level frame. This works very well.

While playing with threading I noticed, that the wx MainLoop is not as blocking
as a call to time.sleep(). In case the wx MainLoop is started from within (no
separate thread) the event loop of a com server, which itself is an outlook
addin, outlook stays responsive, however when I call time.sleep() somewhere,
outlook is blocked completly. What is happening internally in the MainLoop while
wx is waiting for something to happen?

Regards, Christian

Christian K. wrote:

Robin Dunn <robin <at> alldunn.com> writes:

Thanks for the explanation. Unfortunately I do not see a way how to
achieve what I want. I was thinking of reusing the same thread object
and simply start the main loop again, but apparently that is not
possible. In summary, is it right, that it is impossible to run a
wxPython app in its own thread several times during the runtime of the
process?

No, but you can keep the MainLoop running in the thread and create multiple windows as you need them.

Thanks, Mike and Robin. As you sugessted, I let the MainLoop run in its own
thread forever, i.e. until termination of the main thread, and just hide and
show the top level frame. This works very well.

While playing with threading I noticed, that the wx MainLoop is not as blocking
as a call to time.sleep(). In case the wx MainLoop is started from within (no
separate thread) the event loop of a com server, which itself is an outlook
addin, outlook stays responsive, however when I call time.sleep() somewhere,
outlook is blocked completly. What is happening internally in the MainLoop while
wx is waiting for something to happen?

It is fetching and dispatching messages from the system message queue for that thread. As you've discovered it's possible to dispatch messages to non-wx windows too, as long as they don't require any non-standard processing to be happening at the same time. Unfortunately it doesn't work so well going the other direction (having some other "main loop" dispatch messages to wx windows) because wx relies on its own code for taking care of idle events and pending event queues.

···

--
Robin Dunn
Software Craftsman