wx.CallLater being very late

211 views
Skip to first unread message

cool-RR

unread,
Aug 14, 2009, 9:51:21 AM8/14/09
to wxPython-users
Hey everyone,

In my wxPython app I have an EVT_IDLE handler that calls some function
that has to be called once every 150 milliseconds or so. After calling
the function, the handler calls:
wx.CallLater(150,self._clear_idle_block_and_do)

That `_clear_idle_block_and_do` function basically posts another
EVT_IDLE event, continuing the cycle.

Now I'm noticing that when other widgets in the GUI are doing hard
work, the EVT_IDLE event handler hardly gets called! Sometime it is
not called for 4 seconds, which is way too much.

Is this because wx.CallLater is not performing well? Is there anything
I could do?

Cody Precord

unread,
Aug 14, 2009, 10:03:05 AM8/14/09
to wxPytho...@googlegroups.com
Hello,

On Fri, Aug 14, 2009 at 8:51 AM, cool-RR<ram.r...@gmail.com> wrote:
>
> Hey everyone,
>
> In my wxPython app I have an EVT_IDLE handler that calls some function
> that has to be called once every 150 milliseconds or so. After calling
> the function, the handler calls:
> wx.CallLater(150,self._clear_idle_block_and_do)
>
> That `_clear_idle_block_and_do` function basically posts another
> EVT_IDLE event, continuing the cycle.
>
> Now I'm noticing that when other widgets in the GUI are doing hard
> work, the EVT_IDLE event handler hardly gets called! Sometime it is
> not called for 4 seconds, which is way too much.

Well this is to be expected because IDLE events are only sent when the
app is idle ;). CallLater uses a Timer and basic timer events on most
platforms are a low priority event, meaning that they are only
processed when the event queue is empty (i.e the app is idle).

>
> Is this because wx.CallLater is not performing well? Is there anything
> I could do?

If the stuff being done in your other widgets is blocking the main
thread then no other gui code can run while this is going on. You can
try to move some work into a separate thread or break the work up into
smaller pieces and give the app a chance to process the event queue in
between intervals. Hard to advise more without a better idea of what
your application is doing.


Cody

cool-RR

unread,
Aug 14, 2009, 10:07:41 AM8/14/09
to wxPython-users


On Aug 14, 4:03 pm, Cody Precord <codyprec...@gmail.com> wrote:
> Hello,
>
Okay Cody, this is interesting. Let's break this down: You said IDLE
events are called only when the app is idle. But I said I'm calling
the idle event *manually*. Does wxPython still give it low priority
over other events or something?
If the answer is yes, then I will need a way to modify this priority.
If the answer is no, then I'm stumped and I'll probably have to give
you a more complete description of my app.

Ram.

Cody Precord

unread,
Aug 14, 2009, 10:28:41 AM8/14/09
to wxPytho...@googlegroups.com
Hello,

On Fri, Aug 14, 2009 at 9:07 AM, cool-RR<ram.r...@gmail.com> wrote:
>
> Okay Cody, this is interesting. Let's break this down: You said IDLE
> events are called only when the app is idle. But I said I'm calling
> the idle event *manually*. Does wxPython still give it low priority
> over other events or something?
> If the answer is yes, then I will need a way to modify this priority.
> If the answer is no, then I'm stumped and I'll probably have to give
> you a more complete description of my app.

What I interpret from above is that you have an IDLE handler that in
turn calls CallLater that in turn will invoke a function that posts
another idle event. Please correct me if I am miss-reading that.

So when the app gets blocked up it will not be generating its own IDLE
events and the TIMER event used by CallLater will also not be
processed until the event queue is emptied out or when the other
currently running code releases the processor and the app has a chance
to work through the queue.

I am not sure how the idle events are handled in the wx layer (where
your manually posted ones would exist), but I would guess that it
mimics the underlying system library in that they are handled after
events of other types have been dispatched. It probably doesn't make
much sense for you to post IDLE events, it would probably be better
use a custom event type or simply call your idle handler directly.

Though this does not really sound like the main issue, the main issue
sounds like you have some other code that is blocking the main thread,
so no matter what type of event you send it will still not be
processed until the blocking code has completed and released its hold
on the cpu. Does your app become non responsive when the 'other
widgets' are busy?


Cody

cool-RR

unread,
Aug 14, 2009, 11:35:53 AM8/14/09
to wxPython-users


On Aug 14, 4:28 pm, Cody Precord <codyprec...@gmail.com> wrote:
> Hello,
>
> On Fri, Aug 14, 2009 at 9:07 AM, cool-RR<ram.rac...@gmail.com> wrote:
>
> > Okay Cody, this is interesting. Let's break this down: You said IDLE
> > events are called only when the app is idle. But I said I'm calling
> > the idle event *manually*. Does wxPython still give it low priority
> > over other events or something?
> > If the answer is yes, then I will need a way to modify this priority.
> > If the answer is no, then I'm stumped and I'll probably have to give
> > you a more complete description of my app.
>
> What I interpret from above is that you have an IDLE handler that in
> turn calls CallLater that in turn will invoke a function that posts
> another idle event. Please correct me if I am miss-reading that.

You got it right.

> So when the app gets blocked up it will not be generating its own IDLE
> events and the TIMER event used by CallLater will also not be
> processed until the event queue is emptied out or when the other
> currently running code releases the processor and the app has a chance
> to work through the queue.

I see. You are saying that the function I specify with CallLater will
not actually be called until all events are cleared out, even though
the time I specified has long since passed.

> I am not sure how the idle events are handled in the wx layer (where
> your manually posted ones would exist), but I would guess that it
> mimics the underlying system library in that they are handled after
> events of other types have been dispatched. It probably doesn't make
> much sense for you to post IDLE events, it would probably be better
> use a custom event type or simply call your idle handler directly.

I just create a custom event type to replace EVT_IDLE - Didn't help.

I don't think that calling the function directly, as opposed to
calling a function that posts an event will help - I checked and even
that event-calling function doesn't get called in time.

