Làm thế nào để giết vòng lặp while bằng cách nhấn phím?


86

Tôi đang đọc dữ liệu nối tiếp và ghi vào tệp csv bằng vòng lặp while. Tôi muốn người dùng có thể loại bỏ vòng lặp while khi họ cảm thấy đã thu thập đủ dữ liệu.

while True:
    #do a bunch of serial stuff

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

Tôi đã làm điều gì đó như thế này bằng cách sử dụng opencv, nhưng nó dường như không hoạt động trong ứng dụng này (và tôi thực sự không muốn nhập opencv chỉ cho chức năng này) ...

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

Vì thế. Làm cách nào tôi có thể cho phép người dùng thoát ra khỏi vòng lặp?

Ngoài ra, tôi không muốn sử dụng ngắt bàn phím, vì tập lệnh cần tiếp tục chạy sau khi kết thúc vòng lặp while.

Câu trả lời:


144

Cách dễ nhất là chỉ cần ngắt nó bằng thông thường Ctrl-C(SIGINT).

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

Ctrl-Cnguyên nhân KeyboardInterruptđược nêu ra, chỉ cần nắm bắt nó bên ngoài vòng lặp và bỏ qua nó.


2
@Chris: sao bạn không thử. (và sau đó nhận xét)
SilentGhost

Sự cố này (tôi nhận được dấu vết lỗi trở lại) ^Cđược đưa ra khi ở trong do_something(). Làm thế nào bạn có thể tránh điều này?
Atcold

Của tôi do_something()đọc một số giá trị từ USB, vì vậy, nếu ^Cđược cấp khi đang ở bên trong, do_something()tôi sẽ gặp lỗi giao tiếp khó chịu. Thay vào đó, nếu tôi ở trong while, bên ngoài do_something(), tất cả đều suôn sẻ. Vì vậy, tôi rất băn khoăn không biết xử lý tình huống này như thế nào. Tôi không chắc mình đã nói đủ rõ ràng.
Atcold

@Atcold Vậy là bạn đã có một mô-đun mở rộng đã biên dịch mà bạn đang sử dụng. Nó là loại mô-đun nào? Nó có phải là một thư viện C chung đang được bao bọc không?
Keith

Tôi có một cuộc gọi đến pyVISAvà một cuộc gọi tới matplotlibđể tôi có thể hình dung trực tiếp các phép đo của mình. Và đôi khi tôi gặp phải những lỗi thú vị. Tôi nghĩ tôi nên mở một câu hỏi riêng và ngừng làm ô nhiễm câu trả lời của bạn ...
Atcold

34

Có một giải pháp không yêu cầu mô-đun không chuẩn và có thể vận chuyển 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
Chỉ cần lưu ý đối với những người sử dụng Python 3+: raw_input () đã được đổi tên thành input () và mô-đun luồng bây giờ là _thread.
Wieschie

Không hoạt động trong python 3, theo tài liệu python 3: "Các luồng tương tác kỳ lạ với ngắt: ngoại lệ KeyboardInterrupt sẽ được nhận bởi một luồng tùy ý. (Khi mô-đun tín hiệu khả dụng, ngắt luôn chuyển đến luồng chính.)"
Towhid

@Towhid Nhưng điều này không sử dụng ngắt. Nó sử dụng cách đọc từ stdin.
Artyer

@Artyer Nếu tôi không nhầm, tất cả các lần nhấn phím đều gây ra sự gián đoạn, vì chúng được tạo ra bởi một phần cứng. mã này có phù hợp với bạn không và nếu có thì bạn có thực hiện bất kỳ thay đổi cụ thể nào không?
Towhid

2
@Towhid chỉ thread-> _threadraw_input-> input. Bạn phải nhấn enter để cấp dòng. Nếu bạn muốn thực hiện trên bất kỳ phím nào, hãy sử dụng getch .
Artyer

14

mã sau đây phù hợp với tôi. Nó yêu cầu openCV (nhập cv2).

