はじめに
tkinterでデスクトップアプリ上に画像を表示しようとしたが、画像が表示されない不具合が発生した。
本記事ではその解決方法をまとめる。
環境
tkinter: 8.6
現象の再現
import tkinter
from PIL import Image, ImageTk
root = tkinter.Tk()
root.geometry('400x300')
img = Image.open('dog1.jpg')
def create_label():
imgtk = ImageTk.PhotoImage(img)
label = tkinter.Label(root, width=300, height=300, image=imgtk)
label.grid()
create_label()
root.mainloop()
実行結果
原因
上記のコードでは、表示させたい画像を定義しているimgtk
がcreate_label()
関数の中でのみ存在しており、変数のスコープも関数内に限られています。
そのため、create_label()
関数の外からimgtk
への参照(アプリケーション側からの参照)ができなくなります。
その結果、参照されなくなったこの変数がメモリから解放され、ガベージコレクションが発生してしまったのです。
対処法
Label
ウィジェットのプロパティに明示的に画像を設定し、ガベージコレクションの対象から除外する。
具体的には以下のコードを追加してあげます。
# labelはtkinter.Labelのインスタンスであり、そのimageプロパティに画像を指定。
# これで参照を保持
label.image = imgtk
修正後のコード
import tkinter
from PIL import Image, ImageTk
root = tkinter.Tk()
root.geometry('400x300')
img = Image.open('dog1.jpg')
def create_label():
imgtk = ImageTk.PhotoImage(img)
label = tkinter.Label(root, width=300, height=300, image=imgtk)
label.image = imgtk # ←これを追加
label.grid()
create_label()
root.mainloop()
修正後の実行結果
まとめ
ローカルスコープ内で定義した画像(変数も含む)は外部(アプリケーション全体)からアクセスできなくなり、結果として白一色の画像しか表示されなくなる。
回避方法として、ウィジェットのプロパティに明示的に変数を保持させる。
あるいは局所変数として作成しないことで正常に画像が表示し続けることができる。