control for displaying multiple images

I am confused about displaying images in wxPython. I have looked
through the archive and see other people with related problems, but
don't see an answer to my specific one. I am trying to create an
application for associating captions with pictures. I am using Andrea
Gavana's ThumbCtrl to display thumbnails and when I click on a
thumbnail, I want to display it at a larger size and have a textctrl
for adding a caption. The captions and there associated filename
might then be stored in some database or xml file and be used to
generate some html code for a photoalbum or something. I originally
thought that my larger picture would just be a StaticBitmap, but I
have two problems. First, there doesn't seem to be a way to change
the picture for StaticBitmap once it has been created. I need to be
able to load many different images one at a time into this control.
Second, there is a caution about the size of StaticBitmaps. Is this
still a concern? What control should I use and what methods?

Thanks,

Ryan

Ryan Krauss wrote:

I am confused about displaying images in wxPython. I have looked
through the archive and see other people with related problems, but
don't see an answer to my specific one. I am trying to create an
application for associating captions with pictures. I am using Andrea
Gavana's ThumbCtrl to display thumbnails and when I click on a
thumbnail, I want to display it at a larger size and have a textctrl
for adding a caption. The captions and there associated filename
might then be stored in some database or xml file and be used to
generate some html code for a photoalbum or something. I originally
thought that my larger picture would just be a StaticBitmap, but I
have two problems. First, there doesn't seem to be a way to change
the picture for StaticBitmap once it has been created. I need to be

StaticBitmap has a SetBitmap method and although its argument is named 'label'
it should exactly do that.

Christian

HI Ryan,

···

On 5/21/07, Ryan Krauss wrote:

I am confused about displaying images in wxPython. I have looked
through the archive and see other people with related problems, but

don’t see an answer to my specific one. I am trying to create an
application for associating captions with pictures. I am using Andrea
Gavana’s ThumbCtrl to display thumbnails and when I click on a

thumbnail, I want to display it at a larger size and have a textctrl
for adding a caption. The captions and there associated filename
might then be stored in some database or xml file and be used to
generate some html code for a photoalbum or something. I originally

thought that my larger picture would just be a StaticBitmap, but I
have two problems. First, there doesn’t seem to be a way to change
the picture for StaticBitmap once it has been created. I need to be

able to load many different images one at a time into this control.
Second, there is a caution about the size of StaticBitmaps. Is this
still a concern? What control should I use and what methods?

I wouldn’t use a StaticBitmap for that, but a wx.ScrolledWindow in which I would define my own OnPaint method to draw the selected image. This was my approach when I first wrote a small piece of software called “InfinImage”, based on ThumbnailCtrl and the aforementioned approach, with zooming, panning and other whistles and bells. I called for some volunteers in the development of InfinImage on this list, but at that time there wasn’t much interest. In my view, it would have become a competitor of Cornice (and maybe more). However, my suggestion would be to draw the image yourself: you will have much more control on it than with a normal StaticBitmap (or wx.lib.statbmp equivalent).

Andrea.

“Imagination Is The Only Weapon In The War Against Reality.”
http://xoomer.virgilio.it/infinity77/

How would I go about writing the OnPaint method?

···

On 5/21/07, Andrea Gavana <andrea.gavana@gmail.com> wrote:

HI Ryan,

On 5/21/07, Ryan Krauss wrote:
> I am confused about displaying images in wxPython. I have looked
> through the archive and see other people with related problems, but
> don't see an answer to my specific one. I am trying to create an
> application for associating captions with pictures. I am using Andrea
> Gavana's ThumbCtrl to display thumbnails and when I click on a
> thumbnail, I want to display it at a larger size and have a textctrl
> for adding a caption. The captions and there associated filename
> might then be stored in some database or xml file and be used to
> generate some html code for a photoalbum or something. I originally
> thought that my larger picture would just be a StaticBitmap, but I
> have two problems. First, there doesn't seem to be a way to change
> the picture for StaticBitmap once it has been created. I need to be
> able to load many different images one at a time into this control.
> Second, there is a caution about the size of StaticBitmaps. Is this
> still a concern? What control should I use and what methods?

