是否有办法从用户输入中读取单个字符?例如,他们在终端上按下一个键,它就会返回(有点像getch())。我知道Windows中有这个功能,但我想要跨平台的东西。
当前回答
试试这个:http://home.wlu.edu/~levys/software/kbhit.py 它是非阻塞的(这意味着您可以有一个while循环并检测按键而无需停止它)和跨平台的。
import os
# Windows
if os.name == 'nt':
import msvcrt
# Posix (Linux, OS X)
else:
import sys
import termios
import atexit
from select import select
class KBHit:
def __init__(self):
'''Creates a KBHit object that you can call to do various keyboard things.'''
if os.name == 'nt':
pass
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self):
''' Resets to normal terminal. On Windows this is a no-op.
'''
if os.name == 'nt':
pass
else:
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
''' Returns a keyboard character after kbhit() has been called.
Should not be called in the same program as getarrow().
'''
s = ''
if os.name == 'nt':
return msvcrt.getch().decode('utf-8')
else:
return sys.stdin.read(1)
def getarrow(self):
''' Returns an arrow-key code after kbhit() has been called. Codes are
0 : up
1 : right
2 : down
3 : left
Should not be called in the same program as getch().
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
''' Returns True if keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select([sys.stdin], [], [], 0)
return dr != []
使用这个的例子:
import kbhit
kb = kbhit.KBHit()
while(True):
print("Key not pressed") #Do something
if kb.kbhit(): #If a key is pressed:
k_in = kb.getch() #Detect what key was pressed
print("You pressed ", k_in, "!") #Do something
kb.set_normal_term()
或者您可以使用PyPi中的getch模块。但是这会阻塞while循环
其他回答
(目前)排名第一的答案(带有ActiveState代码)过于复杂。当仅仅一个函数就足够了时,我不认为有理由使用类。下面是两个实现,它们实现了相同的功能,但代码可读性更强。
这两个实现:
可以在python2或python3中正常工作 可以在Windows, OSX和Linux上工作 只读取一个字节(即,它们不等待换行符) 不要依赖任何外部库 是自包含的(没有函数定义之外的代码)
版本1:易读且简单
def getChar():
try:
# for Windows-based systems
import msvcrt # If successful, we are on Windows
return msvcrt.getch()
except ImportError:
# for POSIX-based systems (with termios & tty support)
import tty, sys, termios # raises ImportError if unsupported
fd = sys.stdin.fileno()
oldSettings = termios.tcgetattr(fd)
try:
tty.setcbreak(fd)
answer = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
return answer
版本2:避免重复导入和异常处理:
[编辑]我错过了ActiveState代码的一个优点。如果您计划多次读取字符,该代码可以避免在类unix系统上重复Windows导入和ImportError异常处理的成本(可以忽略不计)。虽然你可能应该更关心代码的可读性,而不是可以忽略的优化,这里有一个替代方案(它类似于Louis的答案,但getChar()是自包含的),它的功能与ActiveState代码相同,更可读:
def getChar():
# figure out which function to use once, and store it in _func
if "_func" not in getChar.__dict__:
try:
# for Windows-based systems
import msvcrt # If successful, we are on Windows
getChar._func=msvcrt.getch
except ImportError:
# for POSIX-based systems (with termios & tty support)
import tty, sys, termios # raises ImportError if unsupported
def _ttyRead():
fd = sys.stdin.fileno()
oldSettings = termios.tcgetattr(fd)
try:
tty.setcbreak(fd)
answer = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
return answer
getChar._func=_ttyRead
return getChar._func()
使用上述getChar()版本的示例代码:
from __future__ import print_function # put at top of file if using Python 2
# Example of a prompt for one character of input
promptStr = "Please give me a character:"
responseStr = "Thank you for giving me a '{}'."
print(promptStr, end="\n> ")
answer = getChar()
print("\n")
print(responseStr.format(answer))
你可以用click。它经过了良好的测试,可以在Linux, Mac和Windows上运行。
import click
print('Continue? [yn] ')
c = click.getchar() # Gets a single character
if c == 'y':
print('We will go on')
elif c == 'n':
print('Abort!')
else:
print('Invalid input :(')
python中的curses包可以使用一些语句从终端进入“原始”模式进行字符输入。Curses的主要用途是接管屏幕进行输出,这可能不是你想要的。此代码片段使用print()语句代替,这是可用的,但必须注意curses如何更改附加到输出的行结束符。
#!/usr/bin/python3
# Demo of single char terminal input in raw mode with the curses package.
import sys, curses
def run_one_char(dummy):
'Run until a carriage return is entered'
char = ' '
print('Welcome to curses', flush=True)
while ord(char) != 13:
char = one_char()
def one_char():
'Read one character from the keyboard'
print('\r? ', flush= True, end = '')
## A blocking single char read in raw mode.
char = sys.stdin.read(1)
print('You entered %s\r' % char)
return char
## Must init curses before calling any functions
curses.initscr()
## To make sure the terminal returns to its initial settings,
## and to set raw mode and guarantee cleanup on exit.
curses.wrapper(run_one_char)
print('Curses be gone!')
我对python3的解决方案,不依赖于任何pip包。
# precondition: import tty, sys
def query_yes_no(question, default=True):
"""
Ask the user a yes/no question.
Returns immediately upon reading one-char answer.
Accepts multiple language characters for yes/no.
"""
if not sys.stdin.isatty():
return default
if default:
prompt = "[Y/n]?"
other_answers = "n"
else:
prompt = "[y/N]?"
other_answers = "yjosiá"
print(question,prompt,flush= True,end=" ")
oldttysettings = tty.tcgetattr(sys.stdin.fileno())
try:
tty.setraw(sys.stdin.fileno())
return not sys.stdin.read(1).lower() in other_answers
except:
return default
finally:
tty.tcsetattr(sys.stdin.fileno(), tty.TCSADRAIN , oldttysettings)
sys.stdout.write("\r\n")
tty.tcdrain(sys.stdin.fileno())
接受的答案对我来说并不是那么好(我按住一个键,什么都不会发生,然后我按下另一个键,它就会工作)。
在学习了curses模块之后,这似乎是正确的方法。现在它可以通过Windows -游标(通过pip可用)用于Windows,因此您可以以与平台无关的方式进行编程。下面这个例子是受YouTube上这个教程的启发:
import curses
def getkey(stdscr):
curses.curs_set(0)
while True:
key = stdscr.getch()
if key != -1:
break
return key
if __name__ == "__main__":
print(curses.wrapper(getkey))
以.py扩展名保存,或在交互模式下运行curses.wrapper(getkey)。
推荐文章
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行
- 如何计算两个时间串之间的时间间隔
- 我如何才能找到一个Python函数的参数的数量?
- 您可以使用生成器函数来做什么?
- 将Python诗歌与Docker集成
- 提取和保存视频帧
- 使用请求包时出现SSL InsecurePlatform错误
- 如何检索Pandas数据帧中的列数?
- except:和except的区别:
- 错误:“字典更新序列元素#0的长度为1;2是必需的”