Something like Refresh but not

25 views
Skip to first unread message

cool-RR

unread,
Apr 10, 2010, 3:03:29 AM4/10/10
to wxPython-users
I noticed a nice feature of `Window.Refresh()`: I can call Refresh to
some window from several different places, and all the "refresh
requests" will get collapsed into one, and only one `OnPaint` will
take place.

Is there some way to do it without causing the window to paint? i.e.
Something that has this same feature, just not refreshing the window.

Ram.

cool-RR

unread,
Apr 10, 2010, 3:07:51 AM4/10/10
to wxPython-users
And also a question about `Refresh` while I'm at it: If my `OnPaint`
calls something that calls `Refresh`, will it cause another `OnPaint`?

Ram.

Andrea Gavana

unread,
Apr 10, 2010, 6:29:04 AM4/10/10
to wxpytho...@googlegroups.com
Hi,

I honestly don't understand... why would you call Refresh() if you
don't want to re-paint the window? What are you trying to do?

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/

==> Never *EVER* use RemovalGroup for your house removal. You'll
regret it forever.
http://thedoomedcity.blogspot.com/2010/03/removal-group-nightmare.html <==

Andrea Gavana

unread,
Apr 10, 2010, 6:30:26 AM4/10/10
to wxpytho...@googlegroups.com
Hi,

On 10 April 2010 08:07, cool-RR wrote:
> And also a question about `Refresh` while I'm at it: If my `OnPaint`
> calls something that calls `Refresh`, will it cause another `OnPaint`?

Don't do that: anything in the OnPaint should not trigger another
refresh in the window, or you could end up in an infinite refreshing
loop that slowly eats up all your CPU.

cool-RR

unread,
Apr 10, 2010, 6:34:07 AM4/10/10
to wxpython-users
On Sat, Apr 10, 2010 at 12:30 PM, Andrea Gavana <andrea...@gmail.com> wrote:
Hi,

On 10 April 2010 08:07, cool-RR wrote:
> And also a question about `Refresh` while I'm at it: If my `OnPaint`
> calls something that calls `Refresh`, will it cause another `OnPaint`?

Don't do that: anything in the OnPaint should not trigger another
refresh in the window, or you could end up in an infinite refreshing
loop that slowly eats up all your CPU.

Andrea.


Okay, will avoid, thanks.

Ram.

cool-RR

unread,
Apr 10, 2010, 6:36:17 AM4/10/10
to wxPython-users, Andrea Gavana
On Sat, Apr 10, 2010 at 12:29 PM, Andrea Gavana <andrea...@gmail.com> wrote:
Hi,

On 10 April 2010 08:03, cool-RR wrote:
> I noticed a nice feature of `Window.Refresh()`: I can call Refresh to
> some window from several different places, and all the "refresh
> requests" will get collapsed into one, and only one `OnPaint` will
> take place.
>
> Is there some way to do it without causing the window to paint? i.e.
> Something that has this same feature, just not refreshing the window.

I honestly don't understand... why would you call Refresh() if you
don't want to re-paint the window? What are you trying to do?

Andrea.

Sorry, I'll clarify. What I'm trying to do is have some function that will check some variable to see if the widget needs to change its appearance ("Recalculate"). If so, it will raise a flag called `recaluclate_flag`, and it'll call Refresh. Refresh will cause OnPaint to start, and OnPaint will check if the `recaluclate_flag` is up, if so it'll recalculate. In any case it will redraw.

So I want to have a function that'll check if the widget needs to be recalculated, and if so it raises the flag and calls refresh. But I don't want to be calling this function directly. I want to "call" it the same way you "call" OnPaint when you do Refresh. So I could "call" it several times from several different places, and it'll execute just once, the same way it works with OnPaint,

Ram.

Andrea Gavana

unread,
Apr 10, 2010, 6:43:19 AM4/10/10
to cool-RR, wxPython-users
Hi,

Ah, OK, I got it. Uhm, not sure if you can do it easily
(Refresh/OnPaint rely on the wxWidgets event system and ultimately to
the platform, which will decide if and when the window is "damaged"
and requires a repainting). However, you could still do something like
this:

def OnPaint(self, event):

# Create wx.PaintDC or friends

if not self.needs_recalculation:
return

self.Recalculate()

# Re-draw things

cool-RR