> Though this does not really sound like the main issue, the main issue
> sounds like you have some other code that is blocking the main thread,
> so no matter what type of event you send it will still not be
> processed until the blocking code has completed and released its hold
> on the cpu. Does your app become non responsive when the 'other
> widgets' are busy?

No, it does not. There are three busy widgets, all getting refreshed
continuously. I understand that each of them takes CPU, but it's all
done in little units, and I think it should be possible for wxPython
to do my background process in between these units.

Any more ideas?

Cody Precord

unread,
Aug 14, 2009, 11:51:26 AM8/14/09
to wxPytho...@googlegroups.com
Hello,

On Fri, Aug 14, 2009 at 10:35 AM, cool-RR<ram.r...@gmail.com> wrote:
>
>> So when the app gets blocked up it will not be generating its own IDLE
>> events and the TIMER event used by CallLater will also not be
>> processed until the event queue is emptied out or when the other
>> currently running code releases the processor and the app has a chance
>> to work through the queue.
>
> I see. You are saying that the function I specify with CallLater will
> not actually be called until all events are cleared out, even though
> the time I specified has long since passed.

Yes, because CallLater starts a one shot timer that will fire at the
time you specified. When its timer fires it posts a TIMER event to a
temporary handler that it setup, and as mentioned TIMER events are low
priority and are usually only processed in idle time, so that timer
handler will not be called on time if other things are busy.


>
> I just create a custom event type to replace EVT_IDLE - Didn't help.
>
> I don't think that calling the function directly, as opposed to
> calling a function that posts an event will help - I checked and even
> that event-calling function doesn't get called in time.

Well if this is happening then it means the other running code is
blocking the execution of the code on that thread and that you will
like need to find a more efficient way of doing what your doing.


> No, it does not. There are three busy widgets, all getting refreshed
> continuously. I understand that each of them takes CPU, but it's all
> done in little units, and I think it should be possible for wxPython
> to do my background process in between these units.
>
> Any more ideas?

It all depends on how 'busy' the widgets are, if they are constantly
redrawing themselves or updating like that in some manner the event
queue will be flooded with paint and other events.

What are the busy widgets doing? How continuously are they being
refreshed, you might be refreshing them more than you need to or
something.


Cody

Christopher Barker

unread,
Aug 14, 2009, 12:08:21 PM8/14/09
to wxPytho...@googlegroups.com
cool-RR wrote:
> On Aug 14, 4:28 pm, Cody Precord <codyprec...@gmail.com> wrote:

>> What I interpret from above is that you have an IDLE handler that in
>> turn calls CallLater that in turn will invoke a function that posts
>> another idle event. Please correct me if I am miss-reading that.
>
> You got it right.

> Any more ideas?

yes -- get away from Idle event altogether -- I've never found a use for
them.

From what I understand, what you want is for something to happen about
150 milliseconds or so. In that case, what you want is a wx.Timer, set
to fire every 150ms. It won't guarantee that it will happen, and can
still et blocked by a long-running event handler, but it's likely to be
more reliable -- give it a try.

-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

cool-RR

unread,
Aug 14, 2009, 12:11:11 PM8/14/09
to wxPython-users
> Chris.Bar...@noaa.gov

Chris, I did get away from the idle event - didn't help. Also, I'm
using wx.CallLater, which I was told runs a wx.Timer.

cool-RR

unread,
Aug 14, 2009, 12:19:03 PM8/14/09
to wxPython-users

> Yes, because CallLater starts a one shot timer that will fire at the
> time you specified. When its timer fires it posts a TIMER event to a
> temporary handler that it setup, and as mentioned TIMER events are low
> priority and are usually only processed in idle time, so that timer
> handler will not be called on time if other things are busy.
>


Maybe there is a way to make Timer events have higher priority?



> It all depends on how 'busy' the widgets are, if they are constantly
> redrawing themselves or updating like that in some manner the event
> queue will be flooded with paint and other events.

Yes, they are constantly redrawing themselves. I understand that the
event queue gets flooded, and that's okay: I just want the events that
call my background function to have a high enough priority in the
queue, so they will not be starved by the other events.

> What are the busy widgets doing? How continuously are they being
> refreshed, you might be refreshing them more than you need to or
> something.

My program is a platform for running simulations. It's called
GarlicSim. The three busy widgets are the following:

seek_bar - A seek bar that displays which time point of the simulation
we are watching, and what time segment of the simulation is ready for
watching.

tree_browser - A widgets that shows the "tree" of the simulation. The
tree is usually just a straight line, but if the user wants to explore
alternate scenarios, he will make a split in the tree.

state_showing_window - A widget that shows the state of the simulated
world in the selected point in time.

When playing back the simulation, all three widgets must be constantly
updated.
The "background function" I have to call every 150ms is a function
that collects simulation data from worker threads and integrated them
into the tree.

-----

The best idea I have so far is to change the priority of Timer events
somehow. Do you know how? Or do you have a better idea?

Cody Precord

unread,
Aug 14, 2009, 12:35:24 PM8/14/09
to wxPytho...@googlegroups.com
Hello,

On Fri, Aug 14, 2009 at 11:19 AM, cool-RR<ram.r...@gmail.com> wrote:
>
>
>> Yes, because CallLater starts a one shot timer that will fire at the
>> time you specified. When its timer fires it posts a TIMER event to a
>> temporary handler that it setup, and as mentioned TIMER events are low
>> priority and are usually only processed in idle time, so that timer
>> handler will not be called on time if other things are busy.
>>
>
>
> Maybe there is a way to make Timer events have higher priority?

No I dont think so, but as previously mentioned this will not fix your
problem, as you said that even with your custom event type it was not
being called on time.

>
>
> When playing back the simulation, all three widgets must be constantly
> updated.

What I asked was how often are they updating themselves (time
interval?). Because you may be updating them far more often than you
need to, or faster than a human eye can even discern.


