1

I am looking for a simple solution to display thumbnails using wxPython. This is not about creating the thumbnails. I have a directory of thumbnails and want to display them on the screen. I am purposely not using terms like (Panel, Frame, Window, ScrolledWindow) because I am open to various solutions.

Also note I have found multiple examples for displaying a single image, so referencing any such solution will not help me. The solution must be for displaying multiple images at the same time in wx.

It seems that what I want to do is being done in ThumbnailCtrl, but Andrea's code is complex and I cannot find the portion that does the display to screen. I did find a simple solution in Mark Lutz's Programming Python book, but while his viewer_thumbs.py example definitely has the simplicity that I am looking for, it was done using Tkinter.

So please any wx solution will be greatly appreciated.

EDIT: I am adding a link to one place where Mark Lutz's working Tkinter code can be found. Can anyone think of a wx equivalent?

http://codeidol.com/community/python/viewing-and-processing-images-with-pil/17565/#part-33

Karr
  • 127
  • 12
  • I almost have the solution, just working on tweaking the code. Basically is going to involve dynamically creating sizers. I will post when I have the bugs worked out. If anyone has a reason why this solution is not practical please comment. – Karr Mar 18 '14 at 21:42

3 Answers3

2

I would recommend using the ThumbNailCtrl widget: http://wxpython.org/Phoenix/docs/html/lib.agw.thumbnailctrl.html. There is a good example in the wxPython demo. Or you could use this one from the documentation. Note that the ThumbNailCtrl requires the Python Imaging Library to be installed.

import os

import wx
import wx.lib.agw.thumbnailctrl as TC

class MyFrame(wx.Frame):

    def __init__(self, parent):

        wx.Frame.__init__(self, parent, -1, "ThumbnailCtrl Demo")

        panel = wx.Panel(self)

        sizer = wx.BoxSizer(wx.VERTICAL)

        thumbnail = TC.ThumbnailCtrl(panel, imagehandler=TC.NativeImageHandler)
        sizer.Add(thumbnail, 1, wx.EXPAND | wx.ALL, 10)

        thumbnail.ShowDir(os.getcwd())
        panel.SetSizer(sizer)


# our normal wxApp-derived class, as usual

app = wx.App(0)

frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()

app.MainLoop()

Just change the line thumbnail.ShowDir(os.getcwd()) so that it points at the right folder on your machine.

I also wrote up an article for viewing photos here: http://www.blog.pythonlibrary.org/2010/03/26/creating-a-simple-photo-viewer-with-wxpython/ It doesn't use thumbnails though.

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • Thanks for the response. I am currently and successfully using the Thumbnailctrl widget and if need be I will continue to use it. It is just "more" than I need and was hoping to find something more basic. I have also seen your article on viewing images. I have been referencing it for months and have learned a lot from that tutorial!! Thanks again for that!! – Karr Mar 17 '14 at 15:38
2

Not sure if I am supposed to answer my own question but I did find a solution to my problem and I wanted to share. I was using wx version 2.8. I found that in 2.9 and 3.0 there was a widget added called WrapSizer. Once I updated my version of wx to 3.0 that made the solution beyond simple. Here are the code snippets that matter.

    self.PhotoMaxWidth = 100
    self.PhotoMaxHeight = 100

    self.GroupOfThumbnailsSizer = wx.WrapSizer()      

    self.CreateThumbNails(len(ListOfPhotots),ListOfPhotots)

    self.GroupOfThumbnailsSizer.SetSizeHints(self.whateverPanel) 
    self.whateverPanel.SetSizer(self.GroupOfThumbnailsSizer)

    self.whateverPanel.Layout()


def CreateThumbNails(self, n, ListOfFiles):
    thumbnails = []
    backgroundcolor = "white"

    for i in range(n):

        ThumbnailSizer = wx.BoxSizer(wx.VERTICAL)
        self.GroupOfThumbnailsSizer.Add(ThumbnailSizer, 0, 0, 0)
        thumbnails.append(ThumbnailSizer)

    for thumbnailcounter, thumbsizer in enumerate(thumbnails):

        image = Image.open(ListOfFiles[thumbnailcounter])

        image = self.ResizeAndCenterImage(image, self.PhotoMaxWidth, self.PhotoMaxHeight, backgroundcolor)

        img = self.pil_to_image(image)

        thumb= wx.StaticBitmap(self.timelinePanel, wx.ID_ANY, wx.BitmapFromImage(img))

        thumbsizer.Add(thumb, 0, wx.ALL, 5)

    return

def pil_to_image(self, pil, alpha=True):
    """ Method will convert PIL Image to wx.Image """
    if alpha:
        image = apply( wx.EmptyImage, pil.size )
        image.SetData( pil.convert( "RGB").tostring() )
        image.SetAlphaData(pil.convert("RGBA").tostring()[3::4])
    else:
        image = wx.EmptyImage(pil.size[0], pil.size[1])
        new_image = pil.convert('RGB')
        data = new_image.tostring()
        image.SetData(data)
    return image

def ResizeAndCenterImage(self, image, NewWidth, NewHeight, backgroundcolor):
    width_ratio = NewWidth / float(image.size[0])
    temp_height = int(image.size[1] * width_ratio)
    if temp_height < NewHeight:
        img2 = image.resize((NewWidth, temp_height), Image.ANTIALIAS)
    else:
        height_ratio = NewHeight / float(image.size[1])
        temp_width = int(image.size[0] * height_ratio)
        img2 = image.resize((temp_width, NewHeight), Image.ANTIALIAS)

    background = Image.new("RGB", (NewWidth, NewHeight), backgroundcolor)
    masterwidth = background.size[0]
    masterheight = background.size[1]
    subwidth = img2.size[0]
    subheight = img2.size[1]
    mastercenterwidth = masterwidth // 2
    mastercenterheight = masterheight // 2
    subcenterwidth = subwidth // 2
    subcenterheight = subheight // 2
    insertpointwidth = mastercenterwidth - subcenterwidth
    insertpointheight = mastercenterheight - subcenterheight
    background.paste(img2, (insertpointwidth, insertpointheight))

    return background

I got the pil_to_image portion from another stackoverflow post and I wrote the ResizeAndCenterImage portion to make all of my thumbnails the same size while keeping the aspect ration intact and not do any cropping. The resize and center call can be skipped all together if you like.

Karr
  • 127
  • 12
0

I would just display them as wx.Image inside a frame.

http://www.wxpython.org/docs/api/wx.Image-class.html

From the class: "A platform-independent image class. An image can be created from data, or using wx.Bitmap.ConvertToImage, or loaded from a file in a variety of formats. Functions are available to set and get image bits, so it can be used for basic image manipulation."

Seems it should be able to do what you want, unless I'm missing something.

User
  • 23,729
  • 38
  • 124
  • 207
  • thanks for the response. Can you give me a code example for multiple images. If I am displaying just one image I know how to display it as a wx.image. With a directory of an unknown amount of images within, I do not know how to display all at the same time. – Karr Mar 17 '14 at 00:29