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

330 Views Asked by At

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!!!

2

There are 2 best solutions below

0
On BEST ANSWER

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.

3
On

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))