こんにちは、Youtaです。
今回は、Tkinterのafter
をご紹介します。

Tkinterでプログラム作ったけど、フリーズするんだよね。
「処理中…」って文言も出ないし。

もしかしてtime.sleep
使っていませんか?
Tkinter の after
を利用すると解決しますよ。

after
?何それ?
この記事では、Tkinterで「遅延処理」を司るafter
メソッドを解説します。
基本的な使い方から応用例まで、実践的なコード例を交えて紹介します。
ぜひ最後までご覧ください。
Contents
after
after
は「指定時間後にこの処理を行ってね」と予約を入れる機能です。
言い換えると、処理を好きな時間だけ遅らせることができます。
処理は一度しか呼ばれません。
ただし、定刻に処理が呼ばれるかは、その時のTkinterの忙しさによります。
例えば重い処理が一緒に動いている場合、処理が遅れて実行されるかもしれません。
確実なのは、指定した時間よりも先に実行されないということですね。
基本構文
after
の構文は以下の通りです。
ウィジェットのメソッドとして呼びます。
ウィジェット.after(ms, func=None, *args)
引数のms
とfunc
は必ず覚えましょう。
*args
の存在はあまり知られていない印象です。
引数 | 型 | 必須/任意 | 説明 |
---|---|---|---|
ms | int | 必須 | 遅延時間(ミリ秒) |
func | callable | 任意 | 実行したい関数 |
*args | func の引数の型 | 任意 | func に渡す引数 |
after
メソッドの引数戻り値は「タイマーID」(str
型)です。
このIDは予約をキャンセルする際に使用します(後述)。
使い方
after
の使い方を3パターンに分けてご紹介します。
気になる章からご覧ください。
after
メソッドの使い方after
の最も基本的な使い方です。
一定時間後に一度だけ処理を実行します。
以下は、GUI起動後に3秒経過したらラベルを消すプログラムです。
import tkinter as tk
def hide_label():
label.pack_forget() # 3 秒後にラベルを消す
root = tk.Tk()
label = tk.Label(root, text="3 秒後に消えます")
label.pack()
root.after(3000, hide_label) # ms, callback の2つだけ
root.mainloop()

ポイントを2つ解説します。
まずはなんと言ってもafter
メソッド。
時間の単位はミリ秒なので、3000
は3秒を意味します(1000ミリ秒=1秒)。
root.after(3000, hide_label)
after
はroot
ではなくlabel
からも呼べます。
任意のウィジェットで呼び出せますし、挙動はまったく同じです。
# root.after(3000, hide_label)
label.after(3000, hide_label)
また、時間の後には呼びたい処理を渡します。
今回のhide_label
のように、関数名を直接渡してください。
def hide_label():
label.pack_forget()
root.after(3000, hide_label)
(pack_forget
メソッドはpack
で配置したウィジェットを取り除く機能です。)
>>【Tkinter】pack_forgetメソッドを解説!ウィジェットを非表示にできる!
ちなみに、第二引数のfunc
は関数名だけでなくlambda
式でもOKです。
lambda
式で直接pack_forget
メソッドを呼び出すとこうなります。
root.after(3000, lambda: label.pack_forget())
>>【Python】lambda式(無名関数)をわかりやすく解説!
さて、早速プログラムを起動しましょう。

ピッタリ3秒後にlabel
が消えましたね。

after
で定期的に処理を実行させることもできます。
作れるプログラムの幅がぐっと広がる非常に便利な機能です。
結論、コールバック関数の中で再度after
を呼びます。
時間差で呼ばれる再帰関数のようなイメージです。
def func():
...
# 1秒後に再度funcを呼ぶ
root.after(1000, func)
再帰的なコールバックを定義した後は、好きなタイミングでfunc
関数を呼びます。
これで、1秒ごとに自動で処理を行う無限ループの完成です。
なんだかドミノ倒しに似ていて面白いですよね。
func()
早速、具体例を見てみましょう。
例えば、時計なんかは絶好の例です。
import tkinter as tk
from datetime import datetime
def update_time():
current_time = datetime.now().strftime("%H:%M:%S")
time_label.config(text=current_time)
# 1秒後に再度この関数を呼び出す
root.after(1000, update_time)
root = tk.Tk()
time_label = tk.Label(root, text="")
time_label.pack()
# 最初の実行
update_time()
root.mainloop()
注目ポイントはupdate_time
関数です。
time_label
の時間表示を更新した後、1秒後に再び自身を呼んでいます。
これにより、毎秒時間が更新される時計になるわけです。
def update_time():
current_time = datetime.now().strftime("%H:%M:%S")
time_label.config(text=current_time)
# 1秒後に再度この関数を呼び出す
root.after(1000, update_time)
...
# 最初の実行
update_time()
こんな簡単にデジタルな時計が作れてしまいます。
やはり動的なプログラムはテンション上がりますよね。

ちなみに、アナログな時計にも応用可能です。
>>【Tkinter】アナログ時計の作り方!プログラミング練習に!

after
で登録する関数に引数を渡す方法をご紹介します。
結論、渡す引数をafter
の第3引数以降に指定します。
その際は指定した引数の順序でコールバックに渡っていきます。

可変長引数なので、いくつ渡してもOKです。
例として、count
とname
という2つの引数をgreet
関数に渡してみます。
import tkinter as tk
def greet(count, name):
label.config(text=f"{count}秒経過。こんにちは、{name}さん!")
label = tk.Label(width=30)
label.pack()
label.after(2000, greet, 2, "Mike") # *args 部分にそのまま列記
label.mainloop()
初めは何も表示されていませんが…。

2秒後、ラベルに文字が表示されました。
after
に渡した引数もそのまま現れています。

ついでなのでもう1つ別のやり方を紹介します。
lambda
式で引数をセットしたコールバックをafter
に渡してもOKです。
label.after(2000, lambda: greet(2, "Mike"))
after_cancel
after_cancel
はafter
で登録した処理をキャンセルします。
キャンセルにはafter
の戻り値である「タイマーID」が必要です。
変数に入れるなどをして事前に控えておきましょう。
基本構文
after_cancel
の構文は以下の通りです。
ウィジェット.after_cancel(id)
after_cancel
の引数です。
引数 | 型 | 必須/任意 | 説明 |
---|---|---|---|
id | str | 必須 | 取り消したいafter の戻り値 |
after_cancel
メソッドの引数戻り値はありません。
使い方
after_cancel
の使い方は簡単です。
after
の戻り値「タイマーID」を渡すと、その予約を取り消します。
timer_id = root.after(1000, some_func) # ← 整数や文字列の ID
...
root.after_cancel(timer_id) # これで停止
それでは実際の例を見てみましょう。
after
とafter_cancel
を使って文字の点滅を停止・再開させるデモを作りました。
import tkinter as tk
# 点滅ロジック
timer_id = None # afterが返すタイマーID
is_visible = True # 現在の表示状態
def blink():
global timer_id, is_visible
is_visible = not is_visible
if is_visible:
center_x = canvas.winfo_width() / 2
center_y = canvas.winfo_height() / 2
canvas.create_text(center_x, center_y, text="BLINK", anchor="center")
else:
canvas.delete("all")
timer_id = root.after(300, blink) # 0.3 秒後に自分を再登録
# ボタンコマンド
def start():
blink()
start_button.config(state="disabled")
stop_button.config(state="normal")
def stop():
global timer_id
root.after_cancel(timer_id) # ここが after_cancel
start_button.config(state="normal")
stop_button.config(state="disabled")
# GUI作成
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.pack()
start_button = tk.Button(root, text="Start", command=start)
start_button.pack(side="left", expand=True, fill="x")
stop_button = tk.Button(root, text="Stop", command=stop)
stop_button.pack(side="left", expand=True, fill="x")
root.mainloop()
簡単にコードの説明をします。
まずstart_button
を押すと、after
の効果で文字が0.3 秒ごとに点滅します。
def blink():
...
timer_id = root.after(300, blink) # 0.3 秒後に自分を再登録
def start():
blink()
start_button.config(state="disabled")
stop_button.config(state="normal")
start_button = tk.Button(root, text="Start", command=start)
そしてstop_button
を押すと、after_cancel
が働いて点滅が停止します。
blink
関数内で保持したtimer_id
を利用しているのにご注目ください。
def blink():
...
timer_id = root.after(300, blink) # 0.3 秒後に自分を再登録
def stop():
global timer_id
root.after_cancel(timer_id) # ここが after_cancel
start_button.config(state="normal")
stop_button.config(state="disabled")
stop_button = tk.Button(root, text="Stop", command=stop)
画面を起動してstart_button
を押すと文字の点滅が始まります。
途中でstop_button
を押すと点滅が止まります。
after
に似ている処理
after
と混同しがちな処理を2つピックアップします。
特に遅延処理をafter
ではなくtime.sleep
を使って失敗する方が多いです。
after
メソッドに似ている処理ここからは、Tkinterのメインループを理解している前提で説明します。

メインループって何?
このような方は、一旦次の「メインループ」をお読みください。
Tkinterのアプリはmainloop
を呼び出すことで動作しますね。
そう、コードの末尾にあったおまじないのことです。
...
root.mainloop() # イベントループ開始
実はこのmainloop
、無限ループ開始の合図なのです。
この無限ループは「イベント」を待機して処理するループです。
「イベント」とは、例えばクリック、キー入力、マウス移動などを指します。
このメインループ内で、Tkinterは次の処理を高速で繰り返します。
- イベントの発生をチェック
- イベントがあれば対応する処理を実行
- 画面の描画更新
- ①に戻る
メインループの予備知識としてはこれで十分です。
以上を踏まえ、次章のtime.sleep
をご覧ください。
time.sleep
time.sleep
は「一定時間待つ」という点でafter
と似ています。
しかし、決定的な違いがあります。
それは、time.sleep
はメインループを停止させてしまう点です。
冒頭のフリーズするプログラムを例に出します。
何が問題かを一緒に考えていきましょう。
import time
import tkinter as tk
def on_click():
button.config(text="処理中…")
time.sleep(3) # ← ここで GUI が 3 秒固まる
button.config(text="完了!")
button = tk.Button(text="押してね", width=5, command=on_click)
button.pack()
button.mainloop()

これがやりたいことね。
- ボタン押下
- 「処理中…」表示
- 3秒待機
- 「完了!」表示

けれども、
①→3秒フリーズ→④となるのが問題でしたね。
time.sleep
はメインループを含む一才の処理を一時停止させます。
つまり、その間はイベントの発生を検知しても画面を更新できません。
したがって、ユーザーからするとフリーズに見えるのです。
ではどうしましょうか。
メインループを停止させずに「一定時間待つ」after
を使えば良いのです。
プログラムを書き換えましょう。
def on_click():
button.config(text="処理中…")
# 修正前
# time.sleep(3)
# button.config(text="完了!")
# 修正後
button.after(3000, lambda: button.config(text="完了!"))
①→②→③→④がフリーズ無しに実行できました。

わーい🙌
after_idle
after_idle
は「暇なときにこの処理を行ってね」と予約を入れる機能です。
「暇なとき」とは、メインループが処理すべきイベントがない状態です。
after_idle
の構文は以下の通りです。
ウィジェット.after_idle(func, *args)
引数はafter
とほぼ同じです。
引数 | 型 | 必須/任意 | 説明 |
---|---|---|---|
func | callable | 必須 | 実行したい関数 |
*args | func の引数の型 | 任意 | func に渡す引数 |
after_idle
メソッドの引数戻り値はafter
と同様に「タイマーID」(str
型)です。
このIDはafter_cancel
で予約を取り消す際に使用します。
after
は一定時間後に、after_idle
はイベントが一段落すると実行されます。
つまりタイミングがafter
は時間、after_idle
はシステムの状態に依存します。

んー何となくわかる気がするけど
多分わかってない。

イメージが湧かないですよね…。
では「いつどちらが呼ばれるのか?」を明確に可視化しましょう。
実は、両者の違いを把握するのに欠かせないのがメインループの状態です。
そのため「暇な状態」と「忙しい状態」の2パターンで試します。
import time
import tkinter as tk
# 共通のコールバック
def on_after():
print(f"after {time.perf_counter() - start:6.3f}s")
def on_after_idle():
print(f"after_idle {time.perf_counter() - start:6.3f}s")
# メインループの状態を場合分け
def case_nonblocking():
# ① メインループが暇な状態になる普通のケース
global start
start = time.perf_counter()
print("\n--- non-blocking run ---")
root.after(2000, on_after) # 2秒後
root.after_idle(on_after_idle) # 次に暇になった瞬間
# ここで何もしないのでメインループはすぐ暇な状態
def case_blocking():
# ② メインループを3秒ブロックするケース
global start
start = time.perf_counter()
print("\n--- blocking run ---")
root.after(2000, on_after) # 2秒後(でもあとで3秒ブロック)
root.after_idle(on_after_idle) # 暇になったら
time.sleep(3) # ★メインループを3秒止める
# GUI作成
root = tk.Tk()
tk.Button(root, text="① 普通に実行", command=case_nonblocking).pack()
tk.Button(root, text="② 3秒ブロック付き実行", command=case_blocking).pack()
root.mainloop()

最初にコードの説明をします。
case_nonblocking
関数はメインループが暇な状態のパターンです。
after
を「2秒後」に、after_idle
を「暇な状態になったらすぐ」に実行します。
メインループが何もブロックされないのがポイントです。
def case_nonblocking():
...
root.after(2000, on_after)
root.after_idle(on_after_idle)
tk.Button(root, text="① 普通に実行", command=case_nonblocking).pack()
一方case_blocking
関数はメインループが忙しい状態のパターンです。
先ほどと同じですが、最後に 3秒間time.sleep
でメインループをブロックします。
これで、処理が重すぎて関数終了まで3秒掛かる状況を再現できます。
def case_blocking():
...
root.after(2000, on_after)
root.after_idle(on_after_idle)
time.sleep(3)
tk.Button(root, text="② 3秒ブロック付き実行", command=case_blocking).pack()
画面を立ち上げて実行しましょう。

まずメインループが暇な状態を試すので、①のボタンを押します。
以下がコンソール出力です。
--- non‑blocking run ---
after_idle 0.001s
after 2.001s
after_idle
が即実行され、2秒後にafter
が実行されていますね。
after
実行まで暇なメインループが、先にafter_idle
を呼んだからです。
次にメインループが忙しい状態を試すので、②のボタンを押します。
以下がコンソール出力です。
--- blocking run ---
after 3.002s
after_idle 3.002s
3秒間メインループは止まっており、2秒後に実行予定だったafter
は呼べませんでした。
そのため、メインループ再開時に「期限切れ」のafter
が最優先で実行されたのです。
その後、メインループは暇になりafter_idle
が呼ばれました。
呼ばれるタイミングの違いについて理解できましたでしょうか。
改めてafter
とafter_idle
の違いをまとめます。
メソッド | 目的 | 実行タイミング |
---|---|---|
after | 一定時間後に処理したい | メインループが空いていればその時間後に、空いてなければ遅れて実行される。 |
after_idle | 他の処理がすべて終わったあとに1回だけ処理したい | 「他にやることがない」状態 になると実行される。 |
after
vs after_idle
以上のようにafter_idle
は暇なときに呼ばれる特性があります。
そのため低優先度のバックグラウンド処理等に用いると良いでしょう。
まとめ
今回はTkinterの遅延処理を担うafter
を解説しました。
最後に復習してから終わりましょう。
after
は「指定時間後にこの処理を行ってね」と予約を入れる機能です。
ms
にミリ秒、func
に関数、*args
にfunc
の引数を渡します。
ウィジェット.after(ms, func=None, *args)
特に「単発遅延」と「繰り返し処理」が大切です。
after
メソッドの使い方after_cancel
でafter
で登録した処理をキャンセルできます。
引数の「タイマーID」はafter
の戻り値です。
ウィジェット.after_cancel(id)
また、after
と混同しがちな処理を2つ上げました。
しっかりと区別できるようになりましょう。
after
メソッドに似ている処理この記事が皆様のお役に立てれば幸いです。
ご覧いただきありがとうございました。