I want to cycle through all the images in a folder and .display the images sized to fit in a container, and centred, while maintaining the aspect ratio. I also want any unused areas of the container to be black.
I am having several issues.
While the wxPython docs show me the methods and parameters, the methods and properties don’t always do what I expect. In many cases the description just parrots the method name without explaining what it does. For example, I have a Static Bitmap that I do
self.bitmap.SetScaleMode(wx.StaticBitmap.Scale_AspectFit)
However, this doesn’t scale to fit. If an image is too large it is cropped. So I wrote my own scale method which works fine. Except that I would like the background to be black.
The first picture is being very small. I am currently displaying images only fullscreen do for now I have the onsize event disabled.
Can anyone offer suggestions? There is a lot of content in the wxPython docs regarding images and bitmaps, but very little on how to actually use them. Here is my code. Keep in mind it’s a work in progress.
import os
import sys
import wx
import wx.lib.mixins.inspection
ID_WHITE_BG = wx.NewIdRef()
ID_BLACK_BG = wx.NewIdRef()
ID_GREY_BG = wx.NewIdRef()
ID_CHECK_BG = wx.NewIdRef()
ID_NO_FRAME = wx.NewIdRef()
ID_BOX_FRAME = wx.NewIdRef()
ID_CROP_FRAME = wx.NewIdRef()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY, "")
self.frame.SetSize(wx.DisplaySize())
self.SetTopWindow(self.frame)
self.frame.ShowFullScreen(True, wx.FULLSCREEN_ALL)
return True
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: MyFrame.__init__
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.SetTitle("ImageView")
panel = ImagePanel(self)
class ImagePanel(wx.Panel):
"""
This control implements a basic image viewer. As the control is
resized the image is resized (aspect preserved) to fill the panel.
Methods:
LoadImage(filename)
"""
def __init__(self, parent, id=wx.ID_ANY,
pos=wx.DefaultPosition,
size=wx.DefaultSize,
style=wx.BORDER_SUNKEN
):
wx.Window.__init__(self, parent, id, pos, size, style=style)
self.bitmap = wx.StaticBitmap(self, wx.ID_ANY)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.bitmap, 1, wx.EXPAND, 0)
self.SetSizer(sizer)
self.image = None
self.btnPrev = wx.Button(self, wx.ID_ANY, 'Prev')
self.btnPrev.Visible = False
self.Bind(wx.EVT_BUTTON, self.evt_prev, self.btnPrev)
self.btnNext = wx.Button(self, wx.ID_ANY, 'Next')
self.btnNext.Visible = False
self.Bind(wx.EVT_BUTTON, self.evt_next, self.btnNext)
self.btnExit = wx.Button(self, wx.ID_ANY, 'Exit')
self.btnExit.Visible = False
self.Bind(wx.EVT_BUTTON, self.evt_exit, self.btnExit)
#self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_MOUSEWHEEL, self.on_scroll)
#Define hotkeys
hotkeys = [wx.AcceleratorEntry() for i in range(5)]
hotkeys[0].Set(wx.ACCEL_NORMAL, wx.WXK_DOWN, self.btnNext.Id)
hotkeys[1].Set(wx.ACCEL_NORMAL, wx.WXK_UP, self.btnPrev.Id)
hotkeys[2].Set(wx.ACCEL_NORMAL, wx.WXK_RIGHT, self.btnNext.Id)
hotkeys[3].Set(wx.ACCEL_NORMAL, wx.WXK_LEFT, self.btnPrev.Id)
hotkeys[4].Set(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, self.btnExit.Id)
accel = wx.AcceleratorTable(hotkeys)
self.SetAcceleratorTable(accel)
# Get a list of all image files in the given folder
self.files = self.GetFiles(os.getcwd())
if not self.files:
sys.exit()
self.index = 0
self.maxindex = len(self.files) - 1
# Display the first image in the folder
self.LoadImage(self.files[0])
def evt_next(self, evt):
"""Display the next image in the list"""
self.index = min(self.maxindex, self.index+1)
self.LoadImage(self.files[self.index])
if evt: evt.Skip()
def evt_prev(self, evt):
"""Display the previous image in the list"""
self.index = max(0, self.index-1)
self.LoadImage(self.files[self.index])
if evt: evt.Skip()
def evt_exit(self, evt):
sys.exit()
def on_scroll(self, evt):
"""Display next or previous image depending on scroll direction"""
if evt.WheelRotation < 0:
self.evt_next(None)
else:
self.evt_prev(None)
evt.Skip()
def OnSize(self, event):
"""Resize image too fit container and redisplay"""
self.image = self.ScaleToFit(self.image)
self.DisplayImage(self.image)
event.Skip()
def LoadImage(self, file):
"""Load an image from a file, scale to fit, and display"""
self.image = wx.Image(file, wx.BITMAP_TYPE_ANY)
self.image = self.ScaleToFit(self.image)
self.DisplayImage(self.image)
def DisplayImage(self, image):
self.bitmap.SetBitmap(self.image.ConvertToBitmap())
def ScaleToFit(self, image):
"""Scale an image to fit its container"""
# Get imsge size and container size
cw, ch = self.Parent.GetSize()
iw, ih = self.image.GetSize()
aspect = ih / iw
nw = cw
nh = int(nw * aspect)
if nh > ch:
nh = ch
nw = int(nh / aspect)
return image.Scale(nw, nh)
def GetFiles(self, folder):
"""Return a list of all image files in the given folder"""
files = []
images = ('.jpg','.jpeg','.gif','.png')
for item in os.scandir(folder):
#print(item.name)
ext = os.path.splitext(item.name)[-1].lower()
if ext in images:
files.append(item.name)
#print(item.name)
return files
if __name__ == "__main__":
os.chdir(r'd:\my pictures\.current')
app = MyApp(0)
app.MainLoop()