エンジニアになりたい30代の学習備忘録

学習したプログラミングやマークアップについての備忘録

tkinterでの画像表示について

PythonGUIアプリを作成する際、よく使われているというtkinter
標準モジュールなので気軽に使えるのがいいのですが、私のような初心者にはもちろんつまずきポイントもたくさんあります。
その中でも苦戦した機能の一つである画像表示について、備忘録として記録しておきます。

まず、やり方としてはCanvasやPillowなどもありますが、個人的に一番簡単だったのはLabelでの表示です。
※使用する画像(png)はご自身でご用意の上、実行ファイルと同じ場所に置いといてください。

基本の画像表示

from tkinter import *

#----メイン画面----
root = Tk()
root.title('sample')
root.geometry('400x400+220+100')
#----フレーム----
my_frame = Frame(root)
my_frame.pack()
#----画像----
f = 'sweets_cake_pavlova.png'
image_file = PhotoImage(file=f)
image_label = Label(my_frame, image=image_file)
image_label.pack()

root.mainloop()

root.geometry('400x400+220+100')については、画面サイズと表示位置ですのでなくても結構です。
以下、解説です。

f = 'sweets_cake_pavlova.png'
image_file = PhotoImage(file=f)
image_label = Label(my_frame, image=image_file)
image_label.pack()
  1. 変数に画像のpathを代入します。(絶対ではありませんが可読性と修正のしやすさを考えたらこの方がいいです)
  2. Photoimageウィジェットインスタンスを生成します。
    fileオプションに画像ファイルのpathを指定します。(変数に代入していない場合はここに直接pathを記入します)
  3. Labelウィジェットを生成し、textオプションではなくimageオプションを使用してPhotoimageオブジェクトを指定します。
  4. pack()します。
    実行するとこうなります。

    解決!と思いきや、問題が生じる場合があります。

    関数を使用したとき

from tkinter import *

def widget_create():
    #----フレーム----
    my_frame = Frame(root)
    my_frame.pack()
    #----画像----
    f = 'sweets_cake_pavlova.png'
    image_file = PhotoImage(file=f)
    image_label = Label(my_frame, image=image_file)
    image_label.pack()
    #----ラベル----
    info_label = Label(my_frame, text='sample image')
    info_label.pack()

#----メイン画面----
root = Tk()
root.title('sample')
root.geometry('400x400+220+100')
#----ウィジェットの生成----
widget_create()

root.mainloop()

比較しやすいよう、テキストラベルも追加してあります。
こちらの実行結果はこうなります。

テキストラベルの「sample image」は表示されているのに画像は表示されていません。
私も細かい理由はちゃんと理解出来ていないのですが、どうやらPhotoimageオブジェクトは関数を抜けると消滅するようです。
解決方法としてはざっくり言うと「実行結果が返ってくるような記述にする」って感じかなと思います。
関数ならreturnでPhotoimageが返ってくるようにします。
あんまりこういう書き方はしないかもしれませんがウィジェットの生成をw = widget_create()のように変数に代入し、widget_create()関数の一番最後にreturn image_fileを記述すると画像もちゃんと表示されます。

return image_fileの記述の場合のサンプルコード(折り畳んでいるので三角を押して開いてください)

from tkinter import *
from widget_create import WidgetCreate

def widget_create():
    #----フレーム----
    my_frame = Frame(root)
    my_frame.pack()
    #----画像----
    f = 'sweets_cake_pavlova.png'
    image_file = PhotoImage(file=f)
    image_label = Label(my_frame, image=image_file)
    image_label.pack()
    #----ラベル----
    info_label = Label(my_frame, text='sample image')
    info_label.pack()

    return image_file

#----メイン画面----
root = Tk()
root.title('sample')
root.geometry('400x400+220+100')
#----ウィジェットの生成----
w = widget_create()

root.mainloop()


当然ですが、このやり方だと一つしか画像を返せません。
ので、試してなんとなく理屈さえ理解出来たらこの記述は辞めといた方が良いかと思います。
ウィジェットが増えることもふまえると、「クラスに記述してインスタンスの生成」が個人的には整理しやすかったです。

クラスでウィジェットを作成

クラスに記述の場合のサンプルコード(折り畳んでいるので三角を押して開いてください)

from tkinter import *

class WidgetCreate():
  def frame_create(self, belong):
      #----フレーム----
      self.my_frame = Frame(belong)
      self.my_frame.pack()
  def image_create(self):
      #----画像----
      self.f = 'sweets_cake_pavlova.png'
      self.f2 = 'calender_woman.png'
      self.image_file = PhotoImage(file=self.f)
      self.image_label = Label(self.my_frame, image=self.image_file)
      self.image_label.pack()
      self.image_file2 = PhotoImage(file=self.f2)
      self.image_label2 = Label(self.my_frame, image=self.image_file2)
      self.image_label2.pack()
  def text_label_create(self):
      #----ラベル----
      self.info_label = Label(self.my_frame, text='sample image')
      self.info_label.pack()

#----メイン画面----
root = Tk()
root.title('sample')
root.geometry('400x400+220+100')
#----ウィジェットの生成----
widget = WidgetCreate()
frame = widget.frame_create(belong=root)
image = widget.image_create()
txt_label = widget.text_label_create()

root.mainloop()


比較用に画像をもう一つ追加してあります。
これなら画像二つともちゃんと表示されます。

注意点としては代入する変数をself.変数名でちゃんとインスタンス変数にすること、です。
self.を忘れると表示されなくなります。テキストラベルは表示されるんですけどね…。
ちなみに、正しいかは分かりませんが、私はクラス内で変数を使用する際は一時的に数値を格納するときなど以外は全てself.を付けています。
モジュールが分かれていったりすると思わぬところでまたTOPの画像が消えたりもしますが、理屈さえ分かっていれば対処はしやすいはずです。
長くなってしまったし、今回はとりあえずこんなところかな。
それでは。