Resizable StaticBitmap

Hi all,

I'm new to this forum and to wxPython generally so im sorry if my
question seems a little noobish.
I've tried to create a simple panel that will show a static bitmap and
when it's resized the bitmap will be resized too.

here's the code:

import wx

class ResizableImage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, size = parent.GetSize())

        self.StaticBitmap = wx.StaticBitmap(self, wx.ID_ANY)
        #I dont have a reason to give a specific ID, plz comment if
you think otherwise

        # Initializing the starting Image and quality of the strech
        self.Image = wx.EmptyImage(1, 1)
        self.StrechQuality = wx.IMAGE_QUALITY_NORMAL

        # binding the size event for refreshing the Image scale
        self.Parent.Bind(wx.EVT_SIZE, self.Refresh)

    def SetStrechQuality(self, qual):
        self.StrechQuality = qual

    def SetImage(self, image):
        self.Image = image
        self.Refresh('')

    def SetBitmap(self, bitmap):
        self.Image = bitmap.ConvertToImage()
        self.Refresh('')

    def Refresh(self, event):
        self.Parent.Layout()
        SizeX, SizeY = self.GetSize()
        self.RatioX = 1.0 * SizeX / self.Image.GetWidth()
        self.RatioY = 1.0 * SizeY / self.Image.GetHeight()
        bmp = self.Image.Scale(width = SizeX, height =
SizeY).ConvertToBitmap()
        self.StaticBitmap.SetSize(self.GetSize())
        self.StaticBitmap.SetBitmap(bmp)
        pass

if __name__ == "__main__":
    #test code
    print ' Welcome '
    app = wx.PySimpleApp(0)
    Yosi = wx.Frame(None, title = 'Test', size = (500, 500))
    Yosi.panel = ResizableImage(Yosi)
    sizer = wx.BoxSizer(wx.VERTICAL)
    sizer.Add(Yosi.panel, 1, wx.EXPAND, 0)
    Yosi.SetSizer(sizer)
    sizer.Fit(Yosi)
    Yosi.Layout()
    app.SetTopWindow(Yosi)
    Yosi.Show()

    ImageLocation ='c:/test/1.jpg'
        #replace with Image Path
    im = wx.Image(ImageLocation)
    Yosi.panel.SetImage(im)
    app.MainLoop()

my problem is that when I have more then one ResizableImage classes in
a window I call the
parent.Layout() function more than once
and If I wont call it the sizer holding the panel wont resize the
Panel.
any advises?

Etai Nativ wrote:

Hi all,

I'm new to this forum and to wxPython generally so im sorry if my
question seems a little noobish.

Welcome to the group.

I've tried to create a simple panel that will show a static bitmap and
when it's resized the bitmap will be resized too.

As mentioned in the docs the wxStaticBitmap is "not meant to be a general purpose image display control" so if it will be allowed to get larger than a typical icon size for the platform then you're usually better off not using a static bitmap and just drawing it yourself in the paint event. Never the less, I do have some comments below.

here's the code:

import wx

class ResizableImage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, size = parent.GetSize())

        self.StaticBitmap = wx.StaticBitmap(self, wx.ID_ANY)
        #I dont have a reason to give a specific ID, plz comment if
you think otherwise

        # Initializing the starting Image and quality of the strech
        self.Image = wx.EmptyImage(1, 1)
        self.StrechQuality = wx.IMAGE_QUALITY_NORMAL

        # binding the size event for refreshing the Image scale
        self.Parent.Bind(wx.EVT_SIZE, self.Refresh)

This is a bad choice. A child should not handle its parent's size event for a number of reasons, one of which is the problem you've discovered where there is more than one child trying to do this. Instead the child should be handling its own size event and rely on the parent to resize the child as needed for layout.

    def SetStrechQuality(self, qual):
        self.StrechQuality = qual

    def SetImage(self, image):
        self.Image = image
        self.Refresh('')

    def SetBitmap(self, bitmap):
        self.Image = bitmap.ConvertToImage()
        self.Refresh('')

    def Refresh(self, event):

Using the name 'Refresh' hides the Refresh method in the base class.

        self.Parent.Layout()

If you want to trigger the parent's layout then it would be better to do it after you've adjusted the size of your image, however this is probably not the right place to do it anyway. When the parent does its layout it may resize the child windows, which will send size events to them, which are then going to call the parent's Layout again... I would probably just remove the Layout call from here and only use it if you are going to programatically force a resize some place else. If you are using sizers in the parent then everything should work out okay typical layout scenarios without calling Layout here.

        SizeX, SizeY = self.GetSize()
        self.RatioX = 1.0 * SizeX / self.Image.GetWidth()
        self.RatioY = 1.0 * SizeY / self.Image.GetHeight()
        bmp = self.Image.Scale(width = SizeX, height =
SizeY).ConvertToBitmap()
        self.StaticBitmap.SetSize(self.GetSize())
        self.StaticBitmap.SetBitmap(bmp)