I wouldn't use a StaticBitmap for that, but a wx.ScrolledWindow in which I
would define my own OnPaint method to draw the selected image. This was my
approach when I first wrote a small piece of software called "InfinImage",
based on ThumbnailCtrl and the aforementioned approach, with zooming,
panning and other whistles and bells. I called for some volunteers in the
development of InfinImage on this list, but at that time there wasn't much
interest. In my view, it would have become a competitor of Cornice (and
maybe more). However, my suggestion would be to draw the image yourself: you
will have much more control on it than with a normal StaticBitmap (or
wx.lib.statbmp equivalent).

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.virgilio.it/infinity77/

Hi Ryan,

How would I go about writing the OnPaint method?

Try this piece of code, taken from the InfinImage implementation (it requires PIL, Python Image Library).

import wx
import Image

def BitmapToPil(bitmap):

return ImageToPil(BitmapToImage(bitmap))

def BitmapToImage(bitmap):

return wx.ImageFromBitmap(bitmap)

def ImageToBitmap(image):

return image.ConvertToBitmap()

def PilToBitmap(pil):

return ImageToBitmap(PilToImage(pil))

def PilToImage(pil):

image = wx.EmptyImage(pil.size[0], pil.size[1])
image.SetData(pil.convert(‘RGB’).tostring())
return image

def ImageToPil(image):

pil = Image.new(‘RGB’,(image.GetWidth(), image.GetHeight()))
pil.fromstring(image.GetData())
return pil

class ScrolledImage(wx.ScrolledWindow):

def init(self, parent, id=-1, pos=wx.DefaultPosition,
size=wx.DefaultSize):

wx.ScrolledWindow.init(self, parent, id, pos, size)

self.size = wx.Size(0, 0)

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

def ShowImage(self, image):

PIL_bitmap = Image.open(image)
bmp = PilToBitmap(PIL_bitmap)

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

size = self.GetSize()

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

    self.currentbmp = bmp
    self.size = wx.Size(self.currentbmp.GetSize().width,
                        self.currentbmp.GetSize().height)
   
    self.Refresh()

def OnPaint(self, event):

dc = wx.PaintDC(self)
dc.Clear()

    self.DoPaint(dc)

def DoPaint(self):

self.PrepareDC(dc)

iw, ih = self.size

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

bm_dc = wx.MemoryDC()
bm_dc.SelectObject(self.currentbmp)

if xmin < 0:
xmin = 0
if ymin < 0:
ymin = 0

dc.Blit(xmin, ymin, iw, ih, bm_dc, 0, 0, wx.COPY, True)
bm_dc.SelectObject(wx.NullBitmap)

def OnEraseBackground(self, event):
   
    pass

def OnSize(self, event):

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

scroll_x = w < iw
scroll_y = h < ih

scroll = scroll_x | scroll_y

x, y = self.GetViewStart()

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

···

On 5/21/07, Ryan Krauss wrote:

On 5/21/07, Andrea Gavana andrea.gavana@gmail.com wrote:

HI Ryan,

On 5/21/07, Ryan Krauss wrote:

I am confused about displaying images in wxPython. I have looked
through the archive and see other people with related problems, but

don’t see an answer to my specific one. I am trying to create an
application for associating captions with pictures. I am using Andrea
Gavana’s ThumbCtrl to display thumbnails and when I click on a

thumbnail, I want to display it at a larger size and have a textctrl
for adding a caption. The captions and there associated filename
might then be stored in some database or xml file and be used to

generate some html code for a photoalbum or something. I originally
thought that my larger picture would just be a StaticBitmap, but I
have two problems. First, there doesn’t seem to be a way to change

the picture for StaticBitmap once it has been created. I need to be
able to load many different images one at a time into this control.
Second, there is a caution about the size of StaticBitmaps. Is this

still a concern? What control should I use and what methods?

I wouldn’t use a StaticBitmap for that, but a wx.ScrolledWindow in which I
would define my own OnPaint method to draw the selected image. This was my

