[wxPython] Question regarding proper bitmap updating.

Good day,

I've been mucking around with PIL 1.1.2, VideoCapture .8beta, and
wxPython 2.3.3.1 to attempt to produce an encrypted webcam server/client
(as there are none available that I could find).

Now, I've got the beast working. Sockets are good. GUI is good.
Everything works...except that image display is somewhat quirky. That
is, when I use wxStaticBitmap.SetBitmap(image), over the course of a few
drawings, it seems to be continually layering the static bitmaps on top
of each other...ad infinitim (I can tell because I get an animation at
graphics card speed of allthe images ever there). After around 30 or so
updates, even after stopping future updates, moving the window takes
ALOT of processor as one tries to drag the window around. It keeps
redrawing every image that used to be within the static bitmap.

Right now I'm using the wxPanel subclass given at the end. My concern is
but the (currently) four lines:

        try:
            self.image.SetBitmap(image_as_stringio)
        except:
            self.image = wxStaticBitmap(self, -1, self.GetBitmap(image_as_stringio),(0,0))

For updating a bitmap on the screen, which will be updating every .5-10
seconds, for possibly hours/days/more, what is the proper way for having
a bitmap that gets updated on the screen without saving any old
information?

Thank you,
- Josiah Carlson

class ImagePanel(wxPanel):
    def __init__(self, parent, log):
        wxPanel.__init__(self, parent, -1)
        self.Disp_Image_F('startup.jpg')

    def GetBitmap(self, image_file):
        source = Image.open(image_file, 'r')
        image = apply( wxEmptyImage, source.size )
        image.SetData( source.convert( "RGB" ).tostring() )
        return image.ConvertToBitmap()

    def Disp_Image_S(self, image_as_stringio):
        try:
            self.image.SetBitmap(image_as_stringio)
        except:
            self.image = wxStaticBitmap(self, -1, self.GetBitmap(image_as_stringio), (0,0))
        #print dir(self.image)

    def Disp_Image_F(self, filename):
        fl = open(filename, 'rb')
        data = StringIO(fl.read())
        fl.close()
        self.Disp_Image_S(data)

Josiah Carlson wrote:

I've been mucking around with PIL 1.1.2, VideoCapture .8beta, and
wxPython 2.3.3.1 to attempt to produce an encrypted webcam server/client
(as there are none available that I could find).

Now, I've got the beast working. Sockets are good. GUI is good. Everything works...except that image display is somewhat quirky. That
is, when I use wxStaticBitmap.SetBitmap(image), over the course of a few
drawings, it seems to be continually layering the static bitmaps on top
of each other...ad infinitim (I can tell because I get an animation at
graphics card speed of allthe images ever there). After around 30 or so
updates, even after stopping future updates, moving the window takes
ALOT of processor as one tries to drag the window around. It keeps
redrawing every image that used to be within the static bitmap.

Which platform? I don't see anything in the sources that is obviously causing this problem, but it does sound like a bug so please enter a bug report about it.