> The "background function" I have to call every 150ms is a function
> that collects simulation data from worker threads and integrated them
> into the tree.

To simplify this as Chris had mentioned I would get rid of the IDLE
handling and CallLater and just have a single timer that you start and
tell it to fire every 150ms and use an EVT_TIMER handler.

self.timer = wx.Timer(self)
...
self.Bind(wx.EVT_TIMER, self.OnTimer)
...
self.timer.Start(150)
def OnTimer(self,evt):
do_your background_function()

If its blocking to much for this to be called in a timely manner you
need to figure out how to improve the efficiency of what your doing
elsewhere.

Short story is that you are likely blocking the main thread to much
with your other 'continuous updates' so you need to figure out how to
work around that. You could try Yielding in some places to let the
event queue be dispatched but there is always a danger of recursive
overflow if your not careful with Yield.


Cody

cool-RR

unread,
Aug 14, 2009, 12:57:43 PM8/14/09
to wxPython-users

> > Maybe there is a way to make Timer events have higher priority?
>
> No I dont think so, but as previously mentioned this will not fix your
> problem, as you said that even with your custom event type it was not
> being called on time.

I think you're mixing things up here - Even with my custom event, I
was still dependent on the timer event to make things work in time. So
I think if we could change the priority of the timer events, it will
solve the problem.

>
> > When playing back the simulation, all three widgets must be constantly
> > updated.
>
> What I asked was how often are they updating themselves (time
> interval?). Because you may be updating them far more often than you
> need to, or faster than a human eye can even discern.

Once every 50ms, which I think is not that frequent.


> > The "background function" I have to call every 150ms is a function
> > that collects simulation data from worker threads and integrated them
> > into the tree.
>
> To simplify this as Chris had mentioned I would get rid of the IDLE
> handling and CallLater and just have a single timer that you start and
> tell it to fire every 150ms and use an EVT_TIMER handler.
>
> self.timer = wx.Timer(self)
> ...
> self.Bind(wx.EVT_TIMER, self.OnTimer)
> ...
> self.timer.Start(150)
> def OnTimer(self,evt):
>     do_your background_function()

Did that right now - still the same problem.

> If its blocking to much for this to be called in a timely manner you
> need to figure out how to improve the efficiency of what your doing
> elsewhere.
>
> Short story is that you are likely blocking the main thread to much
> with your other 'continuous updates' so you need to figure out how to
> work around that. You could try Yielding in some places to let the
> event queue be dispatched but there is always a danger of recursive
> overflow if your not careful with Yield.

Unless you have any idea about how to make the timer events have
higher priority, I'd be interested to read about wx.Yield. I couldn't
find any explanation about it.

Ram.

Cody Precord

unread,
Aug 14, 2009, 1:08:54 PM8/14/09
to wxPytho...@googlegroups.com
Hello,

On Fri, Aug 14, 2009 at 11:57 AM, cool-RR<ram.r...@gmail.com> wrote:
>
>
> I think you're mixing things up here - Even with my custom event, I
> was still dependent on the timer event to make things work in time. So
> I think if we could change the priority of the timer events, it will
> solve the problem.
>
>>
>> > When playing back the simulation, all three widgets must be constantly
>> > updated.
>>
>> What I asked was how often are they updating themselves (time
>> interval?). Because you may be updating them far more often than you
>> need to, or faster than a human eye can even discern.
>
> Once every 50ms, which I think is not that frequent.

Actually this is probably a bit aggressive I would think.


> Unless you have any idea about how to make the timer events have
> higher priority, I'd be interested to read about wx.Yield. I couldn't
> find any explanation about it.

I still think that the main problem is your blocking the main thread
with your updates but... If you want to try it you can make your own
thread timer class using the threading and time modules and see if
that does anything different.

i.e) use something like this to replace wx.Timer
import threading
import time
class ThreadTimer(threading.Thread):
def __init__(self, parent)
self.parent = parent
self.continue = False
self.interval = 0

def run(self):
while continue:
time.sleep(self.interval)
event = MyCustomTimerEvent(...)
wx.PostEvent(self.parent, event)

def Start(self, interval):
self.interval = interval
self.start()

def Stop(self):
self.continue = False

Cody

Robin Dunn

unread,
Aug 14, 2009, 1:20:10 PM8/14/09
to wxPytho...@googlegroups.com
Cody Precord wrote:
[...]

> I am not sure how the idle events are handled in the wx layer (where
> your manually posted ones would exist), but I would guess that it
> mimics the underlying system library in that they are handled after
> events of other types have been dispatched.

The real EVT_IDLE events are sent when the system event queue *becomes*
empty. A common misconception is that they are sent continuously while
the app is idle, but unless you tell it otherwise (by calling
event.RequestMore) they are only sent once at the beginning of the idle
period. For the manually sent idle events then it depends on how they
are sent, ProcessEvent will cause the event to be processed within the
scope of the ProcessEvent call, while wx.PostEvent will add it to the
app's pending event queue and they will be processed some time later.

On the other hand, just the act of getting a timer event will cause the
system event queue to *become* empty once more. That means that an idle
event will be coming soon anyway so manually sending one from the timer
handler is a bit of overkill.


> It probably doesn't make
> much sense for you to post IDLE events, it would probably be better
> use a custom event type or simply call your idle handler directly.

Agreed.

>
> Though this does not really sound like the main issue, the main issue
> sounds like you have some other code that is blocking the main thread,
> so no matter what type of event you send it will still not be
> processed until the blocking code has completed and released its hold
> on the cpu.

Agreed. Cool-RR, please (re)read
http://wiki.wxpython.org/LongRunningTasks and
http://wiki.wxpython.org/Non-Blocking_Gui


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

Robin Dunn