Another thing you may want to consider is setting the min size so the parent's sizer won't try to make it smaller than the minimum. (Or you can derive from wx.PyPanel and override DoGetBestSize so you still have the minsize available to override that if needed.)

···

--
Robin Dunn
Software Craftsman

Thanks a lot for the input....
just one question, how do you suggest drawing a picture by myself?

···

On Jul 2, 7:24 pm, Robin Dunn <ro...@alldunn.com> wrote:

Etai Nativ wrote:
> Hi all,

> I'm new to this forum and to wxPython generally so im sorry if my
> question seems a little noobish.

Welcome to the group.

> I've tried to create a simple panel that will show a static bitmap and
> when it's resized the bitmap will be resized too.

As mentioned in the docs the wxStaticBitmap is "not meant to be a
general purpose image display control" so if it will be allowed to get
larger than a typical icon size for the platform then you're usually
better off not using a static bitmap and just drawing it yourself in the
paint event. Never the less, I do have some comments below.

> here's the code:

> import wx

> class ResizableImage(wx.Panel):
> def __init__(self, parent):
> wx.Panel.__init__(self, parent, size = parent.GetSize())

> self.StaticBitmap = wx.StaticBitmap(self, wx.ID_ANY)
> #I dont have a reason to give a specific ID, plz comment if
> you think otherwise

> # Initializing the starting Image and quality of the strech
> self.Image = wx.EmptyImage(1, 1)
> self.StrechQuality = wx.IMAGE_QUALITY_NORMAL

> # binding the size event for refreshing the Image scale
> self.Parent.Bind(wx.EVT_SIZE, self.Refresh)

This is a bad choice. A child should not handle its parent's size event
for a number of reasons, one of which is the problem you've discovered
where there is more than one child trying to do this. Instead the child
should be handling its own size event and rely on the parent to resize
the child as needed for layout.

> def SetStrechQuality(self, qual):
> self.StrechQuality = qual

> def SetImage(self, image):
> self.Image = image
> self.Refresh('')

> def SetBitmap(self, bitmap):
> self.Image = bitmap.ConvertToImage()
> self.Refresh('')

> def Refresh(self, event):

Using the name 'Refresh' hides the Refresh method in the base class.

> self.Parent.Layout()

If you want to trigger the parent's layout then it would be better to do
it after you've adjusted the size of your image, however this is
probably not the right place to do it anyway. When the parent does its
layout it may resize the child windows, which will send size events to
them, which are then going to call the parent's Layout again... I would
probably just remove the Layout call from here and only use it if you
are going to programatically force a resize some place else. If you are
using sizers in the parent then everything should work out okay typical
layout scenarios without calling Layout here.

> SizeX, SizeY = self.GetSize()
> self.RatioX = 1.0 * SizeX / self.Image.GetWidth()
> self.RatioY = 1.0 * SizeY / self.Image.GetHeight()
> bmp = self.Image.Scale(width = SizeX, height =
> SizeY).ConvertToBitmap()
> self.StaticBitmap.SetSize(self.GetSize())
> self.StaticBitmap.SetBitmap(bmp)

Another thing you may want to consider is setting the min size so the
parent's sizer won't try to make it smaller than the minimum. (Or you
can derive from wx.PyPanel and override DoGetBestSize so you still have
the minsize available to override that if needed.)

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

btw I solved the problem by binding the SIZE event to the panel itself
and not to his parent.
instead of:
self.Parent.Bind(wx.EVT_SIZE, self.Refresh)
I inserted
self.Bind(wx.EVT_SIZE, self.OnRefresh)
renamed the Refresh function because didn't want to override the
builtin one

I now have another problem when the pictures get repaint it goes first
to all white and then to the picture itself, I'm stating now to build
a custom bitmap reader to avoid that...

so just wanted to clarify my last question,
is there in wx some class for writing pixels directly to the screen?

···

On Jul 6, 9:31 am, Etai Nativ <etaina...@gmail.com> wrote:

Thanks a lot for the input....
just one question, how do you suggest drawing a picture by myself?

On Jul 2, 7:24 pm, Robin Dunn <ro...@alldunn.com> wrote:

> Etai Nativ wrote:
> > Hi all,

> > I'm new to this forum and to wxPython generally so im sorry if my
> > question seems a little noobish.

> Welcome to the group.

> > I've tried to create a simple panel that will show a static bitmap and
> > when it's resized the bitmap will be resized too.

> As mentioned in the docs the wxStaticBitmap is "not meant to be a
> general purpose image display control" so if it will be allowed to get
> larger than a typical icon size for the platform then you're usually
> better off not using a static bitmap and just drawing it yourself in the
> paint event. Never the less, I do have some comments below.

> > here's the code:

> > import wx

> > class ResizableImage(wx.Panel):
> > def __init__(self, parent):
> > wx.Panel.__init__(self, parent, size = parent.GetSize())

