如何用按键终止while循环?

126

我正在使用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 循环终止后继续运行。

20个回答

1

我修改了rayzinnz的答案,使脚本以特定键结束,这里是escape键。

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()

1
你好!虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请[编辑]您的答案以添加解释并指出适用的限制和假设。 - Brian61354270

1
这是我使用线程和标准库找到的解决方案

循环一直持续,直到按下一个键
将按下的键作为单个字符字符串返回

适用于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()

1
太棒了!我唯一需要更改的是使用“import _thread”而不是“import thread”。在Debian Bullseye和Raspbian 11上运行得非常好。谢谢! - captcha

0

这是另一个示例,使用threading.Event,无需捕获SIGINTCtrl+c)。

正如@Atcold在下面的接受答案的评论中提到的那样,在循环中按下Ctrl+c可能会中断长时间运行的操作,并使其处于未定义状态。如果这个长时间运行的操作来自你正在调用的库,那么这将特别令人烦恼。

在下面的例子中,用户需要按q,然后按Enter。如果你想立即捕获按键,你需要像this answer中所说的使用_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()

0
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")

它运行正常...


0

这里有一个简单的Windows解决方案,可以安全地结束当前迭代并退出。我将其与一个计数器示例一起使用,该示例使用“Esc”键中断循环并退出。它使用kbhit()getch()函数来自msvcrt包。时间包仅出于便利原因而被调用(以设置事件之间的时间延迟)。

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'\x1b' 是“Esc”键的字节字符串字符。


0
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 键时停止


0

这可能会有所帮助 使用以下命令安装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 

0

你可以在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")

0
对于仍在寻找答案的人来说,对我来说,最简单、最短、最稳定的解决方案是。
import keyboard
from multiprocessing import Queue

q = Queue()
keyboard.add_hotkey("ctrl+alt+q", lambda: q.put("q"))
while q.empty():
    ...

-5
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”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接