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

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

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

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

3536閲覧

matplotlibのインタラクティブモードグラフを閉じたときにメインスレッドも終了させたい

shirasublue

総合スコア19

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2021/06/24 06:01

前提・実現したいこと

matplotlib のインタラクティブモードのグラフを閉じたときに、
メインスレッド側も合わせ終了したいです。

該当のソースコード

Python

1import numpy as np 2import matplotlib.pyplot as plt 3 4 5def on_close(event): 6 pass # ここで終了イベントを受け取って渡す? 7 8 9def main(): 10 plt.ion() 11 fig = plt.figure() 12 fig.canvas.mpl_connect('close_event', on_close) 13 14 ax = fig.add_subplot() 15 bg = fig.canvas.copy_from_bbox(ax.bbox) 16 17 line, = ax.plot(np.linspace(0, 99, 100), np.zeros(100)) 18 ax.set_ylim(0.0, 1.0) 19 20 for i in range(1000): 21 line.set_ydata(np.random.rand(100)) 22 23 fig.canvas.restore_region(bg) 24 fig.canvas.blit(ax.bbox) 25 fig.canvas.flush_events() 26 27 # グラフを閉じても1000回ループするまで終わらない 28 29 30if __name__ == '__main__': 31 main() 32

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

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

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

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

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

guest

回答2

0

ベストアンサー

メインスレッド側も合わせ終了したいです。

インタラクティブモードもメインスレッドで動いてます。
なので、プロセス自体を終了したいなら os._exit() 等になりますが、
Jupyter/IPython等で使ってる場合は、プロセスは終了したくないはずです。

「ウィンドウを閉じた時にループを抜ける」には、別解もあり

まずは問題点、質問のコードでは、、
イベントループがインタラクティブモードをブロックした状態になってます。

python

1def main(): 2 3 for i in range(1000): 4 5 fig.canvas.flush_events() 6 7# python -i の実行では、main() が終わるまで対話環境に入力できない。

on_close が呼ばれるのは、fig.canvas.flush_events() 内で、
for ループ中断に直接関与できません。

説明補足
仮にfor ループを抜けても、実行方法次第では外側に対話環境があるはずです。
>メインスレッドが終わるわけではない。

python

1- 対話環境 (python -i で起動した場合を想定) 2 - main() 関数 3  - for ... range(1000) (イベントループ) 4 flush_events()  5 6 ※ 問題は、ループ処理が終わる迄、対話環境での入力ができない点。 7 ※ 但し、jupyter では問題にならないので、実行環境次第。

時間の掛かるループ処理は、ブロッキング処理となり、イベントループとは相性が良くない為、
大抵の場合は、スレッドやイベントループの提供するタイマーを使います。

質問のケースでは、描画アニメーションの為、タイマーが適切な解決策です。
(※matplotlibはスレッドセーフではない。描画処理は基本イベントループ側で行う)

イベントループを正常に稼働させるためには、
→ 時間の掛かる処理はタイマーで分割実行したり、スレッドで実行。


以下はタイマーで同様のアニメーションを実装した例。
ウィンドウを閉じた時にアニメーションのループ処理は終了します。
(インタラクティブモードは抜けません)

python -i FILENAME での起動を想定で、
描画アニメーション中に対話環境での入力も出来るようになります。

python

1from matplotlib.animation import FuncAnimation 2 3 4def main(): 5 6 ... 7 8 def update(i): 9 # print(f"frame {i}") 10 line.set_ydata(np.random.rand(100)) 11 12 fig.canvas.restore_region(bg) 13 fig.canvas.blit(ax.bbox) 14 # fig.canvas.flush_events() 15 16 # ローカル変数では破棄されてアニメーションのタイマーが動かない為 17 # グローバル変数にすることで暫定的に対応。 18 global anim 19 20 anim = FuncAnimation(fig, update, range(1000), interval=15, repeat=False)

追記: 「インタラクティブモード」の実行環境次第ですが、簡易的な解決策でも良いなら
-i オプション無しでの実行する場合は、on_close 内で quit() で済みます。
ここは、インタラクティブモードだと sys.exit() や quit() では例外になるはずです。
対話環境も終了したいかどうかで、対応方法が変わります。

投稿2021/06/24 09:06

編集2021/06/24 09:14
teamikl

総合スコア8664

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

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

0

グローバル変数を見て抜けるので良いのではないでしょうか。

python

1import numpy as np 2import matplotlib.pyplot as plt 3 4stop = False 5 6def on_close(event): 7 global stop 8 stop = True 9 pass # ここで終了イベントを受け取って渡す? 10 11 12def main(): 13 plt.ion() 14 fig = plt.figure() 15 fig.canvas.mpl_connect('close_event', on_close) 16 ax = fig.add_subplot() 17 bg = fig.canvas.copy_from_bbox(ax.bbox) 18 line, = ax.plot(np.linspace(0, 99, 100), np.zeros(100)) 19 ax.set_ylim(0.0, 1.0) 20 for i in range(1000): 21 if stop == True: 22 break 23 line.set_ydata(np.random.rand(100)) 24 fig.canvas.restore_region(bg) 25 fig.canvas.blit(ax.bbox) 26 fig.canvas.flush_events()

投稿2021/06/24 06:34

ppaul

総合スコア24666

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問