Displaying a bitmap in a ScrolledWindow with x,y offset

Hi,

I'm having a bit of trouble displaying a bitmap in a wx.ScrolledWindow
with an x,y offset.
Depending on what I try, either the window does not refresh correctly
when scrolling, or the image is placed at the wrong position.

I've attached an example, based on the wxPIA Chapter 12 draw_image.py
(it uses the image from the source code examples.
The program attempts to place the image with its upper left corner in
the center of the client area.

There are 2 paint methods: OnPaint and XOnPaint.

If you run the example as is, that is modeled on the book example, and
uses wx.PaintDC and dc.Draw, the image is placed ok, but the scrolling
doesn't work.

If you use the XOnPaint method (change names, or edit the Bind call),
that uses a wx.BufferedPaintDC, scrolling is fine, but placement is
off.

I've put comments in the code with more specific symptoms.

The platforms I tested on are
(1) WindowXP with wxPython 2.8.0.1 and python 2.5, and
*(2) GTK 2.6, Solaris 10, wxPython 2.6.3.3, python 2.4.4,

*The platform I need this for is GTK.

Thanks in advance for any help or suggestions.

-Chris Botos

my_draw_image.py (2.08 KB)

chris botos wrote:

    # This OnPaint places the image correctly, but when the window is scrolled,
    # the window is not refreshed correctly.
    def XOnPaint(self, evt):
        wdc, hdc = self.GetClientSize()
        wph, hph = self.photo.GetSize()
        x = max(0, ((wdc-wph)/2))
        y = max(0, ((hdc-hph)/2))
        print x,y
        dc = wx.PaintDC(self)
        brush = wx.Brush("sky blue")
        dc.SetBackground(brush)
        dc.Clear()
        dc.DrawBitmap(self.photo, x, y, True)

You're missing a call to self.PrepareDC

        # This OnPaint places the image incorrectly:
    # -on XP, wxPython 2.8.0.1, python 2.5, always at (0,0).
    # -on GTK 2.6, Solaris 10, wxPython 2.6.3.3, python 2.4.4, # it is offset somewhere in negative territory.
    # But scrolling is fine.
    # Also, the brush and Clear lines cause the image # to not be displayed at all.
    def OnPaint(self, evt):
        wdc, hdc = self.GetClientSize()
        wph, hph = self.photo.GetSize()
        x = max(0, ((wdc-wph)/2))
        y = max(0, ((hdc-hph)/2))
        print x,y
        dc = wx.BufferedPaintDC(self, self.photo, wx.BUFFER_VIRTUAL_AREA)

You probably don't want to use self.photo as the buffer. Yes, the buffer automatically gets blitted to the window, but it will always be at physical pos 0,0, because it is intended to be the whole window's "content". If you want to position the photo elsewhere, or to draw other things then you'll want to use another buffer bitmap that is the size of your virtual area, (or you can let the BufferedPaintDC create one for you) and then draw the photo or other content into that buffer. If you create and maintain your own buffer bitmap then that drawing can happen elsewhere in your app, and the paint event handler can then be just this single line:

     def OnPaint(self, evt):
  wx.BufferedPaintDC(self, self.my_buffer, wx.BUFFER_VIRTUAL_AREA)

since that will cause the buffer to be blitted to the window when the dc is destroyed, and since the buffer contains everything you want to draw already there is nothing else left to do. See the ScrolledWindow sample in the demo for more example code.

···

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

chris botos wrote:

My app is very time sensitive - is there any difference in timing for
the various approaches? For example a PaintDC.DrawBitmap(self.photo,
x, y, True) vs. draws to the external buffer?

There is, but it varies by what kind of drawing you are doing and also by platform quite a bit so it's hard to make any real comparisons or definitive statements in general. If you profile your app then you'll be better able to make decisions about approaches to take.

The basic rule of thumb is to reduce the overall amount of drawing as much as possible. So if you have a lot of things to draw, but it doesn't change that often, then drawing them to a buffer bitmap and reducing the paint event to just a blit (or DrawBitmap) means that all refreshes or scrolling doing are just one drawing operation so they will be very quick. If you have something that changes at high rate on it's own (not in response to user action like a mouse drag) then using a buffer could slow down things overall, although it may still be worth it for some parts or as a combined approach (some things buffered, and others drawn directly after the buffer has been drawn.)

···

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