Як убити цикл 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
@Chris: чому б вам не спробувати. (а потім коментар)
SilentGhost

Цей збій (я отримую помилку назад) ^Cвидається під час роботи в do_something(). Як можна цього уникнути?
Atcold

Я do_something()читає деякі значення з USB, тому, якщо ^Cвидається, коли я перебуваю всередині, do_something()я отримую неприємні помилки в спілкуванні. Натомість, якщо я перебуваю у while, поза межами do_something(), все гладко. Отже, мені було цікаво, як вирішити цю ситуацію. Я не впевнений, що я досить чітко висловився.
Atcold

@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.
Віскі

Не працював у python 3, згідно з python 3 docs: "Потоки дивно взаємодіють із перериваннями: виняток KeyboardInterrupt буде отриманий довільним потоком. (Коли модуль сигналу доступний, переривання завжди йдуть до основного потоку.)"
Towhid

@Towhid Але це не використовує переривання. Він використовує читання з stdin.
Artyer

@Artyer Якщо я не помиляюся, усі натискання клавіш викликають переривання, оскільки вони викликані апаратним забезпеченням. чи працював цей код для вас, і якщо так, чи вносили ви якісь конкретні зміни?
Тоухід

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 занадто важкий, якщо ви вже не використовуєте його для чогось іншого.
огурець

1
чому AND з 255
Talespin_Kit

@Talespin_Kit & 0xff ”маскує змінну, так що вона залишає лише значення в останніх 8 бітах, а всі інші біти ігнорує. В основному це гарантує, що результат буде в межах 0-255. Примітка. Я ніколи не роблю цього в opencv, і все працює нормально.
Ерік

6

Для Python 3.7 я скопіював і змінив дуже приємну відповідь користувача297171, тому він працює у всіх сценаріях 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 потрібно натиснути клавішу <Enter>. Це призведе до виходу з циклу.
rayzinnz

Це гідно, але не узагальнює інші клавіші, крім enter.
Джон Форбс,

не працює для мене на 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'

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.