我有一段时间在一个无限的、不间断的循环中做一些事情。
不应停止或等待用户输入。但我需要用户在按下特定键时可以停止。例如,如果用户按f
做一些新的事情,或者按p
做一些其他的事情。
我应该如何让用户按键不停地按下?
n = 1
while True:
# do somthing
n += 1
if <press p >
# do task 1
if <press f >
# exit while
## do somthing else
我不能使用keyboard
库,因为在Linux 上需要sudo权限
如";不怎么样’’’’答案并不简单。你可以进入输入系统的底层,更改一些设置,以实现无阻塞的键盘阅读,也可以使用第三方库来实现这一点,并提供一些友好的界面。
terminedia
就是这样一个第三方项目-除其他细节外,它实现了inkey()
调用,该调用类似于同名的旧BASIC函数:它返回当前按下的键(如果有(或空字符串。
您只需要使用termedia键盘在上下文块中调用它:with terminedia.keyboard:
因此,您必须首先使用pip install terminedia
在Python环境中安装termedia。那么你的代码可能是这样的:
import terminedia as TM
n = 1
with TM.keyboard:
while True:
# do something
n += 1
if (pressed:=TM.inkey()) == "p":
# do task 1
if pressed == "f":
# exit while
break
与编写代码来设置stdin设置相比,另一个优点是termedia键盘输入是多平台的,将在windows中工作(尽管库中的许多其他功能将仅限于Unix(
(免责声明:我是项目作者(
正如@Kris在评论中所说,线程+队列可以发挥作用。这只是一个例子,它需要一些修复(扰乱终端外观(,但对你来说应该是一个很好的开始(这在这种形式的窗口上不起作用,但只是给你一个如何使用它的例子(。此外,在使用线程之前,请阅读线程文档,特别是关于全局解释器锁定的部分
import sys
import tty
import threading
import queue
import termios
def watcher(q: queue.Queue):
# To bring back terminal look. More info here https://docs.python.org/3/library/termios.html
fd = sys.stdin.fileno()
old_Settings = termios.tcgetattr(fd)
while True:
# Reads 1 char form sdin without need of newline
tty.setraw(sys.stdin.fileno())
i = sys.stdin.read(1)
if i == "p":
q.put_nowait("p")
termios.tcsetattr(fd,termios.TCSADRAIN,old_Settings)
elif i == "f":
q.put_nowait("f")
termios.tcsetattr(fd,termios.TCSADRAIN,old_Settings)
break
def runner(q: queue.Queue):
n = 1
while True:
n += 1
# You need to check if q is empty, or It will throw an empty err
if not q.empty():
key = q.get_nowait()
if key == "f":
print("Got exit event after {} loops".format(n))
break
if key == "p":
print("Got p after {} loops".format(n))
if __name__ == "__main__":
# Queue setup and 2 threads.
q = queue.Queue()
x = threading.Thread(target=watcher, args=(q,))
x.start()
y = threading.Thread(target=runner, args=(q,))
y.start()
输出afrer运行:
python3 ../../test.py
Got p after 1055953 loops
Got exit event after 4369605 loops