获取单击事件并将其传播到多个区域 - python、pynput、pyautogui

问题描述

无法将 1 次鼠标单击转换为多次鼠标单击。基本上我想做的是一次控制多个窗口。我想单击一个主窗口并将单击传播到后续窗口。在这个片段中有 4 个窗口,我通过确定它和主窗口之间的 yield put((dispatch) => { batch(() => { dispatch(action1); dispatch(action2); }); }); 来跟踪它们。

我使用 python3 和 pynput 作为鼠标监听器,pyautogui 用于鼠标控制。

我遇到的问题是设置鼠标侦听器,使其侦听我的实际点击但忽略编程点击。现在,我认为它陷入了无限循环,我的初始点击触发 offset 事件,传播点击,每次触发额外的 on_click 事件,传播点击等。当我运行下面的代码开始正常,然后当我第一次单击它时,鼠标会严重滞后一分钟,然后恢复正常,不再有鼠标侦听器处于活动状态。我的猜测是故障保护启动以使其恢复正常。

我尝试过的事情:

  • 使用 pynput 进行侦听器和控制 - 这不会改变结果
  • 在传播的点击完成后停止侦听器并创建一个新的侦听器 - 仍然没有改变结果的糟糕解决方
  • 使用 on_click 进行信号量锁定以忽略已获取信号量的事件 - 也很笨拙且不起作用
  • 在从 _value 事件返回之前通过线程调用 propagateActions 并等待完成 - 无效
  • 注释掉 on_click - 这允许将鼠标移动到后续位置并在之后将其返回到其初始位置的预期行为。没有点击,它工作完美。随着点击,它滞后,听众死亡。
  • 搜索 stackoverflow - this question 在结果方面有相似之处,但没有得到答复,正在尝试实现不同的目标。

我的片段如下:

pyautogui.click()

我的问题:

  • 有什么方法可以只监听“真实”点击而不是程序化点击?
  • 如果没有,我可以暂停鼠标侦听器,然后在传播的点击发生后以某种方式重新启动它吗?

这是与当前问题相关的一小段代码from pynput import mouse,keyboard import pyautogui pyautogui.PAUSE = 0.01 mouseListener = None killSwitch = False # this is just a keyboard listener for a kill switch def on_release(key): if key == keyboard.Key.f1: global killSwitch print('@@@ Kill switch activated @@@') killSwitch = True # on mouse release I want to propogate a click to 4 other areas def on_click(x,y,button,pressed): print('{0} at {1}'.format('pressed' if pressed else 'Released',(x,y))) if not pressed: propogateActions(x,button) # propogates clicks def propogateActions(x,button): print('propogating actions to {0} windows'.format(len(offsets)+1)) for offset in offsets: pyautogui.moveto(x+offset.x,y+offset.y) print('mouse moved') if button == mouse.Button.left: print('left clicking at ({0},{1})'.format(x+offset.x,y+offset.y)) pyautogui.click() pyautogui.moveto(x,y) # point class for ease of use class Point(): def __init__(self,x,y): self.x = x self.y = y def __repr__(self): return 'Point(x={0},y={1})'.format(self.x,self.y) # main method def doTheThing(): print('started') while not killSwitch: pass # initializations and starting listeners # offsets tracks how far the subsequent clicks are from the initial click point offsets = [Point(50,0),Point(50,50),Point(0,50)] keyboardListener = keyboard.Listener(on_release=on_release) mouseListener = mouse.Listener(on_click=on_click) keyboardListener.start() mouseListener.start() doTheThing() 一个初始化,可以更恰当地设置它,还有其他的花里胡哨,但这是与问题相关的部分。感谢您的帮助。

解决方法

找到答案了!不得不更深入一层。

Pynput 有一个 method of suppressing events,它暴露了点击事件背后的 win32 data。对我的一次点击与 pyautogui.click() 的一次点击进行了测试,发现其中存在差异。 data.flags 在用户点击事件中设置为值 0,在程序化点击中设置为值 1

这足以让我过滤。这是相关的过滤器:

def win32_event_filter(msg,data):
    if data.flags:
        print('suppressing event')
        return False

将其添加到我上面的代码并更改了

mouseListener = mouse.Listener(on_click=on_click)

mouseListener = mouse.Listener(on_click=on_click,win32_event_filter=win32_event_filter)

它有效!

我的真实点击占上风,程序化点击得到传播,我没有陷入无限循环。如果其他人遇到此问题,希望这会有所帮助。