"""
tkinterを使用してグラフィカルに画像二値化を行うアプリケーションのサンプルです。
"""

import pyfie
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox


# 利便のため PyFIE 関数が返すエラーコードに応じて例外を発生させる機能を有効化
pyfie.ctrl.enable_f_err_exception(True)

# ルートウィンドウの作成と設定
root = tk.Tk()
root.grid_columnconfigure(0, weight=1)
root.minsize(400, 100)
root.resizable(True, True)

# グローバル変数
hsrc = None  # 入力画像
hdst = None  # 出力二値画像
src_photo = tk.PhotoImage()
dst_photo = tk.PhotoImage()
thresh_value = tk.IntVar(root, 128)


def update_dst():
    """出力画像の更新"""
    if hsrc is not None:
        pyfie.fnFIE_binarize(hsrc, hdst, thresh_value.get())
        dst_data = pyfie.imencode(hdst, format_ext="png")
        dst_photo.config(data=dst_data)


def on_open_src_image():
    """入力画像ファイルのオープンイベント

    ファイルダイアログで入力画像ファイルを選択させ、
    選択された入力画像ファイルを読み込み、出力画像を更新する"""
    global hsrc
    global hdst
    fname = filedialog.askopenfilename(
        title="入力画像ファイルを選択",
        filetypes=[
            ("画像ファイル", "*.bmp *.png *.jpg *.jpeg *.tif"), ("すべてのファイル", "*.*")]
    )
    if fname:
        try:
            hsrc_candidate = pyfie.imread(fname)
        except Exception as e:
            messagebox.showerror('エラー', '画像の読み込みに失敗しました。\n' + str(e))
            return

        if hsrc_candidate.f_type != pyfie.F_IMG_UC8 or hsrc_candidate.ch != 1:
            messagebox.showerror('エラー', '1チャネルの8ビット濃淡画像のみ受け付けます')
            return
        hsrc = hsrc_candidate
        hdst = hsrc.empty_like(img_type=pyfie.F_IMG_BIN)
        # tk.PhotoImage が対応する画像型は限られるため、PNG形式に再エンコードする
        src_data = pyfie.imencode(hsrc, format_ext="png")
        src_photo.config(data=src_data)
        update_dst()


def on_thresh_changed(value):
    """二値化しきい値の変更イベント"""
    update_dst()


def on_save_dst_image():
    """二値化画像の保存イベント"""
    if hdst is None:
        messagebox.showerror('エラー', '出力画像がありません。')
        return
    fname = filedialog.asksaveasfilename(
        title="名前を付けて保存",
        filetypes=[("画像ファイル", ".png .bmp .tif")],
        defaultextension="png")
    if fname != "":
        try:
            pyfie.imwrite(fname, hdst)
            messagebox.showinfo('保存成功', '二値化画像を保存しました。')
        except Exception as e:
            messagebox.showerror('エラー', '画像の保存に失敗しました。\n' + str(e))
            return


# GUI構成部品の生成と配置
frame_header = tk.Frame(root, padx=10, pady=10)
frame_header.grid(sticky="NSWE")
tk.Button(frame_header, text="入力画像を選択",
          command=on_open_src_image).grid(column=0, row=0)
tk.Button(frame_header, text="出力画像を保存",
          command=on_save_dst_image).grid(column=1, row=0)

frame_body = tk.Frame(root, padx=10, pady=10)
frame_body.grid(sticky="NSWE")
frame_body.grid_columnconfigure(0, weight=1)
frame_body.grid_columnconfigure(1, weight=1)
src_img_frame = tk.LabelFrame(frame_body, text="入力画像")
src_img_frame.grid(column=0, row=0, sticky="NSWE")
dst_img_frame = tk.LabelFrame(frame_body, text="出力画像")
dst_img_frame.grid(column=1, row=0, sticky="NSWE")
tk.Label(src_img_frame, image=src_photo).grid(column=0, row=0)
tk.Label(dst_img_frame, image=dst_photo).grid(column=0, row=0)

frame_footer = tk.Frame(root, padx=10, pady=10)
frame_footer.grid(sticky="NSWE")
tk.Label(frame_footer, text="二値化しきい値").pack(side="left")
frame_footer.grid_columnconfigure(1, weight=1)
tk.Scale(frame_footer, from_=0, to=255, variable=thresh_value, orient=tk.HORIZONTAL,
         command=on_thresh_changed).pack(side="left", fill="x", expand=True)

# メインイベントループ開始
root.mainloop()
