GUI responsive while threads working

1,112 views
Skip to first unread message

Kurt Mueller

unread,
Dec 10, 2009, 11:12:55 AM12/10/09
to wxpytho...@lists.wxwidgets.org
Hi


How do I keep the GUI responsive while threads are working?

I have an wx.python app with a wx.Frame containing a Toolbar
a Statusbar and a GridBagSizer.
The cells of the grid bag contain matplotlib figures or
wx.lib.plot canvas to show plots.
These plots are updated by timers (threads).
So each plot generates a certain processing load.

As long as the processing load is low, the GUI (e.g. Toolbar)
is responsive.
When more plots are added, and the processing load increases,
the plots do still update, but the GUI does not respond at all.

How can I improve the situation?
Can I start a repeating timer to "activate" the GUI? (How)

Linux 2.6.22.19-0.1-default, Core2duo
Python 2.5.1
wx 2.8.9.2


Regards
--
Kurt Mueller

Mike Driscoll

unread,
Dec 10, 2009, 12:12:18 PM12/10/09
to wxPython-users
Hi!

On Dec 10, 10:12 am, Kurt Mueller <kurt.alfred.muel...@gmail.com>
wrote:
There are various suggestions on the wiki for this scenario:

http://wiki.wxpython.org/LongRunningTasks

Did you look at that?

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Kurt Mueller

unread,
Dec 10, 2009, 1:05:31 PM12/10/09
to wxpytho...@googlegroups.com
Hi,

> On Dec 10, 10:12 am, Kurt Mueller <kurt.alfred.muel...@gmail.com>
>> How do I keep the GUI responsive while threads are working?
>> I have an wx.python app with a wx.Frame containing a Toolbar
>> a Statusbar and a GridBagSizer.
>> The cells of the grid bag contain matplotlib figures or
>> wx.lib.plot canvas to show plots.
>> These plots are updated by timers (threads).
>> So each plot generates a certain processing load.
>> As long as the processing load is low, the GUI (e.g. Toolbar)
>> is responsive.
>> When more plots are added, and the processing load increases,
>> the plots do still update, but the GUI does not respond at all.
>> How can I improve the situation?
>> Can I start a repeating timer to "activate" the GUI? (How)
>> Linux 2.6.22.19-0.1-default, Core2duo
>> Python 2.5.1
>> wx 2.8.9.2
> There are various suggestions on the wiki for this scenario:
> http://wiki.wxpython.org/LongRunningTasks
> Did you look at that?

Yes. There are 3 approaches in the wiki:
The first approach is using worker threads.
-> I use threads, I use repeating timers to start the updates for the plots.
The second approach is with wx.Yield() which I do not understand.
The third approach is to start work with an Idle Event.


I think the problem in my application is, that many timers
start threads, they update the plots. If there is always one thread
running, the GUI never gets a chance to respond.

The other thing is, that an update for the plot takes about 0.1[s],
so it is not a "long running task".
If I start 10 update tasks every second, then there is always one thread
running.

How does threading behaves if there is always one thread ready for running,
and there is never an idle situation?



Regards
--
Kurt Mueller

Gabriel Andrade

unread,
Dec 10, 2009, 1:37:14 PM12/10/09
to wxpytho...@googlegroups.com
On Thu, Dec 10, 2009 at 1:12 PM, Kurt Mueller
<kurt.alfr...@gmail.com> wrote:
>
> Hi
>
>
> How do I keep the GUI responsive while threads are working?

Maybe DelayedResult [1] is what you're looking for. I tend to use it
for tasks that takes a little longer than usual (network, I/O, high
CPU load, etc..)

def producer():
long_running_task()

def consumer(result):
try: # just in case producer throws something
r = result.get()
except:
# do whatever you wan to do
else:
# updates GUI....

DelayedResult.startWorker(consumer, producer)

Never, ever, call a wx method inside the producer function, let it to
be done inside the consumer.

[1] http://www.wxpython.org/docs/api/wx.lib.delayedresult-module.html

Mike Driscoll

