質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Jupyter

Jupyter (旧IPython notebook)は、Notebook形式でドキュメント作成し、プログラムの記述・実行、その実行結果を記録するツールです。メモの作成や保存、共有、確認などもブラウザ上で行うことができます。

Google Colaboratory

Google Colaboratoryとは、無償のJupyterノートブック環境。教育や研究機関の機械学習の普及のためのGoogleの研究プロジェクトです。PythonやNumpyといった機械学習で要する大方の環境がすでに構築されており、コードの記述・実行、解析の保存・共有などが可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

2245閲覧

Pythonで実行しているループをipywidgetsのボタンで停止させたい

San

総合スコア8

Jupyter

Jupyter (旧IPython notebook)は、Notebook形式でドキュメント作成し、プログラムの記述・実行、その実行結果を記録するツールです。メモの作成や保存、共有、確認などもブラウザ上で行うことができます。

Google Colaboratory

Google Colaboratoryとは、無償のJupyterノートブック環境。教育や研究機関の機械学習の普及のためのGoogleの研究プロジェクトです。PythonやNumpyといった機械学習で要する大方の環境がすでに構築されており、コードの記述・実行、解析の保存・共有などが可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/02/17 04:46

編集2023/02/17 06:53

長大なループをボタン操作などで停止させたい

  • Google ColabなどのJupyter notebook上でボタン押下でループ処理を実行
  • ループは一定時間(数)処理した後に自動終了する
    • ただし状況で長時間(回数)になるケースがある
  • ユーザー操作により途中で停止させたい

前提(環境)

Google Colab上で実装したい為、tkInterではなくipywidgetsを使って実装。
開始と停止ボタンをipywidgets.Button()で作成し、それぞれループ開始と停止の関数をバインド。
VSCode上のJupyter notebookでも確認したが同じ結果。

状況

開始ボタン押下でループを開始し、停止ボタンで終了用のフラグを立てるようにしています。
ループ中でフラグを監視し、フラグが立った所でループを終了させるようにしましたが停止しません。
ループが優先され、ボタン押下イベントが通知されていないと考えてループを別スレッドで実行するように変更してみました。
しかし、結果は同じでループは終了しません。「停止」ボタンでフラグを立てていますが、そもそもボタン押下時のイベントがループ中は保留されてしまうように見えます。

Threadの使い方が悪いのか、iwidgetsの使い方なのか判然としなく、手掛かりを頂けたらと思い投稿しました。

発生している問題・エラーメッセージ

停止ボタンを押下しても即反映されず、ループ終了後にイベントを受けて処理している様子です。
ループ中にイベントを受けていないので、ループが停止せずに最後まで到達してしまいます。

実行結果

Start Loop:0 Loop:1 Loop:2 Loop:3 Loop:4 Loop:5 Loop:6 Loop:7 Loop:8 Loop:9 not alive Stop Stop Stop Stop Stop

該当のソースコード

Python

1import time 2import ipywidgets as widgets 3 4from IPython.display import display 5import threading 6 7flag = False 8 9def loop_start(e): 10 print('Start') 11 thread = threading.Thread(target=count_loop) 12 thread.start() 13 14 try: 15 while True: 16 thread.join(timeout=0.1) 17 if not thread.is_alive(): 18 print('not alive') 19 return 20 if flag: 21 return 22 except KeyboardInterrupt: 23 print('Interrupt') 24 25def count_loop(): 26 for i in range(10): 27 if flag: 28 return 29 time.sleep(1) 30 current.value = str(i) 31 pass 32 33def loop_stop(e): 34 print('Stop') 35 flag = True 36 37output = widgets.Output() 38start = widgets.Button(description='開始') 39stop = widgets.Button(description='停止') 40buttons = widgets.Box([start, stop]) 41 42start.on_click(loop_start) 43stop.on_click(loop_stop) 44 45display(buttons, output)

試したこと

処理間隔などの問題でボタン押下イベントを受け付けていない可能性を考えて、ループ中にsleep()を挟んでみましたが変わりませんでした。
また、キーボードイベントでインターラプトできるとの事だったので仕込んでみましたが、Ctl+Cも効いているように見えません。
Google Colab環境、VSCode環境どちらでも同じ結果となりました。

補足情報(FW/ツールのバージョンなど)

Google Colaboratory(通常版)
VSCode 上のJupyter notebook(v2023.2.1000481019)
Python 3.10.7

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

下記の変更だけではだめで、ちゃんとやるなら非同期処理を書いてやる必要があるみたいですね。
https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Asynchronous.html

こちらのQAが、参考になると思います。
https://stackoverflow.com/questions/64282319/stop-button-for-breaking-while-loop-within-ipywidgets-ecosystem

python

1import time 2import ipywidgets as widgets 3 4from IPython.display import display 5import threading 6 7current = '-1' 8flag = False 9 10def loop_start(e): 11 with output: 12 print('Start') 13 thread.start() 14 15def count_loop(): 16 global current 17 for i in range(10): 18 with output: 19 print(current) 20 if flag: 21 return 22 time.sleep(1) 23 current = str(i) 24 25def loop_stop(e): 26 global flag 27 flag = True 28 thread.join() 29 with output: 30 print('Stop') 31 32output = widgets.Output() 33start = widgets.Button(description='開始') 34stop = widgets.Button(description='停止') 35buttons = widgets.Box([start, stop]) 36 37start.on_click(loop_start) 38stop.on_click(loop_stop) 39 40display(buttons, output) 41 42thread = threading.Thread(target=count_loop)

最初の回答

loop_stopの中の flag はglobalにしないといけないです。
じゃないと、flag というローカル変数を True でセットするだけになります。

python

1def loop_stop(e): 2 global flag 3 print('Stop') 4 flag = True

投稿2023/02/17 05:55

編集2023/02/17 07:22
bsdfan

総合スコア4899

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

San

2023/02/17 06:54

確かに!失礼しました。 ただ、この部分を変えても結果としては変わりませんでした。 loop_stop関数自体がループ中に呼び出されないのは変化ありませんでした。
San

2023/02/17 07:50

> You may want to pause your Python code to wait for some user interaction with a widget from the frontend. Typically this would be hard to do since running Python code blocks any widget messages from the frontend until the Python code is done. まさにこの部分で言及されていますね。 非同期かジェネレーター、どちらかで実装できるとの事なので、Google Colab環境でできそうな手法を試してみようと思います。 有難うございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問