unread,
Aug 14, 2009, 1:22:30 PM8/14/09
to wxPytho...@googlegroups.com
Cody Precord wrote:
> Hello,
>
> On Fri, Aug 14, 2009 at 11:19 AM, cool-RR<ram.r...@gmail.com> wrote:
>>
>>> Yes, because CallLater starts a one shot timer that will fire at the
>>> time you specified. When its timer fires it posts a TIMER event to a
>>> temporary handler that it setup, and as mentioned TIMER events are low
>>> priority and are usually only processed in idle time, so that timer
>>> handler will not be called on time if other things are busy.
>>>
>>
>> Maybe there is a way to make Timer events have higher priority?
>
> No I dont think so,

Correct. At least wx has no way to do it, I don't know about
platform-specific APIs. (wx.Timers are built on top of the timers
provided by the underlying platform, and EVT_TIMER events are sent
directly from whatever notification that the system provides of a timer
expiration. IOW, wx does very little work in the timers beyond what the
system provides.)


> but as previously mentioned this will not fix your
> problem, as you said that even with your custom event type it was not
> being called on time.

Agreed. If the gui thread is spending too much time outside the
MainLoop then delivery time of all types of events will suffer.

>
>>
>> When playing back the simulation, all three widgets must be constantly
>> updated.
>
> What I asked was how often are they updating themselves (time
> interval?). Because you may be updating them far more often than you
> need to, or faster than a human eye can even discern.

150ms is only about 6-7 frames per second, so that is well below the
human eye threshold... Maybe a better question to ask is how long does
it take to do the processing and the drawing that is being done every
150ms? If it's taking 151ms or more then it becomes clear that there is
more work required than can be done in the time allotted and that the
gui thread is too busy to be able to keep up with the timer events and
paint events and everything else. I'd guess that even if it only took
100ms that it is still too much. So if cool-RR can determine how much
time it takes to do the work as the app is currently organized then that
will help determine how to rebalance things so the gui thread will not
be overloaded.

As an extreme goal try to move all the heavy lifting to another thread
(or even another process) and then make your timer event handler do
nothing but fetch the next chunk of processed data and do a Refresh(),
and your paint handlers do nothing but convert the fetched data to a
drawing usign wx.DC methods. You may not need to go all the way to that
extreme as you may find that somewhere between there and where you are
now is good enough.

Also, if you are not familiar with it already you should learn about the
problems that Python has with multi-threaded apps on a multi-core system
when one or more threads are CPU bound. That may drive some of your
design decisions too. There was a link posted here a few weeks ago of a
video of David Beasley's talk about it.

Robin Dunn

unread,
Aug 14, 2009, 1:23:46 PM8/14/09
to wxPytho...@googlegroups.com
cool-RR wrote:
>

>>> When playing back the simulation, all three widgets must be constantly
>>> updated.
>> What I asked was how often are they updating themselves (time
>> interval?). Because you may be updating them far more often than you
>> need to, or faster than a human eye can even discern.
>
> Once every 50ms, which I think is not that frequent.

If the data is only being updated every 150ms then why is the drawing
being done every 50?


> I'd be interested to read about wx.Yield. I couldn't
> find any explanation about it.

It essentially makes a nested, temporary event loop so you can cause
pending events to be dispatched without needing to return control from
whatever is taking a long time back to the MainLoop. Yielding should be
used very carefully however as your event handlers will need to either
be reentrant or to protect themselves from reentry, and it is an error
for wx.Yield to be called from within some other wx.Yield call.

Robin Dunn

unread,
Aug 14, 2009, 1:30:34 PM8/14/09
to wxPytho...@googlegroups.com
Robin Dunn wrote:
> As an extreme goal try to move all the heavy lifting to another thread
> (or even another process) and then make your timer event handler do
> nothing but fetch the next chunk of processed data and do a Refresh(),
> and your paint handlers do nothing but convert the fetched data to a
> drawing usign wx.DC methods.

Or you can do the drawing in the timer handler with the target being a
wx.Bitmap in a MemoryDC. The paint events then can be nothing but a
DrawBitmap call, then they are super efficient and that will benefit the
refreshes that happen, for example, when part of the window is damaged
by other windows being dragged over your app.

cool-RR

unread,
Aug 14, 2009, 1:35:23 PM8/14/09
to wxPython-users

> > Once every 50ms, which I think is not that frequent.
>
> Actually this is probably a bit aggressive I would think.

Aggressive? That is only 20 fps.
Works great! I edited it and now my background function gets called
consistently even when the GUI is very busy.
Here's my edited version:

#threadtimer.py

import threading
import time
import wx

wxEVT_THREAD_TIMER = wx.NewEventType()
EVT_THREAD_TIMER = wx.PyEventBinder(wxEVT_THREAD_TIMER, 1)

class ThreadTimer(object):
def __init__(self, parent):
self.parent = parent
self.thread = Thread()
self.thread.parent = self
self.alive = False

def start(self, interval):
self.interval = interval
self.alive = True
self.thread.start()

def stop(self):
self.alive = False

class Thread(threading.Thread):
def run(self):
while self.parent.alive:
time.sleep(self.parent.interval / 1000.0)
event = wx.PyEvent()
event.SetEventType(wxEVT_THREAD_TIMER)
wx.PostEvent(self.parent.parent, event)


Thanks!

Now I have another problem. This one happened before this new
ThreadTimer, and still happens now. When my GUI is busy, and I press
the X button to close the window, it doesn't close it. Only when the
GUI stop working (because the simulation playback has finished) does
the application quit.
Any idea what to do?

Ram.

cool-RR

unread,
Aug 14, 2009, 1:47:55 PM8/14/09
to wxPython-users


On Aug 14, 7:23 pm, Robin Dunn <ro...@alldunn.com> wrote:
> cool-RR wrote:
>
> >>> When playing back the simulation, all three widgets must be constantly
> >>> updated.
> >> What I asked was how often are they updating themselves (time
> >> interval?). Because you may be updating them far more often than you
> >> need to, or faster than a human eye can even discern.
>
> > Once every 50ms, which I think is not that frequent.
>
> If the data is only being updated every 150ms then why is the drawing
> being done every 50?

