[wxPython-users] Another question about bitmaps

Andrea Gavana andrea.gavana at gmail.com
Fri Jun 29 14:27:59 PDT 2007


Hi Brian,

On 6/30/07, Brian Wolf wrote:
> I'm still struggling with putting a bitmap on a panel, and then giving
> the user a mechanism to zoom in / zoom out.  So far, I've contructed a
> "popup" window (wx.Frame) that contains a panel on which the bitmap
> should be drawn.  The panel also contains a slider which is supposed to
> let the user increase or decrease the size of the bitmap (for instance,
> a photo).
>
> I've not been able to get PaintDC or ScreenDC to work effectively.  A
> staticbitmap is not so flexible.  PIL provides a .resize() function that
> returns another bitmap, but integrating wxPyton and PIL is not easy.

The handler you want to implement is an OnPaint method which tkes care
of the wx.EVT_PAINT event, plus some other things. Please try the
attached file: it's not a complete application (it doesn't work as-is
out of the box), but it should get you started.
It has zoom capabilities (via mouse-wheel), shadows beneath images
(generated using PIL) and other goodies. It's taken directly from my
software "InfinImage", and it uses a wx.ScrolledWindow to display the
image, drawing it in the OnPaint event handler.

HTH.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/
-------------- next part --------------
import wx
import Image
import ImageFilter
import math

def BitmapToPil(bitmap):

    return ImageToPil(BitmapToImage(bitmap))


def BitmapToImage(bitmap):

    return wx.ImageFromBitmap(bitmap)


def PilToBitmap(pil):

    return ImageToBitmap(PilToImage(pil))


def PilToImage(pil):

    image =3D wx.EmptyImage(pil.size[0], pil.size[1])
    image.SetData(pil.convert('RGB').tostring())
    return image


def PiltoImageAlpha(pil, alpha=3DTrue):

   if alpha:
       image =3D apply(wx.EmptyImage, pil.size)
       image.SetData(pil.convert( "RGB").tostring())
       image.SetAlphaData(pil.convert("RGBA").tostring()[3::4])
   else:
       image =3D wx.EmptyImage(pil.size[0], pil.size[1])
       new_image =3D pil.convert('RGB')
       data =3D new_image.tostring()
       image.SetData(data)
       =

   return image


def ImageToPil(image):

    pil =3D Image.new('RGB',(image.GetWidth(), image.GetHeight()))
    pil.fromstring(image.GetData())
    return pil


def ImageToBitmap(image):

    return image.ConvertToBitmap()


def DropShadow(image, offset=3D(5,5), background=3D0xffffff, shadow=3D0x444=
444, =

              border=3D8, iterations=3D3):
    """
    Add a gaussian blur drop shadow to an image.  =


    image       - The image to overlay on top of the shadow.
    offset      - Offset of the shadow from the image as an (x,y) tuple.  C=
an be
                  positive or negative.
    background  - Background colour behind the image.
    shadow      - Shadow colour (darkness).
    border      - Width of the border around the image.  This must be wide
                  enough to account for the blurring of the shadow.
    iterations  - Number of times to apply the filter.  More iterations =

                  produce a more blurred shadow, but increase processing ti=
me.
    """

    # Create the backdrop image -- a box in the background colour with a =

    # shadow on it.
    totalWidth =3D image.size[0] + abs(offset[0]) + 2*border
    totalHeight =3D image.size[1] + abs(offset[1]) + 2*border
    back =3D Image.new(image.mode, (totalWidth, totalHeight), background)

    # Place the shadow, taking into account the offset from the image
    shadowLeft =3D border + max(offset[0], 0)
    shadowTop =3D border + max(offset[1], 0)
    back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0],
                        shadowTop + image.size[1]])

    # Apply the filter to blur the edges of the shadow.  Since a small kern=
el
    # is used, the filter must be applied repeatedly to get a decent blur.
    n =3D 0
    while n < iterations:
        back =3D back.filter(ImageFilter.BLUR)
        n +=3D 1
      =

    # Paste the input image onto the shadow backdrop  =

    imageLeft =3D border - min(offset[0], 0)
    imageTop =3D border - min(offset[1], 0)
    back.paste(image, (imageLeft, imageTop))

    return back


class ScrolledImage(wx.ScrolledWindow):

    def __init__(self, parent, id=3D-1, pos=3Dwx.DefaultPosition, size=3Dwx=
.DefaultSize):

        wx.ScrolledWindow.__init__(self, parent, id, pos, size)        =


        self.zoom_levels =3D range(8, 1, -1) + [1./r for r in range(1, 9)]
        self.zoom_level =3D self.zoom_levels.index(1.)
        self.scale =3D 1.
        self.size =3D wx.Size(0, 0)
        self.wheel =3D 0
        self.SetExtraStyle(0)

        self.backcolour =3D wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFAC=
