Problem with overlapping animations (bitmap/GIF) on Windows and Linux with double buffer DC

Hi all. I am a new user of wxPython, and this is my first post to the
wxPython-users group.
I am building an application that has multiple animations with index
transparencies (not alpha) overlap contained within a ScrolledWindow.

I came up with two methods that work, one for Linux (Ubuntu 9.10) and
one for Windows (XP). But neither method works for both. The examples
require an animated GIF file with at least two frames called
"animation.gif".

I chose to use wxPython because it supposedly would allow me to write
code only once that is portable for multiple OSs.

First, I tried the simple approach:

#!/usr/bin/env python

import wxversion
wxversion.select("2.8")

import wx
import wx.animate

class MyPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        ag_fname = "animation.gif"
        ag = wx.animate.GIFAnimationCtrl(self, id, ag_fname, pos=(10,
10))
        ag2 = wx.animate.GIFAnimationCtrl(self, id, ag_fname, pos=(40,
12))

        ag.Play()
        ag2.Play()

app = wx.PySimpleApp()
frame = wx.Frame(None, -1, "wx.animate.GIFAnimationCtrl()", size =
(500, 400))
MyPanel(frame, -1)
frame.Show(True)
app.MainLoop()

I placed the coordinates of the animations close to one another, and
they overlap.

In Windows XP, this does not work, because each animation clears its
background with the Panel background (grey by default), and this
produces a strobing effect, where the two animations take turns
showing themselves on top of the other.

In Linux however the two animations display perfectly overlapped.

So I dug around on the wiki, and found the example
http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing . I
simplified the example and integrated it with a ScrolledWindow. I set
a timer to update the animations 10 times a second. Here is the class:

class animation_buffered_scrolled_canvas(wx.ScrolledWindow):
    def __init__(self, parent, id = wx.ID_ANY, size = wx.DefaultSize):
        wx.ScrolledWindow.__init__(self, parent, id, (0, 0),
size=size, style=wx.SUNKEN_BORDER)

        self.width = 600
        self.height = 600
        self.x = 0
        self.y = 0

        self.SetBackgroundColour("WHITE")

        myAnimation = wx.animate.Animation("animation.gif")
        frame0 = myAnimation.GetFrame(0)
        self.bframe0 = frame0.ConvertToBitmap()
        frame1 = myAnimation.GetFrame(1)
        self.bframe1 = frame1.ConvertToBitmap()

        self.bframec = self.bframe0

        self.SetVirtualSize((self.width, self.height))
        self.SetScrollRate(10,10)

        self.idTIMER = wx.NewId() # pick a number
        self.timer = wx.Timer(self, self.idTIMER) # message will be
sent to the panel
        self.timer.Start(100, oneShot = False) # x100 milliseconds
        self.Bind(wx.EVT_TIMER, self.on_timer, id = self.idTIMER) #
call the on_timer function

        self.time_switch = False

        wx.EVT_PAINT(self, self.OnPaint)
        wx.EVT_SIZE(self, self.OnSize)
        wx.EVT_SCROLLWIN(self, self.OnScroll)

        self.OnSize(None)

    def on_timer(self, event):
        if (self.time_switch == False):
            self.time_switch = True
            self.bframec = self.bframe0
        else:
            self.time_switch = False
            self.bframec = self.bframe1

        self.UpdateDrawing()

    def UpdateDrawing(self):
        dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer)
        # self.PrepareDC(dc)
        self.Draw(dc)

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self._Buffer)

    def OnSize(self,event):
        Size = (self.width, self.height)

        self._Buffer = wx.EmptyBitmap(*Size)
        self.UpdateDrawing()

    def OnScroll(self,event):
        Size = (self.width, self.height)
        self._Buffer = wx.EmptyBitmap(*Size)
        self.UpdateDrawing()
        event.Skip()

    def Draw(self, dc):
        dc.SetBackground(wx.Brush("White"))
        dc.Clear()

        x1, y1 = self.CalcScrolledPosition(0, 0)
        x2, y2 = self.CalcScrolledPosition(10, 10)
        x3, y3 = self.CalcScrolledPosition(200, 200)
        x4, y4 = self.CalcScrolledPosition(203, 194)
        dc.DrawBitmap(self.bframe1, x1, y1, True)
        dc.DrawBitmap(self.bframec, x2, y2, True)
        dc.DrawBitmap(self.bframec, x3, y3, True)
        dc.DrawBitmap(self.bframe0, x4, y4, True)

This second method works perfectly on Windows, but on Linux no images
are displayed at all. I think GTK2 might have a problem drawing
bitmaps to a DC canvas. I am not sure.

I would appreciate any help fixing either of these two above examples
so that I can write code once and it will be compatible for both
platforms.

I will also need to draw colored primitive shapes and text on top of
these overlapping animations, so I am thinking that the double
buffered solution is better because I can do these operations
relatively easily with DC canvases.

Would anyone suggest I use Chris Barker's FloatCanvas? I heard that it
doesn't work properly with scrolling windows.

On a side note, how would I use the built-in buffering function
"SetDoubleBuffered(True)"? Can anyone provide an example of this, or
am I better off using the custom buffering system above?

Thanks for reading my post, and super-thanks for responding if you do
so.
-Deus

Deus wrote:

I am building an application that has multiple animations with index
transparencies (not alpha) overlap contained within a ScrolledWindow.

I chose to use wxPython because it supposedly would allow me to write
code only once that is portable for multiple OSs.

It usually does. As it is using native widgets, you can often find (as
you did) a method that works right on one platform but not another, but
there is usually a "right" way to do it that will work everywhere -- if
there isn't then I think it's a bug.

