如何用按键杀死while循环?


86

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

Answers:


143

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

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

由于Ctrl-C会引发原因KeyboardInterrupt,因此只需将其捕获到循环之外并忽略它即可。


2
@克里斯:为什么不尝试一下。(然后发表评论)
SilentGhost 2012年

^C在中发出此崩溃(我得到错误跟踪)信息do_something()。如何避免这种情况?
Atcold

do_something()从USB读取一些值,因此,如果^C在我进入时发出该do_something()错误,则会出现令人讨厌的通信错误。相反,如果我在while之外的do_something(),一切都很顺利。因此,我想知道如何处理这种情况。我不确定自己是否足够清楚。
Atcold

@Atcold因此,您正在使用一个已编译的扩展模块。它是什么样的模块?它是包装的通用C库吗?
基思

我有一个呼叫pyVISA和一个呼叫给matplotlib,因此我可以实时查看自己的测量结果。有时我会得到一些时髦的错误。我认为我应该打开一个单独的问题,并停止污染您的答案……
Atcold

34

有一种解决方案,不需要非标准模块,并且100%可运输

import thread

def input_thread(a_list):
    raw_input()
    a_list.append(True)

def do_stuff():
    a_list = []
    thread.start_new_thread(input_thread, (a_list,))
    while not a_list:
        stuff()

4
对于使用Python 3+的用户,请注意:raw_input()已重命名为input(),线程模块现在为_thread。
Wieschie

根据python 3文档,它在python 3中不起作用:“线程与中断发生奇怪的交互:KeyboardInterrupt异常将被任意线程接收。(当信号模块可用时,中断总是进入主线程。)”
Towhid

@Towhid但这不使用中断。它使用从stdin读取的内容。
Artyer

@Artyer如果我没记错的话,所有击键都会引发中断,因为它们是由硬件引发的。该代码对您有用吗?如果可以,您是否进行了任何特定更改?
Towhid

2
@Towhid只是thread->_threadraw_input-> input。您必须按Enter才能输入行。如果要对任何键进行操作,请使用getch
Artyer

14

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

5
很好,但是cv2太重了,除非您已经将它用于其他用途。
ogurets

1
为什么与255并与
Talespin_Kit

@Talespin_Kit&0xff”会屏蔽该变量,因此它仅将值保留在最后8位中,而忽略其余所有位。基本上可以确保结果在0-255之间。请注意,我从来没有在opencv中这样做,而且一切正常。
eric

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

我不知道自己是在做错什么还是在做什么,但是我不知道如何停止此循环?你是怎样做的?
Mihkel

@Mihkel,您必须按<Enter>键。这将导致循环退出。
rayzinnz

这是体面的,但是除了回车之外,它不能泛化为其他键。
约翰·福布斯

在python2.7上对我不起作用,但是在python3上起作用
crazjo

我也想做多线程,但是我很喜欢上面@Keith的回答。简单明了。
上瘾的


1

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

python 3raw_input中的替换为input
Talha Anwar

1

我修改了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

从沿兔子洞的这个线程开始,我来到这里,可以在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()

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 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 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'

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.