The update that happens once every 150ms brings in new "frames" of the
simulation, it may be a 100 frames at a time. The 50ms delay is the
delay between displaying one frame and the next.


As I said in my post, the timer problem is solved, thanks to
ThreadTimer. Now my only problem is the non-responsive X button.

Christopher Barker

unread,
Aug 14, 2009, 4:28:26 PM8/14/09
to wxPytho...@googlegroups.com
Robin Dunn wrote:
> be reentrant or to protect themselves from reentry, and it is an error
> for wx.Yield to be called from within some other wx.Yield call.

Doesn't SafeYield handle this? It's generally worked for me.

-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

Robin Dunn

unread,
Aug 14, 2009, 4:42:19 PM8/14/09
to wxPytho...@googlegroups.com
cool-RR wrote:

> Now I have another problem. This one happened before this new
> ThreadTimer, and still happens now. When my GUI is busy, and I press
> the X button to close the window, it doesn't close it. Only when the
> GUI stop working (because the simulation playback has finished) does
> the application quit.
> Any idea what to do?

What platform is it? If on Windows and you have paint events that do
not create a wx.PaintDC for the window that sent the event then change
it so they do. I've seen this problem before if there are paint
handlers that don't create a PintDC or do create one but for the wrong
window.

Otherwise, it sounds like more of what we've been discussing already,
something is preventing the normal processing of events from happening.

Robin Dunn

unread,
Aug 14, 2009, 4:43:37 PM8/14/09
to wxPytho...@googlegroups.com
Christopher Barker wrote:
> Robin Dunn wrote:
>> be reentrant or to protect themselves from reentry, and it is an error
>> for wx.Yield to be called from within some other wx.Yield call.
>
> Doesn't SafeYield handle this? It's generally worked for me.

I personally don't like it because it disables every widget in the app
first, and then re-enables them when done, so that adds some overhead
and there can be side-effects.

cool-RR

unread,
Aug 15, 2009, 6:14:01 AM8/15/09
to wxPython-users

> What platform is it?  If on Windows and you have paint events that do
> not create a wx.PaintDC for the window that sent the event then change
> it so they do.  I've seen this problem before if there are paint
> handlers that don't create a PintDC or do create one but for the wrong
> window.
>
> Otherwise, it sounds like more of what we've been discussing already,
> something is preventing the normal processing of events from happening.

Hey Robin,

Firstly, yes, it's Windows.
I did not understand exactly what you mean by that PaintDC thing. All
my widgets have this line in __init__:

self.Bind(wx.EVT_PAINT, self.on_paint)

And this line in self.on_paint:

dc = wx.PaintDC(self)

After which they draw on this dc.

Can you explain exactly what you meant?

As I said before, I have three busy widgets in my app: a seek bar, a
tree browser and a state showing window. I now disabled the seek bar
and the tree browser, but the problem still happens.
The type of widget in the "state showing window" changes between
different simulation packages. I have three simulation packages. The
first one uses a state showing window that I wrote. The problem
happens when I use it. The second package uses a
wx.lib.agw.piectrl.PieCtrl in the state showing window. The problem
happens when I use it. The third one uses a text control. The problem
does NOT happen with it; The program closes immediately when I press
the X button.

Any clue?

Robin Dunn

unread,
Aug 17, 2009, 5:07:13 PM8/17/09
to wxPytho...@googlegroups.com
cool-RR wrote:
>
>> What platform is it? If on Windows and you have paint events that do
>> not create a wx.PaintDC for the window that sent the event then change
>> it so they do. I've seen this problem before if there are paint
>> handlers that don't create a PintDC or do create one but for the wrong
>> window.
>>
>> Otherwise, it sounds like more of what we've been discussing already,
>> something is preventing the normal processing of events from happening.
>
> Hey Robin,
>
> Firstly, yes, it's Windows.
> I did not understand exactly what you mean by that PaintDC thing. All
> my widgets have this line in __init__:
>
> self.Bind(wx.EVT_PAINT, self.on_paint)
>
> And this line in self.on_paint:
>
> dc = wx.PaintDC(self)
>
> After which they draw on this dc.
>
> Can you explain exactly what you meant?

Sometimes people will make the mistake of creating a paint dc for some
other window than the one the event was sent to, or not creating a paint
dc at all. This will cause Windows to resend the paint event
immediately, causing high CPU utilization and most other events to never
be processed.


>
> As I said before, I have three busy widgets in my app: a seek bar, a
> tree browser and a state showing window. I now disabled the seek bar
> and the tree browser, but the problem still happens.
> The type of widget in the "state showing window" changes between
> different simulation packages. I have three simulation packages. The
> first one uses a state showing window that I wrote. The problem
> happens when I use it. The second package uses a
> wx.lib.agw.piectrl.PieCtrl in the state showing window. The problem
> happens when I use it. The third one uses a text control. The problem
> does NOT happen with it; The program closes immediately when I press
> the X button.
>
> Any clue?

Another possibility is if the mouse is being captured and not released
until your simulation is complete...

Please continue to narrow down the source of the problem and if you
still can't figure it out create a small runnable sample that also has
the problem and send it here so we can see it too.

cool-RR

unread,
Aug 17, 2009, 7:44:02 PM8/17/09
to wxPython-users

> Sometimes people will make the mistake of creating a paint dc for some
> other window than the one the event was sent to, or not creating a paint
> dc at all. This will cause Windows to resend the paint event
> immediately, causing high CPU utilization and most other events to never
> be processed.
>

Hold on: Generally speaking, if the CPU utilization is high, let's say
almost 100% continuously, does the X button stop working?

Robin Dunn

unread,
Aug 17, 2009, 9:43:57 PM8/17/09
to wxpytho...@googlegroups.com

No, it depends on the reason why it is high. In the case I described
the subsequent paint events are being sent before any other events can
be processed.

cool-RR

unread,
Aug 18, 2009, 5:40:59 AM8/18/09
to wxPython-users

