如何通过按键来终止 while 循环?
- 2025-01-22 08:45:00
- admin 原创
- 91
问题描述:
我正在使用 while 循环读取串行数据并将其写入 csv 文件。我希望用户能够在他们觉得已经收集到足够的数据后终止 while 循环。
while True:
#do a bunch of serial stuff
#if the user presses the 'esc' or 'return' key:
break
我已经使用 opencv 做过类似的事情,但它似乎无法在这个应用程序中工作(而且我真的不想只是为了这个功能而导入 opencv)...
# Listen for ESC or ENTER key
c = cv.WaitKey(7) % 0x100
if c == 27 or c == 10:
break
那么。我怎样才能让用户跳出循环?
另外,我不想使用键盘中断,因为脚本需要在 while 循环终止后继续运行。
解决方案 1:
最简单的方法就是用通常的方法Ctrl-C
(SIGINT)来中断它。
try:
while True:
do_something()
except KeyboardInterrupt:
pass
由于Ctrl-C
会引起KeyboardInterrupt
引发,因此只需在循环外捕获它并忽略它。
解决方案 2:
有一种解决方案不需要非标准模块并且 100%可移动:
import _thread
def input_thread(a_list):
raw_input() # use input() in Python3
a_list.append(True)
def do_stuff():
a_list = []
_thread.start_new_thread(input_thread, (a_list,))
while not a_list:
stuff()
解决方案 3:
以下代码对我有用。它需要 openCV(导入 cv2)。
代码由一个无限循环组成,该循环不断寻找按下的键。在这种情况下,当按下“q”键时,程序结束。可以按下其他键(在此示例中为“b”或“k”)来执行不同的操作,例如更改变量值或执行函数。
import cv2
while True:
k = cv2.waitKey(1) & 0xFF
# press 'q' to exit
if k == ord('q'):
break
elif k == ord('b'):
# change a variable / do something ...
elif k == ord('k'):
# change a variable / do something ...
解决方案 4:
对于 Python 3.7,我复制并更改了 user297171 的非常好的答案,因此它可以在我测试的 Python 3.7 中的所有场景中起作用。
import threading as th
keep_going = True
def key_capture_thread():
global keep_going
input()
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
while keep_going:
print('still going...')
do_stuff()
更新
上述input()
停止运行的方法在较新版本的 Linux 上不再有效(由于 input() 不是线程特定的,所以设计如此),因此这里提供另一种方法:
from pynput import keyboard
def do_stuff():
keep_going=keyboard.Listener(lambda key: False if key==keyboard.Key.enter else True)
keep_going.start()
while keep_going.is_alive():
print('still going...')
do_stuff()
这里的键是 Enter,但您可以将其更改keyboard.Key.enter
为任何其他键来设置任何键,例如 esc 或 q。
解决方案 5:
pip install keyboard
import keyboard
while True:
# do something
if keyboard.is_pressed("q"):
print("q pressed, ending loop")
break
解决方案 6:
这是一个对我有用的解决方案。从这里和其他地方的帖子中得到了一些想法。循环不会结束,直到按下定义的键(abortKey)。循环尽可能快地停止,并且不会尝试运行到下一个迭代。
from pynput import keyboard
from threading import Thread
from time import sleep
def on_press(key, abortKey='esc'):
try:
k = key.char # single-char keys
except:
k = key.name # other keys
print('pressed %s' % (k))
if k == abortKey:
print('end loop ...')
return False # stop listener
def loop_fun():
while True:
print('sleeping')
sleep(5)
if __name__ == '__main__':
abortKey = 't'
listener = keyboard.Listener(on_press=on_press, abortKey=abortKey)
listener.start() # start to listen on a separate thread
# start thread with loop
Thread(target=loop_fun, args=(), name='loop_fun', daemon=True).start()
listener.join() # wait for abortKey
解决方案 7:
pyHook 可能会有帮助。http ://sourceforge.net/apps/mediawiki/pyhook/index.php? title=PyHook_Tutorial#tocpyHook%5FTutorial4
查看键盘钩子;这是更为通用的——如果您想要特定的键盘交互而不是仅仅使用 KeyboardInterrupt。
此外,一般来说(取决于您的使用情况),我认为仍然可以使用 Ctrl-C 选项来终止您的脚本是有意义的。
另请参阅上一个问题:在 Python 中检测按下了哪些键
解决方案 8:
从这个线程进入兔子洞后,我找到了这个,可以在 Win10 和 Ubuntu 20.04 上运行。我想要的不仅仅是杀死脚本,还要使用特定的键,它必须在 MS 和 Linux 上都能工作。
import _thread
import time
import sys
import os
class _Getch:
"""Gets a single character from standard input. Does not echo to the screen."""
def __init__(self):
try:
self.impl = _GetchWindows()
except ImportError:
self.impl = _GetchUnix()
def __call__(self): return self.impl()
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
class _GetchWindows:
def __init__(self):
import msvcrt
def __call__(self):
import msvcrt
msvcrt_char = msvcrt.getch()
return msvcrt_char.decode("utf-8")
def input_thread(key_press_list):
char = 'x'
while char != 'q': #dont keep doing this after trying to quit, or 'stty sane' wont work
time.sleep(0.05)
getch = _Getch()
char = getch.impl()
pprint("getch: "+ str(char))
key_press_list.append(char)
def quitScript():
pprint("QUITTING...")
time.sleep(0.2) #wait for the thread to die
os.system('stty sane')
sys.exit()
def pprint(string_to_print): #terminal is in raw mode so we need to append
print(string_to_print, end="
")
def main():
key_press_list = []
_thread.start_new_thread(input_thread, (key_press_list,))
while True:
#do your things here
pprint("tick")
time.sleep(0.5)
if key_press_list == ['q']:
key_press_list.clear()
quitScript()
elif key_press_list == ['j']:
key_press_list.clear()
pprint("knock knock..")
elif key_press_list:
key_press_list.clear()
main()
解决方案 9:
我修改了 rayzinnz 的答案,用特定键结束脚本,在本例中是 Esc 键
import threading as th
import time
import keyboard
keep_going = True
def key_capture_thread():
global keep_going
a = keyboard.read_key()
if a== "esc":
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
i=0
while keep_going:
print('still going...')
time.sleep(1)
i=i+1
print (i)
print ("Schleife beendet")
do_stuff()
解决方案 10:
总是有的sys.exit()
。
Python 核心库中的系统库有一个退出函数,在原型设计时非常方便。代码如下:
import sys
while True:
selection = raw_input("U: Create User
Q: Quit")
if selection is "Q" or selection is "q":
print("Quitting")
sys.exit()
if selection is "U" or selection is "u":
print("User")
#do_something()
解决方案 11:
这是我使用线程和标准库找到的解决方案,
循环一直进行,直到按下一个键,将
按下的键作为单个字符串返回
,适用于 Python 2.7 和 3
import thread
import sys
def getch():
import termios
import sys, tty
def _getch():
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
return _getch()
def input_thread(char):
char.append(getch())
def do_stuff():
char = []
thread.start_new_thread(input_thread, (char,))
i = 0
while not char :
i += 1
print "i = " + str(i) + " char : " + str(char[0])
do_stuff()
解决方案 12:
接受的答案KeyboardInterrupt
对我来说不可靠,但以下使用pyinputq
的解决方案有效(Python 3.10,Linux)。当按下时,while 循环结束:
from pynput.keyboard import Listener # pip install pynput
keyboard_quit = False
def keyboard_handler(key):
global keyboard_quit
if hasattr(key, 'char') and key.char == 'q':
keyboard_quit = True
keyboard_listener = Listener(on_press=keyboard_handler)
keyboard_listener.start() # Non-blocking
while not keyboard_quit:
# Do something
解决方案 13:
这可能有助于安装 pynput --pip install pynput
from pynput.keyboard import Key, Listener
def on_release(key):
if key == Key.esc:
# Stop listener
return False
# Collect events until released
while True:
with Listener(
on_release=on_release) as listener:
listener.join()
break
解决方案 14:
这是一个简单的 Windows 解决方案,可以安全地结束当前迭代然后退出。我将其与一个计数器示例一起使用,该示例使用“Esc”键中断循环并退出。它使用msvcrt包中的kbhit()和getch()函数。仅出于便利原因(设置事件之间的时间延迟)调用时间包。
import msvcrt, time
print("Press 'Esc' to stop the loop...")
x = 0
while True:
x += 1
time.sleep(0.5)
print(x)
if msvcrt.kbhit():
if msvcrt.getch() == b'x1b':
print("You have pressed Esc! See you!")
time.sleep(2)
break
如果按键正在等待读取,则kbhit()函数返回 True
getch()函数读取按键并将结果字符作为字节字符串返回。它可以与任何键一起使用
b''是 'Esc' 键的字节字符串字符。
解决方案 15:
这是另一个使用的例子threading.Event
,无需捕获SIGINT
(Ctrl+c
)。
正如 @Atcold 在已接受答案下方的评论中提到的那样,按下Ctrl+c
循环可能会中断长时间运行的操作并使其处于未定义状态。当长时间运行的操作来自您正在调用的库时,这可能会特别烦人。
在下面的例子中,用户需要按下q
然后按下Enter
。如果你想立即捕获按键,你需要类似这个答案_Getch()
的内容。
import time
from threading import Thread, Event
def read_input(q_entered_event):
c = input()
if c == "q":
print("User entered q")
q_entered_event.set()
def do_long_running_stuff():
q_pressed_event = Event()
input_thread = Thread(target=read_input,
daemon=True,
args=(q_pressed_event,))
input_thread.start()
while True:
print("I am working ...")
time.sleep(1)
if q_pressed_event.is_set():
break
print("Process stopped by user.")
if __name__ == "__main__":
do_long_running_stuff()
解决方案 16:
from time import sleep
from threading import Thread
import threading
stop_flag = 0
def Wait_Char():
global stop_flag
v = input("Enter Char")
if(v == "z"):
stop_flag = 1
def h():
while(True):
print("Hello Feto")
time.sleep(1)
if(stop_flag == 1):
break
thread1 = Thread(target=Wait_Char)
thread2 = Thread(target=h)
thread1.start()
thread2.start()
print("threads finished...exiting")
这不是最好的方法,但它可以完成你想要的工作,
运行2个线程,一个等待你想要停止循环的键
(Wait_Char方法)
,一个用于循环
(H方法)
,并且两个线程都看到一个全局变量stop_flag,它控制停止过程当我按下z时停止
解决方案 17:
from pynput import keyboard
def on_press(key):
if key == keyboard.Key.esc:
return False
i = 0
with keyboard.Listener(on_press=on_press) as listener:
# Your infinite loop
while listener.running:
print(i)
i=i+1
print("Done")
有用 ...
解决方案 18:
您可以在 python 3.11 中使用键盘。
pip install keyboard
回答:
from keyboard import add_hotkey, remove_hotkey
from time import sleep
def break_loop():
global stop
stop = True
add_hotkey("q", break_loop)
stop = False
while True:
print("Do something...")
sleep(1)
if stop == True:
break
remove_hotkey("q")
如果你在工作中睡眠充足,并且有 10 秒的睡眠时间,你可以这样做:
from keyboard import add_hotkey, remove_hotkey
from time import sleep
def break_loop():
global stop
stop = True
add_hotkey("q", break_loop)
stop = False
while True:
print("Do something...")
for i in range(10): # Waiting
sleep(1) # Split 10 seconds for fast break
if stop == True: # First break
break
if stop == True: # Second break
break
remove_hotkey("q")
解决方案 19:
对于仍在寻找答案的人来说,对我来说最简单、最简短、最强大的解决方案是。
import keyboard
from multiprocessing import Queue
q = Queue()
keyboard.add_hotkey("ctrl+alt+q", lambda: q.put("q"))
while q.empty():
...
解决方案 20:
import keyboard
while True:
print('please say yes')
if keyboard.is_pressed('y'):
break
print('i got u :) ')
print('i was trying to write you are a idiot ')
print(' :( ')
使用 'ENTER' 输入