Using a Double Buffer and Drawing Normal Objects

Hello -

In an attempt to eliminate flicker from my application, I have
implemented double buffering just like Chris Barker's wiki page.

I apologize in advance if this is such a no-brainier question, but how
can I get stock objects (like a wx.Button) to be drawn on the
wx.BufferedPaintDC ? How can I specify their device context to use
for the painting?

Right now I am setting up the buffer, copying it to the screen and
then the MediaCtrl is updating the on-screen buffer so it appears to
work just fine. However, the problem I have is as soon as I press
down on one of my customer double buffered Volume Up or Volume Down
buttons, the screen gets refreshed to show movement in the button and
the media player (since it was using the on screen buffer) gets
overwritten when the entire double buffer is Blit() to the screen.

I have tried Refreshing the media player after I call the Draw()
method of all my double buffered objects, but I get a very noticeable
flicker.

Another thing I am not totally clear on is why the entire screen is
getting copied over on the paint instead of just invalidating the
rectangle around my button and only refreshing that portion? Instead,
my entire screen is being refreshed with the contents of the double
buffer.

Your help would be greatly appreciated, thank you.

John

John wrote:

Hello -

In an attempt to eliminate flicker from my application, I have
implemented double buffering just like Chris Barker's wiki page.

I apologize in advance if this is such a no-brainier question, but how
can I get stock objects (like a wx.Button) to be drawn on the
wx.BufferedPaintDC ? How can I specify their device context to use
for the painting?

You can't. However since they are children of the window where you do the double buffered drawing (if not then that is probably another problem) then the area where the child widget is located should be clipped out by default when drawing the buffer for the parent since the wx.CLIP_CHILDREN style is turned on by default now.

Right now I am setting up the buffer, copying it to the screen and
then the MediaCtrl is updating the on-screen buffer so it appears to
work just fine. However, the problem I have is as soon as I press
down on one of my customer double buffered Volume Up or Volume Down
buttons, the screen gets refreshed to show movement in the button and
the media player (since it was using the on screen buffer) gets
overwritten when the entire double buffer is Blit() to the screen.

I have tried Refreshing the media player after I call the Draw()
method of all my double buffered objects, but I get a very noticeable
flicker.

Another thing I am not totally clear on is why the entire screen is
getting copied over on the paint instead of just invalidating the
rectangle around my button and only refreshing that portion? Instead,
my entire screen is being refreshed with the contents of the double
buffer.

If you are doing a plain Refresh() then the whole window is invalidated and redrawn when the paint event happens. If you instead pass a rectangle to Refresh or RefreshRect then only that portion (and any others that are invalidated in the meantime) will be drawn in the next paint event.

···

--
Robin Dunn
Software Craftsman

You can’t. However since they are children of the window where you do

the double buffered drawing (if not then that is probably another

problem) then the area where the child widget is located should be

clipped out by default when drawing the buffer for the parent since the

wx.CLIP_CHILDREN style is turned on by default now.

Ok, I was afraid of this. I think I have the style setup correctly, but I have attached my source. If you download the three files and run the python script, it is apparent what I am trying to overcome.

When you press the blue button, I am calling the Draw() for the button so that it can be rendered as depressed. I am just doing an offset on the button by (2,2) and then drawing it greyscale to show it is pressed.

Upon doing the draw, I am seeing the media window overwritten and it disappears until the 5 second video loop starts again. Obviously, I would prefer to have the video remain playing.

Right now I am setting up the buffer, copying it to the screen and

then the MediaCtrl is updating the on-screen buffer so it appears to

work just fine. However, the problem I have is as soon as I press

down on one of my customer double buffered Volume Up or Volume Down

buttons, the screen gets refreshed to show movement in the button and

the media player (since it was using the on screen buffer) gets

overwritten when the entire double buffer is Blit() to the screen.

I have tried Refreshing the media player after I call the Draw()

method of all my double buffered objects, but I get a very noticeable

flicker.

Another thing I am not totally clear on is why the entire screen is

getting copied over on the paint instead of just invalidating the

rectangle around my button and only refreshing that portion? Instead,

my entire screen is being refreshed with the contents of the double

buffer.

If you are doing a plain Refresh() then the whole window is invalidated

and redrawn when the paint event happens. If you instead pass a

rectangle to Refresh or RefreshRect then only that portion (and any

others that are invalidated in the meantime) will be drawn in the next

paint event.

I am not doing any type of Refresh. Instead I am using the wx.BufferedPaintDC which I believe is copied to the screen once I stop writing to it, correct? Is there any way to do a buffer for just part of the screen instead of the whole one?