> > Hold on: Generally speaking, if the CPU utilization is high, let's say
> > almost 100% continuously, does the X button stop working?
>
> No, it depends on the reason why it is high.  In the case I described
> the subsequent paint events are being sent before any other events can
> be processed.

Okay, then generally speaking, if there are paint events continuously
posted before the previous paint events have been handled, does that
mean that the X button will not work?

Cody Precord

unread,
Aug 18, 2009, 5:59:21 AM8/18/09
to wxpytho...@googlegroups.com
Hello,

Its quite simple, if there are thousands (or more) of other events
that are waiting in line to be processed before the EVT_CLOSE that is
sent when you click the X button then it has a long time to wait
before it will be processed.


Cody

cool-RR

unread,
Aug 19, 2009, 12:16:18 PM8/19/09
to wxPython-users

> Its quite simple, if there are thousands (or more) of other events  
> that are waiting in line to be processed before the EVT_CLOSE that is  
> sent when you click the X button then it has a long time to wait  
> before it will be processed.

Cody, I don't think this is the issue here, that EVT_CLOSE has to wait
"a long time"; It seems like it's being "starved" by other events.

I open up the program, begin playback of the simulation, (making some
widgets refresh continuosly,) and then immediately press the X button.
It won't close, I even waited 20 seconds. Other widgets in the program
do respond, I can bring up menus, press the maximize/minimize buttons,
move the window around, all without the program closing. And then if I
press the button that stops the simulation playback, the program
immediately closes.
What am I supposed to do against that?

cool-RR

unread,
Aug 19, 2009, 12:31:15 PM8/19/09
to wxPython-users

> I open up the program, begin playback of the simulation, (making some
> widgets refresh continuosly,) and then immediately press the X button.
> It won't close, I even waited 20 seconds. Other widgets in the program
> do respond, I can bring up menus, press the maximize/minimize buttons,
> move the window around, all without the program closing. And then if I
> press the button that stops the simulation playback, the program
> immediately closes.
> What am I supposed to do against that?

I've attached a program that demonstrates this:

import wx
import random

NUMBER_OF_LINES = 10000 # Increase this if you were blessed with a
very strong computer

def random_point():
return (random.random()*500, random.random()*500)

class Frame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.SetDoubleBuffered(True)
window = Window(self)
self.Show()

class Window(wx.Window):
def __init__(self, *args, **kwargs):
wx.Window.__init__(self, *args, **kwargs)

self.Bind(wx.EVT_PAINT, self.on_paint)

self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer)
self.timer.Start(5)

def on_timer(self, event=None):
self.Refresh()

def on_paint(self, event=None):
dc = wx.PaintDC(self)
for i in range(NUMBER_OF_LINES):
dc.DrawLinePoint(random_point(), random_point())


if __name__ == "__main__":
app = wx.PySimpleApp()
my_frame = Frame(None)

app.MainLoop()

Cody Precord

unread,
Aug 19, 2009, 2:01:40 PM8/19/09
to wxpytho...@googlegroups.com
Hi,

On Wed, Aug 19, 2009 at 11:31 AM, cool-RR<ram.r...@gmail.com> wrote:
>
>
>> I open up the program, begin playback of the simulation, (making some
>> widgets refresh continuosly,) and then immediately press the X button.
>> It won't close, I even waited 20 seconds. Other widgets in the program
>> do respond, I can bring up menus, press the maximize/minimize buttons,
>> move the window around, all without the program closing. And then if I
>> press the button that stops the simulation playback, the program
>> immediately closes.
>> What am I supposed to do against that?

Your flooding it with too many paint events. This can be seen by when
you try to click the X button it doesn't redraw it self as pressed and
appears to be not responsive. If you slow the timer down to a more
reasonable interval of 25 milliseconds (or even 40 ms) the close
button works just fine and the animation doesn't look any different
either.


>
>    def on_paint(self, event=None):
>        dc = wx.PaintDC(self)
>        for i in range(NUMBER_OF_LINES):
>            dc.DrawLinePoint(random_point(), random_point())

p.s) I know this is just an example but it would also be more
efficient to generate a list of points and use DrawLineList instead of
calling DrawLinePoint a 1000 times.


Cody

cool-RR

unread,
Aug 19, 2009, 2:17:49 PM8/19/09
to wxPython-users
> Your flooding it with too many paint events. This can be seen by when
> you try to click the X button it doesn't redraw it self as pressed and
> appears to be not responsive. If you slow the timer down to a more
> reasonable interval of 25 milliseconds (or even 40 ms) the close
> button works just fine and the animation doesn't look any different
> either.

So is this the official wxPython answer? To limit my sending of paint
events? Is there no other way? Because if the minimize button and the
maximize button work even under a lot of paint events, I think it
should be possible for the close event to work as well.

Ram.

Nathaniel Echols

unread,
Aug 19, 2009, 2:46:04 PM8/19/09
to wxpytho...@googlegroups.com
On Wed, Aug 19, 2009 at 11:17 AM, cool-RR <ram.r...@gmail.com> wrote:
So is this the official wxPython answer? To limit my sending of paint
events? Is there no other way? Because if the minimize button and the
maximize button work even under a lot of paint events, I think it
should be possible for the close event to work as well.

 You're assuming too much about how the events are handled internally.  FWIW, that last sample worked fine on my Mac - it is definitely a little sluggish, but the lag is only about a second, and the close/minimize/maximize buttons appear to be equally responsive.  My assumption would be that you've found some bottleneck in the Windows version of wxWidgets.  However, I've also found ways to lock up my programs on Macs by sending too many events (of a completely different kind) too fast, so it's a general thing to be aware of.

-Nat

Robin Dunn

unread,
Aug 19, 2009, 2:51:06 PM8/19/09
to wxpytho...@googlegroups.com
cool-RR wrote:
>
>> I open up the program, begin playback of the simulation, (making some
>> widgets refresh continuosly,) and then immediately press the X button.
>> It won't close, I even waited 20 seconds. Other widgets in the program
>> do respond, I can bring up menus, press the maximize/minimize buttons,
>> move the window around, all without the program closing. And then if I
>> press the button that stops the simulation playback, the program
>> immediately closes.
>> What am I supposed to do against that?
>
> I've attached a program that demonstrates this:

Yep, you are starving out the idle events by not being able to process
the timer and paint events fast enough. On one of my machines the paint
in your sample takes about 0.18-0.19 of a second to complete, so with
the timer firing off faster than that means that there is no time left
to actually sit idle and let lower priority things to be processed.

The close event actually is being sent at the time that the X button is
pressed, (run the attached modified sample to see it,) but for TLWs wx
does not destroy them immediately. Instead it waits until idle time to
do so in case there are events still in the queue for the window or the
widgets it contains. (So those pending events still have a window to be
dispatched to.) By saturating the event queue it never becomes empty and
so there is never any idle event and so the Frame is not actually closed
and destroyed. If I set the timer to 200ms (slightly longer than the
time it takes to run on_paint on my machine) then it works fine. The
interesting thing is that visually it looks the same because even though
the timer's timeout is orders of magnitude different, the actual
effective frame rate is almost the same.

One possible workaround in your sample would be to stop the timer from
the frame's EVT_CLOSE handler, but you still have the problem that while
the event queue is saturated that there will be no idle events and so
anything in your app or in wx that needs idle events to work properly
(deleting TLWs, wx.CallAfter, wx.PostEvent, etc.) simply will not work.

starve.py

Cody Precord

unread,
Aug 19, 2009, 2:54:10 PM8/19/09
to wxpytho...@googlegroups.com
Hi,

1) There no need to redraw the screen 200 times a second.

2) wx is a wrapper around the native toolkit so it has to work to some
extent that way that toolkit works. IIRC In this case (your on Windows
right?) the behavior of MFC is to not process WM_CLOSE until the
message queue is empty. The queue is never getting a chance to become
empty when you are sending 200 paint events a second, so the close
message never gets processed. Which explains the behavior your seeing.

I am sorry but don't have much more energy for explaining this to in
anymore detail at this time.


Cody

cool-RR

unread,
Aug 19, 2009, 4:34:15 PM8/19/09
to wxPython-users

> Yep, you are starving out the idle events by not being able to process
> the timer and paint events fast enough.  On one of my machines the paint
> in your sample takes about 0.18-0.19 of a second to complete, so with
> the timer firing off faster than that means that there is no time left
> to actually sit idle and let lower priority things to be processed.
>
> The close event actually is being sent at the time that the X button is
> pressed, (run the attached modified sample to see it,) but for TLWs wx
> does not destroy them immediately.  Instead it waits until idle time to
> do so in case there are events still in the queue for the window or the
> widgets it contains.  (So those pending events still have a window to be
> dispatched to.) By saturating the event queue it never becomes empty and
> so there is never any idle event and so the Frame is not actually closed
> and destroyed.  If I set the timer to 200ms  (slightly longer than the
> time it takes to run on_paint on my machine) then it works fine.  The
> interesting thing is that visually it looks the same because even though
> the timer's timeout is orders of magnitude different, the actual
> effective frame rate is almost the same.
>
> One possible workaround in your sample would be to stop the timer from
> the frame's EVT_CLOSE handler, but you still have the problem that while
> the event queue is saturated that there will be no idle events and so
> anything in your app or in wx that needs idle events to work properly
> (deleting TLWs, wx.CallAfter, wx.PostEvent, etc.) simply will not work.

I'm trying to cook up a solution here - Please someone tell me whether
if I create a custom event, it will have priority higher, lower, or
identical as EVT_CLOSE.

cool-RR

unread,
Aug 19, 2009, 4:55:11 PM8/19/09
to wxPython-users

> I'm trying to cook up a solution here - Please someone tell me whether
> if I create a custom event, it will have priority higher, lower, or
> identical as EVT_CLOSE.

Okay, I think I've managed to solve the problem completely. But I had
to create a makeshift event system for it, which is not nice. When
someone will answer the above question I could maybe make the solution
nicer. Please tell me if there is any way to change the priority of a
custom event.

Nathaniel Echols

unread,
Aug 19, 2009, 5:08:01 PM8/19/09
to wxpytho...@googlegroups.com
Creating an event system from scratch to deal with this is textbook overengineering.  I also do not understand why you think you need 200fps animation here, but that's beside the point - wxPython is definitely not intended for this and doesn't claim to be.  Slowing down the timer events does not produce any noticeable difference in visual output (at least on my Mac) but will make the controls responsive again.

cool-RR

unread,
Aug 19, 2009, 5:12:05 PM8/19/09
to wxPython-users

> Creating an event system from scratch to deal with this is textbook
> overengineering.  I also do not understand why you think you need 200fps
> animation here, but that's beside the point - wxPython is definitely not
> intended for this and doesn't claim to be.  Slowing down the timer events
> does not produce any noticeable difference in visual output (at least on my
> Mac) but will make the controls responsive again.

In case it was not obvious, the 200fps animation was an intentional
exaggeration. Creating applications that show random lines on the
screen at 200fps is not my goal. I am trying to show a simulation. Now
I implemented a solution, in which the app starts drawing the next
frame only after the last frame has finished drawing. For that I
created a small event system. Now I wish someone would show me a way
to set event priority in wxPython so I could ditch my event system and
use wxPython's.

Robin Dunn

unread,
Aug 19, 2009, 7:11:47 PM8/19/09
to wxpytho...@googlegroups.com

