Pythonとtkinterで画像を表示するプログラムを作りました。
ところが、画像表示部分を関数化すると画像が表示されません。
■owl_on_off_bad.py
# ミミズクを表示/非表示するプログラム import tkinter as tk def display(): if cval.get() == True: img_file = 'owl.png' else: img_file = 'white_box.png' img = tk.PhotoImage(file=img_file) cvs.create_image(201, 151, image=img) root = tk.Tk() root.title('ミミズクプログラム') cval = tk.BooleanVar() cval.set(False) cbtn = tk.Checkbutton(text='ミミズクを表示する', variable=cval, command=display) cbtn.pack() cvs = tk.Canvas(root, width=400, height=300, bg="#fff") cvs.pack() root.mainloop()
どうやら、関数のスコープを抜けるとPhotoImageオブジェクトが消滅してしまうようです。
まあ、言われてみれば当たり前か。
これを解消するには、PhotoImageオブジェクトをグローバル変数に保存しておけば、プログラムが動いている間は生存し続けます。
しかし、ある程度の大きさのプログラムになると画像ファイルもたくさん使うだろうし、それらを全てグローバル変数に保存しておくのは、メンテナンスの観点からも適切ではありませんし、何よりダサいです。
ダサいプログラムを書いてると、あしたの生活もダサくなってしまいます。
そんなわけで、クロージャを使って関数内でPhotoImageオブジェクトを存続させることにしました。
■owl_on_off_good.py
# ミミズクを表示/非表示するプログラム import tkinter as tk def display(): img = None now_img = [img] def func(): if cval.get() == True: print('表示する') img_file = 'owl.png' else: print('表示しない') img_file = 'white_box.png' now_img[0] = tk.PhotoImage(file=img_file) cvs.create_image(201, 151, image=now_img[0]) return func # 画像を表示する関数を返す。 root = tk.Tk() root.title('ミミズクプログラム') cval = tk.BooleanVar() cval.set(False) cbtn = tk.Checkbutton(text='ミミズクを表示する', variable=cval, command=display()) cbtn.pack() cvs = tk.Canvas(root, width=400, height=300, bg="#fff") cvs.pack() root.mainloop()
もし、オブジェクト指向プログラミングを使う場合は、PhotoImageオブジェクトをインスタンス変数として持たせればいいです。
自分ではダサくないプログラムを書いたつもりですが、もしかしたら、まだまだダサいかもしれません。
あしたはどっちだ?