I know there are several ways to skin a cat, but I am worried that with
my resouce limitations whether or not wxPython has too much overhead
involved with it. This problem is surfacing because I am running on a resource limited machine.

Is a solution to my problem to do an individual wx.BufferedPaintDC for each
widget on my screen? But if I go that route, I am right back where I started with
wanting avoid flicker or non-simutaneous redraw of objects.

Any insight on how I could get around this problem would be greatly appreciated.

Thank you,

John

sample2.py (7.63 KB)

btn.jpg

One more item to consider, this sample application was obviously just that. It only has one button. The application I am working on is a touchscreen app that has 10-12 buttons on the screen at once. When you have that many buttons, on the hardware I am working with, you see the flicker or redraw problems surface.

Those problems have been avoided so far with the wx.BufferPaintDC except for the problem I have with the media control being overwritten.

If there is a way I can clip the DC or only copy a portion of it, that would be really beneficial.

Thanks,

John

···

On Tue, Aug 25, 2009 at 8:58 PM, John W jmw136@gmail.com wrote:

You can’t. However since they are children of the window where you do

the double buffered drawing (if not then that is probably another

problem) then the area where the child widget is located should be

clipped out by default when drawing the buffer for the parent since the

wx.CLIP_CHILDREN style is turned on by default now.

Ok, I was afraid of this. I think I have the style setup correctly, but I have attached my source. If you download the three files and run the python script, it is apparent what I am trying to overcome.

When you press the blue button, I am calling the Draw() for the button so that it can be rendered as depressed. I am just doing an offset on the button by (2,2) and then drawing it greyscale to show it is pressed.

Upon doing the draw, I am seeing the media window overwritten and it disappears until the 5 second video loop starts again. Obviously, I would prefer to have the video remain playing.

Right now I am setting up the buffer, copying it to the screen and

then the MediaCtrl is updating the on-screen buffer so it appears to

work just fine. However, the problem I have is as soon as I press

down on one of my customer double buffered Volume Up or Volume Down

buttons, the screen gets refreshed to show movement in the button and

the media player (since it was using the on screen buffer) gets

overwritten when the entire double buffer is Blit() to the screen.

I have tried Refreshing the media player after I call the Draw()

method of all my double buffered objects, but I get a very noticeable

flicker.

Another thing I am not totally clear on is why the entire screen is

getting copied over on the paint instead of just invalidating the

rectangle around my button and only refreshing that portion? Instead,

my entire screen is being refreshed with the contents of the double

buffer.

If you are doing a plain Refresh() then the whole window is invalidated

and redrawn when the paint event happens. If you instead pass a

rectangle to Refresh or RefreshRect then only that portion (and any

others that are invalidated in the meantime) will be drawn in the next

paint event.

I am not doing any type of Refresh. Instead I am using the wx.BufferedPaintDC which I believe is copied to the screen once I stop writing to it, correct? Is there any way to do a buffer for just part of the screen instead of the whole one?

I know there are several ways to skin a cat, but I am worried that with
my resouce limitations whether or not wxPython has too much overhead
involved with it. This problem is surfacing because I am running on a resource limited machine.

Is a solution to my problem to do an individual wx.BufferedPaintDC for each
widget on my screen? But if I go that route, I am right back where I started with
wanting avoid flicker or non-simutaneous redraw of objects.

Any insight on how I could get around this problem would be greatly appreciated.

Thank you,

John

John W wrote:

Ok, I was afraid of this. I think I have the style setup correctly, but I have attached my source.

I tried this on OS-X, and it didn't work at all -- I got the circle shown (your button bitmap), but non events were fired when I clicked on it, and I didn't see anything else. Looking at the code, I see:

self.button = LcdButton( self, "HomeBtn", self.bmp3,
                          pos=(290, 500),size=( self.bmp3.GetWidth(),
                          self.bmp3.GetHeight()) )

