简体   繁体   中英

How to convince the mouse right click popup menu to show near the mouse cursor position in wxPython?

I have a wx frame that uses a SplitterWindow (the frame has also main menu, toolbar, status bar – but this is not relevant here). Everything works as expected, except for the mouse right click popup menu over buttons, in that the popup menu shows up at apparently random positions over the screen – and at "negative" random positions when moving the frame to the second screen (monitor). By "apparently" I mean that the popup menu position seems somewhat related to the actual button (or frame) position, but multiplied with some factor – positive on main screen and negative on the second.

I ran the code only on Windows 10 64bit / Python 3.9.0 64bit / wx '4.1.1 msw (phoenix) wxWidgets 3.1.5'. The first lines of the frame code were generated via wxGlade, so perhaps this could be related in the particular way the frame code was initially generated.

I created a stripped down test code, shown below, that mimics the exact situation of the real code in terms of mouse right click popup menu. In this test code I placed the button in the second pane, but it behaves the same on whatever pane.

I tried the same popup menu code on other simple wx example codes but without using SplitterWindow and there the popup behavior was ok. What should I change or improve in the test code below?

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((400, 300))
        self.SetTitle("test frame")

        sizer_0 = wx.BoxSizer(wx.HORIZONTAL)
        self.window_1 = wx.SplitterWindow(self, wx.ID_ANY)
        self.window_1.SetMinimumPaneSize(20)
        sizer_0.Add(self.window_1, 1, wx.EXPAND, 0)

        self.pane_1 = wx.Panel(self.window_1, wx.ID_ANY)
        sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        self.pane_2 = wx.Panel(self.window_1, wx.ID_ANY)
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)

        self.button = wx.Button(self.pane_2, wx.ID_ANY, "test button")
        sizer_2.Add(self.button, 0, 0, 0)

        self.pane_1.SetSizer(sizer_1)
        self.pane_2.SetSizer(sizer_2)
        self.window_1.SplitVertically(self.pane_1, self.pane_2)
        self.SetSizer(sizer_0)
        self.Layout()

        self.Bind(wx.EVT_CONTEXT_MENU, self.OnButtonContextMenu, self.button)

    def OnButtonContextMenu(self, event):
        self.PopupMenu(ButtonContext(self), event.GetPosition())

##

class ButtonContext(wx.Menu):
    def __init__(self, parent):
        super(ButtonContext, self).__init__()
        self.parent = parent
 
        button_popup = wx.MenuItem(self, wx.ID_ANY, 'test popup')
        self.Append(button_popup)
        self.Bind(wx.EVT_MENU, self.button_action, button_popup)
 
    def button_action(self, event):
        event.Skip()

##

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

##

if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

It's slightly convoluted but I think you are overriding the InvokingWindow 's position for wx.Menu by passing event.GetPosition() to class ButtonContext .

In short if you drop that parameter, event.GetPosition() and just invoke it with self.PopupMenu(ButtonContext(self)) , it will default to the parent window, the button itself.
The result being that it will always focus on the button that you just right clicked.

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM