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
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
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
>> 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
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
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
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
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.
>>> 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.
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.
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
> 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.
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.
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.
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.
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
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
So is this the official wxPython answer? To limit my sending of paintevents? 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.
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.
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
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.
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.