LoginSignup
12
11

More than 3 years have passed since last update.

1.はじめに

前回の記事に、Tkinterのテンプレート化についてご紹介しました。
今回の記事では、このテンプレートを元に、Image Viewerのアプリケーションの作り方をご紹介します。

環境

Windows 10 64 bit
Python 3.7
IDE : Pycharm

2.Image Viewerの概要

次の図に、今回作成するImage Viewerアプリケーションのポンチ絵を示します。。
このアプリケーションの機能は、画像ファイルを選択し、その画像を表示することだけです。
そして、表示画面を消す機能とアプリケーションを終了する機能を追加します。

image.png
写真の人物は、アメリカの免疫学の権威のRalph教授です。

3.Image Viewerの設計

次の図に、Image Viewerのレイアウトを示します。

アプリケーションの部品であるWidgetを用意します。
1.Canvas Widget : Imageを表示するための入れ箱。
2.Label Frame Widget : ボタンを三つ用意しますが、それらのボタンをまとめるための入れ箱。
3.Button : Load Image, Clear Image, Quit Applicationの三つのボタン。

各Widgetの配置には、gridメソッドを用います。
image.png

Tkinter テンプレートの説明

**Tkinterのテンプレート化記事に紹介されたテンプレートのプログラムコードです。

テンプレートの構成 

  1. Applicationクラス Widget設定&配置, Event Callback関数を定義
  2. main関数

このテンプレートに、前章に説明したWidgetの設定・配置、そしてEvent Callback関数を記入する手順で、コードを組み立てます。 (骨組み→肉付け)

import tkinter as tk
from tkinter import ttk

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

        self.master.geometry("300x300")
        self.master.title("Tkinter with Class Template")

        self.create_widgets()

    def create_widgets(self):
        pass

    def callBack(self):
        pass

def main():
    root = tk.Tk()
    app = Application(master=root)#Inherit
    app.mainloop()

if __name__ == "__main__":
    main()

3.1.Canvasの設定

Class Applicationのcreate_widgetsメソッド部分にCanvas Widgetを設定、配置します。
配置には、Gridメソッドを利用します。

    def create_widgets(self):
        #Canvas
        self.canvas1 = tk.Canvas(self)
        self.canvas1.configure(width=640, height=480, bg='orange')
        self.canvas1.create_rectangle(0,0,120, 70, fill='green')
        self.canvas1.grid(column=1, row=0)
        self.canvas1.grid(padx=20, pady=20)

3.2.File Open -> Image Load

ファイルを開き、Imageファイルをロードする部分です。
Buttonを設定した後、Event Callback関数のloadImage()を定義します。
LoadImage()の内部のif文は、画像の縦横比を見て、Canvasのサイズに合わせ、元画像をリサイズするために追加しました。
そして、画像ファイルの変換として、openCVのRGB変換、PILに変換、imageTKに変換する部分が続きます。
最後に、self.canvas1.create_imageで画像をCanvas Widgetに表示します。


    def create_widgets(self):

        #File open and Load Image
        self.button_open = ttk.Button(self.frame_button)
        self.button_open.configure(text = 'Load Image')
        self.button_open.grid(column=0, row=1)
        self.button_open.configure(command=self.loadImage)

    # Event Call Back
    def loadImage(self):

        self.filename = filedialog.askopenfilename()

        self.image_bgr = cv2.imread(self.filename)

        self.height, self.width = self.image_bgr.shape[:2]
        if self.width > self.height:
            self.new_size = (640,480)
        else:
            self.new_size = (480,480)

        self.image_bgr_resize = cv2.resize(self.image_bgr, self.new_size, interpolation=cv2.INTER_AREA)
        self.image_rgb = cv2.cvtColor( self.image_bgr_resize, cv2.COLOR_BGR2RGB )  # imreadはBGRなのでRGBに変換
        self.image_PIL = Image.fromarray(self.image_rgb) # RGBからPILフォーマットへ変換
        self.image_tk = ImageTk.PhotoImage(self.image_PIL) # ImageTkフォーマットへ変換
        self.canvas1.create_image(320,240, image=self.image_tk)

下記の図は、ファイルを開くときにキャプチャした画像です。この機能を使うため、filedialog.askopenfilename()を利用します。
しかし、フォルダー名に日本語文字が(UTF-8)ある場合、エラーが出ます。今回は、画像ファイルが格納されるフォルダー名は、英語にするようにお願いします。(もし、この問題の解決方法のご存知の方は、メッセージを残してください。)

image.png

3.3.画面クリア

