0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python / Tkinter】after / after_idle の使い方 - How to use `after` and `after_idle`

0
Last updated at Posted at 2026-04-09

1. はじめに

Tkinterに存在する after() と after_idle() について記載する。

2. 定義

after

tkinter_init_.py にある after() の docstring:

Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the function which shall be called. Additional parameters are given as parameters to the function call. Return identifier to cancel scheduling with after_cancel.

Google Translate 和訳:

指定された時間後に一度だけ関数を呼び出します。
MSはミリ秒単位で時間を指定します。FUNCは呼び出す関数を指定します。追加のパラメータは関数呼び出しの引数として渡されます。after_cancelを使用してスケジューリングをキャンセルするための識別子を返します。

after_idle

tkinter_init_.py にある after_idle() の docstring:

Call FUNC once if the Tcl main loop has no event to process.
Return an identifier to cancel the scheduling with after_cancel.

Google Translate 和訳:

Tclのメインループに処理すべきイベントがない場合、FUNCを一度呼び出します。
after_cancelでスケジューリングをキャンセルするための識別子を返します。

after と after_idle の比較

以上の定義より、次のような違いとなる。

関数 処理
after 指定時間 [ms] 後に、指定した関数を実行する。
after_idle イベント処理が全て終わった後に、指定した関数を実行する。

この違いは、単なる実行タイミングの違いではなく、Tkinter のイベント処理の仕組みによって決まっている。これを理解するために、処理の流れを以下に示す。

3. イベントキューと処理の流れ

Tkinterでは、ユーザー操作やタイマー処理などは「イベント」としてキューに登録され、順番に処理される。処理の流れは次のようになる。

No. 内容
1 マウス操作やキー入力などのイベント処理
2 bind
3 command
4 after
5 after_idle

after_idle が最後に処理されるのは、下記の検証コードの結果からも分かる。

import tkinter as tk


def on_button_click():
    root.after_idle(lambda: print('after_idle'))
    root.after(0, lambda: print('after'))


if __name__ == '__main__':
    root = tk.Tk()

    button = tk.Button(root, text='Button', command=on_button_click)
    button.pack()

    root.mainloop()
ボタン押下時のコンソール表示
after
after_idle

4. 使用例

after

現在時刻の表示更新。

import tkinter as tk
from datetime import datetime


def on_closing():
    """ウィンドウを閉じる操作を検出したら、`after` を終了してウィンドウを破棄する。"""
    if after_id:
        root.after_cancel(after_id)
    root.destroy()


def update_label():
    """現在日時の表示を更新する。"""
    global after_id
    clock_label.config(text=datetime.now().strftime('%Y-%m-%d (%a) %H:%M:%S'))
    after_id = root.after(1000, update_label)


if __name__ == '__main__':
    after_id = None

    root = tk.Tk()
    root.protocol('WM_DELETE_WINDOW', on_closing)

    clock_label = tk.Label(root)
    clock_label.pack()

    root.after(0, update_label)

    root.mainloop()

after_idle

ttk.Treeview().selection_set() や ttk.Treeview().selection_remove() を実行すると、 <<TreeviewSelect>> を発生させてしまう。
このため、ボタン押下時のコールバック内で selection_set() などを実行すると、その後に <<TreeviewSelect>> の処理が実行され、意図しない状態になる場合がある。
この問題を回避するために、after_idle を使う。これにより、bind に登録したコールバック処理などが終わった後に、任意の処理を実行できる。

下記コードでは、ttk.Treeview のレコード選択時に、ラベルに現在日時を表示する。
ボタン押下時は、最後のレコードを選択する。ただし、ボタン押下時は、ラベルの表示は更新しない。

import tkinter as tk
from datetime import datetime
from tkinter import ttk


def on_button_click():
    global bind_disabled
    bind_disabled = True
    tree.selection_set(iid)

    def func():
        global bind_disabled
        bind_disabled = False

    root.after_idle(func)


def on_tree_select(_):
    if bind_disabled:
        return

    label.config(text=datetime.now().strftime('%Y/%m/%d %H:%M:%S.%f'))


if __name__ == '__main__':
    root = tk.Tk()

    bind_disabled = False

    root.rowconfigure(1, weight=1)
    root.columnconfigure(0, weight=1)

    top_frame = tk.Frame(root)
    top_frame.grid(row=0, column=0, sticky=tk.EW)

    button = ttk.Button(top_frame, text='Button', command=on_button_click)
    button.grid(row=0, column=0, sticky=tk.W)

    label = ttk.Label(top_frame)
    label.grid(row=1, column=0, sticky=tk.W, pady=(10, 0))

    bottom_frame = tk.Frame(root)
    bottom_frame.grid(row=1, column=0, sticky=tk.NSEW)
    bottom_frame.rowconfigure(0, weight=1)
    bottom_frame.columnconfigure(0, weight=1)

    tree = ttk.Treeview(
        bottom_frame,
        columns=(0,),
        show='headings',
        selectmode='browse'
    )
    tree.heading(0, text='Data')
    tree.grid(row=0, column=0, sticky=tk.NSEW)

    y_scrollbar = ttk.Scrollbar(
        bottom_frame,
        orient=tk.VERTICAL,
        command=tree.yview
    )
    tree.config(yscrollcommand=y_scrollbar.set)
    y_scrollbar.grid(row=0, column=1, sticky=tk.NS, padx=(10, 0))

    iid = None
    for i in range(10):
        iid = tree.insert('', tk.END, values=(f'Data{i + 1}'),)

    tree.bind('<<TreeviewSelect>>', on_tree_select)

    root.mainloop()

5. まとめ

after は「時間による制御」、after_idle は「イベント処理完了後の制御」として使い分けるのが正しい使い方である。
特に、イベントの処理順序が問題となる場合には、after_idle を利用することで意図した動作を実現できる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?