How to stop triggering the key while holding the key in pynput? - python

I am using the pynput and winsound modules to make a program that makes the pc play the sound of the keys and mouse. The problem is that when I press a key and hold it down the key is triggered repeatedly, which causes the sound to play repeatedly in a loop until I release the key.
I followed this solution to create the program: Play Sound whenever key is pressed in Python
Then as I wanted to play the mouse sound as well, I found this article: How to Use pynput's Mouse and Keyboard Listener at the Same Time
So, I ended up with this code:
from pynput.mouse import Listener as MouseListener
from pynput.keyboard import Listener as KeyboardListener
import winsound
def on_press(key):
winsound.PlaySound("sound.wav", winsound.SND_ASYNC)
print("Key pressed: {0}".format(key))
def on_release(key):
print("Key released: {0}".format(key))
def on_click(x, y, button, pressed):
if pressed:
winsound.PlaySound("mouse_click.wav", winsound.SND_ASYNC)
print('Mouse clicked at ({0}, {1}) with {2}'.format(x, y, button))
else:
print('Mouse released at ({0}, {1}) with {2}'.format(x, y, button))
keyboard_listener = KeyboardListener(on_press=on_press, on_release=on_release)
mouse_listener = MouseListener(on_click=on_click)
keyboard_listener.start()
mouse_listener.start()
keyboard_listener.join()
mouse_listener.join()
Now, the mouse click does exactly what I want the keyboard to do! It plays the sound once while it is being held until I release it!
I just don't know how to make the keyboard play the sound only once until the key is released, like the mouse!!!

Here's an example that stores the pressed keys in a set so that a single pressed key will not be able to play a sound until it is released. If you press another key whilst holding the first, the sound will play again.
I've modified your code to use the sound aliases so you can test without having your sound.wav and mouse_click.wav files. I changed to use f-strings as they're more intuitive and I added support for Esc to exit the handlers. Please comment if you'd like any further elaboration.
from pynput.mouse import Listener as MouseListener
from pynput.keyboard import Listener as KeyboardListener
from pynput.keyboard import Key
import winsound
keys_pressed = set() # store which keys are pressed
def on_press(key):
# winsound.PlaySound("sound.wav", winsound.SND_ASYNC)
if key not in keys_pressed:
winsound.PlaySound("SystemAsterisk", winsound.SND_ALIAS | winsound.SND_ASYNC)
keys_pressed.add(key)
print(f"Key pressed: {key}")
if key == Key.esc:
mouse_listener.stop() # stop the mouse listener as well
winsound.PlaySound("SystemExit", winsound.SND_ALIAS | winsound.SND_ASYNC)
return False # Stop listener
def on_release(key):
print(f"Key released: {key}")
try:
keys_pressed.remove(key)
except KeyError:
pass # started with key pressed?
def on_click(x, y, button, pressed):
if pressed:
# winsound.PlaySound("mouse_click.wav", winsound.SND_ASYNC)
winsound.PlaySound("SystemHand", winsound.SND_ALIAS | winsound.SND_ASYNC)
print(f"Mouse clicked at ({x}, {y}) with {button}")
else:
print(f"Mouse released at ({x}, {y}) with {button}")
keyboard_listener = KeyboardListener(on_press=on_press, on_release=on_release)
mouse_listener = MouseListener(on_click=on_click)
keyboard_listener.start()
mouse_listener.start()
keyboard_listener.join()
mouse_listener.join()
You may instead want to try the flag winsound.SND_NOSTOP but that will only stop repeated sounds while any other sound is playing so the sounds won't overlap. You'll also need to catch the RuntimeError that is thrown if SND_NOSTOP is set and PlaySound() is called when a sound is already playing.

