Search code examples
positionwxpythonwxwidgets

In wxPython, how to get the mouse position related to image in ScrolledPanel?


I am implementing a simple map editor using wxPython. A StaticBitmap image control is hosted in ScrolledPanel. The image size is larger than the panel size, so the scroll bars appear. In mouse moving event handler, function event.GetPosition() is able to get the mouse position related to the top-left of the panel. However, I want to get the mouse position related to the top-left of the image. How could I achieve that? Thanks.

For example, in the first screenshot (scrolled to the top), if mouse is in the top-left of the panel, the position related to image should be (0, 0). In the second screenshot (scrolled to the bottom), if mouse is in the top-left of the panel, the position related to the image is (0, image_height-panel_height).

#!/usr/bin/python

import wx
import wx.lib.scrolledpanel

class SimpleFrame(wx.Frame):
    def __init__(self, parent):
        super(SimpleFrame, self).__init__(parent)

        # add a panel so it looks the correct on all platforms
        self.frame_panel = wx.Panel(self)
        frame_panel = self.frame_panel
        # image panel
        self.image_panel = wx.lib.scrolledpanel.ScrolledPanel(frame_panel, style=wx.SIMPLE_BORDER)
        image_panel = self.image_panel
        image_panel.SetAutoLayout(True)
        image_panel.SetupScrolling()
        # image panel - image control
        self.image_ctrl = wx.StaticBitmap(image_panel)
        self.image_ctrl.Bind(wx.EVT_MOTION, self.ImageCtrl_OnMouseMove)
        img = wx.Image("image.jpg", wx.BITMAP_TYPE_ANY)
        self.image_ctrl.SetBitmap(wx.BitmapFromImage(img))
        image_panel.Layout()
        image_sizer = wx.BoxSizer(wx.VERTICAL)
        image_sizer.Add(self.image_ctrl)
        image_panel.SetSizer(image_sizer)
        # frame sizer
        frame_sizer = wx.BoxSizer(wx.HORIZONTAL)
        frame_sizer.Add(image_panel, proportion=1, flag=wx.EXPAND | wx.ALL)
        frame_panel.SetSizer(frame_sizer)
        return

    def ImageCtrl_OnMouseMove(self, event):
        # position in control
        ctrl_pos = event.GetPosition()
        print("ctrl_pos: " + str(ctrl_pos.x) + ", " + str(ctrl_pos.y))
        # position in image
        #image_pos = ??? convert control position to image position
        #print("image_pos: " + str(image_pos.x) + ", " + str(image_pos.y))


app = wx.PySimpleApp()
frame = SimpleFrame(None)
frame.Show()
app.MainLoop()

Scrolling to top: Scrolled to top

Scrolling to bottom: Scrolled to bottom


Solution

  • You need to use GetScreenPosition() to get the coordinates of where the window currently is and ScreenToClient() to get the client coordinates of the image. Add them together and you will get your relative position on the image.
    The beauty of doing it this way is that the user can re-position the window or change its size and you get a consistent position.

    Here's your code amended:

    #!/usr/bin/python
    
    import wx
    import wx.lib.scrolledpanel
    
    class SimpleFrame(wx.Frame):
        def __init__(self, parent):
            super(SimpleFrame, self).__init__(parent)
    
            # add a panel so it looks the correct on all platforms
            self.frame_panel = wx.Panel(self)
            frame_panel = self.frame_panel
            # image panel
            self.image_panel = wx.lib.scrolledpanel.ScrolledPanel(frame_panel, style=wx.SIMPLE_BORDER)
            image_panel = self.image_panel
            image_panel.SetAutoLayout(True)
            image_panel.SetupScrolling()
            # image panel - image control
            self.image_ctrl = wx.StaticBitmap(image_panel)
            self.image_ctrl.Bind(wx.EVT_MOTION, self.ImageCtrl_OnMouseMove)
            self.img = wx.Image("image.jpg", wx.BITMAP_TYPE_ANY)
            self.image_ctrl.SetBitmap(wx.BitmapFromImage(self.img))
            image_panel.Layout()
            image_sizer = wx.BoxSizer(wx.VERTICAL)
            image_sizer.Add(self.image_ctrl)
            image_panel.SetSizer(image_sizer)
            # frame sizer
            frame_sizer = wx.BoxSizer(wx.HORIZONTAL)
            frame_sizer.Add(image_panel, proportion=1, flag=wx.EXPAND | wx.ALL)
            frame_panel.SetSizer(frame_sizer)
            return
    
        def ImageCtrl_OnMouseMove(self, event):
            # position in control
            ctrl_pos = event.GetPosition()
            print("ctrl_pos: " + str(ctrl_pos.x) + ", " + str(ctrl_pos.y))
            pos = self.image_ctrl.ScreenToClient(ctrl_pos)
            print "pos relative to screen top left = ", pos
            screen_pos = self.frame_panel.GetScreenPosition()
            relative_pos_x = pos[0] + screen_pos[0]
            relative_pos_y = pos[1] + screen_pos[1]
            print "pos relative to image top left = ", relative_pos_x, relative_pos_y
    
    
    app = wx.PySimpleApp()
    frame = SimpleFrame(None)
    frame.Show()
    app.MainLoop()