このページでは Python の tkinter に用意された after
メソッドの使い方について解説していきます。
after
メソッドは全ウィジェットのクラスに用意されたメソッドであり、after
メソッドを利用することで下記を行うことが可能になります。
- 特定の処理を指定した時間分遅らせて実行する
- 特定の処理をある時間ごとに定期的に実行する
time.sleep
でも上記は実現できそうですが、after
メソッドは time.sleep
とは大きく異なる点があります。こういった time.sleep
との違いについても解説していきたいと思います。
Contents
after
メソッド
まずは after
メソッドがどんなメソッドであるかを解説していきます。
after
メソッドの定義
after
メソッドの定義は下記のようになっています。
after(self, ms, func=None, *args)
after
メソッドの引数
after
メソッドに実行時に指定する引数は下記の3つになります。
ms
(第1引数):どれだけ遅らせて実行するかの時間(単位はミリ秒)func
(第2引数):ms
経過後に実行する関数(メソッド)args
(第3引数):func
に渡す引数
after
メソッドの戻り値
after
メソッドを実行すると、戻り値として ID が返却されます。
この ID は後に説明する after_cancel メソッドによる after の取り消しで説明する after_cancel
メソッドで利用します。
スポンサーリンク
after
メソッドとは
after
メソッドは、処理を遅らせて実行するメソッドです。
after
メソッドの動作
after
メソッドを実行すると、after
メソッド実行してから第1引数 ms
で指定した時間(ミリ秒)経過後に第2引数 func
で指定した関数が自動的に実行されます。
つまり、第1引数 func
で指定した関数を引数 ms
で指定した時間分、遅らせて実行させることができます。もちろん第1引数には関数だけでなく、メソッドも指定することが可能です。
「後で処理させる」ので、メソッド名が「after
」であると考えると覚えやすいと思います。
after
メソッドで行われるのは登録のみ
after
メソッド実行時に行われるのは、あくまでイベント処理の登録(何秒後に何の処理を行うかの登録)のみであり、after
メソッド内で ms
で指定した時間待たされるわけではありません。
ですので、after
メソッドを実行すると、すぐに after
メソッド自体は終了します。
after
メソッドはすぐに終了しますので、after
メソッド実行後に mainloop
を実行すれば、すぐにマウスクリックやキーボードのキー入力などのイベント処理を行うことも可能ですし、after
メソッド実行後にすぐに他の処理を実行することも可能です。
この辺りは time.sleep
と大きな違いになります。after
と time.sleep
との違いの詳細については after メソッドと time.sleep との違いで解説したいと思います。
処理は mainloop
内から自動的に実行される
そして、after
メソッドで遅らせて実行することを指定した関数は、時間経過後に自動的に実行されることになります。
この遅らせて実行される関数やメソッドは、mainloop
メソッドの中から実行されます(実行するのは tkinter 本体)。
つまり、mainloop
メソッドを実行しないと after
メソッドを実行しても、after
メソッドの第2引数で指定した関数やメソッドは自動的に実行されませんので注意してください。
after
メソッドを実行して時間経過後にupdate
メソッドを実行することで強制的に after
メソッドの第2引数で指定した関数やメソッドを実行することも可能です
after
メソッドが実行できるウィジェット
前述の通り、after
メソッドは tkinter の全ウィジェットに用意されているメソッドになります。
after
メソッドの使い方
続いては after
メソッドのサンプルスクリプトを見ながら after
メソッドの使用例を紹介していきたいと思います。
サンプルスクリプト
下記スクリプトは after
メソッドの簡単な使用例になります。
# -*- coding:utf-8 -*-
import tkinter
# 遅らせて実行される関数
def func():
global label
# ラベルのテキストを変更
label.config(
text="3000ms経過しました",
)
# メインウィンドウの作成
app = tkinter.Tk()
app.geometry("300x200")
# ラベルウィジェット作成
label = tkinter.Label(
app,
width=15,
height=1
)
label.pack()
# 3000ms後にfunc関数を実行
app.after(3000, func)
# メインループ
app.mainloop()
スクリプトを実行すると、下記のようなアプリが起動します。起動時にはアプリ上に何も表示されません。
起動後、3秒経過すると下記のようにアプリ上に文字列が表示されます。
サンプルスクリプトの説明
after
メソッドを実行しているのは下記部分になります。
# 3000ms後にfunc関数を実行
app.after(3000, func)
after
メソッドを実行しているのはメインウィンドウの app
です。
前述の通り、tkinter の全ウィジェットに after
メソッドが用意されていますので、他のウィジェット(例えば上のサンプルスクリプトであれば label
)に実行させても良いです。
第1引数で 3000
、第2引数で func
を指定して after
メソッドを実行していますので、after
メソッド実行して 3000
ms 経過した時点でfunc
が実行されることになります。
func
関数ではアプリ上に配置された label
の文字列を "3000ms経過しました"
に設定する処理(config
メソッドで設定)を行っていますので、結果的に after
メソッド実行してから 3000
ms 後にアプリ上に文字列が表示されることになります。
また、下記で mainloop
を実行している点も after
メソッドを利用する点で重要なポイントになります。
前述の通り、after
メソッドの引数で指定した関数やメソッドは mainloop
の中から自動的に実行されます。after
メソッドを利用する場合は mainloop
を実行するのを忘れないようにしましょう(tkinter を利用しているのであれば基本的に mainloop
は必ず実行すると思いますが…)。
# メインループ
app.mainloop()
実行する関数への引数の渡し方
after
メソッドにより遅らせて実行される関数(第2引数 func
で指定する関数)には引数を渡すことも可能です。
この場合は after
メソッドの第3引数以降に引数として渡したいデータを指定します。辞書形式で渡しても良いです。
引数を渡すことで、after
メソッドにより遅らせて実行される関数により詳細な処理を記述することができるようになります。
引数を渡すサンプルスクリプト
下記スクリプトは after
メソッドで遅らせて実行する関数に引数を渡す簡単な例になります。
# -*- coding:utf-8 -*-
import tkinter
# 遅らせて実行される関数
def func(kw):
global label
# ラベルのテキストを変更
label.config(
text=kw['text'],
bg=kw['bg'],
)
# メインウィンドウの作成
app = tkinter.Tk()
app.geometry("300x200")
# ラベルウィジェット作成
label = tkinter.Label(
app,
width=15,
height=1
)
label.pack()
# 関数に渡す辞書を作成
kw = {
"text":"3000ms経過しました",
"bg":"orange"
}
# 3000ms後にfunc関数を実行
app.after(3000, func, kw)
# メインループ
app.mainloop()
after メソッドの使い方とほぼ同じスクリプトですが、起動して3秒後に表示されるアプリの画面は下の図のようになります。
サンプルスクリプトの説明
after
メソッドを実行しているのは下記部分で、第3引数に kw
を指定するようにしています。
# 3000ms後にfunc関数を実行
app.after(3000, func, kw)
この kw
は下記で作成した辞書データになります。
# 関数に渡す辞書を作成
kw = {
"text":"3000ms経過しました",
"bg":"orange"
}
上記のように第3引数を指定して after
メソッドを実行すれば、第2引数(func
)の関数が実行される際に、その関数に第3引数のデータ(kw
)が引数として渡されるようになります。
その実行される func
は下記のようになります。
# 遅らせて実行される関数
def func(kw):
global label
# ラベルのテキストを変更
label.config(
text=kw['text'],
bg=kw['bg'],
)
引数を受け取れるように関数を定義し、その引数を辞書データとしてアクセスして利用しているところがポイントです。
スポンサーリンク
after
メソッドで処理を遅らせて実行する方法
ここからは after
メソッドの実用的な使い方について解説していきたいと思います。
1つ目はこの「after
メソッドで処理を遅らせて実行する」使い方です。
といっても、この使い方は after
メソッドの最も一般的な使い方であり、ここまでの after
メソッドに関する解説の中でもこの使い方は紹介しています。
「after
メソッドで処理を遅らせて実行する」ためには、after
メソッドを実行すれば良いだけです。
使用方法に関しても既に after メソッドの使い方や実行する関数への引数の渡し方でサンプルスクリプトを用いて解説していますので、こちらを参考にしていただければと思います。
after
メソッドで処理を定期的に実行する方法
続いて「after
メソッドで処理を定期的に実行する」使い方について解説していきます。
処理を定期的に実行する考え方
ここまで解説してきた通り after
は特定の処理(第2引数で指定した関数やメソッド)を遅らせて実行するメソッドです。
その遅らせて実行される処理は、 after
メソッドを1回実行するにつき、1回のみ実行されます。回数は指定できません。
ですので、単に1度だけ after
メソッドを実行するだけでは「処理を定期的に実行する」は実現できません。
しかし、下記のようにプログラミングしてやることで、「after
メソッドで処理を定期的に実行する」を実現することができます。
after
メソッドにより遅らせて実行される処理の中で、再度after
メソッドを実行する
例えば定期的に行いたい処理を repeat_func
関数だとすれば、その repeat_func
関数の中に「定期的に行いたい処理」だけでなく「after
メソッドの実行処理」も記述します。
この時、after
メソッドの第2引数には再度、定期的に行いたい処理である repeat_func
を指定します。
def repeat_func():
# 定期的に実行したい処理を記述
# 再度afterを実行
# appはメインウィンドウのインスタンス
app.after(100, repeat_func)
これにより、repeat_func
関数実行時に after
メソッドが実行され、再度遅らせて repeat_func
関数が実行されることになります(上の例では 100
ms 後に実行される)。
次に repeat_func
関数が実行された際にも、after
メソッドにより repeat_func
関数が遅らせて実行されることになります。これが繰り返されるので、結果的に repeat_func
が定期的に実行されることになります(上の例では 100
ms 間隔 で処理が定期的に実行される)。
下の図は after
メソッドにより func
関数を定期的に実行するイメージになります。
スポンサーリンク
サンプルスクリプト
次は実際にサンプルスクリプトを用いて説明していきたいと思います。
下記は after
メソッドを利用して定期的に処理を行う簡単な例になります。
スクリプト実行からの時間を1秒ごとにカウントアップし、そのカウントアップした値をアプリ上のラベルに表示する例になります。
# -*- coding:utf-8 -*-
import tkinter
# 秒数をカウントする変数
count = 0
# 定期的に実行する関数
def repeat_func():
global app
global label
global count
# 定期的に行いたい処理
count += 1
label.config(
text=str(count)
)
# 再度repeat_funcが実行されるようにafter実行
app.after(1000, repeat_func)
# メインウィンドウの作成
app = tkinter.Tk()
app.geometry("300x200")
# ラベルウィジェット作成
label = tkinter.Label(
app,
width=15,
height=1,
text="0",
font=("", 50)
)
label.pack()
# 1000ms後にrepeat_func関数を実行
app.after(1000, repeat_func)
# メインループ
app.mainloop()
スクリプトを実行するとアプリが起動し、下図のような画面が表示されます。起動時にはラベルには “0” が表示されます。
そして起動後1秒ごとにラベルに表示される数字がカウントアップされていきます。
サンプルスクリプトの解説
最初に after
メソッドを実行しているのは下記部分です。これにより after
メソッドを実行してから 1000
ms 後に repeat_func
関数が実行されることになります。
# 1000ms後にrepeat_func関数を実行
app.after(1000, repeat_func)
repeat_func
関数は下記のようになっています。
# 定期的に実行する関数
def repeat_func():
global app
global label
global count
# 定期的に行いたい処理
count += 1
label.config(
text=str(count)
)
# 再度repeat_funcが実行されるようにafter実行
app.after(1000, repeat_func)
ポイントは repeat_func
関数内で after
メソッドを実行している下記部分です。
# 定期的に実行する関数
def repeat_func():
# 〜略〜
# 再度repeat_funcが実行されるようにafter実行
app.after(1000, repeat_func)
after
メソッドの第2引数で repeat_func
関数を指定しているため、再度 1000
ms 後に repeat_func
関数が実行されることになります。
以降、その repeat_func
関数実行時には同様の処理が行われるため、1000
ms 毎に定期的に repeat_func
関数を実行することができます。
こんな感じで、repeat_func
関数に「after
メソッドの実行」+「実行させたい処理」を記述しておけば、定期的に「実行させたい処理」が実行されるようになります。
# 定期的に実行する関数
def repeat_func():
# 〜略〜
# 定期的に行いたい処理
count += 1
label.config(
text=str(count)
)
# 再度repeat_funcが実行されるようにafter実行
app.after(1000, repeat_func)
mainloop
から自動的に関数が実行された場合、その関数終了時に再び自動的に mainloop
に処理が戻ります
ですので、mainloop
は最初に一度だけ実行しておけばよく、関数内で mainloop
を再度実行するようなことは不要です
after_cancel
メソッドによる after
の取り消し
次は after_cancel
メソッドと一緒に覚えておくと良い after
メソッドについて解説します。
スポンサーリンク
after_cancel
とは
after
メソッドを実行することで after
メソッドに指定した関数を遅らせて実行させられるのはここまで解説してきた通りです。
実際には、(これも簡単に説明しましたが)after
メソッド自体でその関数が実行されるのではなく、指定した時間後に関数が実行されるように予約される(登録される)だけです。
after_cancel
メソッドは、その予約をキャンセルするメソッドです。
例えば単に定期的に処理を実行するようにすると、無限にその処理が実行されてしまいますが、after_cancel
メソッドを実行することでその定期的な処理を途中で停止させることもできます。
after_cancel
メソッドの定義
after_cancel
メソッドは下記のように定義されています。
after_cancel(self, id)
実行時に指定する引数は id
であり、戻り値はありません(None
)。
引数で指定する id
は具体的には after
メソッドの戻り値となります。
after
メソッドは戻り値として id
を返却しますので、キャンセルしたい after
メソッドの戻り値を after_cancel
実行時に引数として指定すれば良いです。
after_cancel
メソッドの使い方
下記は after_cancel
メソッドの簡単な使用例になります。
# -*- coding:utf-8 -*-
import tkinter
# 秒数をカウントする変数
count = 0
after_id = None
def stop_func():
global app
global after_id
app.after_cancel(after_id)
# 定期的に実行する関数
def repeat_func():
global app
global label
global count
global after_id
# 定期的に行いたい処理
count += 1
label.config(
text=str(count)
)
# 再度repeat_funcが実行されるようにafter実行
after_id = app.after(1000, repeat_func)
# メインウィンドウの作成
app = tkinter.Tk()
app.geometry("300x200")
# ラベルウィジェット作成
label = tkinter.Label(
app,
width=15,
height=1,
text="0",
font=("", 50)
)
label.pack()
# ボタンウィジェット作成
button = tkinter.Button(
app,
text="STOP",
font=("", 50),
command=stop_func
)
button.pack()
# 1000ms後にrepeat_func関数を実行
after_id = app.after(1000, repeat_func)
# メインループ
app.mainloop()
基本的な作りは after メソッドで処理を定期的に実行する方法で紹介したスクリプトと同じで、アプリを起動すると after
により定期的に repeat_func
関数が実行され、ラベルが1秒毎にカウントアップされていきます。
さらに上記スクリプトではボタンを表示するようにしており、ボタンを押すと after_cancel
メソッドが実行されて、その after
による repeat_func
関数の実行をキャンセルされ、カウントアップが停止します。
スポンサーリンク
after
メソッドと time.sleep
との違い
ここまで解説した内容を見て、「これ time.sleep
でもいいんじゃないの?」と思われる方もいるかもしれないですが、after
と time.sleep
は全く異なるものになります。
time.sleep
関数
time.sleep
は、引数で指定した秒数分プログラムを停止させる(より正確にいうとスレッドを停止させる)関数になります。
time.sleep
を実行すると、time.sleep
関数の中で引数で指定した秒数分、time.sleep
関数の中で待たされます。つまり、その時間の間 time.sleep
関数は終了せず、そのプログラムでは他の処理を実行することができます。
例えば、time.sleep
でプログラムが停止している間は、マウスのクリックやキーボードのキー入力を受け付けてイベント処理を行うようなことはできません(そもそも sleep
を実行するとプログラムが停止するので sleep
が終わるまで mainloop
を実行することができない)。
after
メソッド
一方で、after
メソッドではプログラムが停止しません。after
メソッドでは「何ミリ秒後」に「何の関数」を実行するかの情報を登録するだけであり、after
メソッドの中で待たされるようなことはありません。
ですので、「after
メソッド実行〜引数で指定した関数が実行されるまで」の間でもプログラムはしっかり動作しており、マウスのクリックやキーボードのキー入力を受け付けてイベント処理を行うようなこともできます(mainloop
メソッドを実行しておく必要はあります)。
特に tkinter で作成するような GUI アプリは、ユーザーから様々なイベントを受け付け、そのイベントに応じて処理を即座に実行してユーザに機能やサービスを提供することが重要です。
time.sleep
でプログラムを停止してしまうと、停止している間ユーザーからイベントを受け付けてもその処理が実行できず、ユーザーを変に待たせてしまうことになる操作性が悪く、ユーザーにストレスを与えてしまいます。
特に tkinter においては time.sleep
よりも after
メソッドを利用することをオススメします。
スポンサーリンク
after
メソッドの注意点
最後に after
メソッドの注意点について解説しておきます。
after
メソッドを使いすぎると負荷が高くなる
まず一つ目の注意点は「after
メソッドを使いすぎると負荷が高くなる」ことです。
特に after
メソッドを利用して短い期間で定期的に重い処理(時間のかかる処理)を実行するときの注意点になります。
after
メソッドを実行するのは結局は CPU です。
CPU の処理能力を超えて定期的に処理を実行すると、CPU 使用率が一気に100%になって処理が追いつかなくなってしまいます。
CPU の使用率はアクティブモニターやタスクマネージャーで確認できますので、アプリ実行時に CPU の使用率が上がりすぎていないかを確認すると良いと思います。
CPU 使用率が上がっている場合は、下記で修正できないかを検討してみると良いと思います。
after
メソッドの第1引数に指定する時間を長くするafter
メソッドの第2引数に指定する関数の処理を軽くする
私の経験的に、after
メソッドを利用して tkinter の Canvas
で図形の描画を大量に行うとすぐに CPU 使用率が 100% 付近まで上がってしまいました…。
指定された時間に処理が実行されるとは限らない
二つ目の注意点は「指定された時間に処理が実行されるとは限らない」ことです。
何度も書いているのですが、あくまでも after
メソッド実行時に登録された関数が自動的に実行されるのは mainloop
実行中です。
アプリが暇なときは基本的にそのアプリの Python スクリプトでは mainloop
が実行されています。ですので、アプリが暇なときは基本的に after
メソッド実行時に登録された関数は、指定した時間後に自動的に実行されます。
ただし、アプリが忙しいとき(例えばユーザーからのマウスクリックやキーボードのキー入力等に応じて何らかの処理を実行しているとき)は基本的に mainloop
実行中ではありません。例えば他のイベント処理を実行してたりします。
このとき、 after
メソッド実行時に登録された関数は、次に Python スクリプトで mainloop
が実行された時になります。つまり、次に mainloop
が実行されるまで after
メソッド実行時に登録された関数の実行開始が遅れることになります。
ですので、この遅れを無くすように各処理を軽くしたり、遅れが発生しても問題ないようにアプリを設計することが重要です。
スポンサーリンク
まとめ
このページでは after
メソッドについて解説しました。
after
メソッドを利用することで、処理を遅らせて実行したり、処理を定期的に実行することができます。
また time.sleep
とは異なり、プログラムが停止するようなこともないため、処理が実行されるまでの間もイベント処理を行うことも可能です。
tkinter で GUI アプリ開発で利用するとめちゃめちゃ便利なメソッドだと思います。例えば「テトリスアプリでブロックを定期的に落下させる」みたいに定期的に処理を実行したい場合は必須級のメソッドです!
是非この機会に使い方をマスターしてください!