Прием события с одним кликом и распространение его на несколько областей — python, pynput, pyautogui

Возникли проблемы с превращением одного щелчка мыши в несколько щелчков мыши. В основном то, что я хочу сделать, это управлять несколькими окнами одновременно. Я хочу щелкнуть одно главное окно и распространить щелчки на последующие окна. В этом фрагменте есть 4 окна, и я отслеживаю их, определяя offset между ним и главным окном.

Я использую python3 с pynput для прослушивания мыши и pyautogui для управления мышью.

У меня возникла проблема с настройкой прослушивателя мыши таким образом, чтобы он прослушивал мои фактические щелчки, но игнорировал программные щелчки. Прямо сейчас я думаю, что он застревает в бесконечном цикле, где мой первоначальный щелчок запускает событие on_click, распространяет клики, каждый запускает дополнительное событие on_click, распространяет клики и т. д. Когда я запускаю приведенный ниже код, он запускается нормально, а затем когда я впервые щелкаю по нему, моя мышь сильно отстает на минуту, прежде чем вернуться в нормальное состояние, когда прослушиватель мыши больше не активен. Я предполагаю, что отказоустойчивость срабатывает, чтобы вернуть его в нормальное состояние.

Вещи, которые я пробовал:

  • использование pynput для прослушивания и управления - это не меняет результат
  • остановка прослушивателя и создание нового после завершения распространения кликов - плохое хакерское решение, которое все еще не изменило результат
  • блокировка семафора с просмотром _value для игнорирования событий, если семафор уже был получен - также хакерский и не работал
  • вызов propagateActions через потоки и ожидание завершения перед возвратом из события on_click - не сработало
  • комментирование pyautogui.click() - это позволяет ожидаемому поведению перемещать мышь в последующие места и возвращать ее обратно в исходное положение после. Без щелчка работает отлично. При щелчке он отстает, и слушатель умирает.
  • поиск stackoverflow - этот вопрос имеет сходство с точки зрения результата, но остается без ответа и пытаясь добиться чего-то другого.

Мой фрагмент ниже:

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, y, button)

# propogates clicks
def propogateActions(x, y, 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()

Мой вопрос:

  • Есть ли способ слушать только реальные клики, а не программные клики?
  • Если нет, могу ли я приостановить прослушиватель мыши, а затем перезапустить его после того, как произошли распространенные щелчки?

Это небольшой участок кода, относящийся к рассматриваемой проблеме. offsets имеет инициализацию, которая устанавливает его более подходящим образом, и есть другие прибамбасы, но это раздел, относящийся к проблеме. Я ценю вашу помощь.


person pythonLearner    schedule 23.04.2021    source источник
comment
Для системы все эти щелчки реальны.   -  person furas    schedule 23.04.2021
comment
если вы щелкнете в одном окне, возможно, вам следует проверить положение мыши и распространение, только если щелчок находится в какой-то области.   -  person furas    schedule 23.04.2021
comment
вы также можете измерять время между кликами и пропускать клики, которые составляют несколько миллисекунд после другого клика.   -  person furas    schedule 23.04.2021
comment
да, я больше спрашивал, есть ли что-то другое между кликом пользователя и программным кликом. Из моей программы этого не видно. Что касается ваших двух других комментариев, они могут работать, но ограничат решение. Главное окно может находиться только в фиксированном месте, а время ограничивает скорость выполнения действий. Оба решения хрупкие, но могут работать. Тем не менее, я нашел правильное решение. Ответ ниже.   -  person pythonLearner    schedule 23.04.2021


Ответы (1)


Нашел ответ! Пришлось идти слоем глубже.

Pynput имеет метод подавления событий, раскрывающий данные win32 за событием клика. Провел тест одного из моих кликов по сравнению с 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)

to

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

и это работает!

Мои реальные клики превалируют, программные клики распространяются, и я не застрял в бесконечном цикле. Надеюсь, это поможет, если другие столкнутся с этой проблемой.

person pythonLearner    schedule 23.04.2021
comment
Мне это нравится, потому что вы идете глубже. Большой +1 за ваши усилия. - person furas; 23.04.2021