E)

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)

        self.MainFrame =3D wx.GetTopLevelParent(self)        =



    def CalculateShadow(self, image):
        =

        size =3D image.size
        =

        if size[0] <=3D 32:
            xshadow =3D 3
        elif 32 < size[0] <=3D 200:
            xshadow =3D 5
        elif 200 < size[0] <=3D 600:
            xshadow =3D 8
        else:
            xshadow =3D 10

        if size[1] <=3D 32:
            yshadow =3D 3
        elif 32 < size[1] <=3D 200:
            yshadow =3D 5
        elif 200 < size[1] <=3D 600:
            yshadow =3D 8
        else:
            yshadow =3D 10

        border =3D max(xshadow, yshadow)

        return xshadow, yshadow, border        =

            =


    def ShowImage(self, image):

        PIL_bitmap =3D Image.open(image)

        x, y, border =3D self.CalculateShadow(PIL_bitmap)
                =

        PIL_bitmap =3D DropShadow(PIL_bitmap, offset=3D(x, y), border=3Dbor=
der)
        bmp =3D PilToBitmap(PIL_bitmap)

        self.SetScrollbars(1, 1, PIL_bitmap.size[0], PIL_bitmap.size[1], 0,=
 0)

        size =3D self.GetSize()

        xmin =3D (size.x - PIL_bitmap.size[0])/2
        ymin =3D (size.y - PIL_bitmap.size[1])/2
        =

        self.currentbmp =3D bmp
        self.size =3D wx.Size(self.currentbmp.GetSize().width*self.scale, s=
elf.currentbmp.GetSize().height*self.scale)
        self.SetViewScale(self.zoom_levels[self.zoom_level])
        =

        self.Refresh()
        =


    def Zoom(self, dir):

        self.zoom_level +=3D dir
        if self.zoom_level < 0:
            self.zoom_level =3D 0
            return
        elif self.zoom_level >=3D len(self.zoom_levels):
            self.zoom_level =3D len(self.zoom_levels)-1
            return

        self.SetViewScale(self.zoom_levels[self.zoom_level])
        self.MainFrame.bottompanel.SetScrollValue(self.zoom_level)


    def OnPaint(self, event):

        self.SetScale(1./self.scale,1./self.scale)
        dc =3D wx.PaintDC(self)
        dc.SetBackground(wx.Brush(self.backcolour))

        iw, ih =3D self.size
##        if iw < self.windowsize.x or ih < self.windowsize.y:
        dc.Clear()
            =

        self.DoPaint(dc)
        del dc
        self.SetScale(1., 1.)
        =


    def DoPaint(self, dc=3DNone):

        if dc is None:
            dc =3D wx.ClientDC(self)

        self.PrepareDC(dc)

        iw, ih =3D self.size

        xmin =3D (self.windowsize.x - iw)/2
        ymin =3D (self.windowsize.y - ih)/2

        bm_dc =3D wx.MemoryDC()        =

        bm_dc.SelectObject(self.currentbmp)

##        if xmin >=3D 0 and ymin >=3D 0:
##            self.DrawContour(bm_dc)

        if xmin < 0:
            xmin =3D 0
        if ymin < 0:
            ymin =3D 0

        dc.Blit(xmin*self.scale, ymin*self.scale, iw*self.scale, ih*self.sc=
ale, bm_dc, 0, 0, wx.COPY, True)
        bm_dc.SelectObject(wx.NullBitmap)


    def DrawContour(self, dc):

        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen(wx.BLACK, 2))

        dc.DrawRectangle(1, 1, self.currentbmp.GetWidth()-1, self.currentbm=
p.GetHeight()-1)


    def OnEraseBackground(self, event):
        =

        pass


    def OnSize(self, event):

        iw, ih =3D self.size.width, self.size.height
        w, h =3D event.GetSize()

        scroll_x =3D w < iw
        scroll_y =3D h < ih

        scroll =3D scroll_x | scroll_y

        x, y =3D self.GetViewStart()

        self.SetScrollbars(scroll,scroll,iw,ih,x,y)
        self.windowsize =3D wx.Size(w, h)


    def SetViewScale(self, scale):

        self.SetScale(1.,1.)
        self.GetParent().SetSizeHints(-1,-1,-1,-1)

        x, y =3D self.GetViewStart()
        cw, ch =3D self.GetClientSize()
        midx, midy =3D x + cw / 2, y + ch / 2

        midx /=3D float(self.size.width)
        midy /=3D float(self.size.height)

        self.scale =3D scale
        self.size =3D wx.Size(self.currentbmp.GetSize().width/self.scale, s=
elf.currentbmp.GetSize().height/self.scale)

        iw, ih =3D self.size.width, self.size.height
        w, h =3D self.GetSize()

        scroll_x =3D w < iw
        scroll_y =3D h < ih

        x, y =3D self.GetViewStart()

        new_midx =3D midx * self.size.width
        new_midy =3D midy * self.size.height

        x =3D new_midx - cw/2
        y =3D new_midy - ch/2

        self.SetScrollbars(scroll_x,scroll_y,iw,ih,x,y)

        zoom_percent =3D round((1./self.scale)*100, 2)
        if zoom_percent =3D=3D math.floor(zoom_percent):
            zoom_percent =3D str(zoom_percent).rstrip( '0' )[0:-1]

        self.SetScale(self.scale,self.scale)


    def OnMouseWheel(self, event):

        if event.m_controlDown:

            self.wheel +=3D event.GetWheelRotation()
            if abs(self.wheel) >=3D event.GetWheelDelta():
                if event.GetWheelRotation() < 0:
                    self.Zoom(-1)
                    self.wheel+=3D event.GetWheelDelta()
                else:
                    self.Zoom(+1)
                    self.wheel-=3D event.GetWheelDelta()

        else:

            iw, ih =3D self.size.width, self.size.height
            w, h =3D self.GetSize()

            scroll_x =3D w < iw
            scroll_y =3D h < ih

            if scroll_x or scroll_y:            =

                event.Skip()            =

               =20


More information about the wxpython-users mailing list