NeoFahrenheit
NeoFahrenheit

Reputation: 422

wxPython - Fixing buttons to a given position in a image

I'm building a software where I need to display buttons over a image and make it stay somewhat in the same position (the best as possible) when the sizer the image is in gets resized.

The button-display-on-image is already done. I just need to get the math working now. I've tried a bunch of stuff with no luck yet. I guess the magic need to happen in the updateButtons function.

Thanks!

import wx
import wx.lib.platebtn as pb

class MainFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.bInfoSizerVisibility = False

        self.images = []
        self.initUI()
        self.CenterOnScreen()
        self.Bind(wx.EVT_SIZE, self.OnResizing)

    def initUI(self):
        self.imageSizer = wx.BoxSizer(wx.VERTICAL)
        self.imageSizer.SetMinSize((800, 600))
        self.bitmap = None
        self.image = None
        self.aspect = None

        self.bmpImage = wx.StaticBitmap(self, wx.ID_ANY)
        self.imageSizer.Add(self.bmpImage, 1, wx.EXPAND)

        self.btn = pb.PlateButton(self.bmpImage, -1, 'Click Me!', style=pb.PB_STYLE_NOBG)
        self.btn.Bind(wx.EVT_BUTTON, self.test)
        self.btn.Position = 250, 250

        self.SetSizerAndFit(self.imageSizer)
        self.frameImage()

    def test(self, event):
        print('Button Pressed!')

    def updateButtons(self):
        w, h = self.bmpImage.GetSize()
        u, v = 0.3, 0.7
        self.btn.Position = int(u * w), int(v * h)

    def frameImage(self, isJustResize=False):
        if not isJustResize:
            self.bitmap = wx.Bitmap('image.jpg', wx.BITMAP_TYPE_ANY)
            self.image = wx.Bitmap.ConvertToImage(self.bitmap)
            self.aspect = self.image.GetSize()[1] / self.image.GetSize()[0]

        self.Layout()

        sW, sH = self.imageSizer.GetSize()
        newW = sW
        newH = int(newW * self.aspect)

        if newH > sH:
            newH = sH
            newW = int(newH / self.aspect)

        image = self.image.Scale(newW, newH)
        self.bmpImage.SetBitmap(image.ConvertToBitmap())

        self.Layout()
        self.Refresh()
        self.updateButtons()

        # print(f"Image New Size: ({newW}, {newH})")
        # print(f"App Size: {self.GetSize()}")
        # print(f"imageSizer Size: {self.imageSizer.GetSize()}\n")

    def OnResizing(self, event):
        self.frameImage(True)
        event.Skip()

app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()

Upvotes: 0

Views: 36

Answers (1)

Psionman
Psionman

Reputation: 3699

You need to take into account the fact that as you resize the image, white space appears, either to the left and right, or on top and bottom. Remember, the button position is relative to the frame and not the image.

I have rewritten your updateButtons method to implement this.

I have assumed that your button's position will be 0.3 * the width of the image from the left and 0.7 * the height of the image from the top

import wx
import wx.lib.platebtn as pb

IMAGE_MINIMUM_SIZE = (800, 600)
BUTTON_POSITION_RATIO = (0.3, 0.7)

class MainFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.bInfoSizerVisibility = False

        self.images = []
        self.initUI()
        self.CenterOnScreen()
        self.Bind(wx.EVT_SIZE, self.OnResizing)

    def initUI(self):
        self.imageSizer = wx.BoxSizer(wx.VERTICAL)
        self.imageSizer.SetMinSize(IMAGE_MINIMUM_SIZE)
        self.bitmap = None
        self.image = None
        self.image_aspect = None

        self.bmpImage = wx.StaticBitmap(self, wx.ID_ANY)
        self.imageSizer.Add(self.bmpImage, 1, wx.EXPAND)

        self.btn = pb.PlateButton(self, -1, 'Click Me!', style=pb.PB_STYLE_NOBG)
        self.btn.Bind(wx.EVT_BUTTON, self.test)

        self.SetSizerAndFit(self.imageSizer)
        self.frameImage()

    def test(self, event):
        print('Button Pressed!')

    def updateButtons(self):
        frame_aspect = self.Size[0] / self.Size[1]

        button_horizontal = int(self.Size[0] * BUTTON_POSITION_RATIO[0])
        button_vertical = int(self.Size[1] * BUTTON_POSITION_RATIO[1])
        if self.image_aspect <= frame_aspect:
            # Frame is wider than image so find the horizontal white space size to add
            image_width = self.Size[1] * self.image_aspect
            horizontal_offset = (self.Size[0] - image_width)/2
            button_horizontal = int(horizontal_offset + image_width * BUTTON_POSITION_RATIO[0])

        elif self.image_aspect > frame_aspect:
            # Frame is higher than image so find the vertical white space size to add
            image_height = self.Size[0] / self.image_aspect
            vertical_offset = (self.Size[1] - image_height)/2
            button_vertical = int(vertical_offset + image_height * BUTTON_POSITION_RATIO[1])
        self.btn.Position = (button_horizontal, button_vertical)

    def frameImage(self, isJustResize=False):
        if not isJustResize:
            self.bitmap = wx.Bitmap('image.jpg', wx.BITMAP_TYPE_ANY)
            self.image = wx.Bitmap.ConvertToImage(self.bitmap)
            self.image_aspect = self.image.GetSize()[0] / self.image.GetSize()[1]

        image_width, image_height = self.imageSizer.GetSize()
        new_image_width = image_width
        new_image_height = int(new_image_width / self.image_aspect)

        if new_image_height > image_height:
            new_image_height = image_height
            new_image_width = int(new_image_height * self.image_aspect)

        image = self.image.Scale(new_image_width, new_image_height)

        self.bmpImage.SetBitmap(image.ConvertToBitmap())

        self.Layout()
        self.Refresh()
        self.updateButtons()

    def OnResizing(self, event):
        self.frameImage(True)
        event.Skip()

app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()

Upvotes: 1

Related Questions