import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root)
# ファイルはgifでお願いします
canvas.create_image(50, 50, image=tk.PhotoImage(file='foo.gif'))
canvas.pack()
root.mainloop()
どうです…?まっさらなウィンドウが表示されましたよね!
え?画像はどこ行ったって?
GCに回収されて消えました。ばいばい。
GC。ガーベジコレクション。使用されていない(であろう)オブジェクトのメモリを解放します。
Pythonではオブジェクトの __del__
メソッド(デストラクタ)を呼びます。
今回は作ったはずの tk.PhotoImage
のインスタンスへの参照がなくなった(っぽい)から回収されたみたいです。
検証します。
とりあえずこのコードを追加しましょう。(+
が先頭に書いてあるものが追加したコードです)
+ def gc_konoyarou(fnc):
+ def _inner(instance):
+ print('GCこのやろう')
+ fnc(instance)
+ return _inner
+ tk.PhotoImage.__del__ = gc_konoyarou(tk.PhotoImage.__del__)
root = tk.Tk()
高階関数を使ってるのは元の関数の機能を残したかったからで、それ以外に意図はありません。
これで tk.PhotoImage.__del__
が呼ばれると「GCこのやろう」と表示されるはずです。
検証終了。
グダグダ書きましたが。こう書けば問題はなかったのです。
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root)
# imageがグローバルスコープ内にあるから消えない!
image = tk.PhotoImage(file='foo.gif')
canvas.create_image(50, 50, image=image)
canvas.pack()
root.mainloop()
tkinterを使う時は、下手に関数を書くと関数スコープ内でしか存在できなかったりするので、ちょっと複雑なことをしたい時はクラスを作った方が無難な気がします。(globalしてもいいけど。)
...
def create_image(canvas, path):
# 関数スコープ内
image = tk.PhotoImage(file=path)
canvas.create_image(50, 50, image=image)
create_image(canvas, 'foo.gif')
...
クラスを使う場合はこんな感じで書きましょう。
class MyApp:
def __init__(self, root):
self.canvas = tk.Canvas(root)
def create_image(self, path):
# selfはMyAppのインスタンスなので、
# self.imageはグローバルスコープに存在することになる
self.image = tk.PhotoImage(file=path)
self.canvas.create_image(50, 50, image=self.image)
root = tk.Tk()
# MyAppのインスタンスを生成。グローバルスコープに作られる。
my_app = MyApp(root)
my_app.create_image('foo.gif')
...
みなさんもコーディング楽しみましょう!]]>