1

Currently, I am attempting to write a program that takes a screenshot on mouse-click. The basic functionality works as expected, but, it seems that Pynput is sending two triggers for every one click? (I am getting two screenshots created for every one mouse click).

The end goal is to have a program that takes one screenshot for every mouse-click.

Here is the current code that I am using.

import numpy as np
import cv2
import pyautogui
from pynput import mouse

COUNT = 0

def take_screenshot(x,y,button,pressed):
    COUNT+=1
    if button == mouse.Button.left:
        image = pyautogui.screenshot()
        image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
        cv2.imwrite(f"image{COUNT}.png", image)

with mouse.Listener(on_click=take_screenshot) as listener:
    listener.join()

I have tried a couple things, but it has either made the mouse react oddly, or didn't improve anything.

  • Tried to use booleans values and used "AND" addition to "if" statement:
TAKEN = False
...
if button == mouse.Button.left and TAKEN == False:
    image = pyautogui.screenshot()
    image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
    cv2.imwrite(f"image{COUNT}.png", image)
    TAKEN = True
  • Tried wrapping the listener in different functionality (while loops, etc.)
  • Tried to put the listener in its on function, but this caused the mouse to react oddly.

Attached screenshot, I don't know if it helps any in this situation. Screenshot of images

From other StackOverflow items, like this one --> Why IF judgement in PYNPUT mouse listener will be operated twice?

It feels like the press/release on mouse-click is the culprit. But reviewing pynput documentation, the mouse listener doesn't have an on_release argument like the keyboard listener does. Does anybody know:

  1. If this what is happening? If so, is there an option to disable this that isn't documented?
  2. Alternative tooling for mouse-clicks that does offer a single call per mouse-click event?
  • You can check the fourth argument (boolean `pressed`) to separate press and release events. – Marijn Apr 23 '23 at 15:19
  • Does this answer your question? [Python Pynput release and press](https://stackoverflow.com/questions/65078181/python-pynput-release-and-press) – Marijn Apr 23 '23 at 15:19
  • Note that that answer just repeats a code snippet from https://pynput.readthedocs.io/en/latest/mouse.html#monitoring-the-mouse, which contains other useful information. – Marijn Apr 23 '23 at 15:27
  • @Marijn thank you but unfortunately no. Release seems only to be viable when you want to control the mouse, not listen to it. – Andrew McKenzie Apr 23 '23 at 19:00
  • Your answer below is what I meant, you test if `pressed` is true during the click event (if it is false then the event is release). – Marijn Apr 23 '23 at 19:19

1 Answers1

1

Working answer

I was able to figure this out doing some printing. When using a listener, the on_click function is getting (x, y, button, pressed). For my particular use case I only cared about "button" and "pressed".

  • Button is an enum
  • Pressed is a Boolean

It also turns out that the Listener is sending Button.left True when you click and Button.left False when you release. Since the button object is an enum, just used the following to split the paths based on pressed:

import numpy as np 
import cv2 
import time
import pyautogui 
from pynput.mouse import Listener
from pynput.keyboard import Key, Controller

COUNT = 0
RUNNING = True

def take_screenshot():
    global COUNT
    COUNT+=1
    image = pyautogui.screenshot()
    image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
    cv2.imwrite(f"image{COUNT}.png", image)


def on_click(x, y, button, pressed):
    if pressed == True:
        take_screenshot()
    elif pressed == False:
        pass

with Listener(on_click=on_click) as listener:
    listener.join()