Python自制文本编辑器

import time
import tkinter as tk
from tkinter import filedialog, ttk, messagebox, Text, Menu


class NotepadApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Allay Text")

        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(expand=True, fill="both")

        self.create_menu()
        self.create_new_tab()
        self.text_area = None
        root.protocol("WM_DELETE_WINDOW", self.exit_app)


    def create_menu(self):
        menubar = tk.Menu(self.root)
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="New", command=self.create_new_tab, accelerator="Ctrl+N")
        file_menu.add_command(label="Open", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="Save", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="Save All", command=self.save_all_files)
        file_menu.add_command(label="Save As", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        file_menu.add_command(label="Close", command=self.close_tab)
        file_menu.add_separator()
        file_menu.add_command(label="Statistics", command=self.show_statistics)
        file_menu.add_separator()
        file_menu.add_command(label="Exit", command=self.exit_app)

        # 创建 "Edit" 菜单
        edit_menu = Menu(menubar, tearoff=0)
        edit_menu.add_command(label="Undo", command=self.undo_action, accelerator="Ctrl+Z")
        edit_menu.add_command(label="Redo", command=self.redo_action, accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="Cut", command=self.cut_action, accelerator="Ctrl+X")
        edit_menu.add_command(label="Copy", command=self.copy_action, accelerator="Ctrl+C")
        edit_menu.add_command(label="Paste", command=self.paste_action, accelerator="Ctrl+V")
        edit_menu.add_separator()
        edit_menu.add_command(label="Local Time", command=self.insert_local_time)

        # 创建 "Selection" 菜单
        selection_menu = Menu(menubar, tearoff=0)
        selection_menu.add_command(label="Select All", command=self.select_all, accelerator="Ctrl+A")
        selection_menu.add_command(label="Select Line", command=self.select_line, accelerator="Ctrl+L")
        selection_menu.add_separator()
        selection_menu.add_command(label="Move Cursor to Line Start", command=self.move_cursor_to_line_start)
        selection_menu.add_command(label="Move Cursor to Line End", command=self.move_cursor_to_line_end)
        selection_menu.add_command(label="Move Cursor to Text Start", command=self.move_cursor_to_text_start)
        selection_menu.add_command(label="Move Cursor to Text End", command=self.move_cursor_to_text_end)

        help_menu = Menu(menubar, tearoff=0)
        help_menu.add_command(label="GitHub")
        help_menu.add_command(label="About")

        menubar.add_cascade(label="File", menu=file_menu)
        menubar.add_cascade(label="Edit", menu=edit_menu)
        menubar.add_cascade(label="Selection", menu=selection_menu)
        menubar.add_cascade(label="Help", menu=help_menu)

        self.root.config(menu=menubar)

    def create_new_tab(self, file_path=None):
        text_area = tk.Text(self.notebook, font=("Arial", 11))
        self.text_area = text_area
        tab_name = "Untitled"
        if file_path:
            with open(file_path, "r", encoding="utf-8") as file:
                content = file.read()
            tab_name = file_path.split("/")[-1]
            text_area.insert("1.0", content)
            text_area.file_path = file_path  # associate file path with text area
        self.notebook.add(text_area, text=tab_name)

    def open_file(self, event=None):
        file_path = filedialog.askopenfilename()
        try:
            with open(file_path, 'r') as file:
                self.text.delete(1.0, tk.END)
                self.text.insert(1.0, file.read())
                self.update_title(file_path)
                self.current_file = file_path
        except Exception as e:  # 捕获所有异常
            messagebox.showerror("Error", f"Failed to open file: {str(e)}")

    def save_file(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        file_content = text_widget.get("1.0", "end-1c")
        file_path = getattr(text_widget, "file_path", None)  # retrieve associated file path

        if file_path:
            with open(file_path, "w", encoding="utf-8") as file:
                file.write(file_content)

    def get_file_path_by_tab_name(self, tab_name):
        for i in range(self.notebook.index("end")):
            if self.notebook.tab(i, "text") == tab_name:
                text_widget = self.root.nametowidget(self.notebook.tabs()[i])
                return getattr(text_widget, "file_path", None)
        return None

    def save_all_files(self):
        for tab_id in self.notebook.tabs():
            text_widget = self.root.nametowidget(tab_id)
            file_content = text_widget.get("1.0", "end-1c")
            file_path = getattr(text_widget, "file_path", None)

            if file_path:
                with open(file_path, "w", encoding="utf-8") as file:
                    file.write(file_content)
            else:
                self.save_as_file()

    def save_as_file(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        file_content = text_widget.get("1.0", "end-1c")
        file_path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                 filetypes=[("Text files", "*.txt")])
        if file_path:
            with open(file_path, "w", encoding="utf-8") as file:
                file.write(file_content)

    def exit_app(self):
        unsaved_files = []
        for tab_id in self.notebook.tabs():
            text_widget = self.root.nametowidget(tab_id)
            file_content = text_widget.get("1.0", "end-1c")
            file_path = getattr(text_widget, "file_path", None)

            if file_path and file_content.strip():  # Check if file is modified and not empty
                with open(file_path, "r", encoding="utf-8") as file:
                    original_content = file.read()
                    if file_content != original_content:
                        unsaved_files.append(file_path)
            elif not file_path and file_content.strip():  # Check if it's a new file with content
                unsaved_files.append("Untitled")

        if unsaved_files:
            message = "以下文件未保存:\n\n"
            for file in unsaved_files:
                message += f"{file}\n"
            message += "\n 你想保存吗?"

            user_choice = messagebox.askyesnocancel("未保存的更改", message, icon="warning")  # Updated messagebox
            if user_choice is not None:  # Cancel button returns None
                if user_choice:  # Save all files
                    self.save_all_files()
                self.root.destroy()  # Close the application if user clicks Yes or No
        else:
            self.root.destroy()  # Close the application

    def close_tab(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        file_content = text_widget.get("1.0", "end-1c")
        file_path = getattr(text_widget, "file_path", None)

        if file_path:  # Check if the tab corresponds to an existing file
            with open(file_path, "r", encoding="utf-8") as file:
                original_content = file.read()
                if file_content != original_content:  # If content has been modified
                    user_choice = messagebox.askyesnocancel("未保存的更改", "文件未保存,你想保存吗?", icon="warning")
                    if user_choice is not None:
                        if user_choice:  # Save and Close
                            self.save_file()  # Save the file
                        else:  # Don't save, just close
                            self.notebook.forget(current_tab)  # Close the tab without saving
                else:  # No unsaved changes, close the tab
                    self.notebook.forget(current_tab)
        else:  # For new untitled files
            if file_content.strip():  # Check for unsaved new file
                user_choice = messagebox.askyesnocancel("未保存的更改", "新建文件未保存,你想保存吗?", icon="warning")
                if user_choice is not None:
                    if user_choice:  # Save and Close
                        self.save_as_file()  # Save the new file
                    else:  # Don't save, just close
                        self.notebook.forget(current_tab)  # Close the tab without saving
            else:  # No unsaved changes, close the tab
                self.notebook.forget(current_tab)

        if len(self.notebook.tabs()) == 0:  # If this is the last tab being closed
            self.add_new_tab()  # Add a new tab with Untitled file

    def add_new_tab(self):
        # 记录当前窗口的大小
        window_width = self.root.winfo_width()
        window_height = self.root.winfo_height()

        new_tab = Text(self.root)
        self.notebook.add(new_tab, text="Untitled")
        self.notebook.select(new_tab)

        # 恢复窗口的大小
        self.root.geometry(f"{window_width}x{window_height}")

    def undo_action(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.event_generate("<<Undo>>")

    def redo_action(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.event_generate("<<Redo>>")

    def cut_action(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.event_generate("<<Cut>>")

    def copy_action(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.event_generate("<<Copy>>")

    def paste_action(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.event_generate("<<Paste>>")

    def select_all(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.tag_add("sel", "1.0", "end")  # 选中整个文本框内容

    def select_line(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        current_line = text_widget.index("insert").split(".")[0]  # 获取当前光标所在行号
        start_index = f"{current_line}.0"
        end_index = f"{current_line}.end"
        text_widget.tag_remove("sel", "1.0", "end")  # 先移除所有选择
        text_widget.tag_add("sel", start_index, end_index)  # 选中整行

    def move_cursor_to_line_start(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        current_line = text_widget.index("insert").split(".")[0]
        text_widget.mark_set("insert", f"{current_line}.0")  # 移动光标到行首

    def move_cursor_to_line_end(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        current_line = text_widget.index("insert").split(".")[0]
        line_end_pos = f"{current_line}.end"
        text_widget.mark_set("insert", line_end_pos)  # 移动光标到行末尾

    def move_cursor_to_text_start(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_widget.mark_set("insert", "1.0")  # 移动光标到文件开头

    def move_cursor_to_text_end(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        end_line_num = int(text_widget.index("end-1c").split(".")[0])  # 获取总行数,减去1个字符以忽略文本末尾的换行符
        line_length = len(text_widget.get(f"{end_line_num}.0", "end-1c"))  # 获取最后一行的长度
        text_widget.mark_set("insert", f"{end_line_num}.{line_length}")  # 移动光标到文件结尾的下一个位置

    def insert_local_time(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        local_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        text_widget.insert("insert", local_time)

    def show_statistics(self):
        current_tab = self.notebook.select()
        text_widget = self.root.nametowidget(current_tab)
        text_content = text_widget.get("1.0", "end-1c")
        char_count_without_spaces = len(text_content.replace(" ", ""))
        char_count_with_spaces = len(text_content)
        line_count = text_content.count('\n') + 1

        stats_window = tk.Toplevel(self.root)
        stats_window.title("Statistics")

        stats_label = tk.Label(stats_window, text=f"Character count (excluding spaces): {char_count_without_spaces}\n"
                                                   f"Character count (including spaces): {char_count_with_spaces}\n"
                                                   f"Line count: {line_count}")
        stats_label.pack()


if __name__ == "__main__":
    root = tk.Tk()
    notepad_app = NotepadApp(root)
    root.mainloop()

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值