Strange bitmap update behavior in wxPython

I am trying to write an image control point selector function like MATLAB’s cpselect (http://www.mathworks.com/help/images/ref/cpselect.html) using wxPython. However, I have run into strange behavior that I do not understand. I have images inside a panel, and I would like the images to resize appropriately when the frame is resized. I call GetSize() on the frame, size the image equal to the panel size, and then update the wxBitmap. However, for some reason, the size return by GetSize is wrong the first time it is called. I have to set the bitmap and call it again to get the proper size. The attached code is the smallest subset I could create to recreate the problem.

When you run it, draw_image is called, and the panel size output to stdout is wrong. If you left-click on the image, a wx.EVT_LEFT_DOWN event calls draw_image again, and this time the panel size is correct. Then, when you click again and draw_image is called for a third time, the image is finally how it should be. I am not sure what is going on or if I am missing some sort of update statement so that the image is correct from the first call.

program.pyw (2.29 KB)

Sorry, no time right now to try things out, but I suspect you’d have better luck not using a StaticBitmap, but rather, making your own simple subclass of wx.Window, and drawing the image on it with a DC.

I would do:

cache a wx.Bitmap in the class

define an EVT_PAINT handler that draws the wx.Bitmap at (0,0) - really easy

define an EVT_SIZE handler that re-scales the cached wx.Bitmap to the new size.

define your mouse events however you want…

Another option – depending on how far you want to go with this, you could use wx.lib.floatcanvas, and a ScaledBitmap Object.

-Chris

···

On Mon, Sep 23, 2013 at 6:46 PM, hp34234 helicopter34234@gmail.com wrote:

I am trying to write an image control point selector function like MATLAB’s cpselect (http://www.mathworks.com/help/images/ref/cpselect.html) using wxPython. However, I have run into strange behavior that I do not understand. I have images inside a panel, and I would like the images to resize appropriately when the frame is resized. I call GetSize() on the frame, size the image equal to the panel size, and then update the wxBitmap. However, for some reason, the size return by GetSize is wrong the first time it is called. I have to set the bitmap and call it again to get the proper size. The attached code is the smallest subset I could create to recreate the problem.

When you run it, draw_image is called, and the panel size output to stdout is wrong. If you left-click on the image, a wx.EVT_LEFT_DOWN event calls draw_image again, and this time the panel size is correct. Then, when you click again and draw_image is called for a third time, the image is finally how it should be. I am not sure what is going on or if I am missing some sort of update statement so that the image is correct from the first call.

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/groups/opt_out.

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

I couldn't help myself -- for some reason, I enjoy playing with this stuff.
So, enclosed is a wxWindow subclass that auto-sized an image to fit in the
Window. This example references an image in my repository, or you can pass
a filename in on the command liine.

You'll probably want to add more to this-- perhaps controlling the quality
of the rescaling, preserving the aspect ration of the image, capturing
mouse events and figuring out the pixel coords of the original image, etc.

Note that wx.StaticBitmap is not always a "real" Window -- on some
platforms, it is simply a bitmap drawn on the parent. Thus if you get this
to work with a StaticBitmap on one platform, it may not work on another.
That's at least one reason I used a wx.Window subclass.

This is now part of my wxPython Demos collection:

You may find other useful examples there. Also, if you add stuff to this,
I'd love a pull request.

HTH,
   -Chris

AutoSizeBitmap.py (1.29 KB)

···

On Tue, Sep 24, 2013 at 9:25 AM, Chris Barker <chris.barker@noaa.gov> wrote:

Sorry, no time right now to try things out, but I suspect you'd have
better luck not using a StaticBitmap, but rather, making your own simple
subclass of wx.Window, and drawing the image on it with a DC.

--

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

Thanks for the feedback Chris. I have been messing with it since my post. One of the things I realized I needed to add was binding wx.EVT_PAINT to a function that resizes the image and redraws it. Binding to wx.EVT_SIZE doesn’t seem to get triggered on a maximize of the window, . But, the kicker that took me forever to realize was that I needed to include an event.Skip() at the end, although I am not entirely clear exactly what it does. I also decided to use dc = event.GetDC() and dc.DrawBitmap that is triggered by a wx.EVT_ERASE_BACKGROUND event. This makes it easy to draw on top of the bitmap without changing the bitmap itself.

Your AutoSizeBitmap.py seems to be missing something. When I resize the image, it doesn’t repaint. I have to damage the window (e.g., move another window on top of it) to force it to redraw.

I am going to be working on this some more tonight, so I will probably have a few more questions.

···

On Monday, September 23, 2013 9:46:08 PM UTC-4, hp34234 wrote:

I am trying to write an image control point selector function like MATLAB’s cpselect (http://www.mathworks.com/help/images/ref/cpselect.html) using wxPython. However, I have run into strange behavior that I do not understand. I have images inside a panel, and I would like the images to resize appropriately when the frame is resized. I call GetSize() on the frame, size the image equal to the panel size, and then update the wxBitmap. However, for some reason, the size return by GetSize is wrong the first time it is called. I have to set the bitmap and call it again to get the proper size. The attached code is the smallest subset I could create to recreate the problem.

When you run it, draw_image is called, and the panel size output to stdout is wrong. If you left-click on the image, a wx.EVT_LEFT_DOWN event calls draw_image again, and this time the panel size is correct. Then, when you click again and draw_image is called for a third time, the image is finally how it should be. I am not sure what is going on or if I am missing some sort of update statement so that the image is correct from the first call.

Thanks for the feedback Chris. I have been messing with it since my
post. One of the things I realized I needed to add was binding
wx.EVT_PAINT to a function that resizes the image and redraws it. Binding
to wx.EVT_SIZE doesn't seem to get triggered on a maximize of the window.

hmm -- odd. It sure should. Could be a platform or version issue. I'm
testing this right now on OS-X, python2.7, wx 2.8.12.1.

But if you're not getting a size event with a maximize, that sure sounds
like a bug to me.

But, the kicker that took me forever to realize was that I needed to
include an event.Skip() at the end, although I am not entirely clear
exactly what it does.

End of your Paint handler? That's usually not required. But what
event.Skip() does is tell the system to keep looking ofr even handlers for
this event -- sometimes the system still needs to do something with it.

I also decided to use dc = event.GetDC() and dc.DrawBitmap that is
triggered by a wx.EVT_ERASE_BACKGROUND event. This makes it easy to draw
on top of the bitmap without changing the bitmap itself.

Usually, you draw on top of the bitmap in the paint event, so as long as
you are drawing the bitmap at the beginnin gof the paint event, you should
be all set.

Your AutoSizeBitmap.py seems to be missing something. When I resize the

image, it doesn't repaint. I have to damage the window (e.g., move another
window on top of it) to force it to redraw.

Odd again, this is probably a platform issue -- but what it seems to mean
is that there isn't a paint event triggered when the window is re-sized,
which is very odd.

It might be helpful for you to post a small sample of what you're doing.

-Chris

···

On Sat, Sep 28, 2013 at 3:48 PM, hp34234 <helicopter34234@gmail.com> wrote:

--

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

I am on Win7 64-bit

Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]
wxPython 2.8.12.1-1

I will have to look into all of this further when I get a chance. Thanks so much for your help so far.

···

On Saturday, September 28, 2013 10:33:50 PM UTC-4, Chris Barker wrote:

On Sat, Sep 28, 2013 at 3:48 PM, hp34234 helicop...@gmail.com wrote:

Thanks for the feedback Chris. I have been messing with it since my post. One of the things I realized I needed to add was binding wx.EVT_PAINT to a function that resizes the image and redraws it. Binding to wx.EVT_SIZE doesn’t seem to get triggered on a maximize of the window.

hmm – odd. It sure should. Could be a platform or version issue. I’m testing this right now on OS-X, python2.7, wx 2.8.12.1.

But if you’re not getting a size event with a maximize, that sure sounds like a bug to me.

But, the kicker that took me forever to realize was that I needed to include an event.Skip() at the end, although I am not entirely clear exactly what it does.

End of your Paint handler? That’s usually not required. But what event.Skip() does is tell the system to keep looking ofr even handlers for this event – sometimes the system still needs to do something with it.

I also decided to use dc = event.GetDC() and dc.DrawBitmap that is triggered by a wx.EVT_ERASE_BACKGROUND event. This makes it easy to draw on top of the bitmap without changing the bitmap itself.

Usually, you draw on top of the bitmap in the paint event, so as long as you are drawing the bitmap at the beginnin gof the paint event, you should be all set.

Your AutoSizeBitmap.py seems to be missing something. When I resize the image, it doesn’t repaint. I have to damage the window (e.g., move another window on top of it) to force it to redraw.

Odd again, this is probably a platform issue – but what it seems to mean is that there isn’t a paint event triggered when the window is re-sized, which is very odd.

It might be helpful for you to post a small sample of what you’re doing.

-Chris

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....@noaa.gov

I am on Win7 64-bit
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]
wxPython 2.8.12.1-1