> > self.StaticBitmap = wx.StaticBitmap(self, wx.ID_ANY)
> > #I dont have a reason to give a specific ID, plz comment if
> > you think otherwise

> > # Initializing the starting Image and quality of the strech
> > self.Image = wx.EmptyImage(1, 1)
> > self.StrechQuality = wx.IMAGE_QUALITY_NORMAL

> > # binding the size event for refreshing the Image scale
> > self.Parent.Bind(wx.EVT_SIZE, self.Refresh)

> This is a bad choice. A child should not handle its parent's size event
> for a number of reasons, one of which is the problem you've discovered
> where there is more than one child trying to do this. Instead the child
> should be handling its own size event and rely on the parent to resize
> the child as needed for layout.

> > def SetStrechQuality(self, qual):
> > self.StrechQuality = qual

> > def SetImage(self, image):
> > self.Image = image
> > self.Refresh('')

> > def SetBitmap(self, bitmap):
> > self.Image = bitmap.ConvertToImage()
> > self.Refresh('')

> > def Refresh(self, event):

> Using the name 'Refresh' hides the Refresh method in the base class.

> > self.Parent.Layout()

> If you want to trigger the parent's layout then it would be better to do
> it after you've adjusted the size of your image, however this is
> probably not the right place to do it anyway. When the parent does its
> layout it may resize the child windows, which will send size events to
> them, which are then going to call the parent's Layout again... I would
> probably just remove the Layout call from here and only use it if you
> are going to programatically force a resize some place else. If you are
> using sizers in the parent then everything should work out okay typical
> layout scenarios without calling Layout here.

> > SizeX, SizeY = self.GetSize()
> > self.RatioX = 1.0 * SizeX / self.Image.GetWidth()
> > self.RatioY = 1.0 * SizeY / self.Image.GetHeight()
> > bmp = self.Image.Scale(width = SizeX, height =
> > SizeY).ConvertToBitmap()
> > self.StaticBitmap.SetSize(self.GetSize())
> > self.StaticBitmap.SetBitmap(bmp)

> Another thing you may want to consider is setting the min size so the
> parent's sizer won't try to make it smaller than the minimum. (Or you
> can derive from wx.PyPanel and override DoGetBestSize so you still have
> the minsize available to override that if needed.)

> --
> Robin Dunn
> Software Craftsmanhttp://wxPython.org

I don't do drawing myself, but from what I've read on the list, I
think the common method is using the double-buffered method. See the
wiki here:

http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing

You might also be able to use the frame's Freeze() and Thaw() methods.

- Mike

···

On Jul 6, 6:41 am, Etai Nativ <etaina...@gmail.com> wrote:

btw I solved the problem by binding the SIZE event to the panel itself
and not to his parent.
instead of:
self.Parent.Bind(wx.EVT_SIZE, self.Refresh)
I inserted
self.Bind(wx.EVT_SIZE, self.OnRefresh)
renamed the Refresh function because didn't want to override the
builtin one

I now have another problem when the pictures get repaint it goes first
to all white and then to the picture itself, I'm stating now to build
a custom bitmap reader to avoid that...

so just wanted to clarify my last question,
is there in wx some class for writing pixels directly to the screen?

Etai Nativ wrote:

Thanks a lot for the input....
just one question, how do you suggest drawing a picture by myself?

In the widget's paint event. There is an example in wx.lib.statbmp.

···

--
Robin Dunn
Software Craftsman

Etai Nativ wrote:

I now have another problem when the pictures get repaint it goes first
to all white and then to the picture itself, I

You're probably seeing the results of the default erase event. You can override that to do nothing since you are filling the whole widget in your paint event anyway.

so just wanted to clarify my last question,
is there in wx some class for writing pixels directly to the screen?

Sort of (wx.DC.DrawPoint) but you probably wouldn't want to use it as it's fairly slow. The system is instead optimized for efficiently drawing bitmaps to the screen. If you do need to be able to do raw pixel-level manipulations then you can do it to a bitmap first and then blit that to a DC when you are done.

···

--
Robin Dunn
Software Craftsman

Thanks a lot guys, this was great help for me...
I've decided using the double buffer as it is the fastest method

···

On Jul 6, 9:22 pm, Robin Dunn <ro...@alldunn.com> wrote:

Etai Nativ wrote:
> I now have another problem when the pictures get repaint it goes first
> to all white and then to the picture itself, I

You're probably seeing the results of the default erase event. You can
override that to do nothing since you are filling the whole widget in
your paint event anyway.

> so just wanted to clarify my last question,
> is there in wx some class for writing pixels directly to the screen?

Sort of (wx.DC.DrawPoint) but you probably wouldn't want to use it as
it's fairly slow. The system is instead optimized for efficiently
drawing bitmaps to the screen. If you do need to be able to do raw
pixel-level manipulations then you can do it to a bitmap first and then
blit that to a DC when you are done.

--
Robin Dunn
Software Craftsmanhttp://wxPython.org