unread,
Apr 10, 2010, 6:47:04 AM4/10/10
to wxpython-users, Andrea Gavana
I got the impression there's something more than just events here. I tried having a custom event with an handler, then calling it 100 times and seeing if it'll "collapse" all the events to one handling. It didn't, it did 100 handlings.

But when I did 100 times Refresh, it did just 1 OnPaint. That's the behavior I'm looking for.
 
However, you could still do something like
this:

def OnPaint(self, event):

   # Create wx.PaintDC or friends

   if not self.needs_recalculation:
       return

   self.Recalculate()

   # Re-draw things


Andrea.


If I do this, I think it will fail when the widget doesn't need to be recalculated, just redrawn.

Ram.

Andrea Gavana

unread,
Apr 10, 2010, 6:55:08 AM4/10/10
to cool-RR, wxPython-users
Hi,

Yes, the platform: it's the OS which will decide if the window needs
refreshing or not: you can call Refresh() a million time, but if you
don't use Update() right before/after the Refresh() call, the OS will
not redraw your window unless it has been "damaged" or covered or
changed in size or whatever.

>>
>> However, you could still do something like
>> this:
>>
>> def OnPaint(self, event):
>>
>>    # Create wx.PaintDC or friends
>>
>>    if not self.needs_recalculation:
>>        return
>>
>>    self.Recalculate()
>>
>>    # Re-draw things
>>
>>
>> Andrea.
>>
>
> If I do this, I think it will fail when the widget doesn't need to be
> recalculated, just redrawn.

Uhm, how about:

def OnPaint(self, event):

# Create wx.PaintDC or friends

if self.needs_recalculation:
self.Recalculate()

# Re-draw things

?

cool-RR

unread,
Apr 10, 2010, 7:01:11 AM4/10/10
to wxpython-users, Andrea Gavana
On Sat, Apr 10, 2010 at 12:55 PM, Andrea Gavana <andrea...@gmail.com> wrote:
 
>> Ah, OK, I got it. Uhm, not sure if you can do it easily
>> (Refresh/OnPaint rely on the wxWidgets event system and ultimately to
>> the platform, which will decide if and when the window is "damaged"
>> and requires a repainting).
>
> I got the impression there's something more than just events here.

Yes, the platform: it's the OS which will decide if the window needs
refreshing or not: you can call Refresh() a million time, but if you
don't use Update() right before/after the Refresh() call, the OS will
not redraw your window unless it has been "damaged" or covered or
changed in size or whatever.

What do you mean? Let's say I have some panel that's sitting still and not being interrupted/damaged/covered. It won't refresh. Then some code does `panel.Refresh()`. It will cause that panel to refresh, no?
 
 
>>
>> However, you could still do something like
>> this:
>>
>> def OnPaint(self, event):
>>
>>    # Create wx.PaintDC or friends
>>
>>    if not self.needs_recalculation:
>>        return
>>
>>    self.Recalculate()
>>
>>    # Re-draw things
>>
>>
>> Andrea.
>>
>
> If I do this, I think it will fail when the widget doesn't need to be
> recalculated, just redrawn.

Uhm, how about:

def OnPaint(self, event):

  # Create wx.PaintDC or friends

  if self.needs_recalculation:
      self.Recalculate()

  # Re-draw things

?

Andrea.

This is what I have now. The problem is not there, but in the code that raises the `needs_calculation` flag. I want to have a function that checks if we need calculation, and if so raise the flag and call refresh. But I want to be able to "call" this function the way Refresh "calls" OnPaint.

Ram.

Andrea Gavana

unread,
Apr 10, 2010, 12:55:05 PM4/10/10
to cool-RR, wxPython-users
Hi,

On 10 April 2010 12:01, cool-RR wrote:
>
>
> On Sat, Apr 10, 2010 at 12:55 PM, Andrea Gavana <andrea...@gmail.com>
> wrote:
>>
>>
>>
>> >> Ah, OK, I got it. Uhm, not sure if you can do it easily
>> >> (Refresh/OnPaint rely on the wxWidgets event system and ultimately to
>> >> the platform, which will decide if and when the window is "damaged"
>> >> and requires a repainting).
>> >
>> > I got the impression there's something more than just events here.
>>
>> Yes, the platform: it's the OS which will decide if the window needs
>> refreshing or not: you can call Refresh() a million time, but if you
>> don't use Update() right before/after the Refresh() call, the OS will
>> not redraw your window unless it has been "damaged" or covered or
>> changed in size or whatever.
>
> What do you mean? Let's say I have some panel that's sitting still and not
> being interrupted/damaged/covered. It won't refresh. Then some code does
> `panel.Refresh()`. It will cause that panel to refresh, no?

Sorry, what I meant is: Refresh will raise a request for a window
refresh to wx and to the platform, but the refreshing will not happen
until the next event loop iteration:

"""
virtual void wxWindow::Refresh ( bool eraseBackground = true, const
wxRect * rect = NULL ) [virtual]

Causes this window, and all of its children recursively (except under
wxGTK1 where this is not implemented), to be repainted.

Note that repainting doesn't happen immediately but only during the
next event loop iteration, if you need to update the window
immediately you should use Update() instead
"""

Moreover, if you are using double-buffering or if the platform uses
double-buffering implicitly (like Mac and possibly Windows 7, don't
know about Linux), I doubt it will redraw your window anyway *unless*
something happened to the window (content changed, resized, damaged,
whatever).

>> Uhm, how about:
>>
>> def OnPaint(self, event):
>>
>>   # Create wx.PaintDC or friends
>>
>>   if self.needs_recalculation:
>>       self.Recalculate()
>>
>>   # Re-draw things
>>
>> ?
>>
>> Andrea.
>
> This is what I have now. The problem is not there, but in the code that
> raises the `needs_calculation` flag. I want to have a function that checks
> if we need calculation, and if so raise the flag and call refresh. But I
> want to be able to "call" this function the way Refresh "calls" OnPaint.

Sorry, I am still confused: how is different to do what you ask and to
do what I posted above? You simply call Refresh(), and if there is
need for a re-calculation, the OnPaint method will take care of this
too. I used this kind of strategy myself in some of the AGW widgets,
even outside the OnPaint handler (for example, in a wx.EVT_IDLE event
handler).

cool-RR

unread,
Apr 10, 2010, 1:11:06 PM4/10/10
to Andrea Gavana, wxPython-users
I see. 

 
>> Uhm, how about:
>>
>> def OnPaint(self, event):
>>
>>   # Create wx.PaintDC or friends
>>
>>   if self.needs_recalculation:
>>       self.Recalculate()
>>
>>   # Re-draw things
>>
>> ?
>>
>> Andrea.
>
> This is what I have now. The problem is not there, but in the code that
> raises the `needs_calculation` flag. I want to have a function that checks
> if we need calculation, and if so raise the flag and call refresh. But I
> want to be able to "call" this function the way Refresh "calls" OnPaint.

Sorry, I am still confused: how is different to do what you ask and to
do what I posted above? You simply call Refresh(), and if there is
need for a re-calculation, the OnPaint method will take care of this
too. I used this kind of strategy myself in some of the AGW widgets,
even outside the OnPaint handler (for example, in a wx.EVT_IDLE event
handler).

Andrea.

I'll give you more specific details. There's a variable `pseudoclock` somewhere in my program, and there's a widget `ScratchWheel`. When something changes `pseudoclock`, the `ScratchWheel` *might* need to redraw, and might not. So after every change to `pseudoclock`, I want to check if `ScratchWheel` needs to redraw. So I want to recalculate, but redraw only if the recalculation says we need to redraw.

So what do I do? If I just raise the recalculation flag and call `Refresh`, it's possible that no redraw is needed and the `ScratchWheel` will just waste time refreshing. If I call recalculate directly after changing `pseudoclock`, and then have the recalculation function call `Refresh` if a redraw is needed, then this is a problem too, cause I could have many `pseudoclock` changes happening rapidly, and I don't want to run the recalculate function for every one of them. When the `pseudoclock` changes, I want to call a function that is to the `Recalculate` function what `Refresh` is to the `OnPaint` function. i.e., a function that even if it's called 100 times, the `Recalculate` function will execute only once.

Ram.

Andrea Gavana

unread,
Apr 10, 2010, 1:31:54 PM4/10/10
to cool-RR, wxPython-users

OK, I understand. It seems to very overly complex, but there might be
a solution. I am not sure it will fit your problem but it's worth a
try. If your ScratchWheel is an instance of wx.PyControl (or
wx.PyPanel, or wx.PyWindow or any other wx.PySomething), my suggestion
is to define a method called OnInternalIdle inside your class. This
method gets called by wxWidgets/wxPython when the control is sitting
idle, and I have used it myself for CustomTreeCtrl to check if the
widget needed recalculation of items' positions, or refreshing, or
scrollbars adjustments and so on:

def OnInternalIdle(self):
"""
This method is normally only used internally, but sometimes an
application
may need it to implement functionality that should not be disabled by an
application defining an `OnIdle` handler in a derived class.

This method may be used to do delayed painting, for example, and most
implementations call `wx.Window.UpdateWindowUI` in order to
send update events
to the window in idle time.
"""

# after all changes have been done to the tree control,
# we actually redraw the tree when everything is over

if not self._needs_recalculation and not self._needs_refresh:
return

if self._needs_recalculation:
self._needs_recalculation = False
self.Recalculate()

if self._needs_refresh:
self.Refresh()
self._needs_refresh = False


Basically, what you should do is to change your
self._needs_recalculation and self._needs_refresh flag any (and every)
time you wish (based on your pseudoclock variable), then at some point
in the wxPython event loop wxPython will call this OnInternalIdle
method and redraw/recalculate your widget at idle time. I couldn't
find any performance issue with CustomTreeCtrl using this method, it
is very reliable and it doesn't waste memory/cpu.

cool-RR

unread,
Apr 10, 2010, 3:15:17 PM4/10/10
to wxpytho...@googlegroups.com, Andrea Gavana
Interesting. So is it called after every loop?

I'll start implementing.

Ram.

cool-RR

unread,
Apr 10, 2010, 3:38:32 PM4/10/10
to wxpytho...@googlegroups.com, Andrea Gavana
I tried it now. OnInternalIdle gets called infrequently. My app is frequently busy because it's rendering the simulation. I have seen that only once in a while `OnInternalIdle` gets called, and the widget doesn't get refreshed frequently enough.

I may just stick with Refresh if you there's no good solution...

Ram.

Robin Dunn

unread,
Apr 10, 2010, 3:52:35 PM4/10/10
to wxpytho...@googlegroups.com
On 4/10/10 10:31 AM, Andrea Gavana wrote:

> OK, I understand. It seems to very overly complex, but there might be
> a solution. I am not sure it will fit your problem but it's worth a
> try. If your ScratchWheel is an instance of wx.PyControl (or
> wx.PyPanel, or wx.PyWindow or any other wx.PySomething),

And if it isn't then you can do the same approach with a EVT_IDLE
handler. This is a fairly common pattern: set a flag from where ever,
check the flag in an idle handler, if it is set do something and clear
the flag.

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

cool-RR

unread,
Apr 10, 2010, 4:03:49 PM4/10/10
to wxpython-users, robin, Andrea Gavana
Assuming the app gets idle. And in my app I can't assume it, because it's doing animation.

P.S., I'm gonna be hiking now until about Wed., so I'll look at any other suggestions when I return.

Thanks for your help,
Ram.

cool-RR

unread,
Apr 16, 2010, 7:23:38 AM4/16/10
to wxpython-users, robin, Andrea Gavana
Hey all, I'm back from hiking.

Is there a way to have some action run after every iteration of wxPython's event loop? I think this would solve this problem, and would be very helpful in some other problems.

Ram.

--
To unsubscribe, send email to wxPython-user...@googlegroups.com
or visit http://groups.google.com/group/wxPython-users?hl=en

cool-RR

unread,
Apr 18, 2010, 9:39:44 AM4/18/10
to Robin Dunn, wxPython-users
On Fri, Apr 16, 2010 at 5:50 PM, Robin Dunn <ro...@alldunn.com> wrote:
On 4/16/10 4:23 AM, cool-RR wrote:
Is there a way to have some action run after every iteration of
wxPython's event loop? I think this would solve this problem, and would
be very helpful in some other problems.

I think the closest thing we have to that is to override FilterEvent in your derived wx.App class.  As it's name implies it is intended to use for filtering events, so it is actually called before each event is processed, but it should give you what you want.  Just be sure to return -1 so the events will be processed normally.  You'll also need to call SetCallFilterEvent(True) to turn on calls to your FilterEvent method.
--
Robin Dunn

So this will get called for every event being processed? Won't this slow everything up? Also, how do I hack this into something that gets called once after every iteration of the loop?

Ram.
 

cool-RR

unread,
Apr 20, 2010, 8:18:21 AM4/20/10
to wxpython-users, Andrea Gavana
On Sat, Apr 10, 2010 at 12:43 PM, Andrea Gavana <andrea...@gmail.com> wrote:
Hi,
<snip>
(Refresh/OnPaint rely on the wxWidgets event system and ultimately to
the platform, which will decide if and when the window is "damaged"
and requires a repainting).

