问题
I want to capture "all" keyboard input on the command line immediately (without waiting for a new line) in a non-blocking way.
This question demonstrates how you can read from stdin
in a non-blocking way using select()
. This is how it works:
while True:
if select.select([sys.stdin], [], [], 0)[0] == [sys.stdin]:
print(sys.stdin.read(1))
... do other stuff ...
Unfortunately you only get results after pressing RETURN
.
My first guess was that stdin
is just line buffered so after reading this question I turned my code into
ubuf_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
while True:
if select.select([ubuf_stdin], [], [], 0)[0] == [ubuf_stdin]:
print(ubuf_stdin.read(1))
... do other stuff ...
This code works better - the buffer is being read completely without interruptions - but it still waits for RETURN
being pressed.
I also tried to detach
stdin
:
ubuf_stdin = os.fdopen(sys.stdin.detach().fileno(), 'rb', buffering=0)
How do I react on keyboard input immediately after a key press?
回答1:
Use the getch
module to get a single keypress at a time, as described in this question.
I don't think you'll be able to read stdin in a non-blocking-yet-character-at-a-time way, it's really not intended for that purpose.
回答2:
Ok this one does it but it has some disadvantages yet:
- uses the
curses
module which seems a bit heavy to me - input has to be polled - no way to
select()
yet. - the keyboard input still gets printed to the terminal
.. but it works :).
import curses
def read_stdin(self):
def cb(screen):
result = []
screen.nodelay(1)
while True:
try:
result.append(screen.getkey())
except curses.error:
return result
# contains a list of keys pressed since last call
return curses.wrapper(cb)
来源:https://stackoverflow.com/questions/36066830/unbuffered-non-blocking-keyboard-input-on-command-line