interesting, off the cuff, I would have expected more difference with wx

2.9 that Win/Mac in this case. But something is up -- yuo should certainly
get an EVT_SIZE when Maximize is hit.

but a kludge: bind EVT_MAXIMIZE to the Onsize method, and you should be set.

And for what it's worth, my code works fine on the Mac with wxPython 2.9.4.0

- Chris

···

On Mon, Sep 30, 2013 at 6:55 AM, hp34234 <helicopter34234@gmail.com> wrote:

I will have to look into all of this further when I get a chance. Thanks
so much for your help so far.

On Saturday, September 28, 2013 10:33:50 PM UTC-4, Chris Barker wrote:

On Sat, Sep 28, 2013 at 3:48 PM, hp34234 <helicop...@gmail.com> wrote:

Thanks for the feedback Chris. I have been messing with it since my
post. One of the things I realized I needed to add was binding
wx.EVT_PAINT to a function that resizes the image and redraws it. Binding
to wx.EVT_SIZE doesn't seem to get triggered on a maximize of the window.

hmm -- odd. It sure should. Could be a platform or version issue. I'm
testing this right now on OS-X, python2.7, wx 2.8.12.1.

But if you're not getting a size event with a maximize, that sure sounds
like a bug to me.

But, the kicker that took me forever to realize was that I needed to
include an event.Skip() at the end, although I am not entirely clear
exactly what it does.

