メインスレッド側も合わせ終了したいです。
インタラクティブモードもメインスレッドで動いてます。
なので、プロセス自体を終了したいなら 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() では例外になるはずです。
対話環境も終了したいかどうかで、対応方法が変わります。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。