前回作成した「Python コードで画像に枠線をつける」Python コードを毎回実行するのが面倒になったので、 GUI アプリにしてみました。
前回の記事は下記からご覧ください。
Github にプロジェクトを公開しています。
よろしければご利用ください。
環境
今回の環境は下記のとおりです。
開発端末
- OS: Windows 11 Pro
- CPU: Intel Core i7-9750H
- メモリ: 64GB
- GPU: NVIDIA GeForce RTX 2070 Max-Q
Python 環境
- Python: 3.11.0
-
ライブラリ:
- pillow
- webcolors
- tkinterdnd2
- pyinstaller
コードの紹介
画像に枠線をつけるコードは前回と同じものになります。
ライブラリのインストール
ライブラリのインストールはお使いの環境にあわせて行ってください。
pip の場合
pip install pillow
pip install webcolors
pip install tkinterdnd2
pip install pyinstaller
poetry の場合
事前に pyproject.toml
の Python バージョンを修正する必要があります。
[tool.poetry.dependencies]
- python = "^=3.11"
+ python = ">=3.11,<3.13"
修正を行い、上書き保存したら、モジュールのインストールを行います。
poetry add pillow
poetry add webcolors
poetry add tkinterdnd2
poetry add pyinstaller
Poetry について
Poetry の詳細は、下記の記事を参考にしてください。
https://qiita.com/IoriGunji/items/290db948c11fdc81046a
ソースコード
Python のソースコードは下記のとおりです。
image_liner.py
については前回のコードを参考にしてください。
import sys
import re
import configparser
import tkinter as tk
import tkinter.messagebox as mb
from tkinterdnd2 import DND_FILES, TkinterDnD
from imageliner import image_liner
def main():
config = read_config()
gui(config)
# GUI ウィンドウ
def gui(config):
# ウィンドウ設定
root = TkinterDnD.Tk()
root.title('ImageLiner')
root.geometry('330x100')
default_color = root.cget("bg")
# 変数宣言
color = tk.StringVar()
color.set(config['color'])
size = tk.StringVar()
size.set(config['size'])
# ボーダー設定
border_frame = tk.Frame(root, height=80, width=150, pady=10, padx=10, relief=tk.SOLID, bg=default_color, bd=1)
border_frame.place(x=10, y=10)
border_color_label = tk.Label(border_frame, text='Color:')
border_color_label.place(x=0, y=10)
border_color_input = tk.Entry(border_frame, textvariable=color, readonlybackground='white', justify=tk.CENTER)
border_color_input.configure(validate='key', vcmd=(border_color_input.register(pre_validation_color), '%s', '%P'))
border_color_input.place(x=50, y=10, width=60, height=21)
border_size_label = tk.Label(border_frame, text='Size :')
border_size_label.place(x=0, y=35)
border_size_input = tk.Entry(border_frame, textvariable=size, readonlybackground='white', justify=tk.CENTER)
border_size_input.configure(validate='key', vcmd=(border_size_input.register(pre_validation_size), '%s', '%P'))
border_size_input.place(x=50, y=35, width=60, height=21)
border_size_unit = tk.Label(border_frame, text='px')
border_size_unit.place(x=110, y=35)
# ドロップエリア
drop_frame = tk.Frame(root, height=80, width=150, pady=10, padx=10, relief=tk.SOLID, bg=default_color, bd=1)
drop_frame.drop_target_register(DND_FILES)
drop_frame.dnd_bind('<<Drop>>', lambda e: drop(e.data, color.get(), size.get()))
drop_frame.place(x=170, y=10)
drop_label = tk.Label(drop_frame, text='* Drop image file here')
drop_label.place(x=65, y=30, anchor=tk.CENTER)
# 各種タイトル
border_label = tk.Label(root, text='Border settings')
border_label.place(x=20, y=0)
dorp_label = tk.Label(root, text='Drop files')
dorp_label.place(x=180, y=0)
# 閉じるボタンの制御
root.protocol("WM_DELETE_WINDOW", lambda: exit(color.get(), size.get()))
# GUI描画
root.mainloop()
# 画像処理
def drop(files, color, size):
try:
validation_color(color)
validation_size(size)
except Exception as e:
mb.showerror('Error', e)
return
files = dnd2_parse_files(files)
color = '#' + color
size = int(size)
for file in files:
for ext in image_liner.IMAGE_EXTS:
if re.search(f'\.{ext}$', file):
image_liner.image_edit(file, size, color)
# DnD2 ファイルパスの解析
def dnd2_parse_files(files_str):
start = 0
length = len(files_str)
files = []
while start < length:
if files_str[start] == '{':
end = files_str.find('}', start+1)
file = files_str[start+1 : end]
start = end + 2
else:
end = files_str.find(' ', start)
if end < 0:
file = files_str[start:]
start = length
else:
file = files_str[start : end]
start = end + 1
files.append(file)
return files
# 終了処理
def exit(color, size):
try:
validation_color(color)
validation_size(size)
except Exception as e:
print(e)
sys.exit()
save_config(color, size)
sys.exit()
# バリデーションチェック
def pre_validation_color(before_word, after_word):
return (len(after_word) <= 6 or len(after_word) == 0)
def pre_validation_size(before_word, after_word):
return ((after_word.isdecimal() or after_word == '') and (len(after_word) <= 4 or len(after_word) == 0))
def validation_color(color):
if len(color) != 6:
raise ValueError("Color code is not 6 digits!")
if not re.match(r'^[0-9a-fA-F]{6}$', color):
raise ValueError("Color code is invalid!")
def validation_size(size):
if not size.isdecimal():
raise ValueError("Size is not a number!")
if int(size) <= 0:
raise ValueError("Size is invalid!")
# 設定の読み込み
def read_config():
config = configparser.ConfigParser()
config.read('config.ini', encoding='utf-8')
default = config['DEFAULT']
configs = {
'color': default.get('color') if default.get('color') != None else ''
, 'size': default.get('size') if default.get('size') != None else ''
}
return configs
# 設定の保存
def save_config(color, size):
config = configparser.ConfigParser()
config.set('DEFAULT', 'color', color)
config.set('DEFAULT', 'size', size)
with open('config.ini', 'w', encoding='utf-8') as configfile:
config.write(configfile)
if __name__ == "__main__":
main()
ディレクトリ構成
ディレクトリ構成は下記のようになっています。
└ プロジェクトディレクトリ
└ imageliner
├ gui.py
└ image_liner.py
使い方
-
枠線の設定を行います。
-
Color
にカラーコードを指定します。(例:808080
) -
Size
に枠線の太さを指定します。(例:1
)
-
-
Drop files
の枠線内に加工したい画像をドラッグドロップします。 -
変換された画像は、元画像と同一ディレクトリ内の
./resized
フォルダに出力されます。
PyInstaller
PyInstaller で exe ファイルにビルドする方法を紹介します。
hook-tkinterdnd2.py のダウンロード
今回はモジュールに TkinterDnD2 を利用しているため PyInstaller を利用する場合は、 hook-tkinterdnd2.py が必要になります。
Github からダウンロードして利用してください。
hook-tkinterdnd2.py
を gui.py
や image_liner.py
のスクリプトと同一のディレクトリに保存します。
ディレクトリ構成
ディレクトリ構成は下記のようになっています。
└ プロジェクトディレクトリ
└ imageliner
├ gui.py
├ image_liner.py
└ hook-tkinterdnd2.py
PyInstaller の実行
PyInstaller で実行ファイルを作成する場合は、次のオプションを追加します。
--additional-hooks-dir [hook-tkinterdnd2.py が保存されているディレクトリのパス]
pip の場合
pyinstaller ./imageliner/gui.py --name ImageLiner --onefile --noconsole --collect-data tkinterdnd2 --additional-hooks-dir ./imageliner
Poetry の場合
poetry run pyinstaller ./imageliner/gui.py --name ImageLiner --onefile --noconsole --collect-data tkinterdnd2 --additional-hooks-dir ./imageliner
参考文献
下記のサイトを参考にさせて頂きました。