キーストロークでwhileループを強制終了する方法は?


88

シリアルデータを読み取り、whileループを使用してcsvファイルに書き込んでいます。ユーザーが十分なデータを収集したと感じたら、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ループが終了した後もスクリプトを実行し続ける必要があるため、キーボード割り込みを使用したくありません。

回答:


144

最も簡単な方法は、通常のCtrl-C(SIGINT)で中断することです。

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

Ctrl-C原因が発生KeyboardInterruptするので、ループの外側でキャッチして無視します。


2
@クリス:やってみませんか。(そしてコメント)
SilentGhost 2012年

このクラッシュ(エラートレースバックが返されます)は^C、中に発行されdo_something()ます。どうすればこれを回避できますか?
atcold 2016

do_something()はUSBからいくつかの値を読み取るので、^C中にいる間に発行されるdo_something()と、厄介な通信エラーが発生します。代わりに、私がのwhile外にいるdo_something()場合、すべてがスムーズです。それで、私はこの状況をどのように処理するのか疑問に思いました。自分を十分に明確にしたかどうかはわかりません。
atcold 2016

@Atcoldこれで、使用しているコンパイル済みの拡張モジュールができました。どんなモジュールですか?ラップされているのは一般的なCライブラリですか?
キース

測定値をライブで視覚化できるようpyVISAmatplotlib、への呼び出しとへの呼び出しがあります。そして、私は時々ファンキーなエラーを受け取ります。別の質問を開いて、あなたの答えを汚染するのをやめるべきだと思います...
Atcold 2016

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 2016

Python 3のドキュメントによると、Python 3では機能しませんでした:「スレッドは割り込みと奇妙に相互作用します:KeyboardInterrupt例外は任意のスレッドによって受信されます(シグナルモジュールが使用可能な場合、割り込みは常にメインスレッドに送られます)。」
Towhid 2017年

@Towhidしかし、これは割り込みを使用しません。stdinからの読み取りを使用します。
Artyer 2017年

@Artyer私が間違っていなければ、すべてのキーストロークはハードウェアによって発生するため、割り込みを発生させます。このコードはあなたのために機能しましたか?もしそうなら、あなたは特定の変更を加えましたか?
Towhid 2017年

2
@Towhidだけthread->_threadraw_input-> input。行をフィードするには、Enterキーを押す必要があります。任意のキーで実行する場合は、getchを使用します。
Artyer 2017年

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 2017年

1
なぜANDと255
Talespin_Kit19年

@ Talespin_Kit&0xff」は変数をマスクして、最後の8ビットの値のみを残し、残りのビットをすべて無視します。基本的に、結果が0〜255の範囲内になるようにします。私はopencvでこれを行うことはなく、問題なく動作することに注意してください。
エリック・

7

Python 3.7の場合、user297171による非常に優れた回答をコピーして変更したため、テストしたPython3.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以外のキーに一般化されていません。
ジョンフォーブス

python3のpython2.7しかし、作品の私のために動作しません
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 3raw_inputでは次のように置き換えられますinput
TalhaAnwar20年

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とUbuntu20.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

これは、-pip installpynputを使用して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

これは、スレッドと標準ライブラリで見つけたソリューションです。

ループは、1つのキーが押されるまで続行され
ます。押されたキーを1文字の文字列として返します

。Python2.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.