approach when I first wrote a small piece of software called “InfinImage”,
based on ThumbnailCtrl and the aforementioned approach, with zooming,
panning and other whistles and bells. I called for some volunteers in the

development of InfinImage on this list, but at that time there wasn’t much
interest. In my view, it would have become a competitor of Cornice (and
maybe more). However, my suggestion would be to draw the image yourself: you

will have much more control on it than with a normal StaticBitmap (or
wx.lib.statbmp equivalent).

Andrea.

“Imagination Is The Only Weapon In The War Against Reality.”

http://xoomer.virgilio.it/infinity77/


To unsubscribe, e-mail:
wxPython-users-unsubscribe@lists.wxwidgets.org
For additional commands, e-mail: wxPython-users-help@lists.wxwidgets.org


Andrea.

“Imagination Is The Only Weapon In The War Against Reality.”
http://xoomer.virgilio.it/infinity77/

Thanks Andrea, I think I have a decent prototype. I have attached a
screen shot, my main python and wxglade files and a slightly modified
version of the code you sent me. There was one bug in it (the OnPaint
method needed dc as a second argument) and I added a method for
showing an image that fits in the space available. I also added to
the OnSize method to make it auto-resize the picture to fill the
available space, but I commented these lines back out because it makes
the resize event slow and a little choppy. I would prefer a refresh
button to this behavior I think.

Thanks for all your help. I will keep tinkering with this and add the
textctrl and the other functionality I mentioned, but I think I am off
and running now.

There are two other small bugs I will probably live with for now
unless someone else wants to tinker with them. ScrolledImage flickers
really strangely if it is not given an initial image before it is
shown. And the ThumbnailCtrl crashes violently if you zoom in much
bigger than a 200*180 or so image.

Thanks again,

Ryan

pyphotoalbum.py (4.71 KB)

pyphotoalbum.wxg (6.17 KB)

ScrolledImage.py (3.64 KB)

···

On 5/21/07, Andrea Gavana <andrea.gavana@gmail.com> wrote:

Hi Ryan,

On 5/21/07, Ryan Krauss wrote:
> How would I go about writing the OnPaint method?

Try this piece of code, taken from the InfinImage implementation (it
requires PIL, Python Image Library).

import wx
import Image

def BitmapToPil(bitmap):

    return ImageToPil(BitmapToImage(bitmap))

def BitmapToImage(bitmap):

    return wx.ImageFromBitmap(bitmap)

def ImageToBitmap(image):

    return image.ConvertToBitmap()

def PilToBitmap(pil):

    return ImageToBitmap(PilToImage(pil))

def PilToImage(pil):

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

def ImageToPil(image):

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

class ScrolledImage(wx.ScrolledWindow):

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

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

        self.size = wx.Size(0, 0)

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

    def ShowImage(self, image):

        PIL_bitmap = Image.open(image)
        bmp = PilToBitmap(PIL_bitmap)

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

        size = self.GetSize()

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

        self.currentbmp = bmp
        self.size = wx.Size(self.currentbmp.GetSize().width,
                            self.currentbmp.GetSize().height)

        self.Refresh()

    def OnPaint(self, event):

        dc = wx.PaintDC(self)
        dc.Clear()

        self.DoPaint(dc)

    def DoPaint(self):

        self.PrepareDC(dc)

        iw, ih = self.size

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

        bm_dc = wx.MemoryDC()
        bm_dc.SelectObject(self.currentbmp)

        if xmin < 0:
            xmin = 0
        if ymin < 0:
            ymin = 0

        dc.Blit(xmin, ymin, iw, ih, bm_dc, 0, 0, wx.COPY, True)
        bm_dc.SelectObject(wx.NullBitmap)

    def OnEraseBackground(self, event):

        pass

    def OnSize(self, event):

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

        scroll_x = w < iw
        scroll_y = h < ih

        scroll = scroll_x | scroll_y

        x, y = self.GetViewStart()

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