Mã này bao gồm một vòng lặp vô hạn liên tục tìm kiếm một phím được nhấn. Trong trường hợp này, khi nhấn phím 'q', chương trình sẽ kết thúc. Các phím khác có thể được nhấn (trong ví dụ này là 'b' hoặc 'k') để thực hiện các hành động khác nhau như thay đổi giá trị biến hoặc thực thi một hàm.

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
Tốt, nhưng cv2 quá nặng, trừ khi bạn đã sử dụng nó cho việc khác.
ogurets

1
tại sao VÀ với 255
Talespin_Kit

@Talespin_Kit & 0xff ”che dấu biến để nó chỉ để lại giá trị trong 8 bit cuối cùng và bỏ qua tất cả các bit còn lại. Về cơ bản, nó đảm bảo kết quả sẽ nằm trong khoảng 0-255. Lưu ý rằng tôi chưa bao giờ làm điều này trong opencv và mọi thứ hoạt động tốt.
eric

6

Đối với Python 3.7, tôi đã sao chép và thay đổi câu trả lời rất hay bởi user297171 để nó hoạt động trong tất cả các tình huống trong Python 3.7 mà tôi đã thử nghiệm.

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

Tôi không biết mình đang làm gì sai hay sao, nhưng tôi không thể tìm ra cách để dừng vòng lặp này? Làm thế nào để bạn làm điều đó?
Mihkel

@Mihkel bạn phải nhấn phím <Enter>. Điều này sẽ làm cho vòng lặp thoát ra.
rayzinnz

Điều này là phù hợp, nhưng không tổng quát cho các khóa khác ngoài enter.
John Forbes

không làm việc cho tôi trên python2.7 nhưng hoạt động trên python3
crazjo

thực hiện đa luồng cũng là suy nghĩ của tôi nhưng tôi khá thích câu trả lời của @Keith ở trên. Đơn giản và đủ rõ ràng.
nghiện

4

pyHook có thể giúp. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

Xem móc bàn phím; điều này mang tính khái quát hơn - nếu bạn muốn các tương tác bàn phím cụ thể chứ không chỉ sử dụng KeyboardInterrupt.

Ngoài ra, nói chung (tùy thuộc vào việc sử dụng của bạn), tôi nghĩ rằng việc có tùy chọn Ctrl-C vẫn có sẵn để giết tập lệnh của bạn là hợp lý.

Xem thêm câu hỏi trước: Phát hiện trong python phím nào được nhấn


1

Luôn luôn có sys.exit().

Thư viện hệ thống trong thư viện lõi của Python có một chức năng thoát rất tiện dụng khi tạo mẫu. Mã sẽ nằm dọc theo các dòng:

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

trong python 3 raw_inputđược thay thế bằnginput
Talha Anwar

1

Tôi đã sửa đổi câu trả lời từ rayzinnz để kết thúc tập lệnh bằng một khóa cụ thể, trong trường hợp này là khóa thoát

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

Xin chào! Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do tại sao điều này giải quyết vấn đề sẽ thực sự giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều phiếu bầu hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những giới hạn và giả định áp dụng.
Brian

1

Từ việc theo dõi chủ đề này xuống lỗ thỏ, tôi đã đi đến điều này, hoạt động trên Win10 và Ubuntu 20.04. Tôi không chỉ muốn giết tập lệnh và sử dụng các khóa cụ thể, và nó phải hoạt động trên cả MS và 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

Điều này có thể hữu ích khi cài đặt pynput với - pip cài đặt 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

Đây là giải pháp tôi đã tìm thấy với các luồng và thư viện tiêu chuẩn

Vòng lặp tiếp tục diễn ra cho đến khi một phím được nhấn
Trả lại phím được nhấn dưới dạng một chuỗi ký tự đơn

Hoạt động trong Python 2.7 và 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('  :( ')

để nhập sử dụng 'ENTER'

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.