Is there any way to force a refresh? I mean, cause the `on_paint` to be called regardless of what the OS considers dirty?

Ram.

Andrea Gavana

unread,
Apr 20, 2010, 8:21:25 AM4/20/10
to cool-RR, wxpython-users
Hi,

On 20 April 2010 13:18, cool-RR wrote:
> On Sat, Apr 10, 2010 at 12:43 PM, Andrea Gavana <andrea...@gmail.com>
> wrote:
>>
>> Hi,
>> <snip>
>> (Refresh/OnPaint rely on the wxWidgets event system and ultimately to
>> the platform, which will decide if and when the window is "damaged"
>> and requires a repainting).
>
> Is there any way to force a refresh? I mean, cause the `on_paint` to be
> called regardless of what the OS considers dirty?

Try this:

self.Refresh()
self.Update()

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/

==> Never *EVER* use RemovalGroup for your house removal. You'll
regret it forever.
http://thedoomedcity.blogspot.com/2010/03/removal-group-nightmare.html <==

cool-RR

unread,
Apr 20, 2010, 8:31:45 AM4/20/10
to Andrea Gavana, wxPython-users
On Tue, Apr 20, 2010 at 2:21 PM, Andrea Gavana <andrea...@gmail.com> wrote:
> Is there any way to force a refresh? I mean, cause the `on_paint` to be
> called regardless of what the OS considers dirty?

Try this:

self.Refresh()
self.Update()

Andrea.

Tried it now, didn't work. I have a pane in PyAUI on Ubuntu, and it's getting redrawn only when I resize the window. I am trying to call `Refresh` and `Update` on it, but it won't get redrawn. <Searches for home addresses of GTK developers in order to dispatch Samurais.>

Ram.

Robin Dunn

unread,
Apr 20, 2010, 3:33:17 PM4/20/10
to wxpytho...@googlegroups.com
On 4/20/10 5:31 AM, cool-RR wrote:
>
>
> On Tue, Apr 20, 2010 at 2:21 PM, Andrea Gavana <andrea...@gmail.com
> <mailto:andrea...@gmail.com>> wrote:
>
> > Is there any way to force a refresh? I mean, cause the `on_paint`
> to be
> > called regardless of what the OS considers dirty?
>
> Try this:
>
> self.Refresh()
> self.Update()
>
> Andrea.
>
>
> Tried it now, didn't work. I have a pane in PyAUI on Ubuntu, and it's
> getting redrawn only when I resize the window. I am trying to call
> `Refresh` and `Update` on it, but it won't get redrawn.

Is it not redrawing or is it not recalculating the layout of child
widgets? (I ask because some people seem to confuse these two things.)

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

cool-RR

unread,
Apr 22, 2010, 5:36:21 PM4/22/10
to Robin Dunn, wxPython-users
On Tue, Apr 20, 2010 at 9:36 PM, Robin Dunn <ro...@alldunn.com> wrote:
On 4/18/10 6:39 AM, cool-RR wrote:
On Fri, Apr 16, 2010 at 5:50 PM, Robin Dunn <ro...@alldunn.com
<mailto:ro...@alldunn.com>> wrote:

   On 4/16/10 4:23 AM, cool-RR wrote:

       Is there a way to have some action run after every iteration of
       wxPython's event loop? I think this would solve this problem,
       and would
       be very helpful in some other problems.


   I think the closest thing we have to that is to override FilterEvent
   in your derived wx.App class.  As it's name implies it is intended
   to use for filtering events, so it is actually called before each
   event is processed, but it should give you what you want.  Just be
   sure to return -1 so the events will be processed normally.  You'll
   also need to call SetCallFilterEvent(True) to turn on calls to your
   FilterEvent method.
   --
   Robin Dunn


So this will get called for every event being processed?

Yes.



Won't this slow
everything up?

Perhaps (that is why you have to call a method to explicitly turn it on) but probably about the same as implementing your own MainLoop would.



Also, how do I hack this into something that gets called
once after every iteration of the loop?

Derive a class from wx.App.  Give it a FilterEvent method.  Do whatever you  need from there.  Return -1.


--
Robin Dunn

Right, thanks.

Ram. 
 

Reply all
Reply to author
Forward
0 new messages