• 国立大学法人電気通信大学と電気通信大学発ベンチャー企業の連携によるプログラミング教室

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')
...
みなさんもコーディング楽しみましょう!]]>