Как убить цикл while нажатием клавиши?

86

Я читаю последовательные данные и пишу в CSV-файл с помощью цикла while. Я хочу, чтобы пользователь мог завершить цикл 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.

Крис
источник

Ответы:

143

Самый простой способ - просто прервать его обычным Ctrl-C(SIGINT).

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

Поскольку Ctrl-Cпричины KeyboardInterruptдолжны быть подняты, просто перехватите их вне цикла и игнорируйте.

Кит
источник
2
@ Крис: почему бы тебе не попробовать. (а затем комментарий)
SilentGhost 01
Этот сбой (я получаю обратную трассировку ошибок) ^Cвыдается во время работы do_something(). Как этого избежать?
Атколд,
My do_something()считывает некоторые значения с USB-накопителя, поэтому, если ^Cвыдается, когда я внутри, do_something()я получаю неприятные ошибки связи. Вместо этого, если я внутри while, за пределами do_something(), все идет гладко. Итак, мне было интересно, как справиться с этой ситуацией. Я не уверен, что достаточно ясно выразился.
Атколд
@Atcold Итак, у вас есть скомпилированный модуль расширения, который вы используете. Что это за модуль? Оборачивается ли это обычная библиотека C?
Кейт
У меня есть звонок pyVISAи звонок matplotlib, чтобы я мог визуализировать свои измерения в реальном времени. И иногда я получаю фанковые ошибки. Думаю, мне следует открыть отдельный вопрос и перестать
искажать
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 Но здесь не используются прерывания. Он использует чтение из стандартного ввода.
Artyer 07
@Artyer Если я не ошибаюсь, все нажатия клавиш вызывают прерывания, так как они вызываются оборудованием. работал ли этот код на вас, и если да, то внесли ли вы какие-либо конкретные изменения?
Towhid
2
@Towhid только что thread-> _threadи raw_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 02
1
почему И с 255
Talespin_Kit 02
@Talespin_Kit & 0xff »маскирует переменную, поэтому она оставляет только значение в последних 8 битах и ​​игнорирует все остальные биты. В основном это гарантирует, что результат будет в пределах 0-255. Обратите внимание: я никогда не делаю этого в opencv, и все работает нормально.
Эрик
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()
Rayzinnz
источник
Я не знаю, делаю ли я что-то не так или что, но не могу понять, как остановить этот цикл? Как ты это делаешь?
Михкель
@Mihkel, вы должны нажать клавишу <Enter>. Это приведет к выходу из цикла.
rayzinnz
Это неплохо, но не распространяется на клавиши, кроме ввода.
Джон Форбс
не работает для меня на python2.7, но работает на python3
Crazjo
я тоже думаю о многопоточности, но мне очень нравится ответ @Keith выше. Достаточно просто и понятно.
пристрастился
4

pyHook может помочь. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4

См. Крючки для клавиатуры; это более общий характер - если вам нужны определенные взаимодействия с клавиатурой, а не просто использование KeyboardInterrupt.

Кроме того, в целом (в зависимости от вашего использования) я думаю, что опция Ctrl-C, по-прежнему доступная для уничтожения вашего скрипта, имеет смысл.

См. Также предыдущий вопрос: определить в python, какие клавиши нажаты

Анов
источник
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 3 raw_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'

Таймур Ариф
источник