Canvas上の画面を消す機能です。canvas.delete()メソッドを利用します。

    def clearImage(self):
        self.canvas1.delete("all")

3.4.Quitボタン

終了の際に、MessageBoxを表示させ「本当に終了するの?」とユーザーの判断をもう一度確認する部分です。

    def quit_app(self):
        self.Msgbox = tk.messagebox.askquestion("Exit Applictaion", "Are you sure?", icon="warning")
        if self.Msgbox == "yes":
            self.master.destroy()
        else:
            tk.messagebox.showinfo("Return", "you will now return to application screen")

下記の図は、MesageBoxが表示されたときの画面です。
image.png

4.実行結果

次の図に、実行結果を表示します。設計通りに動作することが確認できました。
Clear ImageやQuitもOKであることが確認できました。

image.png

5.まとめ

  1. Tkinterの基本テンプレートを利用して、Image Viewer Applicationを作成しました。
  2. 基本テンプレートに、必要なWidgetを追加することで、様々な目的のアプリケーションの作成が可能であることを確認しました。
  3. なかなか優れものですね。Tkinter。

6.全体コード

import tkinter as tk
from tkinter import ttk

import sys
import os
from tkinter import *
from tkinter import messagebox,filedialog
import numpy as np
from PIL import Image, ImageTk
import cv2
# coding: utf-8
# Folder名に日本語文字を入れたらダメでした。


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

        self.master.geometry("800x600")
        self.master.title("Tkinter with Class Template")

        self.create_widgets()



    def create_widgets(self):
        #Canvas
        self.canvas1 = tk.Canvas(self)
        self.canvas1.configure(width=640, height=480, bg='orange')
        self.canvas1.create_rectangle(0,0,120, 70, fill='green')
        self.canvas1.grid(column=1, row=0)
        self.canvas1.grid(padx=20, pady=20)

        #Frame
        self.frame_button = ttk.LabelFrame(self)
        self.frame_button.configure(text=' Button Frame ')
        self.frame_button.grid(column=1,row=1)
        self.frame_button.grid(padx=20, pady=20)

        #File open and Load Image
        self.button_open = ttk.Button(self.frame_button)
        self.button_open.configure(text = 'Load Image')
        self.button_open.grid(column=0, row=1)
        self.button_open.configure(command=self.loadImage)

        # Clear Button
        self.button_clear = ttk.Button( self.frame_button )
        self.button_clear.configure( text='Clear Image' )
        self.button_clear.grid( column=1, row=1 )
        self.button_clear.configure(command=self.clearImage)

        # Quit Button
        self.button_quit = ttk.Button( self.frame_button )
        self.button_quit.config( text='Quit' )
        self.button_quit.grid( column=2, row=1 )
        self.button_quit.configure(command = self.quit_app)

    # Event Call Back
    def loadImage(self):

        #self.folder_name = filedialog.askdirectory()
        self.filename = filedialog.askopenfilename()
        #print(self.folder_name)
        print(self.filename)

        self.image_bgr = cv2.imread(self.filename)
        self.height, self.width = self.image_bgr.shape[:2]
        print(self.height, self.width)
        if self.width > self.height:
            self.new_size = (640,480)
        else:
            self.new_size = (480,480)

        self.image_bgr_resize = cv2.resize(self.image_bgr, self.new_size, interpolation=cv2.INTER_AREA)
        self.image_rgb = cv2.cvtColor( self.image_bgr_resize, cv2.COLOR_BGR2RGB )  # imreadはBGRなのでRGBに変換

       # self.image_rgb = cv2.cvtColor(self.image_bgr, cv2.COLOR_BGR2RGB) # imreadはBGRなのでRGBに変換
        self.image_PIL = Image.fromarray(self.image_rgb) # RGBからPILフォーマットへ変換
        self.image_tk = ImageTk.PhotoImage(self.image_PIL) # ImageTkフォーマットへ変換
        self.canvas1.create_image(320,240, image=self.image_tk)


    def clearImage(self):
        self.canvas1.delete("all")

    def quit_app(self):
        self.Msgbox = tk.messagebox.askquestion("Exit Applictaion", "Are you sure?", icon="warning")
        if self.Msgbox == "yes":
            self.master.destroy()
        else:
            tk.messagebox.showinfo("Return", "you will now return to application screen")

def main():
    root = tk.Tk()
    app = Application(master=root)#Inherit
    app.mainloop()

if __name__ == "__main__":
    main()

7.参考資料

1.Tkinterのクラス化のテンプレート
2.【Python】Tkinterのcanvasを使ってみる
2.Tkinterで作成したウインドウにOpenCV-Pythonの画像を表示

12
11
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11