以下は『ハイライトちゃん』の最新版ソースコード全文です:
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import unicodedata
import os
import csv
class HighlightApp:
def __init__(self, root):
self.root = root
self.root.title("文字ハイライトツール - ハイライトちゃん")
self.highlight_mode = tk.StringVar(value="zenkaku")
self.highlight_color = tk.StringVar(value="yellow")
self.result_text = None
self.target_folder = None
self.scan_results = []
# メニュー作成
menu = tk.Menu(self.root)
settings_menu = tk.Menu(menu, tearoff=0)
settings_menu.add_radiobutton(label="全角をハイライト", variable=self.highlight_mode, value="zenkaku")
settings_menu.add_radiobutton(label="半角をハイライト", variable=self.highlight_mode, value="hankaku")
color_menu = tk.Menu(settings_menu, tearoff=0)
color_menu.add_radiobutton(label="黄", variable=self.highlight_color, value="yellow", command=self.update_highlight_color)
color_menu.add_radiobutton(label="赤", variable=self.highlight_color, value="tomato", command=self.update_highlight_color)
color_menu.add_radiobutton(label="青", variable=self.highlight_color, value="lightblue", command=self.update_highlight_color)
settings_menu.add_cascade(label="色", menu=color_menu)
menu.add_cascade(label="設定", menu=settings_menu)
menu.add_command(label="一括全角スペース探索", command=self.open_batch_window)
self.root.config(menu=menu)
self.root.columnconfigure(0, weight=1)
self.root.columnconfigure(1, weight=1)
self.root.rowconfigure(0, weight=1)
self.text_input = scrolledtext.ScrolledText(self.root, wrap="word")
self.text_input.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
self.text_highlighted = scrolledtext.ScrolledText(self.root, wrap="word")
self.text_highlighted.grid(row=0, column=1, padx=5, pady=5, sticky="nsew")
btn_frame = tk.Frame(self.root)
btn_frame.grid(row=1, column=0, columnspan=2, pady=5)
tk.Button(btn_frame, text="表示", command=self.highlight_text).pack()
self.update_highlight_color()
# バージョン表示
version_label = tk.Label(root, text="version 2.0.1", font=("Arial", 8), fg="gray")
version_label.grid(row=6, column=0, columnspan=3, sticky="s", pady=(10, 4))
def update_highlight_color(self):
color = self.highlight_color.get()
self.text_highlighted.tag_configure("highlight", background=color)
if self.result_text:
self.result_text.tag_configure("highlight", background=color)
def highlight_text(self):
text = self.text_input.get("1.0", tk.END)
self.text_highlighted.delete("1.0", tk.END)
self.text_highlighted.insert("1.0", text)
self.text_highlighted.tag_remove("highlight", "1.0", tk.END)
checker = self.is_fullwidth if self.highlight_mode.get() == "zenkaku" else self.is_halfwidth
for idx, char in enumerate(text):
if checker(char):
self.apply_highlight(idx)
def apply_highlight(self, idx):
start = f"1.0 + {idx} chars"
end = f"1.0 + {idx + 1} chars"
self.text_highlighted.tag_add("highlight", start, end)
def is_fullwidth(self, char):
return unicodedata.east_asian_width(char) in ('F', 'W', 'A')
def is_halfwidth(self, char):
return unicodedata.east_asian_width(char) == 'Na'
def open_batch_window(self):
top = tk.Toplevel(self.root)
top.title("全角スペース一括探索")
top.geometry("600x400")
top.columnconfigure(0, weight=1)
top.rowconfigure(4, weight=1)
tk.Button(top, text="フォルダ選択", command=lambda: self.select_folder(top)).grid(row=0, column=0, sticky="w", padx=10, pady=5)
self.folder_path_var = tk.StringVar(value="(未選択)")
tk.Label(top, textvariable=self.folder_path_var, fg="gray").grid(row=1, column=0, sticky="w", padx=10)
self.include_subdirs = tk.BooleanVar()
tk.Checkbutton(top, text="サブフォルダも含める", variable=self.include_subdirs).grid(row=2, column=0, sticky="w", padx=10)
tk.Button(top, text="探索開始", command=self.start_batch_search).grid(row=3, column=0, sticky="w", padx=10, pady=5)
self.result_text = scrolledtext.ScrolledText(top, wrap="word", height=10)
self.result_text.grid(row=4, column=0, sticky="nsew", padx=10, pady=(0, 5))
self.result_text.tag_configure("highlight", background=self.highlight_color.get())
tk.Button(top, text="CSV出力", command=self.export_csv).grid(row=5, column=0, sticky="e", padx=10, pady=5)
self.batch_top = top
def select_folder(self, parent):
folder = filedialog.askdirectory(parent=parent)
if folder:
self.target_folder = folder
self.folder_path_var.set(folder)
def start_batch_search(self):
if not self.target_folder:
messagebox.showwarning("警告", "フォルダが選択されていません。")
return
result = self.scan_folder_for_zenkaku_spaces(self.target_folder, self.include_subdirs.get())
self.scan_results = result
self.display_results(result)
def scan_folder_for_zenkaku_spaces(self, folder, include_subdirs=False):
result = []
for root_dir, dirs, files in os.walk(folder):
for file in files:
filepath = os.path.join(root_dir, file)
try:
with open(filepath, encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
for lineno, line in enumerate(lines, 1):
if " " in line:
result.append((filepath, lineno, line.strip()))
except Exception:
continue
if not include_subdirs:
break
return result
def display_results(self, result):
self.result_text.delete("1.0", tk.END)
if not result:
self.result_text.insert(tk.END, "全角スペースは見つかりませんでした。\n")
return
self.result_text.tag_remove("highlight", "1.0", tk.END)
for filepath, lineno, line in result:
basename = os.path.basename(filepath)
start_index = self.result_text.index(tk.END)
display_line = f"{basename} (行{lineno}): {line}\n"
self.result_text.insert(tk.END, display_line)
offset = len(f"{basename} (行{lineno}): ")
for i, char in enumerate(line):
if char == " ":
tag_start = f"{start_index}+{offset + i}c"
self.result_text.tag_add("highlight", tag_start, f"{tag_start}+1c")
def export_csv(self):
if not self.scan_results:
messagebox.showinfo("情報", "出力するデータがありません。")
return
save_path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV files", "*.csv")])
if save_path:
try:
with open(save_path, mode="w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["ファイル名", "行番号", "行内容"])
for filepath, lineno, line in self.scan_results:
writer.writerow([os.path.basename(filepath), lineno, line])
messagebox.showinfo("完了", f"CSVに出力しました:\n{save_path}")
except Exception as e:
messagebox.showerror("エラー", f"CSV出力中にエラーが発生しました:\n{e}")
if __name__ == "__main__":
root = tk.Tk()
##タイトルバーアイコン表示用
data = '''R0lGODdhEAAQAIYAAAAAAIlpV/Tkz+vax3JVR9nJtraklY14aX9/f5NzW8q4pnlbTKSLfFVVVUs2MP///y8nG/8AADs3Mreki3hoYKaYjmtQQGlVSde6u1VFM1E+N007MsW5pTwzNPXZcjgrJ//44f//qvrZue3Sb+HPvubOtuHImOPFlunNZv+/v97Ru93Bs8S1pMOzoNq+tsivmcCsnLywpb+/v72xn7mqmLCYiayejqWRjrKbV7acW7eeP79/f6pVVZyJhZ2HeZqCTx8fH4NkUoFrK+PWv35za39/P39/AHBlYH9iUn9vSXtqXmZmM35eTnNWR2hPQ2ZMTGFIO2dMP2JLPn8/P38AAFVVAFhLRl1IPk5EOkY3NEAzIUw3Kz8yLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAEAAQAEAI8AABCASAAIAUJkEWWBDYYCCADVywbMnAQsELA0ocCnwAYAESBgYGDDhAgEAHAA0QSKBwIMEABQMKCBgSkgMAjgQBXAnAs2cCJx8KAjAC4AIBHzIHkBBQI8DCJTsA9Bjxo4IAIhqOCBAwAAKACFUgXDjhAYWHHBMmwLDRgsKHKQMLRgmQgGeCBQ6+DiwCoAlPAgwYEAhgEoCEgU8IhAQRs4CBA1lQ6nUAJcCArQICaFiwAQAVHgCE6BDI4YCVA5dpAAgBIAWAGyZwxBBQYMZWFQIqAJBhGICWJBMyGIBZQAGIAkAeOIBQYgUGDC5EaBwYEAA7'''
root.tk.call('wm', 'iconphoto', root._w, tk.PhotoImage(data=data))
####
root.geometry("900x500")
app = HighlightApp(root)
root.mainloop()