2

I'm trying to capture the keyboard using the pynput ('pijnput' translates to the pit of pain :-) ) module. This my code:

    class KeyPress:
    def __init__(self,parent):
        self.parent=parent

    def on_press(self,key):
        #keyboard.Listener.stop()
        try:
            self.parent.pressedKey=key.char
            self.parent.pressedKeyCode=None
        except AttributeError:
            self.parent.pressedKey=key
            self.parent.pressedKeyCode=key.value.vk
        return False


    def on_release(self,key):
        if key == keyboard.Key.esc:
            # Stop listener
            return False

    # Collect events until released
    def run(self):
        with keyboard.Listener(
                on_press=self.on_press,
                on_release=self.on_release,suppress=True) as listener:
            listener.join()

I create a subclass who assignes the key and keyCode to properties from the main class If I run it, it works ok, until I pressed a certain number of keys and then X crashes and I get this error. At first I ran this in ipython + tmux, but also in pure bash, this happens.

Exception in thread Thread-131:
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/site-packages/pynput/_util/__init__.py", line 136, in run
    self._run()
  File "/usr/lib/python3.7/site-packages/pynput/keyboard/_xorg.py", line 499, in _run
    super(Listener, self)._run()
  File "/usr/lib/python3.7/site-packages/pynput/_util/xorg.py", line 370, in _run
    self._display_record = Xlib.display.Display()
  File "/usr/lib/python3.7/site-packages/Xlib/display.py", line 89, in __init__
    self.display = _BaseDisplay(display)
  File "/usr/lib/python3.7/site-packages/Xlib/display.py", line 71, in __init__
    protocol_display.Display.__init__(self, *args, **keys)
  File "/usr/lib/python3.7/site-packages/Xlib/protocol/display.py", line 163, in __init__
    auth_prot_data = auth_data)
  File "/usr/lib/python3.7/site-packages/Xlib/protocol/display.py", line 1070, in __init__
    display.send_and_recv(request = -1)
  File "/usr/lib/python3.7/site-packages/Xlib/protocol/display.py", line 610, in send_and_recv
    raise self.socket_error
Xlib.error.ConnectionClosedError: Display connection closed by server

My guess is that I create too many Threads, because I don't terminate them. Although I assumed 'with' construct takes care of that ? Is this correct ? And if so, what should I do to make it work ?

Thanks in advance

oneindelijk
  • 606
  • 1
  • 6
  • 18
  • 1
    I'm encountering similar problems with pynput, causing my whole X to crash. Did you happen to figure out what was going on here? – sumeet Oct 18 '19 at 20:24

1 Answers1

1

When you return False pynput stops but the thread is still open, and when you go back to it it creates a brand new thread and repeats the process, I had pynput in a loop and ended up with many many threads, what I did in my code to use pynput with only 1 thread is this:

from pynput.keyboard import Key, Listener, Controller
listener = None
keyPressed = None

def on_press(key):
    if key == Key.enter:

        # here add a bolean that will detect when enter pressed

        global saveFile
        saveFile = True  

def on_release(key):
    global xPosition
    global yPosition
    global zPosition

   # in my case I'm moving and object with the keys so if I keep my finger on it it 
   #keeps calling this method, you can simple have a string that is equal to key.char 
   #to know the pressed key from anywhere

    global keyPressed
    keyPressed = key.char

    if key.char == ('d'):
        xPosition -= 0.03
    elif key.char == ('a'):
        xPosition += 0.03
    elif key.char == ('w'):
        zPosition += 0.03    
    elif key.char == ('s'):
        zPosition -= 0.03
    elif key.char == ('q'):
        yPosition += 0.03   
    elif key.char == ('e'):
        yPosition -= 0.03   

def CheckWhichKeyIsPressed():
    global listener

    # here is the secret, create only if it doesn't exist yet
    if listener == None:  
        listener = Listener(on_press=on_press, on_release=on_release,suppress=True)
        listener.start()

by only creating the thread if is hasn't been created yet I manage to have only 1 that is always listening, then I use the on_press and on_release to change variables, in my case the SaveFile boolean is turned off after using, so as soon as the user press enter again pynput will pick it up and I can save a new file:

global saveFile
if saveFile:
        saveFile = False
        SaveFile()

With this pynput is always listening and telling you what key you pressed with only 1 thread

pau Fer
  • 130
  • 7