【Python/tkinter】Canvasに画像を表示する

まず、Canvasを作成し、画像ファイルを開き、Canvasに画像を表示するサンプルは以下のようになります。

import tkinter as tk
from PIL import ImageTk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)

        # Canvasの作成
        self.canvas = tk.Canvas(self.master)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # 画像ファイルを開く(対応しているファイルフォーマットはPGM、PPM、GIF、PNG)
        self.photo_image = ImageTk.PhotoImage(file = "Mandrill.png")

        # キャンバスのサイズを取得
        self.update() # Canvasのサイズを取得するため更新しておく
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

プログラムの説明

画像ファイルは PhotoImage 関数の file引数に ファイル名を指定します。ただし、PhotoImage 関数が対応している画像ファイルフォーマットは PGM、PPM、GIF、PNG の4つのみとなります。

PhotoImage関数で開いた画像データは戻り値で取得できる(上記のプログラムではself.photo_image)ので、このデータを create_image関数の image に渡すことで、Canvas上に画像を表示することができます。

PhotoImage関数が対応している画像ファイルフォーマットが4つのみというのは、画像処理のプログラムを作成するのには致命的(最低限 bmpは必要)なので、画像ファイルをPillowで開いてPhotoImageへ変換し、create_imageで画像を表示する例を次に示します。

Pillowで画像ファイルを開く例

import tkinter as tk
from PIL import Image, ImageTk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)

        # Canvasの作成
        self.canvas = tk.Canvas(self.master)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # PillowのPIL.Imageで画像ファイルを開く
        pil_image = Image.open("Mandrill.bmp")

        # PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # キャンバスのサイズを取得
        self.update() # Canvasのサイズを取得するため更新しておく
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

上記の例はPillowの Image.open関数で画像ファイルを開いています。このopen関数で開くことのできる画像ファイルフォーマットは、bmpやjepg, tiff, png など、ほとんどの画像ファイルを開くことができます。詳しくはこちらのページを参照ください。

あとは、Image.open関数で開いた画像データをPhotoImageで変換し、create_imageで画像を表示します。

応用編

これまで、ウィンドウの作成ウィジェットの配置MenuCanvasファイルを開くダイアログボックスと説明してきましたが、これらを使ってCanvasに画像を表示するプログラムを作成しました。

 

(作成したプログラムのイメージ)

(プログラム)

import tkinter as tk
from tkinter import filedialog

from PIL import Image, ImageTk, ImageOps  # 画像データ用

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)
        
        # メニューの作成
        self.create_menu()

        # Canvasの作成
        self.back_color = "#008B8B" # 背景色
        self.canvas = tk.Canvas(self.master, bg = self.back_color)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

    def create_menu(self):
        # メニューバーの作成
        menubar = tk.Menu(self)

        # ファイル
        menu_file = tk.Menu(menubar, tearoff = False)
        menu_file.add_command(label = "画像ファイルを開く", command = self.menu_file_open_click, accelerator="Ctrl+O")
        menu_file.add_separator() # 仕切り線
        menu_file.add_command(label = "終了", command = self.master.destroy)
        # ショートカットキーの関連付け
        menu_file.bind_all("<Control-o>", self.menu_file_open_click)

        # メニューバーに各メニューを追加
        menubar.add_cascade(label="ファイル", menu = menu_file)

        # 親ウィンドウのメニューに、作成したメニューバーを設定
        self.master.config(menu = menubar)

    def menu_file_open_click(self, event=None):
        filename = filedialog.askopenfilename(
            title = "ファイルを開く",
            filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ
            initialdir = "./" # 自分自身のディレクトリ
            )
        # 画像の表示
        self.disp_image(filename)

    def disp_image(self, filename):
        '''画像をCanvasに表示する'''
        if not filename:
            return
        # PIL.Imageで開く
        pil_image = Image.open(filename)

        # キャンバスのサイズを取得
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像のアスペクト比(縦横比)を崩さずに指定したサイズ(キャンバスのサイズ)全体に画像をリサイズする
        pil_image = ImageOps.pad(pil_image, (canvas_width, canvas_height), color = self.back_color)

        #PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

ポイント

  • Canvasへ画像を表示する際はcreate_imageメソッドを用いますが、引数のimageへ渡す事ができる画像データはPhotoImageか、BitmapImageのどちらかとなります。
    BitmapImageはXBMファイルと呼ばれる二値化された画像なので、実質的に使うのはPhotoImageのみとなります。
  • PhotoImageで開く事のできるファイルフォーマットはPGM、PPM、GIF、PNGの4つのみ。
    これでも画像処理をするには物足りない(BMPは必須)ので、Pillowで画像ファイルを開き、PhotoImageへ変換します。
    Pillowなら、たくさんのファイルフォーマットに対応しています。
    (参考)

    https://docs.python.org/ja/3/library/tkinter.html?highlight=photoimage

  • create_imageメソッドで画像を表示する位置は初期状態では画像の中心座標を指定します。
    画像の左上の座標を指定する場合には、anchor = tk.NW を指定します。
  • Canvasの大きさを取得するにはwinfo_width()、winfo_height()を用います。
  • キャンバスの大きさに合わせて縦横比を崩さずに全体に表示するにはImageOps.pad()メソッドを用いていますが、ImageOpsモジュールはexperimental(試験的)という事ですが、便利なので使っちゃってます。。
    (参考)
    https://pillow.readthedocs.io/en/stable/reference/ImageOps.html

 

さらに画像の拡大・縮小・平行移動の機能を追加したプログラムについて、以下のページにまとめましたので、参照頂けると幸いです。

【Python】画像ビューア(ズーム(拡大/縮小)、移動表示)

関連ページ

【Python/tkinter】ウィンドウの作成

【Python/tkinter】ウィジェットの配置(pack)

【Python/tkinter】Menu(メニュー)

【Python/tkinter】Canvas(キャンバス)の作成

【Python/tkinter】ファイルを開くダイアログボックスの表示