python non-blocking non-messing-my-tty key press detection

筅森魡賤 提交于 2019-12-05 22:34:58
Strikeskids

This has been asked before. Someone posted a nice, short, refactored solution

Reposted here

import sys
import select
import tty
import termios

class NonBlockingConsole(object):

    def __enter__(self):
        self.old_settings = termios.tcgetattr(sys.stdin)
        tty.setcbreak(sys.stdin.fileno())
        return self

    def __exit__(self, type, value, traceback):
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)


    def get_data(self):
        if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
            return sys.stdin.read(1)
        return False


if __name__ == '__main__':
    # Use like this
    with NonBlockingConsole() as nbc:
        i = 0
        while 1:
            print i
            i += 1

            if nbc.get_data() == '\x1b':  # x1b is ESC
                break

Here is a solution I came up with. Not perfect, because it relies on timeouts and sometimes can catch only half of escape sequences, if the key is pressed mili(micro? nano?)seconds before timeout expires. But it's the least bad solution I could come up with. Disappointing...

def timeout_call(func, args=(), kwargs=None, timeout_duration=1.0, default=None):
    if not kwargs:
        kwargs = {}
    import signal

    class TimeoutError(Exception):
        pass

    def handler(signum, frame):
        raise TimeoutError()

    # set the timeout handler
    signal.signal(signal.SIGALRM, handler)
    signal.setitimer(signal.ITIMER_REAL, timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
    finally:
        signal.alarm(0)

    return result


class NonBlockingConsole(object):

    def __enter__(self):
        self.old_settings = termios.tcgetattr(sys.stdin)
        tty.setcbreak(sys.stdin.fileno())
        return self

    def __exit__(self, type, value, traceback):
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)

    def get_data(self):
        k = ''
        while True:
            c = timeout_call(sys.stdin.read, args=[1], timeout_duration=0.05)
            if c is None:
                break
            k += c

        return k if k else False

Usage:

with NonBlockingConsole() as nbc:
    while True:
        sleep(0.05)  # or longer, but not shorter, for my setup anyways...
        data = nbc.get_data()
        if data:
            print data.encode('string-escape')
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!