>
> On 5/21/07, Andrea Gavana <andrea.gavana@gmail.com> wrote:
> > HI Ryan,
> >
> > On 5/21/07, Ryan Krauss wrote:
> > > I am confused about displaying images in wxPython. I have looked
> > > through the archive and see other people with related problems, but
> > > don't see an answer to my specific one. I am trying to create an
> > > application for associating captions with pictures. I am using Andrea
> > > Gavana's ThumbCtrl to display thumbnails and when I click on a
> > > thumbnail, I want to display it at a larger size and have a textctrl
> > > for adding a caption. The captions and there associated filename
> > > might then be stored in some database or xml file and be used to
> > > generate some html code for a photoalbum or something. I originally
> > > thought that my larger picture would just be a StaticBitmap, but I
> > > have two problems. First, there doesn't seem to be a way to change
> > > the picture for StaticBitmap once it has been created. I need to be
> > > able to load many different images one at a time into this control.
> > > Second, there is a caution about the size of StaticBitmaps. Is this
> > > still a concern? What control should I use and what methods?
> >
> > I wouldn't use a StaticBitmap for that, but a wx.ScrolledWindow in which
I
> > would define my own OnPaint method to draw the selected image. This was
my
> > approach when I first wrote a small piece of software called
"InfinImage",
> > based on ThumbnailCtrl and the aforementioned approach, with zooming,
> > panning and other whistles and bells. I called for some volunteers in
the
> > development of InfinImage on this list, but at that time there wasn't
much
> > interest. In my view, it would have become a competitor of Cornice (and
> > maybe more). However, my suggestion would be to draw the image yourself:
you
> > will have much more control on it than with a normal StaticBitmap (or
> > wx.lib.statbmp equivalent).
> >
> > Andrea.
> >
> > "Imagination Is The Only Weapon In The War Against Reality."
> > http://xoomer.virgilio.it/infinity77/
>
---------------------------------------------------------------------
> To unsubscribe, e-mail:
wxPython-users-unsubscribe@lists.wxwidgets.org
> For additional commands, e-mail:
wxPython-users-help@lists.wxwidgets.org
>

--

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.virgilio.it/infinity77/

Andrea Gavana wrote:

Try this piece of code, taken from the InfinImage implementation (it requires PIL, Python Image Library).

Why PIL? I now it is pretty capable, but wxImage does everything you need for this -- is PIL faster or better quality?

Ryan Krauss wrote:

I also added to
the OnSize method to make it auto-resize the picture to fill the
available space, but I commented these lines back out because it makes
the resize event slow and a little choppy.

The issue here is that some systems give a Size event every couple of pixel moves of the mouse as you re-size frames -- the ideas is that app keeps updating itself while in the re-size process. Nifty, but ugly if re-painting to new size takes any time, as you've seen. I think you can turn that off in Windows and certainly Linux, but that requires the user to change their settings.

A better solution is to set off a wx.Timer when you get a Size event, then keep resetting it each event. Only when the Timer finishes (maybe about 0.1 or 0.2 seconds), i.e. when the user has stopped moving, do you actually do the re-size.

There is an implementation of this in wx.lib.floatcanvas.FloatCanvas.py, if you want to take a look.

-Chris

PS: This is looking nice -- maybe you can merge back with Andrea's code, and post it up with all of his other great stuff.

···

--
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

Hi Chris,

Andrea Gavana wrote:
> Try this piece of code, taken from the InfinImage implementation (it
> requires PIL, Python Image Library).

Why PIL? I now it is pretty capable, but wxImage does everything you
need for this -- is PIL faster or better quality?

There is no particular reason for that. I simply took part of the code
I implemented in InfinImage and pasted here. I know wx can do it
without problems, but as I am using it in ThumbnailCtrl it gets
imported anyway. I have never tried a comparison between
PIL.thumbnail() and wx.Image.Resize() (or whatever is called), but
it's worth trying to see which one is the fastest.

PS: This is looking nice -- maybe you can merge back with Andrea's code,
and post it up with all of his other great stuff.

