如何通过击键杀死 while 循环?

How to kill a while loop with a keystroke?

提问人:Chris 提问时间:11/2/2012 最后编辑:Chris 更新时间:8/23/2023 访问量:300690

问:

我正在读取串行数据并使用 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 循环终止后继续运行。

python while-loop 中断

评论


答:

190赞 Keith 11/2/2012 #1

最简单的方法是用通常的 (SIGINT) 打断它。Ctrl-C

try:
    while True:
        do_something()
except KeyboardInterrupt:
    pass

既然要引起,就把它抓到循环之外,忽略它。Ctrl-CKeyboardInterrupt

评论

2赞 SilentGhost 11/2/2012
@Chris:你为什么不试一试。(然后评论)
1赞 Atcold 4/21/2016
此崩溃(我得到错误跟踪)是在 .你怎么能避免这种情况?^Cdo_something()
2赞 Atcold 4/23/2016
我从 USB 读取了一些值,因此,如果在我在里面时发出,我会得到令人讨厌的通信错误。相反,如果我在 , 外面 ,一切都很顺利。所以,我想知道如何处理这种情况。我不确定我是否把自己说得足够清楚。do_something()^Cdo_something()whiledo_something()
1赞 Keith 4/23/2016
@Atcold 所以你有一个正在使用的编译扩展模块。它是一种什么样的模块?是被包装的普通 C 库吗?
1赞 Atcold 4/25/2016
我有一个电话和一个电话,这样我就可以实时可视化我的测量结果。我有时会遇到时髦的错误。我想我应该打开一个单独的问题,停止污染你的答案......pyVISAmatplotlib
4赞 Anov 11/2/2012 #2

pyHook 可能会有所帮助。http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

请参阅键盘挂钩;这更笼统 - 如果你想要特定的键盘交互,而不仅仅是使用 KeyboardInterrupt。

此外,一般来说(取决于您的使用情况),我认为仍然可以使用 Ctrl-C 选项来终止您的脚本是有意义的。

另请参阅上一个问题:在 python 中检测按下了哪些键

48赞 user297171 8/22/2014 #3

有一种解决方案不需要非标准模块,并且 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()

评论

7赞 Wieschie 9/22/2016
对于那些使用 Python 3+ 的人来说,请注意:raw_input() 已重命名为 input(),线程模块现在已_thread。
0赞 Towhid 3/30/2017
根据 python 3 文档,在 python 3 中不起作用:“线程与中断交互很奇怪:任意线程将接收 KeyboardInterrupt 异常。(当信号模块可用时,中断总是转到主线程。
0赞 Artyer 6/8/2017
@Towhid 但这不使用中断。它使用从 stdin 读取。
1赞 Towhid 6/12/2017
@Artyer如果我没记错的话,所有的击键都会引发中断,因为它们是由硬件引发的。这段代码对你有用吗,如果是,你有没有做任何具体的改变?
2赞 Artyer 6/13/2017
@Towhid只是 -> 和 -> .您必须按回车键才能馈送线路。如果要对任何键执行操作,请使用 getchthread_threadraw_inputinput
18赞 Luis Jose 9/1/2016 #4

以下代码对我有用。它需要 openCV(import 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 ...

评论

8赞 ogurets 6/2/2017
很好,但是 cv2 太重了,除非您已经将它用于其他用途。
1赞 Talespin_Kit 3/3/2019
为什么和 255
0赞 eric 1/10/2020
@Talespin_Kit & 0xff“ 屏蔽了变量,因此它只保留最后 8 位的值,而忽略所有其他位。基本上,它确保结果在 0-255 以内。注意:我从来没有在 opencv 中这样做过,一切正常。
1赞 Julian Wise 9/24/2018 #5

总是有.sys.exit()

Python 核心库中的系统库有一个退出功能,在原型设计时非常方便。 代码将遵循以下内容:

import sys

while True:
    selection = raw_input("U: Create User\nQ: 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()

评论

2赞 Talha Anwar 5/13/2020
在 Python 3 中替换为raw_inputinput
14赞 rayzinnz 4/24/2019 #6

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

评论

0赞 Mihkel 8/17/2019
我不知道我是做错了什么还是什么,但我不知道如何停止这个循环?你是怎么做到的?
0赞 rayzinnz 8/18/2019
@Mihkel您必须按<Enter>键。这将导致循环退出。
1赞 John Forbes 12/13/2019
这是不错的,但不能推广到除 enter 之外的键。
0赞 crazjo 12/20/2019
在 python2.7 上对我不起作用,但在 python3 上有效
0赞 addicted 3/20/2020
做多线程也是我的想法,但我非常喜欢上面@Keith的答案。简单明了。
0赞 ANKIT YADAV 7/6/2019 #7

这可能会有所帮助 使用 -- 安装 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 
-5赞 Taimoor Arif 3/7/2020 #8
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”

1赞 Pascal Wendler 4/24/2020 #9

我修改了 rayzinnz 的答案,以使用特定键结束脚本,在本例中为转义键

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 4/24/2020
你好!虽然这段代码可以解决这个问题,但包括解释它如何以及为什么解决这个问题,将真正有助于提高你的帖子的质量,并可能导致更多的赞成票。请记住,您是在为将来的读者回答问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释,并指出适用的限制和假设。
1赞 Berni Gf 5/6/2020 #10

这是我在线程和标准库

中找到的解决方案 循环一直持续下去,直到按下一个键 以单个字符串

的形式返回按下
的键 适用于 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赞 captcha 5/15/2022
这太棒了!我唯一需要更改的是使用“import _thread”而不是“import thread”。在 Debian Bullseye 和 Raspbian 11 上工作得很好。谢谢!
2赞 ArthurH 6/26/2020 #11

从兔子洞的这个线程开始,我来到了这个,适用于 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 \r\n
    print(string_to_print, end="\r\n")

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()
13赞 user15083300 1/26/2021 #12
pip install keyboard

import keyboard

while True:
    # do something
    if keyboard.is_pressed("q"):
        print("q pressed, ending loop")
        break

评论

3赞 PeterS 1/26/2021
嗨,新用户,感谢您的回答。您能否添加更多有关其工作原理的解释,并在必要时提供任何支持文档链接。仅仅粘贴代码并不总是有帮助的,描述解决方案将帮助未来的读者了解您的答案是否适合他们。
0赞 rhody 5/7/2021
我认为这是对原始问题的正确且当然最简单的解决方案。适用于 Windows 10、python 3.8
3赞 László Báthory 9/8/2021
这个在 *nix 系统中不起作用,除非用户是 root(又名 never)
0赞 Facu 3/13/2021 #13

下面是一个简单的 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'\x1b' 是“Esc”键的字节字符串字符。

5赞 LuettgeM 4/1/2021 #14

这是一个对我有用的解决方案。从这里和其他地方的帖子中得到了一些想法。在按下定义的键 (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
0赞 zardosht 7/29/2021 #15

这是另一个使用线程的例子。事件,无需捕获 ()。SIGINTCtrl+c

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

在下面的示例中,用户需要按 ,然后按 。如果你想立即捕获击键,你需要从这个答案中得到类似的东西。qEnter_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赞 Mohamed Fathallah 1/15/2022 #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 时停止

0赞 Shay 1/21/2023 #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")

它有效......

1赞 aimfeld 3/21/2023 #18

接受的答案对我来说是不可靠的,但以下带有pyinput的解决方案有效(Python 3.10,Linux)。按下时,while循环结束:KeyboardInterruptq

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
0赞 Pairen1383 8/23/2023 #19

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

如果您使用 Sleep 并且您有 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")