質問: `widget.after_idle(func)`と`widget.after(ms, func)`についてです。 この2つの関数は引数に時間指定があるか無いかだけで、他に違いはないのでしょうか。 また、after_idle(func) は after(0, func) のようなイメージでしょうか。
回答: 使い方によっては違いが解らないかもしれませんが、 内部の実装も異なります。 ``` #!/usr/bin/env python3.8 import tkinter as tk root = tk.Tk() root.title("Test after") tk.Button(root, text="Quit", command=root.quit).pack() def loop(): print("LOOP") # NG: loop が優先的に処理される為、他のイベントが処理されない # root.after(0, loop) # root.after_idle(loop) # OK: イベントループを酷使しない。安全なタイマーの呼び出し方 root.after_idle(root.after, 0, loop) # OK: # root.after(0, root.after_idle, loop) # NG: # root.after_idle(root.after_idle, loop) root.after_idle(loop) root.mainloop() ``` > > after_idle(func) は after(0, func) のようなイメージでしょうか。 > > > `after_idle(func)` と `after(0, func)` が異なる事の実証として、 上記のコードで `root.after_idle(root.after_idle, loop)` を試してみてください。 もし双方が同じなら置き換え可能になるはずですが、そうはなりません。 表面的な「イメージ」としては、解らなくはないのですが、 感覚的には * after 0 ... 直ぐに(優先的に)呼び出したい * after idle ... 手が空いている時でいい 注意が必要なのは、呼び出しがループする場合です。 詳細 個別のイベントキューがあると考えると解りやすいです。 * after タイマーイベント * after_idle 他にイベントがない場合に処理される 処理の流れ (※説明の為に簡略化したもので、実際の実装とは異なります) 通常のイベント処理 → アイドル中のイベント処理 → 描画更新等他の処理 → (補足: after 内で処理する) after(0, loop) では、キューに loop を入れる loop を呼ぶを繰り返し プログラムを中断したりしない限り、次の idle 中の処理に進むことはありません。 (補足: after_idle 内で処理する) after_idle(loop) も同様に、 idle中に行う処理を行っている最中に、idle中に行う処理を追加します。 仕事を増やし続ける事になり、通常のイベント処理に移る事なく、 無限ループになってしまいます。→ GUI のイベントループに処理が回らなくなる。 この場合は明確な区別が必要で、 after idle と after 0ms を組み合わせる対処法が知られてます。 ちなみに、この対処が必要なのは、 0ms の場合のみで、 after 1ms の場合は(実行効率はともかく) GUI を止めることはありません。