Real-time capture and processing of keypresses (e.g. keypress event)
Question: _Note: I want to do this without using any external packages, like PyGame,
etc._

I am attempting to capture individual keypresses as they arrive and perform an
action for specific characters, whether I simply want to "re-echo" the
character, or not display it at all and do something else.

I have found a cross-platform (though not sure about OS X) getch()
implementation because I do not want to read a whole line like input() does:

    
    
    # http://code.activestate.com/recipes/134892/
    def getch():
        try:
            import termios
        except ImportError:
            # Non-POSIX. Return msvcrt's (Windows') getch.
            import msvcrt
            return msvcrt.getch
    
        # POSIX system. Create and return a getch that manipulates the tty.
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch
    

**[Attempt 1]** I first tried a simple while-true loop to poll getch, but if I
type too fast, characters go missing. Reducing the sleep time makes it worse.
The debug statements only print on press of the enter key and not consistently
in time nor position. (It appears there might be some line buffering going
on?) Taking out the loop and sleep lets it work once but perfectly.

    
    
    #!/usr/bin/env python3
    
    import sys
    import tty
    import time
    
    
    def main():
        while True:
            time.sleep(1)
            sys.stdout.write(" DEBUG:Before ")
            sys.stdout.write(getch())
            sys.stdout.write(" DEBUG:After ")
    
    
    if __name__ == "__main__":
        main()
    

**[Attempt 2]** I got an example for using a threaded approach
(<http://stackoverflow.com/a/14043979/2752206>) but it "locks up" and won't
accept any input (including `Ctrl-C`, and etc)..

    
    
    #!/usr/bin/env python3
    
    import sys
    import tty
    import time
    import threading
    
    key = 'Z'
    
    
    def main():
        threading.Thread(target=getchThread).start()
    
        while True:
            time.sleep(1)
            sys.stdout.write(" DEBUG:Before ")
            sys.stdout.write(key)
            sys.stdout.write(" DEBUG:After ")
    
    
    def getchThread():
        global key
        lock = threading.Lock()
        while True:
            with lock:
                key = getch()
    
    
    if __name__ == "__main__":
        main()
    

Does anyone have any advice or guidance? Or more importantly, can someone
explain _why_ the two attempts do not work? Thanks.


Answer: First off, I don't really thing you need multithreading. You'd need that if
you, for example, wanted to do some tasks like drawing on screen or whatever
and capturing keys while you do this.

Let's consider a case where you only want to capture keys and after each
keypress execute some action: Exit, if **x** was pressed, otherwise just print
the character. All you need for this case is simple while loop

    
    
    def process(key):
        if key == 'x':
            exit('exitting')
        else:
            print(key, end="", flush=True)
    
    if __name__ == "__main__":
        while True:
            key = getch()
            process(key)
    

Notice absence of sleep(). I am assuming you thought getch() won't wait for
user input so you set 1s sleep time. However, your getch() waits for one entry
and then returns it. In this case, global variable is not really useful, so
you might as well just call process(getch()) inside the loop.

`print(key, end="", flush=True)` => the extra arguments will ensure pressed
keys stay on one line, not appending newline character every time you print
something.

The other case, where you'd want to execute different stuff simultaneously,
should use threading.

Consider this code:

    
    
    n = 0
    quit = False
    
    def process(key):
        if key == 'x':
            global quit
            quit = True
            exit('exitting')
        elif key == 'n':
            global n
            print(n)
        else:
            print(key, end="", flush=True)
    
    def key_capturing():
        while True:
            process(getch())
    
    if __name__ == "__main__":
        threading.Thread(target=key_capturing).start()
        while not quit:
            n += 1
            time.sleep(0.1)
    

This will create global variable `n` and increment it 10 times a second in
main thread. Simultaneously, `key_capturing` method listens to keys pressed
and does the same thing as in previous example + when you press **n** on your
keyboard, current value of the global variable `n` will be printed.

Closing note: as @zondo noted, you really missed braces in the getch()
definition. `return msvcrt.getch` should most likely be `return
msvcrt.getch()`

