Centering image in resized panel – panel.GetSize returns wrong info

I’m centering an image (of arbitrary
size) in a panel. Dynamically, both for arbitrary images, and when the app/frame/panel size changes.

EVT_SIZE binds to 1) getting the
image size, 2) getting the panel size, 3) scaling the image to fit in the
panel, and 4) placing the image in the panel.

Which “kind of” works. It works flawlessly for images with arbitrary dimensions. But – if I
resize the App window quickly – the resulting image is often not-quite-centered. As if panel.GetSize()
returned incorrect width/height.

Does this have something to do with
the frame size (and the size of everything in the frame) getting re-calculated?
How long does it take for the dimensions of everything in the frame (including the target panel) to be updated? If this
is the case, is there a graceful way to know when the panel size recalculation
is finished, before doing panel.GetSize() ?

Hi,

I’m centering an image (of arbitrary
size) in a panel. Dynamically, both for arbitrary images, and when the app/frame/panel size changes.

EVT_SIZE binds to 1) getting the
image size, 2) getting the panel size, 3) scaling the image to fit in the
panel, and 4) placing the image in the panel.

Which “kind of” works. It works flawlessly for images with arbitrary dimensions. But – if I
resize the App window quickly – the resulting image is often not-quite-centered. As if panel.GetSize()
returned incorrect width/height.

Does this have something to do with
the frame size (and the size of everything in the frame) getting re-calculated?
How long does it take for the dimensions of everything in the frame (including the target panel) to be updated? If this
is the case, is there a graceful way to know when the panel size recalculation
is finished, before doing panel.GetSize() ?

It is always a good idea to post code that shows what you are doing.

It sounds like you might be calling panel.GetSize() in the event handler for EVT_SIZE. Perhaps you would have better results using event.GetSize() in the event handler.

Also:, are you binding to EVT_PAINT?

I have had very good experience with a resizable panel showing a live image from a GigE camera running at about 15 fps that responds well to resizing, with little or no flicker. I also draw overlays such as for a calibrated scalebar and the display is able to keep up with the camera framerate (3M pixel, color, at 15fps). Assuming that the full image size from the camera is held in self.img_w, self.img_h, this panel binds EVT_SIZE and EVT_PAINT approximately as (you may need to add some sanity checks):

class ImagePanel_Base(wx.Panel):

def __init__(self, parent, ....):

    ....

    self.pan_w, self.pan_h = 800.0, 600.0    # size of wx Panel

    self.img_w, self.img_h = 1600.0, 1200.0 .  # size of image

    self.scale = 0.5

self.Bind(wx.EVT_SIZE, self.onSize)

    self.Bind(wx.EVT_PAINT, self.onPaint)

def onSize(self, event):

    "onSize event: calculate image scaling factor from new panel size"

self.pan_w, self.pan_h = event.GetSize()

self.scale = min(fself.pan_w/self.img_w, pan_h/self.img_h)

self.Refresh() # will generate a paint event

def onPaint(self, event):

    "onPaint event: draw scaled bitmap of image to screen"

    # get wxImage from whatever source

    wximage = self.GrabWxImage()

    wximage.Rescale(int(self.scale*self.img_w), int(self.scale*self.img_h), quality=wx.IMAGE_QUALITY_HIGH)

    bitmap = Bitmap(wximage)

    bmp_w, bmp_h =  bitmap.GetSize()

    pad_w, pad_h = int(1+(self.pan_w-bmp_w)/2.0), int(1+(self.pan_h-bmp_h)/2.0)

    dc = wx.AutoBufferedPaintDC(self)

    dc.Clear()

    dc.DrawBitmap(bitmap, pad_w, pad_h, useMask=True)

The ‘padding’ done in the EVT_PAINT handler should help center the image. I admit that I don’t check that at the pixel level, but the horizontal or vertical border seems symmetric even when the panels aspect ratio is very different from the images aspect ratio.

Hope that helps.

–Matt Newville

···

On Fri, Aug 17, 2018 at 4:41 PM Tim Kuo sinkvb@gmail.com wrote:

On what platform are you using the code???

Johnf

···

On 08/19/2018 06:41 PM, Matt Newville
wrote:

Hi,

On Fri, Aug 17, 2018 at 4:41 PM Tim Kuo sinkvb@gmail.com wrote:

              I’m centering an image

(of arbitrary
size) in a panel. Dynamically, both for arbitrary
images, and when the app/frame/panel size changes.

              EVT_SIZE binds to 1)

getting the
image size, 2) getting the panel size, 3) scaling the
image to fit in the
panel, and 4) placing the image in the panel.

              Which “kind of” works. It

works flawlessly for images with arbitrary dimensions.
But – if I
resize the App window quickly – the resulting image
is often not-quite-centered. As if panel.GetSize()
returned incorrect width/height.

              Does this have something

to do with
the frame size (and the size of everything in the
frame) getting re-calculated?
How long does it take for the dimensions of everything
in the frame (including the target panel) to be
updated? If this
is the case, is there a graceful way to know when the
panel size recalculation
is finished, before doing panel.GetSize() ?

        It is always

a good idea to post code that shows what you are doing.

        It sounds