First, I tried the simple approach:

I'm not surprised that you have issues with that -- wx.Windows are not
designed to overlap.

So I dug around on the wiki, and found the example
http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing.

That's probably the way to go -- though depending on how complicated
your final drawing is, you may not need the double buffering.

> I will also need to draw colored primitive shapes and text on top of
> these overlapping animations, so I am thinking that the double
> buffered solution is better because I can do these operations
> relatively easily with DC canvases.

Yup. If you're drawing anyway, then using a DC (or maybe
GraphicsContext) is probably the way to go for all the drawing.

> Would anyone suggest I use Chris Barker's FloatCanvas?

Maybe -- if you want to be able to zoom, then yes. If not, then maybe.

> I heard that it doesn't work properly with scrolling windows.

correct, it expects to take care of all the coordinate transforms
itself. However, that doesn't mean you can't make a scrolled FloatCanvas
-- I've been meaning to do that for ages, but haven't had enough
motivation to do so. It does support "panning" out of the box -- which
is really the same thing, with a different UI. What you would need to do
is put the scroll bars on yourself, and then catch their events and call
Canvas.MoveImage. Not that big a deal, really.

I
simplified the example and integrated it with a ScrolledWindow. I set
a timer to update the animations 10 times a second. Here is the class:

I don't have GTK set up rigth now to test on :-(, but you can add
another non-bitmap draw call to the Draw() method, and see if has
anything to do with bitmaps -- I doubt it. You can also try it without
the timer, jsut to make sure that your drawing is what you want. I
suspect it may be an issue with the system's double buffering -- I've
seen that on OS-X -- you're drawing to the system's buffer, but the
system is waiting for a good time to show that. You might try a call to:

wx.GetApp().Yield(onlyIfNeeded=True)

after the Draw() call.

Fro more help:

1) put your code in an attachment, rather than inline -- mail clients
often muck it up.

2) MakingSampleApps - wxPyWiki -- you want to make it as
easy as you can for us to test.

-Chris

···

class animation_buffered_scrolled_canvas(wx.ScrolledWindow):
    def __init__(self, parent, id = wx.ID_ANY, size = wx.DefaultSize):
        wx.ScrolledWindow.__init__(self, parent, id, (0, 0),
size=size, style=wx.SUNKEN_BORDER)

        self.width = 600
        self.height = 600
        self.x = 0
        self.y = 0

        self.SetBackgroundColour("WHITE")

        myAnimation = wx.animate.Animation("animation.gif")
        frame0 = myAnimation.GetFrame(0)
        self.bframe0 = frame0.ConvertToBitmap()
        frame1 = myAnimation.GetFrame(1)
        self.bframe1 = frame1.ConvertToBitmap()

        self.bframec = self.bframe0

        self.SetVirtualSize((self.width, self.height))
        self.SetScrollRate(10,10)

        self.idTIMER = wx.NewId() # pick a number
        self.timer = wx.Timer(self, self.idTIMER) # message will be
sent to the panel
        self.timer.Start(100, oneShot = False) # x100 milliseconds
        self.Bind(wx.EVT_TIMER, self.on_timer, id = self.idTIMER) #
call the on_timer function

        self.time_switch = False

        wx.EVT_PAINT(self, self.OnPaint)
        wx.EVT_SIZE(self, self.OnSize)
        wx.EVT_SCROLLWIN(self, self.OnScroll)

        self.OnSize(None)

    def on_timer(self, event):
        if (self.time_switch == False):
            self.time_switch = True
            self.bframec = self.bframe0
        else:
            self.time_switch = False
            self.bframec = self.bframe1

        self.UpdateDrawing()

    def UpdateDrawing(self):
        dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer)
        # self.PrepareDC(dc)
        self.Draw(dc)

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self._Buffer)

    def OnSize(self,event):
        Size = (self.width, self.height)

        self._Buffer = wx.EmptyBitmap(*Size)
        self.UpdateDrawing()

    def OnScroll(self,event):
        Size = (self.width, self.height)
        self._Buffer = wx.EmptyBitmap(*Size)
        self.UpdateDrawing()
        event.Skip()

    def Draw(self, dc):
        dc.SetBackground(wx.Brush("White"))
        dc.Clear()

        x1, y1 = self.CalcScrolledPosition(0, 0)
        x2, y2 = self.CalcScrolledPosition(10, 10)
        x3, y3 = self.CalcScrolledPosition(200, 200)
        x4, y4 = self.CalcScrolledPosition(203, 194)
        dc.DrawBitmap(self.bframe1, x1, y1, True)
        dc.DrawBitmap(self.bframec, x2, y2, True)
        dc.DrawBitmap(self.bframec, x3, y3, True)
        dc.DrawBitmap(self.bframe0, x4, y4, True)

This second method works perfectly on Windows, but on Linux no images
are displayed at all. I think GTK2 might have a problem drawing
bitmaps to a DC canvas. I am not sure.

I would appreciate any help fixing either of these two above examples
so that I can write code once and it will be compatible for both
platforms.

I will also need to draw colored primitive shapes and text on top of
these overlapping animations, so I am thinking that the double
buffered solution is better because I can do these operations
relatively easily with DC canvases.

Would anyone suggest I use Chris Barker's FloatCanvas? I heard that it
doesn't work properly with scrolling windows.

On a side note, how would I use the built-in buffering function
"SetDoubleBuffered(True)"? Can anyone provide an example of this, or
am I better off using the custom buffering system above?

Thanks for reading my post, and super-thanks for responding if you do
so.
-Deus

>

--
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