文字ハイライトツール『ハイライトちゃん』 最新版ソースコード

以下は『ハイライトちゃん』の最新版ソースコード全文です:

 
        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()
               
    

📦 ダウンロード 🏠 ホームへ