จะฆ่า while loop ด้วยการกดแป้นพิมพ์ได้อย่างไร?


88

ฉันกำลังอ่านข้อมูลอนุกรมและเขียนไปยังไฟล์ csv โดยใช้ while loop ฉันต้องการให้ผู้ใช้สามารถฆ่า while loop ได้เมื่อพวกเขารู้สึกว่าได้รวบรวมข้อมูลเพียงพอแล้ว

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 สิ้นสุดลง

คำตอบ:


144

วิธีที่ง่ายที่สุดคือเพียงแค่ขัดจังหวะด้วยCtrl-CSIGINT ปกติ

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

เนื่องจากCtrl-CสาเหตุKeyboardInterruptที่ต้องยกขึ้นเพียงแค่จับมันนอกลูปและเพิกเฉย


2
@ คริส: ทำไมคุณไม่ลองดู (แล้วแสดงความคิดเห็น)
SilentGhost

เกิดความผิดพลาดนี้ (ผมได้รับข้อผิดพลาดร่องรอยกลับ) จะมีการออกขณะที่อยู่ใน^C do_something()คุณจะหลีกเลี่ยงสิ่งนี้ได้อย่างไร?
Atcold

ฉันdo_something()อ่านค่าบางอย่างจาก USB ดังนั้นหาก^Cออกในขณะที่ฉันอยู่ข้างในdo_something()ฉันได้รับข้อผิดพลาดในการสื่อสารที่น่ารังเกียจ แต่ถ้าฉันอยู่whileข้างนอกdo_something()ทุกอย่างก็ราบรื่น ดังนั้นฉันจึงสงสัยว่าจะจัดการกับสถานการณ์นี้อย่างไร ฉันไม่แน่ใจว่าตัวเองชัดเจนพอ
Atcold

@Atcold ดังนั้นคุณจึงมีโมดูลส่วนขยายที่คอมไพล์แล้วที่คุณใช้อยู่ เป็นโมดูลประเภทใด มีการห่อไลบรารี C ทั่วไปหรือไม่?
Keith

ฉันมีสายpyVISAและโทรหาmatplotlibเพื่อที่ฉันจะได้เห็นภาพการวัดของฉันแบบสดๆ และบางครั้งฉันก็ได้รับข้อผิดพลาดที่ขี้ขลาด ฉันคิดว่าฉันควรเปิดคำถามแยกต่างหากและหยุดสร้างมลพิษให้กับคำตอบของคุณ ...
Atcold

36

มีวิธีแก้ปัญหาที่ไม่ต้องใช้โมดูลที่ไม่ได้มาตรฐานและสามารถขนส่งได้ 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()

5
หมายเหตุสำหรับผู้ที่ใช้ Python 3+: raw_input () ถูกเปลี่ยนชื่อเป็น input () และตอนนี้โมดูลเธรดเป็น _thread
Wieschie

ไม่ทำงานใน python 3 ตามเอกสารของ python 3: "เธรดโต้ตอบอย่างแปลก ๆ กับการขัดจังหวะ: ข้อยกเว้น KeyboardInterrupt จะได้รับโดยเธรดโดยพลการ (เมื่อโมดูลสัญญาณพร้อมใช้งานการขัดจังหวะจะไปที่เธรดหลักเสมอ)"
Towhid

@Towhid แต่สิ่งนี้ไม่ใช้การขัดจังหวะ ใช้การอ่านจาก stdin
Artyer

@Artyer ถ้าฉันจำไม่ผิดการกดแป้นพิมพ์ทั้งหมดจะเพิ่มการขัดจังหวะเนื่องจากฮาร์ดแวร์ยกระดับขึ้น รหัสนี้ได้ผลสำหรับคุณหรือไม่และคุณได้ทำการเปลี่ยนแปลงใด ๆ หรือไม่
Towhid

2
@Towhid เพียงthread-> _threadและ->raw_input inputคุณต้องกด Enter เพื่อป้อนเส้น หากคุณต้องการที่จะทำในปุ่มใด ๆ ใช้getch
Artyer

14

รหัสต่อไปนี้ใช้ได้กับฉัน ต้องใช้ 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 ...

5
ดี แต่ cv2 หนักเกินไปเว้นแต่คุณจะใช้งานอย่างอื่นอยู่แล้ว
ogurets

1
why AND with 255
Talespin_Kit

@Talespin_Kit & 0xff” มาสก์ตัวแปรเพื่อให้เหลือเฉพาะค่าใน 8 บิตสุดท้ายและละเว้นบิตที่เหลือทั้งหมด โดยทั่วไปจะทำให้มั่นใจได้ว่าผลลัพธ์จะอยู่ในช่วง 0-255 โปรดทราบว่าฉันไม่เคยทำสิ่งนี้ใน opencv และสิ่งต่างๆก็ใช้ได้ดี
eric

7

สำหรับ 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

นี่เป็นสิ่งที่ดี แต่ไม่ได้เน้นไปที่คีย์อื่นนอกจาก enter
John Forbes

ใช้งานไม่ได้สำหรับฉันใน python2.7 แต่ใช้งานได้กับ python3
crazjo

การทำมัลติเธรดเป็นสิ่งที่อยู่ในความคิดของฉันเช่นกัน แต่ฉันค่อนข้างชอบคำตอบของ @Keith ข้างต้น ง่ายและชัดเจนเพียงพอ
ติด

4

pyHook อาจช่วยได้ http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

ดูตะขอแป้นพิมพ์ นี่เป็นข้อมูลทั่วไปมากขึ้น - หากคุณต้องการการโต้ตอบกับแป้นพิมพ์ที่เฉพาะเจาะจงและไม่ใช่แค่ใช้ KeyboardInterrupt

นอกจากนี้โดยทั่วไป (ขึ้นอยู่กับการใช้งานของคุณ) ฉันคิดว่าการมีตัวเลือก Ctrl-C ยังคงพร้อมใช้งานเพื่อฆ่าสคริปต์ของคุณนั้นสมเหตุสมผล

ดูคำถามก่อนหน้านี้: ตรวจหาในหลามว่ากดปุ่มใด


1

มีอยู่เสมอsys.exit().

ไลบรารีระบบในไลบรารีหลักของ Python มีฟังก์ชัน exit ซึ่งมีประโยชน์อย่างยิ่งเมื่อสร้างต้นแบบ รหัสจะเป็นไปตามบรรทัดของ:

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 3 raw_inputถูกแทนที่ด้วยinput
Talha Anwar

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

สวัสดี! แม้ว่ารหัสนี้จะช่วยแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมจึงแก้ปัญหานี้ได้จะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้อย่างแท้จริงและอาจส่งผลให้มีการโหวตเพิ่มขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่แค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Brian

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

นี่เป็นวิธีแก้ปัญหาที่ฉันพบกับเธรดและไลบรารีมาตรฐาน

Loop ยังคงดำเนินต่อไปจนกว่าจะกดปุ่มหนึ่งปุ่ม
ส่งคืนคีย์ที่กดเป็นสตริงอักขระเดี่ยว

ทำงานใน 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'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.