【Python】tkinterのmainloopについて解説

tkinterのmainloopの解説ページのアイキャッチ

このページでは、Python の tkinter における mainloop について解説します。

mainloop は僕もよく使うよ

とりあえずスクリプトの最後で実行してる

あれ何か意味あるの?

mainloop こそが tkinter の本体と言ってもいいくらい、重要なメソッドだよ

知っていると tkinter でより良い GUI アプリが作れるようになるよ

あなたも mainloop をおまじないのように使っていませんか?

最初はそれでも良いですが、mainloop を理解しておくことで、tkinter でよりユーザーが使いやすい GUI アプリの開発や tkinter が上手く動いてくれない時の解決方法を見つけやすくなります。

mainloop とは

まず mainloop とは何か?について解説します。

mainloop の使い方

mainloop は tkinter の全ウィジェットに用意されたメソッドになります。

どのウィジェットからも実行可能ですが、メインウィンドウから実行させるのが一番一般的だと思います。

mainloop メソッドの定義は下記のようになります。

mainloopの定義
mainloop(self, n=0)

引数 n が指定可能ですが、まずは無視して引数なしで実行して良いです。

戻り値は無し(None)です。

例えば下記は mainloop を実行する簡単なスクリプト例になります。

mainloopの使い方
import tkinter
app = tkinter.Tk()
app.mainloop()

スポンサーリンク

mainloop はループを行うメソッド

mainloop とはその名の通り「ループ」処理を行うメソッドになります。

mainloop メソッドを実行すると、mainloop の中でループ処理が行われます。

基本的にアプリが終了するまで行われる無限ループと考えて良いです。

mainloop の役割

mainloop の主な役割は下記の2つです。

  • アプリの待機
  • イベントの処理

「アプリの待機」はループ処理を行うことで実現しており、「イベントの処理」はそのループの中で実行される処理になります。

この「アプリの待機」と「イベントの処理」についてここから詳しく解説していきます。

mainloop によるアプリの待機

では mainloop の主な役割の1つである「アプリの待機」について解説していきたいと思います。

スポンサーリンク

プログラムは最後まで実行されると終了する

まず思い浮かべていただきたいのが「あなたがいつも作っている Python スクリプト」です。

実行すると、スクリプトの最後まで実行されるとプログラムが終了すると思います。

プログラムの終了

これは tkinter を利用している場合でも一緒です。スクリプトの最後まで実行されるとプログラムが終了してしまいます。

例えば下記のようなスクリプトを実行しても即座にプログラムが終了してしまうことになります。

メインループなし
import tkinter
app = tkinter.Tk()

GUI アプリではアプリの待機が必要

一方で、通常 GUI アプリではユーザーからの操作(ボタン操作やマウス操作など)を受け付けるため・アプリ上に情報を表示させるために、終了させず待機させておくことが必要です。

例えば計算機アプリは、起動すると待機し、その後ユーザーからのマウス操作やキーボード操作により数字の入力や計算の実行を指示されるのを待ちますよね。

こんな感じで自作したアプリも待機させ、ユーザーからの操作を待つことが必要です。

アプリが待機している様子

mainloop を実行すればアプリを待機させられる

mainloop はそのアプリを待機させる役割を担っています。

なぜ mainloop によりアプリを待機させることができるかと言うと、mainloop の中がループ処理になっているからです。

mainloop のループ処理は、終了ボタンが押されるなど、意図的にアプリが終了しない限り続きます。

ですので、mainloop を実行するだけで、プログラムが終了するのを防ぎ、アプリを待機させることができるようになります。

なるほど!

確かに mainloop の実行を忘れててアプリが起動しなくて焦ったことあったよ…

mainloop の実行を忘れると、アプリが起動すらせずプログラムが終了してしまいます(私もよく忘れます…)。

エラーが出ていないのにアプリが全く起動しない場合は、まずは mainloop を実行しているかどうかを確認しましょう!

でも単に待機させたいだけなら while でループしちゃえばいいんじゃないの?

例えば下記のスクリプトみたいに!

mainloop の代わりに自作の無限ループを実行した場合はアプリは起動するでしょうか…?

自作のループ
import tkinter
app = tkinter.Tk()
while True:
	pass

いい気づきだね!でも…

とりあえず実行してみ?

あれ?アプリが起動しない…

プログラムを終了させないだけであれば、確かに上記のスクリプトで実現できます。

ただし、前述の通り、mainloop の役割には「アプリの待機」だけでなく「イベント処理」もあります。

アプリの画面をスクリーン上に描画するのもこの「イベント処理」で行われます。なので、上記のスクリプトでは「アプリの待機」はできてもアプリの画面を表示するようなことはできません。

mainloop においてはこの「イベント処理」が行われる点が特に重要です。次はこの mainloop によるイベントの処理について解説していきたいと思います。

スポンサーリンク

mainloop によるイベントの処理

では mainloop によるイベントの処理について解説していきます。

イベントの処理とは

mainloop の中身は前述の通りループです。そして、そのループの中では「イベントの処理」が行われます。

まず「イベント」とはそのアプリで発生した出来事のことを言います。

tkinter では下記のようなイベントを扱うことができます。

  • マウスボタンのクリック
  • マウスの移動
  • キーボードのキー入力
  • ウィジェットの作成・配置・設定変更

「イベントの処理(イベント処理)」とは、そのイベントが発生した時に何かしらの処理(関数)を実行することを言います。

例えば下記のようなものはイベントの処理です。

  • マウスボタンのクリックイベントが発生した時(ユーザーがアプリ上でマウスボタンをクリックした時)に関数 click_func を実行する

どのイベントが発生した時に、どの関数を実行するかは bind メソッドにより設定可能です。

そして、このイベント発生時に実行される関数は「イベントハンドラ」と呼ばれます。

イベントハンドラが実行される様子

この辺りは下記ページで解説していますので、イベントについて理解したりイベントを使いこなしたい方は読んでみてください。

イベント処理解説ページのアイキャッチTkinterの使い方:イベント処理を行う

前述の通り、どのイベントが発生した時に、どの関数を実行するかは bind メソッドにより設定可能ですが、もともと tkinter 本体で設定されているイベント処理もあります。

例えばウィジェットの作成や配置・設定変更もイベントとして扱われ、これらを反映してスクリーンに表示する処理はもともと tkinter 本体で設定されているイベントハンドラによって実行されます。

イベントハンドラは mainloop から実行される

ではこのイベントハンドラがどうやって実行されるかについて解説していきたいと思います。

イベントハンドラは mainloop の中から実行されます。

mainloop は単にループするだけでなく、ループの中で下記のような処理を行なっています。

  • イベントが発生していないかを確認
  • そのイベントに対するイベントハンドラを実行

図で表すとこんな感じです。

mainloopがイベント処理する様子

これらの処理が無限ループとして実行されます。

mainloop 実行後のほぼ全ての処理はイベント処理として実行されます。ですので、tkinter で開発したアプリの処理のほぼ全ては mainloop から実行されると考えて良いです。

こういう視点で考えると、mainloop は tkinter で開発したアプリの本体のように捉えることもできます。

イベントハンドラは mainloop の中で実行されているため、mainloop が実行されないとイベント処理は実行されません。

ウィジェット等の表示もイベント処理として実行されるため、たとえウィジェットの作成や配置を行ったとしても、mainloop を実行しないことにはスクリーン上には表示されないことになります。

これが先ほど示した自作のループでアプリが起動しなかった(アプリの画面が表示されなかった)理由になります。

また mainloop から実行されたイベントハンドラの処理が終了すると、再び自動的に mainloop に戻ります。

イベントハンドラ実行後にイベントハンドラに戻る様子

ですので、一つのイベントに対するイベントハンドラの実行後には、特にプログラマーが意識しなくても、その次に受け付けたイベントに対するイベントハンドラが自動的に実行されることになります。

スポンサーリンク

イベント処理が終わらないと次のイベント処理が出来ない

逆に言うと、イベント処理が終わらないと次のイベント処理が実行されないことになります。

例えば下記のようなスクリプトについて考えてみましょう!

次のイベント処理ができない例
# -*- coding:utf-8 -*-
import tkinter

# ボタンが押された時の処理
def button_click(event):
	global label

	# ラベルの文字列を変更
	label.config(
		text="ボタンが押されました"
	)

	# 10秒間スリープ
	import time
	time.sleep(10)

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("200x100")

# ボタンの作成と配置
button = tkinter.Button(
	app,
	text="ボタン",
)
button.pack()

# ラベルの作成と配置
label = tkinter.Label(
	app,
	width=20,
	height=1
)
label.pack()

# イベント処理の設定
button.bind("<ButtonPress>", button_click)

# メインループ
app.mainloop()

このスクリプトでアプリ上に配置しているのはボタンウィジェットとラベルウィジェットです。

イベント処理が行われない例

ボタンが押されたときには button_click 関数が行われるようにイベント処理の設定(bind メソッドによる設定)を行っています。

button_click 関数ではラベルに表示する文字列を「ボタンが押されました」に設定する処理を行っています。さらにその後、time.sleep 関数を使って10秒間スリープするようにしています。

スクリプトを実行してアプリを起動し、ボタンを押すとどうなるでしょうか?

うーん、ラベルに「ボタンが押されました」が表示されてから10秒待たされるのかな?
実は逆で、ボタンを押してから10秒後に「ボタンが押されました」が表示されるんだ

え? time.sleep よりも先に label.config 実行してるのに…

スクリプトの実行順序と逆に感じるかもしれませんが、実は10秒待たされた後にラベルに「ボタンが押されました」が表示されます。

これは、下記のような順序でイベント処理としてウィジェットの表示が行われるためです。

  1. ボタンが押されて mainloop からイベントハンドラである button_click 関数が実行される
  2. button_click の中で label.config が実行され、「ラベルの設定変更」のイベントが発生する
  3. time.sleep により10秒間スリープする
  4. button_click 関数が終了して mainloop に戻る
  5. mainloop の中で「ラベルの設定変更」のイベントハンドラが実行され、設定変更内容が画面上のラベルに反映される

ポイントは 2. ではイベントが発生するだけで、そのイベントに対するイベントハンドラによる処理は mainloop に戻ってから 5. で実行されることです。

こんな感じでイベントハンドラの実行は mainloop でのループ中にしか実行されません。イベントハンドラ実行中は他のイベントに対するイベントハンドラは実行されないのです。

他のイベントハンドラが実行できない様子

これは、もし他のイベントハンドラの処理の実行時間が長いと、次のイベントに対するイベントハンドラの処理がその分遅くなるということを意味します。

例えばイベントハンドラの処理時間が長いと、ユーザーがボタンを押してもそのボタンに対するイベントハンドラの処理が遅れて実行されることになります。

ユーザーからすると「ボタンを押してもなかなか反応しない」と感じるため、アプリの使用感が悪くなってしまいます。

tkinter ではいかにして mainloop に処理を戻すかが重要

tkinter でアプリを開発するときは「いかにして mainloop に処理を戻すか」を意識してプログラミングするようにしましょう!

これにより イベントをこまめに実行できるようになり、ユーザーの使用感を向上させることができます。

例えばイベントハンドラでは下記のような処理は行わないほうが良いです。

  • スリープ処理(スリープしている間他のイベントが処理されない)
  • 負荷の高い処理(処理している間他のイベントが処理されない)

こういった処理が必要な場合は、まずは下記に置き換えられないかどうかを検討してみてください。

  • スリープ処理を after メソッドに置き換えられないか
  • 負荷の高い処理を細かい単位の処理に分解できないか

スリープ処理をafter メソッドに置き換える

実際に「いかにして mainloop に処理を戻すか」を考慮した時にどのようにスクリプトを変更すれば良いかを実例を踏まえて説明しておきます。

下記はスリープ処理を用いたタイマーアプリ(1から10を1秒ずつカウントアップしていくアプリ)になります。

sleepを使用したタイマーアプリ
# -*- coding:utf-8 -*-
import tkinter

start_flag = False

# タイマー
def timer(count):
	global label
	global start_flag

	if start_flag:
		import time
		time.sleep(1)

		label.config(text=count)

# スタートボタンが押された時の処理
def start_button_click(event):
	global start_flag
	start_flag = True

	for i in range(10):
		timer(i + 1)

# ストップボタンが押された時の処理
def stop_button_click(event):
	global start_flag
	start_flag = False

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("200x100")

# ボタンの作成と配置
start_button = tkinter.Button(
	app,
	text="スタート",
)
start_button.pack()

stop_button = tkinter.Button(
	app,
	text="ストップ",
)
stop_button.pack()


# ラベルの作成と配置
label = tkinter.Label(
	app,
	width=5,
	height=1,
	text=0,
	font=("", 20)
)
label.pack()

# イベント処理の設定
start_button.bind("<ButtonPress>", start_button_click)
stop_button.bind("<ButtonPress>", stop_button_click)

# メインループ
app.mainloop()

スクリプトは下記のようにアプリを動作させることを考えて実装しています。

  • スタートボタンを押すとイベントハンドラ start_button_click が実行され、1秒ずつラベルに表示する数字がカウントアップされる(カウントアップは timer 関数で実行)
    • 10になったら終了
  • ストップボタンを押すとイベントハンドラ stop_button_click が実行され、その時点で数字のカウントアップが終了

が、実行してみると全くこのようには動作してくれません。

スクリプトを実行すると下のような画面のアプリが起動します。

タイマーアプリの起動画面

ここまでは良いのですが、スタートボタンを押しても全くラベルの数字が変化しません…。

ラベルの数字が更新されない様子

さらにストップボタンを押しても全くアプリが反応してくれません。

ボタンを押しても反応しない様子

10秒経過するとようやくラベルの数字が変化しますが、いきなり10が表示されてしまいます…。

いきなりラベルが更新される様子

なぜこのような動きになってしまうのでしょうか?

おそらく mainloop についてしっかり理解している人には理由がわかるはず!

問題はイベントハンドラ start_button_click にあります(timer 関数も含めて)。

スタートボタンをクリックすると mainloop の中からイベントハンドラ start_button_click が実行されますが、この  start_button_click から実行される timer 関数では「1秒間のスリープ処理&ラベルの text 更新」を10回繰り返しています。

つまり、この処理が終わらないと、start_button_click は終了しないことになり、その間 mainloop に処理が戻りません。

mainloopに処理が戻らない様子

したがって ラベルの text 更新が行われても、実際にその更新後のラベルが画面に表示されるのは、次に mainloop に戻った時、つまりスタートボタンクリック後の10秒後になります(設定更新後のウィジェットへの画面の表示もイベントハンドラとして mainloop から実行される)。

ラベルの画面への反映が遅れる様子

またストップボタンが押された時に実行されるイベントハンドラ start_button_click も mainloop の中から実行されますので、これも同様に mainloop に処理が戻らないと実行されません。したがってストップボタンを押してもボタンが反応しないように感じられてしまいます。

ボタンクリックの反応が遅れる様子

こんな感じでスリープ処理を行うと、その間イベントハンドラ内で処理が実行されていることになって長い期間 mainloop に処理が戻りません。ですので、その間イベントハンドラが実行されなくなり、アプリ全体が停止してしまったように感じてしまいます。

ですので、スリープを使わずにとにかく mainloop に処理を戻すことを意識してプログラミングすることが重要です。

特に tkinter ではスリープの代わりに after メソッドを使うと良いです。

下記はスリープを使わずに after メソッドを用いてほぼ同等のタイマーアプリを実現したスクリプトになります。

sleepをafterに置き換えたタイマーアプリ
# -*- coding:utf-8 -*-
import tkinter

start_flag = False

# タイマー
def timer(count):
	global label, app
	global start_flag

	if start_flag and count <= 10:
		label.config(text=count)
		app.after(1000, timer, count + 1)

# スタートボタンが押された時の処理
def start_button_click(event):
	global app
	global start_flag
	start_flag = True

	app.after(1000, timer, 1)

# ストップボタンが押された時の処理
def stop_button_click(event):
	global start_flag
	start_flag = False

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("200x100")

# ボタンの作成と配置
start_button = tkinter.Button(
	app,
	text="スタート",
)
start_button.pack()

stop_button = tkinter.Button(
	app,
	text="ストップ",
)
stop_button.pack()


# ラベルの作成と配置
label = tkinter.Label(
	app,
	width=5,
	height=1,
	text=0,
	font=("", 20)
)
label.pack()

# イベント処理の設定
start_button.bind("<ButtonPress>", start_button_click)
stop_button.bind("<ButtonPress>", stop_button_click)

# メインループ
app.mainloop()

ポイントは start_button_click で実行している下記部分です。

afterの実行
app.after(1000, timer, 1)

after は、第1引数で指定した時間(ms 単位)分遅らせてから第2引数で指定した関数を実行するメソッドになります(第3引数はその関数に渡す引数)。

詳細は下記ページで解説していますので、after について詳しく知りたい方はこちらを参考にしてください。

Tkinterの使い方:after で処理を「遅らせて」or 処理を「定期的」に実行する

スリープの代わりに after メソッドを利用することで、イベントハンドラではなく mainloop で時間の経過を待つことができるようになります。

「遅らせる」という部分がスリープに似ていますが、after メソッド実行してもその時点で待たされるようなことはありません。上記のように関数を実行するように登録だけして、すぐに after メソッドは終了します。

つまり、start_button_click は after メソッド実行後に即座に終了することになります。したがってスタートボタンを押すとすぐに mainloop に戻ります。

そして第1引数で指定した時間が経った後に、第2引数で指定した関数 timer が実行されます。これも mainloop の中から実行されます。

timer の中ではラベルの text の設定を行った後にまた自分自身(timer)を1秒後に行わせるように after メソッドを実行しています。これが10秒分カウントアップするまで続けられます。

前述の通り after メソッドは遅らせて実行する情報の登録を行うだけですぐに終了しますので、timer 関数もすぐに終了して mainloop に戻ります。

mainloop に戻れば timer 関数の中で実行された text の設定更新に対するイベントハンドラがすぐに実行され、ラベルの更新がすぐにアプリの画面に反映されることになります。

すぐにmainloopに戻ってラベル更新が画面に反映される様子

またイベントハンドラではなく mainloop の中で時間が経過することを待つことが可能ですので、ストップボタンがクリックされた時に即座に時間のカウントアップを終了させることも可能です。

すぐにmainloopに戻ってボタンクリックに対するイベントハンドラが実行される様子

つまり、スリープの代わりに after メソッドを利用することで、下記のことが行えることになりました。

  • ラベルの更新を即座に画面に反映
  • ストップボタンでカウントアップを即座に終了

こんな感じで、mainloop に処理を戻すための工夫をしてやることで、よりユーザーが使いやすいアプリを作れるようになります。

そもそも上記2つが行えないとアプリとして機能していないのでバグとも言えます…。mainloop を理解していないとこういった「アプリの画面が更新されない」「ボタンが効かない」といった問題が発生しやすいので、mainloop をしっかり理解し、mainloop にいかにして処理を戻すかを意識しながらプログラミングすることが重要です。

負荷の高い処理を細かい単位の処理に分解する

今度は負荷の高い処理をいかにして mainloop に戻しながら処理を行うかについて解説します。

重い処理を行う例
# -*- coding:utf-8 -*-
import tkinter

start_flag = False

# 負荷の高い処理
def calc():
	global start_flag

	for j in range(100000):
		if not start_flag:
				return
		for i in range(1000):
			print(i * j)

# スタートボタンが押された時の処理
def start_button_click(event):
	global start_flag
	start_flag = True

	calc()

# ストップボタンが押された時の処理
def stop_button_click(event):
	global start_flag
	start_flag = False

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("200x100")

# ボタンの作成と配置
start_button = tkinter.Button(
	app,
	text="スタート",
)
start_button.pack()

stop_button = tkinter.Button(
	app,
	text="ストップ",
)
stop_button.pack()

# イベント処理の設定
start_button.bind("<ButtonPress>", start_button_click)
stop_button.bind("<ButtonPress>", stop_button_click)

# メインループ
app.mainloop()

スクリプトは下記のようにアプリを動作させることを考えて実装しています(負荷の高い処理を実行したいだけなので意味のない計算をしています…)。

  • スタートボタンを押すとイベントハンドラ start_button_click が実行され、ループの中で計算処理を実行(計算処理は calc 関数で実行)
  • ストップボタンを押すとイベントハンドラ stop_button_click が実行され、その時点で計算処理を終了

が、実行してみるとストップボタンが効きません。

これは calc 関数がイベントハンドラ内で実行されている& calc 関数の処理負荷が高くて実行時間が長いため、その間 mainloop に処理が戻らないためです。

処理実行中に他のイベントハンドラが実行されない様子

負荷の高い処理をイベントハンドラで実行すると、他のイベントハンドラが実行されずにアプリ全体が停止したように感じてしまいます。

こういった負荷の高い処理は、細かい単位の処理に分解できないかどうかを検討すると良いです。そして、その分解した細かい単位の処理の実行が完了するたびに、mainloop に戻すように制御します。

上記スクリプトの計算処理を細かい単位に分解して実行するようにしたスクリプトが下記のようになります。

重い処理を細かく分解する例
# -*- coding:utf-8 -*-
import tkinter

start_flag = False

# 負荷の高い処理
def calc(j):
	global start_flag
	global app

	if start_flag:
		for i in range(1000):
			print(i * j)
		if j < 100000:
		app.after(1, calc, j + 1)

# スタートボタンが押された時の処理
def start_button_click(event):
	global start_flag
	global app
	start_flag = True

	app.after(1, calc, 0)

# ストップボタンが押された時の処理
def stop_button_click(event):
	global start_flag
	start_flag = False

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("200x100")

# ボタンの作成と配置
start_button = tkinter.Button(
	app,
	text="スタート",
)
start_button.pack()

stop_button = tkinter.Button(
	app,
	text="ストップ",
)
stop_button.pack()

# イベント処理の設定
start_button.bind("<ButtonPress>", start_button_click)
stop_button.bind("<ButtonPress>", stop_button_click)

# メインループ
app.mainloop()

calc 関数の中で1度に全ての計算処理を行うのではなく、i に対するループだけを行うように処理を分解しています。

さらに calc 関数の最後で j+1 して再度 calc を実行するように after メソッドを実行しています(これにより j に対するループを実現しています)。

これにより i に対するループが終了すればイベントハンドラの処理も一旦終了し、mainloop に処理が戻るようになります。

したがって、ストップボタンが押されていればその mainloop に戻ったタイミングでストップボタンによる処理の終了を行うことができるようになっています。

処理を分割することで他のイベントハンドラをすぐに実行できる様子

こんな感じで処理を小さな単位に分解し、分解した処理が終わるたびに mainloop に戻るようにしてやることで、他のイベント処理を行いながら負荷の高い処理を実行することができるようになります。

そしてこれによりユーザーの使用感を向上させることができます。

mainloop 関連の陥りやすい問題

最後に、ここまでの解説を踏まえ、mainloop 関連の陥りやす問題をいくつか紹介しておきます。これらは mainloop をしっかり理解していれば解決できる問題です。

スポンサーリンク

アプリの画面が全く表示されない

アプリの画面やウィジェットは mainloop の中でもともと tkinter に用意されているイベントハンドラが処理されることによって表示されます。

したがって mainloop を実行しないとアプリの画面やウィジェットは表示されません。

エラーが出ていないのにアプリの画面が一切何も表示されない時は、まずは mainloop を実行しているかどうかを確認してみましょう!

mainloop の後ろに記述した処理が実行されない

mainloop はアプリが起動している間に実行される無限ループです。

ですので、mainloop の後ろに処理を記述しても、アプリが終了しないと実行されません。

mainloopの後ろ側の処理
import tkinter
app = tkinter.Tk()
app.mainloop()
print("このメッセージはアプリ終了するまで表示されません")

mainloop 実行前に処理が記述できないかどうかをまず検討し、無理そうであればその後イベントを発生させ、イベントハンドラの中で処理を実行するようにしましょう!

逆にアプリ終了後に行いたい処理は mainloop の後ろで実行しても良いです。

ウィジェットの表示が更新されない

config メソッド等でウィジェットの設定を行っても、config メソッド実行時にその設定が画面に反映されるわけではありません。

config メソッド実行時に行われるのは設定の変更だけで、その設定が画面上に反映されるのは次に mainloop に戻った時になります。

したがって、config メソッド実行後に負荷の高い処理を行ってイベントハンドラの処理が続くと、その間は mainloop に処理が戻らず、ウィジェットの表示が更新されないことになります。

config メソッドでウィジェットの設定変更直後にその設定を画面上に反映したい場合は、config メソッド実行直後に mainloop に処理が戻るようにイベントハンドラの作りを工夫してみましょう!

スポンサーリンク

ボタンがすぐに効かない

これも「ウィジェットの表示が更新されない」と同じ考え方です。

ボタンクリック時に実行するイベントハンドラも mainloop の中から実行されますので、他のイベントハンドラで負荷の高い処理を実行していたりすると、その間 mainloop に処理が戻らないのでボタンをクリックしてもそのイベントハンドラが実行されません。

そのためボタンが効かないようにユーザーは感じてしまいます。

イベントハンドラがこまめに実行できるように、負荷の高い処理も細かい処理に分割し、こまめに mainloop に処理が戻るように工夫してみましょう!

まとめ

このページでは tkinter の mainloop について解説しました!

mainloop の主な役割は下記の二つです。

  • アプリの待機
  • イベントの処理

アプリの待機ができないとアプリの画面がすぐに消えてしまいます(そもそもアプリ画面が表示されない)。

また tkinter の処理はほぼイベントの処理として扱われるため、イベントの処理ができないとアプリの画面の更新やボタンクリック等のイベントに対する処理も行えません。

tkinter のアプリ開発でボタンの反応やウィジェットの更新タイミングが遅い時は、まずご自身のスクリプトで mainloop に処理が戻っているのかを確認するのが良いと思います。

また mainloop を理解していれば、よりユーザーの使用感を向上したアプリ作成もできるようになります!

mainloop について理解し、ぜひ mainloop を意識したアプリ開発に挑戦してみてください!

2 COMMENTS

匿名

tkinterの初心者です。説明がとてもわかりやすいです。

sleepをafterに置き換えたタイマーアプリ”のコードですが、下記の誤記ではないでしょうか?

7 def timer(count):
8 global label, app
9 global start_flag
10
11 if start_flag and count <=10:
12 label.config(text=count)
13 app.after(1000, timer, count + 1)

返信する
daeu

匿名さん

コメントありがとうございます!

ご指摘の通り、誤記でしたので早速修正させていただきました。

気づいていなかったので大変助かりました!ありがとうございます!
また何かありましたら気軽にコメント頂ければと思います!

返信する

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です