could possibly set up a variable that would store whether you have played the sound on the click, and would prevent playing another sound until you have released, and the variable is reset.
canPlaySoundFromPress = True
def onPress(...):
if canPlaySoundFromPress:
winsound.PlaySound("sound.wav", winsound.SND_ASYNC)
canPlaySoundFromPress = False
print("Key pressed: {0}".format(key))
def onRelease(...):
canPlaySoundFromPress = True
print("Key released: {0}".format(key))

Related

Making an autoclicker in python?

I have designed this simple autoclicker in python using the pynput library
import time
import threading
from pynput.mouse import Button, Controller
from pynput.keyboard import Listener, KeyCode
TOGGLE_KEY = KeyCode(char="t")
activated = False
mouse = Controller()
def on_press(key):
global activated
if key == TOGGLE_KEY:
activated = not activated
while activated:
mouse.click(Button.left, 1)
time.sleep(0.01)
listner = Listener(on_press=on_press)
listner.start()
input()
This code activates a listner and when the user presses any key it calls the on_press function which checks if the button is equal to 't' then it inverts the property of activated and starts the while loop
I tried the above code and it worked but when I pressed 't' again the autoclicker did not switch off
I believe you are stuck in your while activated: loop. Pushing T again does not make the function run again unless the first function has stopped. A simple solution would be to put the click event in its own thread.
import time
import threading
from pynput.mouse import Button, Controller
from pynput.keyboard import Listener, KeyCode
TOGGLE_KEY = KeyCode(char="t")
activated = False
mouse = Controller()
def on_press(key):
global activated
if key == TOGGLE_KEY:
activated = not activated
def doClick():
global activated
while True:
if activated:
mouse.click(Button.left, 1)
time.sleep(0.01)
threading.Thread(target = doClick).start()
listner = Listener(on_press=on_press)
listner.start()
input()
I think the problem is that the program is stuck in the loop. A solution I found is to use a thread
import threading
import time
from pynput.keyboard import Listener, KeyCode
from pynput.mouse import Controller
mouse = Controller()
class Clicker(threading.Thread):
__TOGGLE_KEY = KeyCode(char="t")
__activated = False
def run(self) -> None:
super().run()
while True:
if self.__activated:
# mouse.click(Button.left, 1)
time.sleep(1)
print("activated")
def keyboard_listener(self, key) -> None:
if key == self.__TOGGLE_KEY:
self.__activated = not self.__activated
clicker = Clicker()
listner = Listener(on_press=clicker.keyboard_listener)
listner.start()
clicker.start()
print("Listener started")
input()

Why do I always have the error message Controller has no attribute is_pressed?

I've been coding a very simple autoclicker. My autoclicker works just fine, but the only way to kill it is forcefully shut down my computer because it clicks so fast I can't access my taskbar. I could make it slower, but I'd prefer a way for the user to close the autoclicker with the press of a button. I've tried if keyboard.is_presssed('q'): break but I always get the error message AttributeError: 'Controller' object has no attribute 'is_pressed'. Did you mean: 'alt_pressed'? I expected my code to break the loop when I press q, but instead I get an error message. The error message will also pop up without the pressing of q. My code as of now is:
from pynput.mouse import Button, Controller
from pynput.keyboard import Key, Controller
import time
keyboard = Controller()
mouse = Controller()
while True:
time.sleep(10)
mouse.click(Button.left)
if keyboard.is_pressed('q'):
break
pynput doesn't have is_pressed() - I don't know where you find it.
You should rather use pynput.keyboard.Listener to run code when q is pressed and set some variable - q_is_pressed = True - or run code which ends program.
You can't have two objects with the same name Controller.
One Controller replaces other Controller and later you use the same Controller to create mouse and keyboard - and this makes problem.
You have to use pynput.mouse.Controller and pynput.keyboard.pynput.Controller
import pynput
from pynput.mouse import Button
from pynput.keyboard import Key
import time
keyboard = pynput.keyboard.Controller()
mouse = pynput.mouse.Controller()
while True:
time.sleep(10)
mouse.click(Button.left)
#if keyboard.is_pressed('q'): # <-- this will need Listener()
# break
EDIT:
To end code when q was pressed you have to use Listener
For example:
import pynput
from pynput.mouse import Button
import time
mouse_controller = pynput.mouse.Controller()
def on_press(key):
print('pressed:', key)
if str(key) == "'q'": # it has to be with `' '` inside `" "`
# Stop listener
print("exit listener")
return False # `False` ends listener
with pynput.keyboard.Listener(on_press=on_press) as keyboard_listener:
while keyboard_listener.is_alive():
time.sleep(10)
mouse_controller.click(Button.left)
print('clicked')

