Bagaimana cara membunuh while loop dengan keystroke?


88

Saya membaca data serial dan menulis ke file csv menggunakan loop sementara. Saya ingin pengguna dapat menghentikan while loop setelah mereka merasa telah mengumpulkan cukup data.

while True:
    #do a bunch of serial stuff

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

Saya telah melakukan sesuatu seperti ini menggunakan opencv, tetapi tampaknya tidak berfungsi di aplikasi ini (dan saya benar-benar tidak ingin mengimpor opencv hanya untuk fungsi ini) ...

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

Begitu. Bagaimana cara membiarkan pengguna keluar dari loop?

Juga, saya tidak ingin menggunakan interupsi keyboard, karena skrip harus terus berjalan setelah loop sementara dihentikan.

Jawaban:


144

Cara termudah adalah dengan menyela dengan biasa Ctrl-C(SIGINT).

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

Karena Ctrl-Cpenyebab KeyboardInterruptdimunculkan, tangkap saja di luar loop dan abaikan.


2
@ Chris: mengapa Anda tidak mencobanya. (dan kemudian komentar)
SilentGhost

Crash ini (saya mendapatkan jejak kesalahan kembali) ^Cdikeluarkan saat masuk do_something(). Bagaimana Anda bisa menghindari ini?
Atcold

Saya do_something()membaca beberapa nilai dari USB, jadi, jika ^Cdikeluarkan saat saya di dalam, do_something()saya mendapatkan kesalahan komunikasi yang parah. Sebaliknya, jika saya di dalam while, di luar do_something(), semuanya lancar. Jadi, saya bertanya-tanya bagaimana menangani situasi ini. Saya tidak yakin saya telah membuat diri saya cukup jelas.
Atcold

@Atcold Jadi Anda memiliki modul ekstensi terkompilasi yang Anda gunakan. Modul apa itu? Apakah itu perpustakaan C umum yang dibungkus?
Keith

Saya memiliki panggilan ke pyVISA dan panggilan matplotlib, sehingga saya dapat memiliki visualisasi langsung dari pengukuran saya. Dan terkadang saya mendapatkan kesalahan yang funky. Saya pikir saya harus membuka pertanyaan terpisah dan berhenti mencemari jawaban Anda ...
Atcold

36

Ada solusi yang tidak memerlukan modul non-standar dan 100% dapat dipindahkan

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

5
Sekadar catatan untuk mereka yang menggunakan Python 3+: raw_input () telah diganti namanya menjadi input (), dan modul utas sekarang menjadi _thread.
Wieschie

Tidak berfungsi di python 3, menurut dokumen python 3: "Utas berinteraksi secara aneh dengan interupsi: pengecualian KeyboardInterrupt akan diterima oleh utas arbitrer. (Saat modul sinyal tersedia, interupsi selalu masuk ke utas utama.)"
Towhid

@Towhid Tapi ini tidak menggunakan interupsi. Ini menggunakan membaca dari stdin.
Artyer

@Artyer Jika saya tidak salah, semua penekanan tombol meningkatkan interupsi, karena mereka dimunculkan oleh perangkat keras. apakah kode ini berfungsi untuk Anda, dan jika demikian, apakah Anda membuat perubahan khusus?
Towhid

2
@Towhid saja thread-> _threaddan raw_input-> input. Anda harus menekan enter untuk memberi makan baris. Jika Anda ingin melakukan pada tombol apa saja, gunakan getch .
Artyer

14

kode berikut berfungsi untuk saya. Ini membutuhkan openCV (impor cv2).

Kode ini terdiri dari loop tak terbatas yang terus mencari tombol yang ditekan. Dalam kasus ini, ketika tombol 'q' ditekan, program berakhir. Tombol lain dapat ditekan (dalam contoh ini 'b' atau 'k') untuk melakukan berbagai tindakan seperti mengubah nilai variabel atau menjalankan fungsi.

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
Bagus, tapi cv2 terlalu berat, kecuali jika Anda sudah menggunakannya untuk hal lain.
ogurets

1
mengapa DAN dengan 255
Talespin_Kit

@Talespin_Kit & 0xff ”menutupi variabel sehingga hanya menyisakan nilai dalam 8 bit terakhir, dan mengabaikan semua bit lainnya. Pada dasarnya memastikan hasil akan berada dalam rentang 0-255. Catatan Saya tidak pernah melakukan ini di opencv dan semuanya berfungsi dengan baik.
eric

7

Untuk Python 3.7, saya menyalin dan mengubah jawaban yang sangat bagus oleh user297171 sehingga berfungsi di semua skenario di Python 3.7 yang saya uji.

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

Saya tidak tahu apakah saya melakukan sesuatu yang salah atau apa, tetapi saya tidak tahu bagaimana menghentikan putaran ini? Bagaimana kamu melakukannya?
Mihkel

@Mihkel Anda harus menekan tombol <Enter>. Ini akan menyebabkan loop keluar.
rayzinnz

Ini layak, tetapi tidak menggeneralisasi tombol selain enter.
John Forbes

tidak bekerja untuk saya di python2.7 tetapi bekerja pada python3
crazjo

melakukan multithreading juga ada dalam pikiran saya, tetapi saya sangat menyukai jawaban @Keith di atas. Sederhana dan cukup jelas.
kecanduan

4

pyHook mungkin membantu. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

Lihat kait keyboard; ini lebih digeneralisasikan - jika Anda menginginkan interaksi keyboard tertentu dan tidak hanya menggunakan KeyboardInterrupt.

Juga, secara umum (tergantung pada penggunaan Anda) saya pikir memiliki opsi Ctrl-C masih tersedia untuk mematikan skrip Anda masuk akal.

Lihat juga pertanyaan sebelumnya: Mendeteksi dengan python tombol mana yang ditekan


1

Selalu ada sys.exit() .

Pustaka sistem di pustaka inti Python memiliki fungsi keluar yang sangat berguna saat membuat prototipe. Kode tersebut akan berada di sepanjang baris:

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

di python 3 raw_inputdigantikan olehinput
Talha Anwar

1

Saya memodifikasi jawaban dari rayzinnz untuk mengakhiri skrip dengan kunci tertentu, dalam hal ini kunci pelarian

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

Halo! Meskipun kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan dan memberikan indikasi batasan dan asumsi apa yang berlaku.
Brian

1

Dari mengikuti utas ini ke lubang kelinci, saya sampai pada ini, berfungsi pada Win10 dan Ubuntu 20.04. Saya ingin lebih dari sekadar mematikan skrip, dan menggunakan kunci tertentu, dan itu harus berfungsi di MS dan 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

Ini mungkin berguna untuk menginstal pynput dengan - 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

Ini adalah solusi yang saya temukan dengan utas dan pustaka standar

Loop terus berlanjut sampai satu tombol ditekan
Mengembalikan tombol yang ditekan sebagai string karakter tunggal

Bekerja dengan Python 2.7 dan 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('  :( ')

untuk masuk gunakan 'ENTER'

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.