Created
January 23, 2024 08:34
-
-
Save HiroshiOkada/3ae65f399ffbcc09ec4bf0626a19e57d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 画像の上か下にテキストを追加し、リサイズして保存するプログラム | |
# 動作確認環境 Windows 11 Personal 日本語版 | |
# Python 3.10.6 64bit | |
# このプログラムは ChatGPT 4 を使って作成したプログラムに | |
# 岡田洋が多少の改変を加えたものです。 | |
import tkinter as tk | |
from tkinter import filedialog, colorchooser | |
from PIL import Image, ImageTk, ImageDraw, ImageFont | |
import os | |
import re | |
# フォントのパス | |
FONT_PATH = "C:/Windows/Fonts/BIZ-UDGothicB.ttc" | |
class ImageEditorApp: | |
def __init__(self, root): | |
self.root = root | |
self.root.title("Image Editor") | |
self.create_menu() | |
root.grid_rowconfigure(0, weight=1) | |
root.grid_columnconfigure(0, weight=1) | |
self.canvas = tk.Canvas(root, bg="grey") | |
self.canvas.grid(row=0, column=0, sticky="nsew") | |
self.scrollbar_v = tk.Scrollbar( | |
root, orient="vertical", command=self.canvas.yview | |
) | |
self.scrollbar_v.grid(row=0, column=1, sticky="ns") | |
self.scrollbar_h = tk.Scrollbar( | |
root, orient="horizontal", command=self.canvas.xview | |
) | |
self.scrollbar_h.grid(row=1, column=0, sticky="ew") | |
self.canvas.config( | |
yscrollcommand=self.scrollbar_v.set, xscrollcommand=self.scrollbar_h.set | |
) | |
# 画像とテキストの属性 | |
self.image = None | |
self.text = "" | |
self.text_color = "black" | |
self.text_position = "bottom" | |
self.font_size = 20 | |
# テキスト入力と数値入力 | |
self.text_entry = tk.Entry(root) | |
self.text_entry.grid(row=2, column=0, sticky="ew") | |
self.text_entry.bind("<KeyRelease>", self.update_text) | |
self.text_entry.insert(0, string="input text here") | |
self.width_entry = tk.Entry(root) | |
self.width_entry.grid(row=3, column=0, sticky="ew") | |
# テキスト色選択 | |
self.color_button = tk.Button(root, text="テキスト色選択", command=self.choose_color) | |
self.color_button.grid(row=4, column=0, sticky="ew") | |
# テキスト位置選択 | |
self.position_var = tk.StringVar(value="bottom") | |
self.top_radio = tk.Radiobutton( | |
root, | |
text="上", | |
variable=self.position_var, | |
value="top", | |
command=self.update_text_position, | |
) | |
self.bottom_radio = tk.Radiobutton( | |
root, | |
text="下", | |
variable=self.position_var, | |
value="bottom", | |
command=self.update_text_position, | |
) | |
self.top_radio.grid(row=5, column=0, sticky="ew") | |
self.bottom_radio.grid(row=6, column=0, sticky="ew") | |
def create_menu(self): | |
menubar = tk.Menu(self.root) | |
self.root.config(menu=menubar) | |
file_menu = tk.Menu(menubar, tearoff=0) | |
file_menu.add_command(label="開く(O)...", command=self.open_image) | |
file_menu.add_command(label="名前をつけて保存(A)...", command=self.save_image) | |
menubar.add_cascade(label="ファイル(F)", menu=file_menu) | |
def open_image(self): | |
file_path = filedialog.askopenfilename() | |
if file_path: | |
self.image = Image.open(file_path) | |
self.resize_image_if_large() | |
self.display_image() | |
self.image_file_path = file_path | |
# 保存時の幅をテキストボックスに設定 | |
self.width_entry.delete(0, tk.END) # 既存のテキストをクリア | |
self.width_entry.insert(0, str(self.image.width)) | |
def resize_image_if_large(self): | |
max_width = 2400 | |
if self.image.width > max_width: | |
# 画像の縦横比を保持しながらリサイズ | |
ratio = max_width / self.image.width | |
new_height = int(self.image.height * ratio) | |
self.image = self.image.resize((max_width, new_height), Image.LANCZOS) | |
def choose_color(self): | |
color_code = colorchooser.askcolor(title="色を選択")[1] | |
if color_code: | |
self.text_color = color_code | |
self.update_text() | |
def update_text_position(self): | |
self.text_position = self.position_var.get() | |
self.update_text() | |
def update_text(self, event=None): | |
if self.image: | |
self.edited_image = self.image.copy() | |
draw = ImageDraw.Draw(self.edited_image) | |
width, height = self.edited_image.size | |
text = self.text_entry.get() | |
font_size = self.calculate_font_size(draw, text, width, height) | |
try: | |
font_path = FONT_PATH | |
font = ImageFont.truetype( | |
font_path, font_size, index=0 | |
) # index=0 は最初のフォントを示す | |
except IOError: | |
font = ImageFont.load_default(size=font_size) | |
bbox = draw.textbbox((0, 0), text, font=font) | |
text_width = bbox[2] - bbox[0] | |
text_height = bbox[3] - bbox[1] | |
# テキスト位置を決定 | |
if self.text_position == "top": | |
position = (width / 2 - text_width / 2, 10) | |
else: | |
position = (width / 2 - text_width / 2, height - text_height - 10) | |
draw.text(position, text, fill=self.text_color, font=font) | |
self.display_image(self.edited_image) | |
def calculate_font_size(self, draw, text, width, height): | |
font_size = 1 | |
while True: | |
try: | |
font_path = FONT_PATH | |
font = ImageFont.truetype( | |
font_path, font_size, index=0 | |
) # index=0 は最初のフォントを示す | |
except IOError: | |
font = ImageFont.load_default(size=font_size) | |
# "WW"を追加してbboxを計算するこてで、テキストの幅に少し余裕を持たせる | |
bbox = draw.textbbox((0, 0), text + "WW", font=font) | |
text_width = bbox[2] - bbox[0] | |
text_height = bbox[3] - bbox[1] | |
if text_width >= width or text_height >= height or font_size > 100: | |
break | |
font_size += 1 | |
return font_size | |
def display_image(self, image=None): | |
if image: | |
image_to_show = image | |
else: | |
image_to_show = self.image | |
self.canvas_image = ImageTk.PhotoImage(image_to_show) | |
self.canvas.create_image(0, 0, image=self.canvas_image, anchor=tk.NW) | |
# Canvasのスクロールリージョンを更新 | |
self.canvas.config(scrollregion=self.canvas.bbox("all")) | |
def save_image(self): | |
if not self.edited_image: # 編集後の画像がある場合のみ保存処理を行う | |
return | |
# 元のファイル名とテキストを取得 | |
original_file_name = os.path.splitext(os.path.basename(self.image_file_path))[0] | |
text = self.text_entry.get() | |
safe_text = re.sub(r'[<>:"/\\|?*]', "_", text) | |
default_file_name = f"{original_file_name}_{safe_text}.jpg" | |
# 保存ダイアログを開く | |
file_path = filedialog.asksaveasfilename( | |
initialfile=default_file_name, | |
filetypes=[("JPEG", "*.jpg")], | |
defaultextension=".jpg", | |
) | |
if file_path: | |
target_width = int(self.width_entry.get()) | |
ratio = target_width / self.edited_image.width | |
target_height = int(self.edited_image.height * ratio) | |
resized_image = self.edited_image.resize( | |
(target_width, target_height), Image.LANCZOS | |
) | |
resized_image.save(file_path, "JPEG") | |
root = tk.Tk() | |
app = ImageEditorApp(root) | |
root.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment