1

I need to be able to rescale an image (in realtime) in a wx.Panel when parent wx.Frame is resized (for example for wxPython for image and buttons (resizable)).

This code now works, the behaviour is like in a standard Photo Viewer : the image fits perfectly the parent window, and the rescaling respects aspect ratio.

import wx

class MainPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.FULL_REPAINT_ON_RESIZE)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.img = wx.Image('background.png', wx.BITMAP_TYPE_PNG)
        self.imgx, self.imgy = self.img.GetSize()

    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        dc.Clear()
        x,y = self.GetSize()
        posx,posy = 0, 0
        newy = int(float(x)/self.imgx*self.imgy)
        if newy < y:
            posy = int((y - newy) / 2) 
            y = newy
        else:
            newx = int(float(y)/self.imgy*self.imgx)
            posx = int((x - newx) / 2)
            x = newx        

        img = self.img.Scale(x,y, wx.IMAGE_QUALITY_HIGH)
        self.bmp = wx.BitmapFromImage(img)
        dc.DrawBitmap(self.bmp,posx,posy)

class MainFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, title='Test', size=(600,400))
        self.panel = MainPanel(self)
        self.Show()

app = wx.App(0)
frame = MainFrame(None)
app.MainLoop()        

Before continuing to implement some things, I would to know :

  • is this the "good way" to do it ?
  • the FULL_REPAINT_ON_RESIZE might be a bit too much (inefficient), but I couldn't make it without this, do you have ideas to improve this ?
  • how to track mouse clicks ? Example : I want to track a click in a rectangle (10,20) to (50,50) in the original coordinates of the original image. Should I convert this in new coordinates (because the image has been rescaled!) ? This would mean I should do all things now in a very low-level...
Community
  • 1
  • 1
Basj
  • 41,386
  • 99
  • 383
  • 673

1 Answers1

1

This is not a bad way to do it but there are a couple of things you could easily optimize:

  1. Don't call wxImage::Scale() every time in OnPaint(). This is a slow operation and you should do it only once, in your wxEVT_SIZE handler instead of doing it every time you repaint the window.
  2. Call SetBackgroundStyle(wxBG_STYLE_PAINT) to indicate that your wxEVT_PAINT handler already erases the background entirely, this will reduce flicker on some platforms (you won't see any difference on the others which always use double buffering anyhow, but it will still make repainting marginally faster even there).

As for the mouse clicks, I think you don't have any choice but to translate the coordinates yourself.

VZ.
  • 21,740
  • 3
  • 39
  • 42
  • I tried `1.` but I cannot use `dc = wx.PaintDC(self)` in something else than `OnPaint()` (`wx.EVT_PAINT`). If it is possible for you to modify the code that I gave here in order to show your modifications (I think this could be useful for many users) ? – Basj Jan 27 '14 at 14:43
  • Why do you need the `dc` to resize the image? It's completely separate, just assign `self.bmp` in your `wxEVT_SIZE` handler. You still need to keep `dc.DrawBitmap()` call itself in `wxEVT_PAINT` handler, of course. – VZ. Jan 27 '14 at 15:06