【问题标题】:Providing right-click context menu for a separate program using wxPython使用 wxPython 为单独的程序提供右键单击上下文菜单
【发布时间】:2019-03-01 15:29:05
【问题描述】:

我在格式化这个问题的标题时遇到了问题,因为我不确定我是否以正确的方式解决这个问题,所以让我解释一下。

我想尝试将右键单击上下文菜单添加到我没有源代码的现有程序中。 wxPython 通常是我选择的框架。我想有几种方法可以做到这一点:

1) 创建一个透明的 wx.Frame,它与现有程序绑定并位于其之上,拦截鼠标事件。如果我这样做,我不确定鼠标事件是否可以传递到底层窗口。我喜欢这个选项,因为它允许在叠加层中添加更多有用的信息。

2) 创建一个无头程序,它全局拦截右键单击事件,并在满足某些条件时在指针位置生成上下文菜单。根据我迄今为止所做的研究,如果不连续轮询鼠标位置,这似乎是不可能的。

我错过了什么?有没有更优雅的解决方案?这甚至可以使用 Python 实现吗?

编辑:我有一个部分概念验证工作,如下所示:

import wx
import win32gui
import win32api
import win32con

class POC_Frame(wx.Frame):

    def __init__(self, parent):
        wx.Frame.__init__(self, parent, id=wx.ID_ANY, title='POC', pos=(0,0), size=wx.Size(500, 500), style=wx.DEFAULT_FRAME_STYLE)
        self.ToggleWindowStyle(wx.STAY_ON_TOP)

        extendedStyleSettings = win32gui.GetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE)
        win32gui.SetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE,
                               extendedStyleSettings | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT)
        win32gui.SetLayeredWindowAttributes(self.GetHandle(), win32api.RGB(0,0,0), 100, win32con.LWA_ALPHA)

        self.Bind(wx.EVT_RIGHT_DOWN, self.onRightDown)
        self.Bind(wx.EVT_RIGHT_UP, self.onRightUp)

        self.CaptureMouse()

    def onRightDown(self, event):
        print(event)

    def onRightUp(self, event):
        print(event)

app = wx.App(False)
MainFrame = POC_Frame(None)
MainFrame.Show()
app.MainLoop()

这似乎工作正常,因为它将右键单击事件传递给底层窗口,同时仍然识别它们,但它只执行一次。一旦失去焦点,它就会停止工作,而且我试图将注意力重新集中到它似乎没有任何效果。

【问题讨论】:

    标签: python event-handling wxpython


    【解决方案1】:

    我总是用pyHook而不是wx来挂钩全局鼠标和键盘事件。这是一个简单的例子:

    import pyHook
    import pyHook.cpyHook  # ensure its included by cx-freeze
    
    
    class ClickCatcher:
        def __init__(self):
    
            self.hm = None
    
            self._is_running = True
            self._is_cleaned_up = False
            self._is_quitting = False
    
            self.set_hooks()
    
        # this is only necessary when not using wx
        # def send_quit_message(self):
        #     import ctypes
        #     win32con_WM_QUIT = 18
        #     ctypes.windll.user32.PostThreadMessageW(self.pump_message_thread.ident, win32con_WM_QUIT, 0, 0)
    
        def __del__(self):
            self.quit()
    
        def quit(self):
            if not self._is_running:
                return
            self._is_quitting = True
            self._is_running = False
            if self.hm:
                # self.hm.UnhookKeyboard()
                self.hm.UnhookMouse()
            # self.send_quit_message()
            self._is_cleaned_up = True
    
        def set_hooks(self):
            self._is_running = True
            self._is_cleaned_up = False
            self.hm = pyHook.HookManager()
            self.hm.MouseRightUp = self.on_right_click
            # self.hm.HookKeyboard()
            self.hm.HookMouse()
    
    
        def on_right_click(self):
            # create your menu here
            pass
    

    如果您不使用wx,则必须使用pythoncom.PumpMessages 将鼠标和键盘事件推送到您的程序,但App.Mainloop() 完成同样的事情(如果您将 PumpMessages 和 Mainloop 一起使用大约一半的事件不会被推送到您的程序)。

    创建wx.Menu 非常简单。您可以使用wx.GetMousePosition()找到鼠标坐标

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      • 2017-04-22
      相关资源
      最近更新 更多