unread,
Dec 10, 2009, 2:11:27 PM12/10/09
to wxPython-users
Hi,

On Dec 10, 12:05 pm, Kurt Mueller <kurt.alfred.muel...@gmail.com>
wrote:
I've never tried the 2nd or 3rd ones...I thought there was an example
using Queues too though...also something I haven't tried.


>
> I think the problem in my application is, that many timers
> start threads, they update the plots. If there is always one thread
> running, the GUI never gets a chance to respond.
>
> The other thing is, that an update for the plot takes about 0.1[s],
> so it is not a "long running task".
> If I start 10 update tasks every second, then there is always one thread
> running.
>
> How does threading behaves if there is always one thread ready for running,
> and there is never an idle situation?
>
> Regards
> --
> Kurt Mueller

I've never had any issues with running multiple threads in my
applications. Note that wxPython isn't thread-safe, so if you are
trying to update the GUI directly from a thread, you'll probably have
issues.

DON'T DO THIS:

# inside thread
myFrame.SetBackgroundColour()
myFrame.myTextCtrl.SetValue()


Instead, use wx.CallAfter, wx.CallLater or wx.PostEvent

Of course, if you are already doing that, then we'll probably need a
sample app:

See http://wiki.wxpython.org/MakingSampleApps for more info.

Kurt Mueller

unread,
Dec 10, 2009, 2:45:57 PM12/10/09
to wxpytho...@googlegroups.com
Hi,


Mike Driscoll:
> I've never had any issues with running multiple threads in my
> applications. Note that wxPython isn't thread-safe, so if you are
> trying to update the GUI directly from a thread, you'll probably have
> issues.
> DON'T DO THIS:
> # inside thread
> myFrame.SetBackgroundColour()
> myFrame.myTextCtrl.SetValue()
> Instead, use wx.CallAfter, wx.CallLater or wx.PostEvent

Within the threads I collect some data,
make some numpy calculations, and
then I update the plot with the
wx.lib.plot.PlotCanvas.Draw() method.
Does that makes trouble?
I could call it with wx.CallAfter if this would help.

> Of course, if you are already doing that, then we'll probably need a
> sample app:
> See http://wiki.wxpython.org/MakingSampleApps for more info.
Jouh, I was afraid of that.
The app has about 5000 lines of code.
But...


Regards
--
Kurt Mueller

Kurt Mueller

unread,
Dec 10, 2009, 2:53:01 PM12/10/09
to wxpytho...@googlegroups.com
Gabriel Andrade:

> Maybe DelayedResult [1] is what you're looking for. I tend to use it
> for tasks that takes a little longer than usual (network, I/O, high
> CPU load, etc..)
> def producer():
> long_running_task()
> def consumer(result):
> try: # just in case producer throws something
> r = result.get()
> except:
> # do whatever you wan to do
> else:
> # updates GUI....
> DelayedResult.startWorker(consumer, producer)
> Never, ever, call a wx method inside the producer function, let it to
> be done inside the consumer.
> [1] http://www.wxpython.org/docs/api/wx.lib.delayedresult-module.html

Thanks Gabriel for the hint.
Maybe I was not careful in not calling any wx method in the threads.
I'll check that first.

But maybe only tomorrow because it is 09:00pm (21:00) here :-)



Regards
--
Kurt Mueller

Mike Driscoll

unread,
Dec 10, 2009, 2:59:59 PM12/10/09
to wxPython-users


On Dec 10, 1:45 pm, Kurt Mueller <kurt.alfred.muel...@gmail.com>
wrote:
> Hi,
>
> Mike Driscoll:
>
> > I've never had any issues with running multiple threads in my
> > applications. Note that wxPython isn't thread-safe, so if you are
> > trying to update the GUI directly from a thread, you'll probably have
> > issues.
> > DON'T DO THIS:
> > # inside thread
> > myFrame.SetBackgroundColour()
> > myFrame.myTextCtrl.SetValue()
> > Instead, use wx.CallAfter, wx.CallLater or wx.PostEvent
>
> Within the threads I collect some data,
> make some numpy calculations, and
> then I update the plot with the
> wx.lib.plot.PlotCanvas.Draw() method.
> Does that makes trouble?
> I could call it with wx.CallAfter if this would help.


This could be the issue. Try wrapping it in wx.CallAfter and see if it
helps.

Christopher Barker

unread,
Dec 10, 2009, 7:37:54 PM12/10/09
to wxpytho...@googlegroups.com
Kurt Mueller wrote:
> Within the threads I collect some data,
OK
> make some numpy calculations,
OK
> and then I update the plot with the
> wx.lib.plot.PlotCanvas.Draw() method.
> Does that makes trouble?

Probably, yes -- it's going to be making wx calls -- at least 1, anyway.
if you are using wxAgg, then it is doing the drawing in its own code, to
an agg buffer in memory, then copying that over to a wxBitmap and
blitting it to the screen. Those past two steps are not thread-safe.

I don't know that there is a way to get MPL do do most of its drawing in
a thread, then do the final blit separately -- it would take some poking
into the wx back-end code, which is a bit messy.

you might try doing your calculations in a separate thread, then calling
MPL from the main thread, and see how that works for you.

> The other thing is, that an update for the plot takes about 0.1[s],
> so it is not a "long running task".
> If I start 10 update tasks every second, then there is always one thread
> running.
>
> How does threading behaves if there is always one thread ready for running,
> and there is never an idle situation?

That should work -- the whole point of threads is that they run in
parallel ,they are not waiting for an idle state, or one to finish
before the next one starts. However, there is the issue of the global
interpreter lock (GIL). In Python, if operations may not be thread safe,
they ca lock the interpreter to assure that they don't et stomped on by
another thread -- this is complicated, and I have no idea when it is
invoked, but it does allow one thread to freeze of the interpreter in
some situations -- maybe that's happening here, but I'd try the above first.

>> Of course, if you are already doing that, then we'll probably need a
>> sample app:
>> See http://wiki.wxpython.org/MakingSampleApps for more info.
> Jouh, I was afraid of that.
> The app has about 5000 lines of code.
> But...

you don't need all of it at all -- just make a simple app that does a
bunch of MPL plotting and see what you get -- asking on the MPL list
might be helpful too.

-Chris



--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris....@noaa.gov

Manne Merak

unread,
Dec 11, 2009, 2:15:24 AM12/11/09
to wxpytho...@googlegroups.com
Just to be clear. You have multiple timers and with each timer start
you create a thread that does all the calculations and updates the plot.
Are all the threads updating the same plot?

Manne

Kurt Mueller

unread,
Dec 12, 2009, 4:37:51 AM12/12/09
to wxpytho...@googlegroups.com
Manne Merak:
>> I think the problem in my application is, that many timers
>> start threads, they update the plots. If there is always one thread
>> running, the GUI never gets a chance to respond.
> Just to be clear. You have multiple timers and with each timer start
> you create a thread that does all the calculations and updates the plot.
> Are all the threads updating the same plot?

Each plot has its own timer.
e.g. 10 repeating timers update 10 plots.
A call to threading.enumerate() shows the main thread and
the 10 timer threads.


I use RepeatTimer from Geoffrey Foster.
http://g-off.net/software/a-python-repeatable-threadingtimer-class


Each thread does:
- collect data
- make some numpy calculations
- calls wx.lib.plot.PlotGraphics() (no specific wx stuff I presume)
- wx.lib.plot.PlotCanvas.Draw() (wx stuff)
The execution time for one thread is about 0.1[s].

With this setup if I start about 10 timers/plots (repeat rate 1.0[s])
the GUI *does not* respond at all even after many minutes waiting.

Now I moved the call to wx.lib.plot.PlotCanvas.Draw() to a method in the
main thread and call it from the thread via wx.CallAfter().
With 10 timers/plots the GUI *does not* respond at all even after
many minutes waiting.

Now if I run the same app without calling wx.lib.plot.PlotCanvas.Draw()
(without any wx stuff in the threads) I see that
the GUI *does* respond even under heavy load:
40 timers/plots and a Unix load average of 16.
Sometimes the GUI takes several seconds to respond,
but it responds.

On a similar problem somebody suggested to add a call to wx.Yield().
I put it in the thread before or after the call to the method in
the main thread. No Change.
If I put SafeYield() the app crashes.



Is it possible to force the execution of the main thread with
a repeating timer?



Regards
--
Kurt Mueller

Robin Dunn

unread,
Dec 13, 2009, 7:39:23 PM12/13/09
to wxpytho...@googlegroups.com
My guess is that one of the following things is happening:

* The worker threads are too busy and end up starving the main thread,
preventing it from being able to execute. Try reducing the frequency
that the worker threads do their thing to see how that affects things.
Keep in mind that Python on multi-core machines has a real problem with
multi-threading when one or more threads are CPU-bound. See
http://blip.tv/file/2232410

* The main (gui) thread is getting blocked somehow. In other words,
something in an event handler or other callback is getting stuck and not
returning to the main loop, and so further events are not able to be
dispatched.

* You're still doing some UI operation in a worker thread, possibly
creating some UI object. If it's not giving you an error or simply
crashing when this happens then it's possible that the UI object that
was created "belongs" to the other thread and can cause problems when
the main thread tries to service it in some way, like blocking until the
owning thread responds, which it never will.



>
> On a similar problem somebody suggested to add a call to wx.Yield().
> I put it in the thread before or after the call to the method in
> the main thread. No Change.
> If I put SafeYield() the app crashes.

Don't call the yield functions from a worker thread. They are
essentially just a nested event loop that runs until there are no more
pending events. You obviously don't want events to be dispatched in the
worker thread. In addition, SafeYield does all kinds of stuff with the
UI objects before it enters the event loop to make them "safe," all of
which are not legal to do in a non-gui thread.

> Is it possible to force the execution of the main thread with
> a repeating timer?

If you don't block it and you don't starve it then there shouldn't be a
problem with it running normally.


--
Robin Dunn
Software Craftsman
http://wxPython.org

Kurt Mueller

unread,
Dec 15, 2009, 11:09:02 AM12/15/09
to wxpytho...@googlegroups.com
Robin Dunn wrote:
> My guess is that one of the following things is happening:
>
> * The worker threads are too busy and end up starving the main thread,
> preventing it from being able to execute. Try reducing the frequency
> that the worker threads do their thing to see how that affects things.
> Keep in mind that Python on multi-core machines has a real problem with
> multi-threading when one or more threads are CPU-bound. See
> http://blip.tv/file/2232410
>
> * The main (gui) thread is getting blocked somehow. In other words,
> something in an event handler or other callback is getting stuck and not
> returning to the main loop, and so further events are not able to be
> dispatched.
>
> * You're still doing some UI operation in a worker thread, possibly
> creating some UI object. If it's not giving you an error or simply
> crashing when this happens then it's possible that the UI object that
> was created "belongs" to the other thread and can cause problems when
> the main thread tries to service it in some way, like blocking until the
> owning thread responds, which it never will.
>

>> Is it possible to force the execution of the main thread with
>> a repeating timer?
> If you don't block it and you don't starve it then there shouldn't be a
> problem with it running normally.

Each worker thread does:
1) collect data
2) make some numpy calculations
3) calls wx.lib.plot.PlotGraphics() (no specific wx stuff I presume)
4) wx.lib.plot.PlotCanvas.Draw() (wx stuff) called with wx.CallAfter()
The execution time for one thread is about 0.1[s].


If I run a lot of worker threads with steps 1 to 4, the GUI does not
respond.

If I make a method in the main thread to execute step 4 in the main thread
and call this method from the worker threads with wx.CallAfter(),
the GUI does not respond.

If I leave out step 4 and run only steps 1 to 3, I can start as much
worker threads
as I want, the GUI does always respond (maybe very slow).

So I guess it is the wx.lib.plot.PlotCanvas.Draw() command that changes
the behavior of the GUI.




Regards
--
Kurt Mueller

Christopher Barker