That said, a wxStaticBitmap is probably not the best control for what you are wanting to do (because your image is not "static" :wink: Probably the best thing is to derive a class from wxWindow and when a new image arives use a wxClientDC to Blit the bitmap to the window. You'll also want to give it a EVT_PAINT handler to redraw the current image if the window needs refreshing.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin, thank you for your quick response.

Which platform? I don't see anything in the sources that is obviously
causing this problem, but it does sound like a bug so please enter a bug
report about it.

Ahh, one thing I forgot to mention. I'm using windows 2000.

That said, a wxStaticBitmap is probably not the best control for what
you are wanting to do (because your image is not "static" :wink:

I read the same thing in the mailing list archives. But other ways of
drawing/updating bitmaps aren't nearly as friendly (maybe they should be).

Probably
the best thing is to derive a class from wxWindow and when a new image
arives use a wxClientDC to Blit the bitmap to the window. You'll also
want to give it a EVT_PAINT handler to redraw the current image if the
window needs refreshing.

This is where you lost me. I suppose I should have told you that I've
only been using wxPython since Tuesday or so. Though the fact that I've
been able to come this far with tutorials and documentation, just shows
how well you guys are doing in terms of documentation on the most part.

I tried to get it to work (took me a bit)...but I'm having problems.
Farther below you'll see what I've got so far, it looks like it gets
stuck redrawing the bitmap with OnPaint (I've done some console IO to
prove this). Any ideas on how I can get it to only update as changes
occur?

Thanks,
- Josiah

class ImageWindow(wxWindow):
    def __init__(self, parent, id, position, size):
        wxWindow.__init__(self, parent, id, position, size)

        self.Disp_Image_F('sleeping.jpg')

        EVT_PAINT(self, self.OnPaint)

    def OnPaint(self, event):
        self.DoDrawing()

    def DoDrawing(self):
        dest = wxClientDC(self)
        source = wxMemoryDC()
        source.SelectObject(self.bmp)
        dest.Blit( 0,0,self.bmp_w,self.bmp_h, source, 0,0)

    def GetBitmap(self, image_file):
        source = Image.open(image_file, 'r')
        image = apply( wxEmptyImage, source.size )
        image.SetData( source.convert( "RGB" ).tostring() )
        return image.ConvertToBitmap()

    def Disp_Image_S(self, image_as_stringio):
        self.bmp = self.GetBitmap(image_as_stringio)
        self.bmp_w = self.bmp.GetWidth()
        self.bmp_h = self.bmp.GetHeight()
        self.OnPaint(None)

    def Disp_Image_F(self, filename):
        fl = open(filename, 'rb')
        data = StringIO(fl.read())
        fl.close()
        self.Disp_Image_S(data)

Josiah Carlson wrote:

Probably the best thing is to derive a class from wxWindow and when a new image arives use a wxClientDC to Blit the bitmap to the window. You'll also want to give it a EVT_PAINT handler to redraw the current image if the window needs refreshing.

This is where you lost me. I suppose I should have told you that I've
only been using wxPython since Tuesday or so. Though the fact that I've
been able to come this far with tutorials and documentation, just shows
how well you guys are doing in terms of documentation on the most part.

I tried to get it to work (took me a bit)...but I'm having problems.
Farther below you'll see what I've got so far, it looks like it gets
stuck redrawing the bitmap with OnPaint (I've done some console IO to
prove this). Any ideas on how I can get it to only update as changes
occur?

[...]

    def OnPaint(self, event):
        self.DoDrawing()

    def DoDrawing(self):
        dest = wxClientDC(self)
        source = wxMemoryDC()
        source.SelectObject(self.bmp)
        dest.Blit( 0,0,self.bmp_w,self.bmp_h, source, 0,0)

When you handle the EVT_PAINT event you *must* use a wxPaintDC otherwise Windows thinks that the event was not fully handled and so will send the PAINT message again. This would be better:

      def OnPaint(self, event):
          self.DoDrawing(wxPaintDC(self))

      def DoDrawing(self, dest):
          source = wxMemoryDC()
          source.SelectObject(self.bmp)
          dest.Blit( 0,0,self.bmp_w,self.bmp_h, source, 0,0)

    def GetBitmap(self, image_file): source = Image.open(image_file, 'r') image = apply( wxEmptyImage, source.size ) image.SetData( source.convert( "RGB" ).tostring() )
        return image.ConvertToBitmap()

    def Disp_Image_S(self, image_as_stringio):
        self.bmp = self.GetBitmap(image_as_stringio)
        self.bmp_w = self.bmp.GetWidth()
        self.bmp_h = self.bmp.GetHeight()
        self.OnPaint(None)

And this last line should be:

  self.DoDrawing(wxClientDC(self))

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

> def OnPaint(self, event):
> self.DoDrawing()
>
> def DoDrawing(self):
> dest = wxClientDC(self)
> source = wxMemoryDC()
> source.SelectObject(self.bmp)
> dest.Blit( 0,0,self.bmp_w,self.bmp_h, source, 0,0)

When you handle the EVT_PAINT event you *must* use a wxPaintDC otherwise
Windows thinks that the event was not fully handled and so will send the
PAINT message again. This would be better:

Thank you. It works perfectly now. If you'd like, I could update the
wiki with the code that has been produced so that others won't be
bothering you again...
- Josiah

Josiah Carlson wrote:

Thank you. It works perfectly now. If you'd like, I could update the
wiki with the code that has been produced so that others won't be
bothering you again...
- Josiah

Adding good stuff to the Wiki is Always a good idea. A number of folks
have been bitten by the wxClientDC vs wxPaintDC issue before (myself
included)

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                        
NOAA/OR&R/HAZMAT (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

Josiah Carlson wrote:

Thank you. It works perfectly now. If you'd like, I could update the
wiki with the code that has been produced so that others won't be
bothering you again...

The rule is that if something is useful to you then it will be useful to someone else, so add it to the wiki.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!