like you might be calling panel.GetSize() in the event
handler for EVT_SIZE. Perhaps you would have better results
using event.GetSize() in the event handler.

        Also:, are

you binding to EVT_PAINT?

        I have had

very good experience with a resizable panel showing a live
image from a GigE camera running at about 15 fps that
responds well to resizing, with little or no flicker. I
also draw overlays such as for a calibrated scalebar and the
display is able to keep up with the camera framerate (3M
pixel, color, at 15fps). Assuming that the full image size
from the camera is held in self.img_w, self.img_h, this
panel binds EVT_SIZE and EVT_PAINT approximately as (you may
need to add some sanity checks):

        class

ImagePanel_Base(wx.Panel):

def __init__(self, parent, ....):
    ....
                  self.pan_w, self.pan_h = 800.0, 600.0 

size of wx Panel

self.img_w, self.img_h = 1600.0, 1200.0 . # size of image

    self.scale = 0.5

self.Bind(wx.EVT_SIZE, self.onSize)

    self.Bind(wx.EVT_PAINT, self.onPaint)
              def

onSize(self, event):

“onSize event: calculate image scaling factor from new
panel size”

self.pan_w, self.pan_h = event.GetSize()

self.scale = min(fself.pan_w/self.img_w, pan_h/self.img_h)

self.Refresh() # will generate a paint event

                 def

onPaint(self, event):

“onPaint event: draw scaled bitmap of image to screen”

                      #

get wxImage from whatever source

wximage = self.GrabWxImage()

wximage.Rescale(int(self.scaleself.img_w),
int(self.scale
self.img_h), quality=wx.IMAGE_QUALITY_HIGH)

bitmap = Bitmap(wximage)

bmp_w, bmp_h = bitmap.GetSize()

pad_w, pad_h = int(1+(self.pan_w-bmp_w)/2.0),
int(1+(self.pan_h-bmp_h)/2.0)

                      dc

= wx.AutoBufferedPaintDC(self)

dc.Clear()

dc.DrawBitmap(bitmap, pad_w, pad_h, useMask=True)

        The 'padding'

done in the EVT_PAINT handler should help center the image.
I admit that I don’t check that at the pixel level, but the
horizontal or vertical border seems symmetric even when the
panels aspect ratio is very different from the images aspect
ratio.

        Hope that

helps.

      --Matt

Newville

  You received this message because you are subscribed to the Google

Groups “wxPython-users” group.

  To unsubscribe from this group and stop receiving emails from it,

send an email to wxpython-users+unsubscribe@googlegroups.com.

  For more options, visit [https://groups.google.com/d/optout](https://groups.google.com/d/optout).

without your code sample, it’s hard to tell if there is some subtle order of cals that’s causing yr issue. But a couple comments:

  1. check out his demo – it may be close to what you are doing:

https://github.com/PythonCHB/wxPythonDemos/blob/master/ImageWindow.py

  1. It sounds like you have issues when resizing the Window quickly – this is a tricky thing to handle – if you are getting a lot of resize events in quick succession, then you may get a new one before the code run in the previous one has run. This can be a real problem if it takes a while t draw a Window – as they will stack up. Aside from flicker, and your app having trouble keeping up, everything should stay synchronised, but, well, maybe not.

So, one option:

a) On each re-size, rather than immediately call your layout code, trigger a wx.Timer for a brief time – when the timer goes off, do the layout of your window. This way, it will be sure to get the actual Window size.

Then, to prevent overlapping layout code running, in each size event, check if there is an active timer, and cancel it if there is. This way, of you get a lot of size events in really quick succession, it will only do the layout on the last one.

(there is a chance that you could trigger the layout in a wxCallAfter, but this wouldn’t let you cancel it if the size events come in really quick succession.

-CHB

···

On Fri, Aug 17, 2018 at 2:33 PM, Tim Kuo sinkvb@gmail.com wrote:

I’m centering an image (of arbitrary
size) in a panel. Dynamically, both for arbitrary images, and when the app/frame/panel size changes.

EVT_SIZE binds to 1) getting the
image size, 2) getting the panel size, 3) scaling the image to fit in the
panel, and 4) placing the image in the panel.

Which “kind of” works. It works flawlessly for images with arbitrary dimensions. But – if I
resize the App window quickly – the resulting image is often not-quite-centered. As if panel.GetSize()
returned incorrect width/height.

Does this have something to do with
the frame size (and the size of everything in the frame) getting re-calculated?
How long does it take for the dimensions of everything in the frame (including the target panel) to be updated? If this
is the case, is there a graceful way to know when the panel size recalculation
is finished, before doing panel.GetSize() ?

You received this message because you are subscribed to the Google Groups “wxPython-users” group.

To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

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

For production, I run this on Windows, with a camera that is plugged into a dedicated NIC on that machine. But I do regularly test the code on Linux, and have run it on MacOSX in the past. For sure, performance and flicker of this sort of application can vary quite a bit and depend on many factors.

–Matt

···

On Mon, Aug 20, 2018 at 10:10 AM john fabiani` fabiani.john@gmail.com wrote:

On what platform are you using the code???