unread,
Dec 15, 2009, 12:06:19 PM12/15/09
to wxpytho...@googlegroups.com
Kurt Mueller wrote:
> Each worker thread does:
> 1) collect data
> 2) make some numpy calculations
> 3) calls wx.lib.plot.PlotGraphics() (no specific wx stuff I presume)

Why are you presuming that? It's wx code -- it could well be making
non-thread-safe wx calls -- worth checking out, anyway.

> So I guess it is the wx.lib.plot.PlotCanvas.Draw() command that changes
> the behavior of the GUI.

Could be -- unfortunately, as I learned from David Beazley's talk that
Robin pointed us to -- threading in Python is tricky and unreliable,
particularly with multiple processors/cores.

You might try adding a little sleep() call into your threads -- maybe
that will allow some things to get cleaned up a bit.

Another thought:

I wonder if this is similar to the signal trapping issue (ctrl-C not
working) that Avid BEazley talked about. If so, I suppose you could try
running wx in a thread other than the main thread -- wx has to all runin
the same thread, but it does not have to be the main one (though it's
easiest to do that way!). You might look at the ipython project for an
example of how to do that. now that I think about it, you might try
running your code under iPython (with -wthread), and see if it behaves
any differently there.

If all that fails, then you may need to give up on on threading --
either doing averyint the old-fashioned way, or using multi-processing
instead -- the new-in-2.6 multiprocessing module may be helpful for that.

Good luck -- and do report back what you learn!

Kurt Mueller

unread,
Dec 15, 2009, 1:18:13 PM12/15/09
to wxpytho...@googlegroups.com
Christopher Barker wrote:
> Kurt Mueller wrote:
>> Each worker thread does:
>> 1) collect data
>> 2) make some numpy calculations
>> 3) calls wx.lib.plot.PlotGraphics() (no specific wx stuff I presume)
> Why are you presuming that? It's wx code -- it could well be making
> non-thread-safe wx calls -- worth checking out, anyway.
I did now.
I put also wx.lib.plot.PlotGraphics() into the main thread.
It did not change anything.


>> So I guess it is the wx.lib.plot.PlotCanvas.Draw() command that changes
>> the behavior of the GUI.
> Could be -- unfortunately, as I learned from David Beazley's talk that
> Robin pointed us to -- threading in Python is tricky and unreliable,
> particularly with multiple processors/cores.
>
> You might try adding a little sleep() call into your threads -- maybe
> that will allow some things to get cleaned up a bit.
I added a time.sleep(0.02) but no change, except that I could start
one more thread until the app blocked.


> Another thought:
>
> I wonder if this is similar to the signal trapping issue (ctrl-C not
> working) that Avid BEazley talked about. If so, I suppose you could try
> running wx in a thread other than the main thread -- wx has to all runin
> the same thread, but it does not have to be the main one (though it's
> easiest to do that way!).
That would be a hard job, because the app is now about 5000 lines of code.


> You might look at the ipython project for an
> example of how to do that. now that I think about it, you might try
> running your code under iPython (with -wthread), and see if it behaves
> any differently there.
I do not know iPython.
What would be the benefit using iPython in this situation?


>
> If all that fails, then you may need to give up on on threading --
> either doing averyint the old-fashioned way, or using multi-processing
> instead -- the new-in-2.6 multiprocessing module may be helpful for that.
We have 2.5 for now.
There is no chance to update for the next few weeks
because of an other important project. Maybe later.


There is another weird problem with the app.
There is a right mouse click menu on the canvas of the plots.
When right clicking on a plot while heavy load, sometimes
not only the app freezes, but the whole X11 window freezes.
One cannot do anything but <ctl><alt><F1> to get a terminal.
After killing python, the rest of X11 comes to life again.
Linux 2.6.22.19-0.1-default, Core2duo
Python 2.5.1
wx 2.8.9.2

Maybe I'll take the time to make a sample app.



Regards
--
Kurt Mueller

Christopher Barker

unread,
Dec 15, 2009, 2:52:40 PM12/15/09
to wxpytho...@googlegroups.com
Kurt Mueller wrote:
> I put also wx.lib.plot.PlotGraphics() into the main thread.
> It did not change anything.