that is putting the button on the frame, which then is under the DrawWindow, which may explain why it's not getting mouse events. I moved it and changed it to:

         self.button = LcdButton( self.Window, "HomeBtn", ...

but then got:

   File "sample2.py", line 165, in Draw
     self.parent.button.Draw( dc )
AttributeError: 'MainWndFrame' object has no attribute 'button'

which makes sense, as it is trying to Draw when created, and teh button hasn't been put there yet. I changed that to

         try:
             self.parent.button.Draw( dc )
         except AttributeError:
             pass

which is a total kludge, but I'm trying to get it working ...

Then I added:

         self.Window.UpdateDrawing()

after creating the button.

which lead to:

     self.parent.Window.UpdateDrawing()
AttributeError: 'DrawWindow' object has no attribute 'Window'

again -- refencding things by parent etc, is fragile! changed that to:

         self.parent.UpdateDrawing()

Now the button works, but I don't see the media player, which is probably a similar issue -- you are putting the media player and the DrawWindow on top of each-other on the Frame:

so I did some re-positioning:

         self.Window = DrawWindow(self, pos=(0,490), size=(600, 100))
         self.button = LcdButton( self.Window, "HomeBtn", self.bmp3, pos=(100, 10),size=( self.bmp3.GetWidth(), self.bmp3.GetHeight()) )

but I still can't see the media player. I'm out of time now, but a few thoughts:

Why are you piling this all on top of each-other? this looks pretty straightfroward, actually. You could put a sizer in teh frame, and put the media palyer and whatever buttons you need in that sizer. UNless you are trying to put the buttons on top of the media player, which simpily may not work on all platforms.

I've attached my somewhat altered version.

HTH,

-Chris

sample2.py (7.87 KB)

···

--
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.Barker@noaa.gov

John W wrote:

    You can't. However since they are children of the window where you do
    the double buffered drawing (if not then that is probably another
    problem) then the area where the child widget is located should be
    clipped out by default when drawing the buffer for the parent since the
    wx.CLIP_CHILDREN style is turned on by default now.

Ok, I was afraid of this. I think I have the style setup correctly, but I have attached my source. If you download the three files and run the python script, it is apparent what I am trying to overcome.

When you press the blue button, I am calling the Draw() for the button so that it can be rendered as depressed. I am just doing an offset on the button by (2,2) and then drawing it greyscale to show it is pressed.

Upon doing the draw, I am seeing the media window overwritten and it disappears until the 5 second video loop starts again. Obviously, I would prefer to have the video remain playing.

Problem 1: You have three widgets created as children of the frame (the DrawWindow, the media ctrl, and the LcdButton) but the latter two are arranged so they appear to be on top of the first one. You can't do that with siblings, they should be children of the DrawWindow instead of its siblings. The flashing/clearing you are seeing is the DrawWindow being redrawn and overwriting the sibling windows. Since the media ctrl is using DirectDraw natively then it is actually using a different "layer" on the display then it is still visible because of the way that DD works. Otherwise it would probably be hidden all the time like the button's panel is.

Problem 2: You are painting the button from within the DrawWindow's paint event. The fact that you are telling the button to draw on the DrawWindow's DC helps a little, but it still leaves the button as a separate entity with no clue of how to draw itself. Either make the button know how to draw itself, or make it be more of a conceptual button that is part of the DrawWindow instead of a separate widget.

Problem 3: This isn't technically a part of the problem you are asking about, but doing things like self.parent.button.Draw(dc) or self.parent.Window.UpdateDrawing() is a bad idea. It requires too much coupling between components of your application and is very fragile when things are changed (as Chris demonstrated in his message.) Try to encapsulate things more so that classes are doing their own work instead of having others do it for them, and try to reduce the amount of knowledge that a component needs to have about the environment it is used in.

I am not doing any type of Refresh. Instead I am using the wx.BufferedPaintDC which I believe is copied to the screen once I stop writing to it, correct? Is there any way to do a buffer for just part of the screen instead of the whole one?

I know there are several ways to skin a cat, but I am worried that with my resouce limitations whether or not wxPython has too much overhead involved with it. This problem is surfacing because I am running on a resource limited machine.

Is a solution to my problem to do an individual wx.BufferedPaintDC for each widget on my screen?

Yes. Either that or make your button not be a separate (real) widget. Since you are already drawing it directly on the DrawWindow it shouldn't be too much of a hassle to take it the rest of the way.

But if I go that route, I am right back where I started with wanting avoid flicker or non-simutaneous redraw of objects.

If done correctly it really isn't that hard to be flicker free, or at least mostly flicker free. As I mentioned before child widgets are automatically clipped from parent redraws, so there won't be any flicker caused by the children being overdrawn by the parent. And as long as the children don't overlap siblings then they shouldn't interfere with each other either. The key for being able to manage the rest of the flicker is to not make multiple drawing steps within a widget be visible to the user, and you're already on the right track for that. You just eliminate the background erase and reduce the drawing to the screen down to one step by buffering the drawing to a bitmap first and then moving that to the screen.

If there is a way I can clip the DC or only copy a portion of it, that would be really beneficial.

Making the media ctrl be a child of the widget it is on will take care of this automatically, but for future reference see wx.DC.SetClippingRegion.

···

--
Robin Dunn
Software Craftsman