I am open to all suggestions and also to co-operate in the
development/enhancement of new things :smiley:

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.virgilio.it/infinity77/

···

On 5/21/07, Christopher Barker wrote:

Ryan Krauss wrote:

I am confused about displaying images in wxPython. I have looked
through the archive and see other people with related problems, but
don't see an answer to my specific one. I am trying to create an
application for associating captions with pictures. I am using Andrea
Gavana's ThumbCtrl to display thumbnails and when I click on a
thumbnail, I want to display it at a larger size and have a textctrl
for adding a caption. The captions and there associated filename
might then be stored in some database or xml file and be used to
generate some html code for a photoalbum or something. I originally
thought that my larger picture would just be a StaticBitmap, but I
have two problems. First, there doesn't seem to be a way to change
the picture for StaticBitmap once it has been created.

  statbmp.SetBitmap(newBitmap)

I need to be
able to load many different images one at a time into this control.
Second, there is a caution about the size of StaticBitmaps. Is this
still a concern? What control should I use and what methods?

I think it is still a problem only in win98 or winMe, but probably not on the other platforms. If it is a concern for you then you can use the generic widget in wx.lib.statbmp, or you could derive your own class from that one and add any custom functionality you need. OTOH, as others have already said it's not too hard to draw a bitmap yourself in a window's EVT_PAINT handler. Then you can be even more flexible, and better handle things like scrolling. You can look at the code in statbmp.py for a simple example.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Andrea Gavana wrote:

There is no particular reason for that. I simply took part of the code
I implemented in InfinImage and pasted here.

Which begs the question: why did you use PIL there? It's not a big deal, but it's always nice to have minimal dependencies.

Nice stuff, in any case.

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

Hi Chris,

Andrea Gavana wrote:
> There is no particular reason for that. I simply took part of the code
> I implemented in InfinImage and pasted here.

Which begs the question: why did you use PIL there? It's not a big deal,
but it's always nice to have minimal dependencies.

I should have answered in my previous post: when I first wrote
ThumbnailCtrl , back in December 2005, PIL was a bit faster in
generating thumbnails.
However, I took the chance to compare wxPython and PIL in thumbnails
generation speed. Basically I compared:

1) Image.thumbnail() vs wx.Image.Scale(), with a bunch of jpeg
pictures in one of my directories;
2) Different image qualities (nearest or bicubic interpolation);
3) I took the mean time over 3 loops.

It is a fairly simple script (attached to this email): you can try it
out for yourself. You can change the folder location, the thumbnail
size, the image extensions and the number of loops on which the mean
value is calculated.

From my rough tests, it looks like PIL is 30 to 40% faster than

wxPython for the nearest interpolation method and 50 to 60% faster
with the bicubic/antialias method.
In any case, I attach also the text file with the results, if someone
is interested. I may be overlooked something and my tests are quite
rough (I tested only jpeg, only 160 images, images not bigger than
1000x700 and so on), so please enlighten my ignorance if I have made
some stupid mistake in my tests.

This has been tested on Windows XP SP2, AMD Athlon 3.7 GHz, 1GB RAM,
Python 2.5, wxPython 2.8.4.0 unicode.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.virgilio.it/infinity77/

PilVsWx.py (3.86 KB)

PilVsWx.txt (29.3 KB)

···

On 5/21/07, Christopher Barker wrote:

Andrea Gavana wrote:

However, I took the chance to compare wxPython and PIL in thumbnails
generation speed. Basically I compared:

Thanks, Andrea. I suppose I should try this on some other systems, and see if the results are similar.

From my rough tests, it looks like PIL is 30 to 40% faster than
wxPython for the nearest interpolation method and 50 to 60% faster
with the bicubic/antialias method.

Interesting -- I wonder why? In any case, this doesn't look like enough performance to matter in most cases, but I'm doing some work with some pretty large images in the future -- I'll keep this in mind of wx seems slow.

so please enlighten my ignorance if I have made
some stupid mistake in my tests.

They look good to me!

Thanks, this is good to know.

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