End of your Paint handler? That's usually not required. But what
event.Skip() does is tell the system to keep looking ofr even handlers for
this event -- sometimes the system still needs to do something with it.

I also decided to use dc = event.GetDC() and dc.DrawBitmap that is
triggered by a wx.EVT_ERASE_BACKGROUND event. This makes it easy to draw
on top of the bitmap without changing the bitmap itself.

Usually, you draw on top of the bitmap in the paint event, so as long as
you are drawing the bitmap at the beginnin gof the paint event, you should
be all set.

Your AutoSizeBitmap.py seems to be missing something. When I resize the

image, it doesn't repaint. I have to damage the window (e.g., move another
window on top of it) to force it to redraw.

Odd again, this is probably a platform issue -- but what it seems to mean
is that there isn't a paint event triggered when the window is re-sized,
which is very odd.

It might be helpful for you to post a small sample of what you're doing.

-Chris

--

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....@noaa.gov

--
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/groups/opt_out.

--

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

@Chris

On Windows, you code doesn’t repaint correctly when resizing. We need:

b = AutoSizeBitmap(f, img, style=wx.FULL_REPAINT_ON_RESIZE)

or add a line at the bottom of function ‘OnSize’:

self.Refresh()

Mark

在 2013-10-01 02:26:55,“Chris Barker” chris.barker@noaa.gov 写道:

···

I am on Win7 64-bit

Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]
wxPython 2.8.12.1-1

interesting, off the cuff, I would have expected more difference with wx 2.9 that Win/Mac in this case. But something is up – yuo should certainly get an EVT_SIZE when Maximize is hit.

but a kludge: bind EVT_MAXIMIZE to the Onsize method, and you should be set.