No, from the wx perspective they (the system events and most wx events)
are all the same and are processed in the order that they arrive unless
they are sent in a pending state to begin with. (At the platform level
that is not always true, but unless you'll be sending custom platform
events using platform APIs instead of wx events that won't matter much.)

To help boil it all down to something that is hopefully more
comprehensible, this is what the basic high level pseudo code of the
MainLoop looks like:

---------------------------------------------------------------------

loop until time to exit:

while there are pending system events:
dispatch system events to wx windows
[ this translates the system message to a wxEvent and goes
through the flow described in Figure 3.3 in the book. ]

process pending wx events
[ These are events sent with wx.PostEvent, AddPendingEvent(),
the event used internally by wx.CallAfter, some other
events used internally by wx, etc. ]

send an idle event(s) to the app's window(s)

send an idle event to the app object

delete objects in the pending delete list
[ like TLWs that have been Destroy()'d ]

flush any waiting wxLog messages

---------------------------------------------------------------------

So as you can see it is very important that the inner while loop is able
to exit once in a while so the rest of the outer loop can run. But your
sample, (and your application I expect,) was essentially causing the
inner loop to never exit so all the rest of the outer loop never happened.

One important point to realize is that for any of the events
sent/dispatched in the above code that while the sending or handling of
that event is taking place then all the rest of the code above is on
hold until the sending and handling of the event is complete and no
other events can be processed in that time. This is why long[1] running
tasks in event handlers are so problematic.

To put that in terms of your sample, the inner loop above is blocked
while processing the paint event and while it is waiting for that to be
finished Windows adds another timer message to the message queue. When
the paint event handler returns then the test for pending system events
is true and so the timer event is fetched and processed. It calls
Refresh and since the message queue is currently empty Windows goes
ahead and adds a paint message to the queue immediately. Then when
control returns to the inner loop it sees the paint message and
processes it, and while it is doing so another timer message is sent...
And you're stuck forever in in that inner loop until something stops
the cycle.

So, how to apply that to your real app? I can't be sure since I still
don't have a real clear picture of what you are doing, but I would start by:

* Optimize your paint handlers. If you do a little buffering then as I
mentioned the other day your paint handler can simply be a single
DrawBitmap call. If you implement the concept of currentFrame and
nextFrame then on_paint can just always draw currentFrame while your
worker-thread/idle/timer/whatever code is working on number crunching
and drawing the nextFrame bitmap. When it's done it just needs to do
something like "currentFrame = nextFrame; nextFrame = new bitmap; call
Refresh" The benefit of this approach is that refreshes caused by other
windows, dragging the app windows, etc. will not have to go through the
overhead of redrawing all the contents of the current animation frame,
they just draw currentFrame again.

* Decide if the user really needs to see every frame. I expect that if
you're running at slow speed through the simulation then they'll
want/need to see more of the frames, but if they want so go fast then it
will be okay to not show them absolutely everything. If so then do
something like drop every Nth frame of the data stream before going to
the overhead of drawing that frame's bitmap, where N is based on the
speed of the playback of the simulation. You should be able to also
base this on how long it takes the code that draws each bitmap to do its
job. IOW, your displayed FPS will be approximately the same no matter
what the simulation playback speed is, only the number of hops that it
takes to go through the whole simulation will change.

* Don't outsmart yourself. I've found in GUI programming that often
things come down to a choice between perfection and what is good enough,
where perfection may not be possible or may take orders of magnitude of
time and/or complexity more than good-enough. How good I can do
something right now usually falls somewhere between those two points,
but all the user really needs is the good-enough level. Doing better
than that can come later if it really is necessary.


[1] 'long' is relative to the type of event and other factors within the
app.

Chris Barker

unread,
Aug 19, 2009, 11:21:59 PM8/19/09
to wxpytho...@googlegroups.com
cool-RR wrote:
> In case it was not obvious, the 200fps animation was an intentional
> exaggeration.

but also the cause of your problem. IN any case, frame rate is limited by:

1) How fast you can draw each frame
2) How fast the screen refreshes (and/or how fast people can see changes
-- 200fps is pointless for all these reasons.

There is no point in trying to go faster than that -- it will just
create bottle necks, as you've seen.

If it really takes to long to draw each frame to get the frame rate you
need, then you need to figure out how to speed that up. Robin gave you
some good idea, and if you post more detail of what you need to draw, we
may be able to help more. If you really want smooth animation, maybe
OpenGL is the way to go.

> I implemented a solution, in which the app starts drawing the next
> frame only after the last frame has finished drawing. For that I
> created a small event system.

Could you just use a one-shot timer or wx.CallAfter to set off the next
frame after the current one is done?

Also note that X-windows is asynchronous, so even when the drawing calls
return, the rendering may not be done. Last I tried this was not the
case on Windows.

> Now I wish someone would show me a way
> to set event priority in wxPython so I could ditch my event system and
> use wxPython's.

You can't -- and you were told that a while ago. When you keep not
getting the answer you want, it probably means you're asking the wrong
question.

cool-RR

unread,
Aug 20, 2009, 4:35:37 AM8/20/09
to wxPython-users

> > I implemented a solution, in which the app starts drawing the next
> > frame only after the last frame has finished drawing. For that I
> > created a small event system.
>
> Could you just use a one-shot timer or wx.CallAfter to set off the next
> frame after the current one is done?

No, I have to make sure all the events from the entire program have
been handled.

cool-RR

unread,
Aug 20, 2009, 4:44:16 AM8/20/09
to wxPython-users
Thanks for the detailed explanation Robin. If I can't change event
priority, it seems like the makeshift event system will have to stay.

This is how my solution works. In __init__, I have this:


self.stuff_to_do_when_idle = queue.Queue()


In my idle handler, I have this:


try:
mission = self.stuff_to_do_when_idle.get(block=False)
mission()
event.RequestMore(True)
except queue.Empty:
pass



In my __play_next, which is the function called repeatedly to show the
animation, I have this:



def mission():
self.timer_for_playing = wx.FutureCall(self.delay*1000, \
functools.partial
(self.__play_next,\
next_node))
self.stuff_to_do_when_idle.put(mission)

That's it. The X button is responsive, and I don't have to worry about
my paint handlers being too heavy.

Thanks everyone for your patient help in this matter.

Ram.
Reply all
Reply to author
Forward
0 new messages