darn.

>> You might look at the ipython project for an
>> example of how to do that. now that I think about it, you might try
>> running your code under iPython (with -wthread), and see if it behaves
>> any differently there.
> I do not know iPython.
> What would be the benefit using iPython in this situation?

It would be a way to test running wx in the non-main thread. I think you
could simply fire up ipython with "ipython -wthread" then type "run
YourMainScript.py"

And it would run, putting wx in a separate thread from the main
(ipython) one. I have no idea if this would make any difference for you,
but it would eliminate one possible issue.

Did you watch David Beazly's talk? It's pretty illuminating. In
particular, he identified that if a you have a thread running
computationally intensive stuff, it can totally starve a low-intensity
IO thread -- wx may fall into that category, when it's simply waiting
for events.

In his example, when a computationally intensive thread was running, it
could take 100s of "tics" for an IO thread to run, that ultimately only
use 3 tics to do its job. This was made much worse by multiple processors.

If you have more that one processor or core, you might try turning them
off, ans see if things run better with only one.


>> If all that fails, then you may need to give up on on threading --
>> either doing averyint the old-fashioned way, or using multi-processing
>> instead -- the new-in-2.6 multiprocessing module may be helpful for that.
> We have 2.5 for now.

look and see if it's been back-ported.

> Maybe I'll take the time to make a sample app.

always a good idea.

Sorry we're not more help!

Kurt Mueller

unread,
Dec 18, 2009, 9:43:27 AM12/18/09
to wxpytho...@googlegroups.com
Kurt Mueller schrieb:

> Christopher Barker wrote:
>> Kurt Mueller wrote:
>>> Each worker thread does:
>>> 1) collect data
>>> 2) make some numpy calculations
>>> 3) calls wx.lib.plot.PlotGraphics() (no specific wx stuff I presume)

>>> So I guess it is the wx.lib.plot.PlotCanvas.Draw() command that changes


>>> the behavior of the GUI.
>> Could be -- unfortunately, as I learned from David Beazley's talk that
>> Robin pointed us to -- threading in Python is tricky and unreliable,
>> particularly with multiple processors/cores.

I do not like threads at all! (Not only in Python)
I would rather like to use the multi-processing in 2.6. But...

>> If all that fails, then you may need to give up on on threading --
>> either doing averyint the old-fashioned way, or using multi-processing
>> instead -- the new-in-2.6 multiprocessing module may be helpful for that.
> We have 2.5 for now.
> There is no chance to update for the next few weeks
> because of an other important project. Maybe later.

I gave up threading.

I put the callable of the "worker" methods in a list (besides other
information).
Then in a method called from EVT_IDLE a method is started to call all
"worker" methods in the list once.

No the problem arises that EVT_IDLE is only fired if there is something on
in the GUI like mouse movement.
I added a method called from EVT_IDLE that wx.CallLater(500, method)
calling wx.Frame.UpdateWindowUI() to have at least a EVT_EVENT every
500[ms].
500[ms] is tunable.


This construct runs now very well.
All mentioned problems are gone.
The caveat is, that the "worker" methods do not update regularly
because they are related to EVT_IDLE.


Thanks to everybody who helped.
Regards
--
Kurt Mueller

Robin Dunn

unread,
Dec 18, 2009, 5:01:44 PM12/18/09
to wxpytho...@googlegroups.com
On 12/18/09 6:43 AM, Kurt Mueller wrote:
> No the problem arises that EVT_IDLE is only fired if there is something on
> in the GUI like mouse movement.
> I added a method called from EVT_IDLE that wx.CallLater(500, method)
> calling wx.Frame.UpdateWindowUI() to have at least a EVT_EVENT every
> 500[ms].
> 500[ms] is tunable.

Something else you could consider is to call wx.WakeUpIdle() after you
have gone through the list of workers. If there are pending events then
they should be processed first, otherwise the event queue will "become
empty" again and that will trigger the sending of another idle event.

Reply all
Reply to author
Forward
0 new messages