And for what it’s worth, my code works fine on the Mac with wxPython 2.9.4.0

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

  • Chris

On Windows, you code doesn't repaint correctly when resizing. We need:

    b = AutoSizeBitmap(f, img, style=wx.FULL_REPAINT_ON_RESIZE)

Ah -- that explains it.

or add a line at the bottom of function 'OnSize':

    self.Refresh()

yup -- that's clean and easy.

I thought FULL_REPAINT_ON_RESIZE was the default, but I guess not. It seems
a bit easier to call Refresh(), so I've added that.

fixed in git.

-Chris

···

On Mon, Sep 30, 2013 at 12:21 PM, Mark Weng <wenkuoweng@163.com> wrote:

Mark

在 2013-10-01 02:26:55,"Chris Barker" <chris.barker@noaa.gov> 写道:

On Mon, Sep 30, 2013 at 6:55 AM, hp34234 <helicopter34234@gmail.com>wrote:

I am on Win7 64-bit
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]
wxPython 2.8.12.1-1

interesting, off the cuff, I would have expected more difference with wx

2.9 that Win/Mac in this case. But something is up -- yuo should certainly
get an EVT_SIZE when Maximize is hit.

but a kludge: bind EVT_MAXIMIZE to the Onsize method, and you should be
set.

And for what it's worth, my code works fine on the Mac with
wxPython 2.9.4.0

wxPythonDemos/AutoSizeBitmap.py at master · PythonCHB/wxPythonDemos · GitHub

- Chris

--
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/groups/opt_out.

--

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

Chris Barker wrote:

    On Windows, you code doesn't repaint correctly when resizing. We need:

         b = AutoSizeBitmap(f, img, style=wx.FULL_REPAINT_ON_RESIZE)

Ah -- that explains it.

    or add a line at the bottom of function 'OnSize':

         self.Refresh()

yup -- that's clean and easy.

I thought FULL_REPAINT_ON_RESIZE was the default, but I guess not. It
seems a bit easier to call Refresh(), so I've added that.

It used to be the default, but I think it was changed around 2.5 or perhaps earlier.

Here is a little info for those who are not familiar with this flag: By default when a resize happens the update region (the area of the window that is marked as damaged) will only include the newly exposed regions of the window. The areas of the window that were visible before will be clipped out of the update region and so they will be excluded from the paint. This is a system level optimization that is intended to reduce overhead and also reduce flicker, especially on Windows. In many (most?) cases this works well and is the way that you want the window to be refreshed, to have it just show a new area of your drawing or whatever. However when the window draws things relative to the window size then you do want all of the pixels in the window to be redrawn when the size changes, and using the wx.FULL_REPAINT_ON_RESIZE style will turn off the optimization, or using Refresh() will circumvent it.

And to add a little variety to the mix, on OSX the full window is always marked as damaged after a resize. IIRC the way that OSX handles drawing and compositing the window onto the display means that an optimization like the above is not needed, or can't work the same way, or something like that.

···

On Mon, Sep 30, 2013 at 12:21 PM, Mark Weng <wenkuoweng@163.com > <mailto:wenkuoweng@163.com>> wrote:

--
Robin Dunn
Software Craftsman

Chris Barker wrote:

    But, the kicker that took me forever to realize was that I needed
    to include an event.Skip() at the end, although I am not entirely
    clear exactly what it does.

End of your Paint handler? That's usually not required. But what
event.Skip() does is tell the system to keep looking ofr even handlers
for this event -- sometimes the system still needs to do something with it.

On windows if the EVT_PAINT handler doesn't actually create and use a wx.PaintDC then the system will assume that it still needs to be done and will send the event again. And again. And again... Calling event.Skip will allow the default event handler to still be run even though you have handled the event yourself. However, if you feel the need to have a EVT_PAINT handler without drawing anything to a wx.PaintDC then there is likely something wrong with the design or use of the window class, or there is a bug.

···

On Sat, Sep 28, 2013 at 3:48 PM, hp34234 <helicopter34234@gmail.com > <mailto:helicopter34234@gmail.com>> wrote:

--
Robin Dunn
Software Craftsman