Because several of my apps must display imagers I wanted to create an image control that I could reuse. Initially all I wanted it to do was display the image file in the control, scaled to fit (show the entire image), and to be resizeable. Simple task which appeared to work except I noticed one small glitch.
When the image is initially loaded, part of the image is cropped. If I resized the window, even by as little as one pixel, the entire image was redisplayed with no cropping. Sizing it back down to the original app size properly scaled the image with no cropping. After several hours of dicking around with it I still cannot get the image to display with no cropping on load. Any suggestions?
FYI the mouse handler is a place holder for now. I eventually hope to add zoom/pan features.
I’ll try to post two screen caps. The first should show the display on first load. The second shows the display after I very slightly resize up, then down by one pixel.
import wx
class ImagePanel(wx.Panel):
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.bmpImage = wx.StaticBitmap(self, wx.ID_ANY)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.bmpImage, 1, wx.EXPAND, 0)
self.SetSizer(sizer)
self.Layout()
self.bitmap = None #loaded image in bitmap format
self.image = None #loaded image in image format
self.blank = wx.Bitmap(1,1)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
def OnSize(self, event):
"""When panel is resized, scale the image to fit"""
self.ScaleToFit()
event.Skip()
def OnMouseWheel(self, event):
print('')
obj = event.GetEventObject()
m = wx.GetMouseState()
print(f'{m.ControlDown()=}')
print(f'{m.ShiftDown()=}')
print(f'{m.LeftIsDown()=}')
print(f'{m.RightIsDown()=}')
print(f'{m.GetPosition()=}')
print(f'{m.GetX()=}')
print(f'{m.GetY()=}')
mx,my = m.GetPosition()
px,py = obj.Parent.GetPosition()
iw,ih = obj.GetSize()
print(f'{mx-px=} {my-py=} {iw=} {ih=}')
event.Skip()
def Load(self, file:str) -> None:
"""Load the image file into the control for display"""
self.bitmap = wx.Bitmap(file, wx.BITMAP_TYPE_ANY)
self.image = wx.Bitmap.ConvertToImage(self.bitmap)
self.bmpImage.SetBitmap(self.bitmap)
self.ScaleToFit()
def Clear(self):
"""Set the displayed image to blank"""
self.bmpImage.SetBitmap(self.blank)
def ScaleToFit(self) -> None:
"""
Scale the image to fit in the container while maintaining
the original aspect ratio.
"""
if self.bitmap:
#get container (c) and image (i) dimensions
cw,ch = self.Parent.GetSize()
iw,ih = self.image.GetSize()
aspect = ih/iw
#calculate width to fit, and height from aspect ratio
nw = cw
nh = int(nw * aspect)
#if new height is too large then fit height,
#and calculate width from aspect ratio
if nh > ch:
nh = ch
nw = int(nh / aspect)
print(f'{cw=} {ch=} {nw=} {nh=}')
#scale the image to new dimensions and display
image = self.image.Scale(nw,nh)
self.bmpImage.SetBitmap(image.ConvertToBitmap())
self.Refresh()
if __name__ == "__main__":
app = wx.App()
frame = wx.Frame(None)
frame.SetSize((786,486))
panel = ImagePanel(frame)
frame.Show()
panel.Load('D:\\test.jpg')
app.MainLoop()