Wie man eine while-Schleife mit einem Tastendruck beendet?


86

Ich lese serielle Daten und schreibe mit einer while-Schleife in eine CSV-Datei. Ich möchte, dass der Benutzer die while-Schleife beenden kann, sobald er das Gefühl hat, genügend Daten gesammelt zu haben.

while True:
    #do a bunch of serial stuff

    #if the user presses the 'esc' or 'return' key:
        break

Ich habe so etwas mit opencv gemacht, aber es scheint in dieser Anwendung nicht zu funktionieren (und ich möchte opencv wirklich nicht nur für diese Funktion importieren) ...

        # Listen for ESC or ENTER key
        c = cv.WaitKey(7) % 0x100
        if c == 27 or c == 10:
            break

Damit. Wie kann ich den Benutzer aus der Schleife ausbrechen lassen?

Außerdem möchte ich keinen Tastaturinterrupt verwenden, da das Skript nach Beendigung der while-Schleife weiter ausgeführt werden muss.

Antworten:


143

Am einfachsten ist es, es einfach mit dem üblichen Ctrl-C(SIGINT) zu unterbrechen .

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

Da die Ctrl-CUrsachen KeyboardInterruptausgelöst werden, fangen Sie es einfach außerhalb der Schleife und ignorieren Sie es.


2
@ Chris: Warum probierst du es nicht aus? (und dann Kommentar)
SilentGhost

Dieser Absturz (ich bekomme eine Fehlerverfolgung zurück) ^Cwird ausgegeben, während in do_something(). Wie können Sie das vermeiden?
Atcold

Mein do_something()liest einige Werte vom USB, also ^Cbekomme do_something()ich böse Kommunikationsfehler , wenn sie ausgegeben werden, während ich drinnen bin. Wenn ich stattdessen whiledraußen bin do_something(), ist alles glatt. Also habe ich mich gefragt, wie ich mit dieser Situation umgehen soll. Ich bin mir nicht sicher, ob ich mich klar genug ausgedrückt habe.
Atcold

@Atcold Sie haben also ein kompiliertes Erweiterungsmodul, das Sie verwenden. Was für ein Modul ist das? Ist es eine übliche C-Bibliothek, die verpackt wird?
Keith

Ich habe einen Anruf bei pyVISAund einen Anruf bei matplotlib, damit ich meine Messungen live visualisieren kann. Und manchmal bekomme ich funky Fehler. Ich denke, ich sollte eine separate Frage öffnen und aufhören, Ihre Antwort zu verschmutzen ...
Atcold

34

Es gibt eine Lösung, die keine nicht standardmäßigen Module erfordert und zu 100% transportabel ist

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

3
Nur ein Hinweis für Benutzer von Python 3+: raw_input () wurde in input () umbenannt, und das Thread-Modul heißt jetzt _thread.
Wieschie

Funktionierte in Python 3 laut Python 3-Dokumenten nicht: "Threads interagieren seltsamerweise mit Interrupts: Die KeyboardInterrupt-Ausnahme wird von einem beliebigen Thread empfangen. (Wenn das Signalmodul verfügbar ist, werden Interrupts immer an den Hauptthread gesendet.)"
Towhid

@Towhid Dies verwendet jedoch keine Interrupts. Es verwendet das Lesen von stdin.
Artyer

@Artyer Wenn ich mich nicht irre, lösen alle Tastenanschläge Interrupts aus, da sie von einer Hardware ausgelöst werden. Hat dieser Code für Sie funktioniert und wenn ja, haben Sie bestimmte Änderungen vorgenommen?
Towhid

2
@Towhid nur thread-> _threadund raw_input-> input. Sie müssen die Eingabetaste drücken, um die Leitung zu füttern. Wenn Sie einen Schlüssel verwenden möchten , verwenden Sie getch .
Artyer

14

Der folgende Code funktioniert für mich. Es erfordert openCV (Import cv2).

Der Code besteht aus einer Endlosschleife, die ständig nach einer gedrückten Taste sucht. In diesem Fall endet das Programm, wenn die Taste 'q' gedrückt wird. Andere Tasten können gedrückt werden (in diesem Beispiel 'b' oder 'k'), um verschiedene Aktionen auszuführen, z. B. einen Variablenwert ändern oder eine Funktion ausführen.

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
Gut, aber cv2 ist zu schwer, es sei denn, Sie verwenden es bereits für etwas anderes.
Ogurets

1
warum UND mit 255
Talespin_Kit

@Talespin_Kit & 0xff ”maskiert die Variable so, dass nur der Wert in den letzten 8 Bits übrig bleibt, und ignoriert alle übrigen Bits. Grundsätzlich wird sichergestellt, dass das Ergebnis zwischen 0 und 255 liegt. Hinweis: Ich mache das nie in opencv und die Dinge funktionieren gut.
Eric

6

Für Python 3.7 habe ich die sehr schöne Antwort von user297171 kopiert und geändert, damit sie in allen von mir getesteten Szenarien in Python 3.7 funktioniert.

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

Ich weiß nicht, ob ich etwas falsch mache oder was, aber ich kann nicht herausfinden, wie ich diese Schleife stoppen kann. Wie machst du das?
Mihkel

@Mihkel Sie müssen die <Eingabetaste> drücken. Dadurch wird die Schleife beendet.
Rayzinnz

Dies ist anständig, verallgemeinert sich jedoch nicht auf andere Tasten als die Eingabe.
John Forbes

funktioniert nicht für mich auf Python2.7, aber funktioniert auf Python3
Crazjo

Multithreading ist auch das, woran ich denke, aber ich mag die Antwort von @Keith oben sehr. Einfach und klar genug.
süchtig

4

pyHook könnte helfen. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

Siehe Tastaturhaken; Dies ist allgemeiner - wenn Sie bestimmte Tastaturinteraktionen wünschen und nicht nur KeyboardInterrupt verwenden möchten.

Im Allgemeinen (abhängig von Ihrer Verwendung) halte ich es für sinnvoll, die Strg-C-Option weiterhin verfügbar zu haben, um Ihr Skript zu beenden.

Siehe auch vorherige Frage: Ermitteln Sie in Python, welche Tasten gedrückt werden


1

Es gibt immer sys.exit().

Die Systembibliothek in Pythons Kernbibliothek verfügt über eine Exit-Funktion, die beim Prototyping sehr praktisch ist. Der Code würde wie folgt lauten:

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

in Python 3 raw_inputwird ersetzt durchinput
Talha Anwar

1

Ich habe die Antwort von rayzinnz geändert, um das Skript mit einem bestimmten Schlüssel zu beenden, in diesem Fall dem Escape-Schlüssel

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

Hallo! Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Brian

1

Nachdem ich diesem Thread im Kaninchenbau gefolgt bin, bin ich zu diesem Thema gekommen, das unter Win10 und Ubuntu 20.04 funktioniert. Ich wollte mehr als nur das Skript beenden und bestimmte Schlüssel verwenden, und es musste sowohl unter MS als auch unter Linux funktionieren.

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

Dies kann hilfreich sein, um pynput mit - pip install pynput zu installieren

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

Dies ist die Lösung, die ich mit Threads und Standardbibliotheken gefunden habe. Die

Schleife wird fortgesetzt, bis eine Taste gedrückt wird.
Gibt die als einzelne Zeichenfolge gedrückte Taste zurück.

Funktioniert in Python 2.7 und 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('  :( ')

für die Eingabe verwenden Sie 'ENTER'

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.