Python Global Capturing and Disposing Mouse Click Event

I want to create a tool, which allows me to send keys to active window using my mouse.
Let's say I want to send the key "A" when I click the left mouse button. But I also want to suppress / dispose that particular click event. So the target app will only feel the keyboard input "A", but not the left click of the mouse event.
With the following code I am able to see the mouse clicks. But I want to dispose / stop the event and not processed by the system.
from pynput import mouse
def do_something():
print("something")
def on_click(x, y, button, pressed):
print('{0} {1} at {2}'.format(button, 'Pressed' if pressed else 'Released', (x, y)))
if (button.value == 1):
do_something()
#suppress / dispose the click event...
# Collect events until released
with mouse.Listener( on_click=on_click ) as listener:
listener.join()
By the way I am using Ubuntu 20.04.
Thanks in advance.
I found a way that suppress all the click events.
from pynput import mouse
def do_something():
print("something")
def on_click(x, y, button, pressed):
print('{0} {1} at {2}'.format(button, 'Pressed' if pressed else 'Released', (x, y)))
if (button.value == 1):
do_something()
#suppress / dispose the click event...
# Collect events until released
with mouse.Listener( on_click=on_click, suppress=True ) as listener:
listener.join()
I am still looking for a solution to suppress a certain button click.

How to disable the <KeyRelease> event only when the enter key is pressed

I want to disable the <KeyRelease> event only when the enter key is pressed because I have two functions that use either the <Return> event or the <KeyRelease> event, but when I press enter to activate the function that uses the <Return> event, the other function that gets activated with the <KeyRelease> event also activates, and this is an issue. Anything I looked up simply said to disable the specific key, but I need the enter key enabled for one of the functions to activate.
import tkinter as tk
root = tk.Tk()
root.geometry("500x500+0+0")
def function1(e):
print('hi')
def function2(e):
print('hello')
root.bind("<Return>", function1)
root.bind("<KeyRelease>", function2)
root.mainloop()
You can exit from function2 if Enter key was pressed. To do this, you can check keysym event property:
def function2(e):
if e.keysym == 'Return':
return
print('hello')

Question about mouse and keyboard events in python

from pynput.keyboard import Key, Controller as KeyboardController
from pynput.mouse import Button, Controller as MouseController
from pynput.keyboard import Key, Listener
keyboard = KeyboardController()
mouse = MouseController()
def on_press(key):
if key.char == 'q':
print(mouse.position)
return True
# Collect events until released
with Listener(
on_press=on_press) as listener:
listener.join()
I made this simple script to return the mouse's position if I press the letter q. It works fine until I press something that is not a char, for example, the Enter key. I've been searching around but couldn't find good ways of implementing an if "certain button clicked" to do that. How can I fix this?
Evidently, the key object returned to on_press does not have a char attribute unless you have actually hit a character key. So you have to check for the existence of that attribute. You can do that using hasattr:
from pynput.keyboard import Key, Controller as KeyboardController
from pynput.mouse import Button, Controller as MouseController
from pynput.keyboard import Key, Listener
keyboard = KeyboardController()
mouse = MouseController()
def on_press(key):
if hasattr(key, 'char'):
if key.char == 'q':
print(mouse.position)
return True
# Collect events until released
with Listener(
on_press=on_press) as listener:
listener.join()

Categories