10.14 软件构造实验五 记事本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
作业五:现代化记事本软件
功能:完整的文本编辑器,包含文件操作、编辑功能、格式设置及实用辅助功能
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, colorchooser, simpledialog
import os
import sys
import re
import time
from datetime import datetime
import webbrowser

class NotepadApp:
    def __init__(self, root):
        self.root = root
        self.root.title("作业五 - 现代化记事本")
        self.root.geometry("1200x800")
        
        # 现代化配色方案
        self.colors = {
            'primary': '#2c3e50',
            'secondary': '#3498db',
            'accent': '#e74c3c',
            'success': '#27ae60',
            'warning': '#f39c12',
            'light_bg': '#ecf0f1',
            'dark_bg': '#34495e',
            'text_light': '#ffffff',
            'text_dark': '#2c3e50'
        }
        
        self.root.configure(bg=self.colors['light_bg'])
        
        # 当前文件路径
        self.current_file = None
        self.text_modified = False
        self.word_count = 0
        self.char_count = 0
        self.line_count = 1
        
        # 设置图标(如果有的话)
        try:
            self.root.iconbitmap("notepad.ico")
        except:
            pass
        
        self.setup_ui()
        self.setup_bindings()
        
    def setup_ui(self):
        """设置用户界面"""
        # 创建菜单栏
        self.create_menu_bar()
        
        # 创建工具栏
        self.create_toolbar()
        
        # 创建状态栏
        self.create_status_bar()
        
        # 创建文本编辑区域
        self.create_text_area()
        
    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="另存为", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        file_menu.add_separator()
        file_menu.add_command(label="页面设置", command=self.page_setup)
        file_menu.add_command(label="打印", command=self.print_file, accelerator="Ctrl+P")
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.exit_app)
        menubar.add_cascade(label="文件", menu=file_menu)
        
        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        edit_menu.add_command(label="撤销", command=self.undo_text, accelerator="Ctrl+Z")
        edit_menu.add_command(label="重做", command=self.redo_text, accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="剪切", command=self.cut_text, accelerator="Ctrl+X")
        edit_menu.add_command(label="复制", command=self.copy_text, accelerator="Ctrl+C")
        edit_menu.add_command(label="粘贴", command=self.paste_text, accelerator="Ctrl+V")
        edit_menu.add_command(label="删除", command=self.delete_text, accelerator="Del")
        edit_menu.add_separator()
        edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
        edit_menu.add_separator()
        edit_menu.add_command(label="查找", command=self.find_replace, accelerator="Ctrl+F")
        edit_menu.add_command(label="替换", command=self.find_replace, accelerator="Ctrl+H")
        edit_menu.add_separator()
        edit_menu.add_command(label="插入时间戳", command=self.insert_timestamp, accelerator="F5")
        edit_menu.add_command(label="插入日期", command=self.insert_date, accelerator="F6")
        menubar.add_cascade(label="编辑", menu=edit_menu)
        
        # 格式菜单
        format_menu = tk.Menu(menubar, tearoff=0)
        format_menu.add_command(label="自动换行", command=self.toggle_word_wrap)
        format_menu.add_separator()
        format_menu.add_command(label="字体", command=self.choose_font)
        format_menu.add_command(label="背景颜色", command=self.choose_bg_color)
        format_menu.add_command(label="文本颜色", command=self.choose_text_color)
        menubar.add_cascade(label="格式", menu=format_menu)
        
        # 查看菜单
        view_menu = tk.Menu(menubar, tearoff=0)
        self.zoom_var = tk.StringVar(value="100%")
        view_menu.add_command(label="放大", command=self.zoom_in, accelerator="Ctrl++")
        view_menu.add_command(label="缩小", command=self.zoom_out, accelerator="Ctrl+-")
        view_menu.add_command(label="恢复默认缩放", command=self.reset_zoom, accelerator="Ctrl+0")
        view_menu.add_separator()
        view_menu.add_command(label="自动换行", command=self.word_wrap)
        view_menu.add_separator()
        view_menu.add_command(label="状态栏", command=self.toggle_statusbar)
        menubar.add_cascade(label="查看", menu=view_menu)
        
        # 主题菜单
        theme_menu = tk.Menu(menubar, tearoff=0)
        theme_menu.add_command(label="浅色主题", command=lambda: self.change_theme('light'))
        theme_menu.add_command(label="深色主题", command=lambda: self.change_theme('dark'))
        theme_menu.add_command(label="护眼主题", command=lambda: self.change_theme('eye_care'))
        menubar.add_cascade(label="主题", menu=theme_menu)
        
        # 工具菜单
        tools_menu = tk.Menu(menubar, tearoff=0)
        tools_menu.add_command(label="备份管理器", command=self.show_backup_manager)
        tools_menu.add_command(label="文本分析", command=self.show_statistics)
        menubar.add_cascade(label="工具", menu=tools_menu)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(label="帮助", command=self.show_help)
        help_menu.add_command(label="关于", command=self.show_about)
        menubar.add_cascade(label="帮助", menu=help_menu)
        
        self.menubar = menubar
    
    def create_toolbar(self):
        """创建现代化工具栏"""
        toolbar_frame = tk.Frame(self.root, bg=self.colors['dark_bg'], relief=tk.FLAT, bd=0, height=50)
        toolbar_frame.pack(side=tk.TOP, fill=tk.X)
        toolbar_frame.pack_propagate(False)  # 保持固定高度
        
        # 工具栏按钮 - 现代化设计
        buttons = [
            ("新建", self.new_file, "📄", self.colors['secondary']),
            ("打开", self.open_file, "📁", self.colors['secondary']),
            ("保存", self.save_file, "💾", self.colors['success']),
            ("", None, "│", None),  # 分隔符
            ("剪切", self.cut_text, "✂️", self.colors['warning']),
            ("复制", self.copy_text, "📋", self.colors['warning']),
            ("粘贴", self.paste_text, "📎", self.colors['warning']),
            ("", None, "│", None),
            ("撤销", self.undo_text, "↶", self.colors['accent']),
            ("重做", self.redo_text, "↷", self.colors['accent']),
            ("", None, "│", None),
            ("查找", self.find_replace, "🔍", self.colors['primary']),
            ("替换", self.find_replace, "🔄", self.colors['primary']),
            ("", None, "│", None),
            ("统计", self.show_statistics, "📊", self.colors['success']),
            ("格式化", self.format_text, "✨", self.colors['secondary']),
        ]
        
        for text, command, icon, color in buttons:
            if text == "":  # 分隔符
                sep = tk.Label(toolbar_frame, text=icon, bg=self.colors['dark_bg'], 
                             fg=self.colors['text_light'], font=('Arial', 12))
                sep.pack(side=tk.LEFT, padx=8, pady=12)
            else:
                btn = tk.Button(toolbar_frame, 
                              text=f"{icon}", 
                              command=command, 
                              relief=tk.FLAT, 
                              bg=color,
                              fg=self.colors['text_light'],
                              font=('微软雅黑', 10, 'bold'),
                              width=4,
                              height=1,
                              cursor='hand2')
                btn.pack(side=tk.LEFT, padx=3, pady=8)
                
                # 添加工具提示
                self.create_tooltip(btn, text)
        
        # 添加搜索框
        search_frame = tk.Frame(toolbar_frame, bg=self.colors['dark_bg'])
        search_frame.pack(side=tk.RIGHT, padx=10, pady=8)
        
        self.search_var = tk.StringVar()
        search_entry = tk.Entry(search_frame, textvariable=self.search_var, 
                               width=20, font=('微软雅黑', 10),
                               bg='white', fg=self.colors['text_dark'])
        search_entry.pack(side=tk.LEFT, padx=(0, 5))
        search_entry.bind('<Return>', lambda e: self.quick_search())
        
        search_btn = tk.Button(search_frame, text="🔍", 
                             command=self.quick_search,
                             relief=tk.FLAT, bg=self.colors['secondary'],
                             fg=self.colors['text_light'], font=('微软雅黑', 10))
        search_btn.pack(side=tk.LEFT)
        
        # 为搜索按钮添加工具提示
        self.create_tooltip(search_btn, "快速搜索文本")
        
        self.toolbar_frame = toolbar_frame
    
    def create_status_bar(self):
        """创建现代化状态栏"""
        self.status_bar = tk.Frame(self.root, bg=self.colors['dark_bg'], height=25)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        self.status_bar.pack_propagate(False)
        
        # 左侧状态信息
        self.status_info = tk.Label(self.status_bar, text="就绪", 
                                   anchor=tk.W,
                                   bg=self.colors['dark_bg'], 
                                   fg=self.colors['text_light'],
                                   font=('微软雅黑', 9))
        self.status_info.pack(side=tk.LEFT, padx=10, pady=3)
        
        # 中间统计信息
        stats_frame = tk.Frame(self.status_bar, bg=self.colors['dark_bg'])
        stats_frame.pack(side=tk.LEFT, padx=20)
        
        self.word_count_label = tk.Label(stats_frame, text="字数: 0",
                                        bg=self.colors['dark_bg'],
                                        fg=self.colors['text_light'],
                                        font=('微软雅黑', 8))
        self.word_count_label.pack(side=tk.LEFT, padx=5)
        
        self.char_count_label = tk.Label(stats_frame, text="字符: 0",
                                        bg=self.colors['dark_bg'],
                                        fg=self.colors['text_light'],
                                        font=('微软雅黑', 8))
        self.char_count_label.pack(side=tk.LEFT, padx=5)
        
        self.line_count_label = tk.Label(stats_frame, text="行数: 1",
                                        bg=self.colors['dark_bg'],
                                        fg=self.colors['text_light'],
                                        font=('微软雅黑', 8))
        self.line_count_label.pack(side=tk.LEFT, padx=5)
        
        # 右侧光标位置
        self.cursor_info = tk.Label(self.status_bar, text="第1行, 第1列", 
                                   anchor=tk.E,
                                   bg=self.colors['dark_bg'],
                                   fg=self.colors['text_light'],
                                   font=('微软雅黑', 9))
        self.cursor_info.pack(side=tk.RIGHT, padx=10, pady=3)
    
    def create_text_area(self):
        """创建现代化文本编辑区域"""
        # 创建主容器
        text_container = tk.Frame(self.root, bg=self.colors['light_bg'])
        text_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 创建滚动条 - 现代化样式
        style = ttk.Style()
        style.configure("Modern.Vertical.TScrollbar", 
                       background=self.colors['secondary'],
                       troughcolor=self.colors['light_bg'],
                       bordercolor=self.colors['light_bg'],
                       arrowcolor=self.colors['text_light'],
                       gripcount=0)
        
        v_scrollbar = ttk.Scrollbar(text_container, orient=tk.VERTICAL, style="Modern.Vertical.TScrollbar")
        h_scrollbar = ttk.Scrollbar(text_container, orient=tk.HORIZONTAL, style="Modern.Vertical.TScrollbar")
        
        # 创建文本框 - 现代化设计
        self.text_area = tk.Text(text_container, 
                                 wrap=tk.WORD,
                                 yscrollcommand=v_scrollbar.set,
                                 xscrollcommand=h_scrollbar.set,
                                 font=('微软雅黑', 11),
                                 bg='#ffffff',
                                 fg=self.colors['text_dark'],
                                 selectbackground=self.colors['secondary'],
                                 selectforeground='white',
                                 insertbackground=self.colors['accent'],
                                 relief=tk.FLAT,
                                 bd=2,
                                 padx=15,
                                 pady=15,
                                 undo=True,
                                 maxundo=-1)
        
        # 配置滚动条
        v_scrollbar.config(command=self.text_area.yview)
        h_scrollbar.config(command=self.text_area.xview)
        
        # 布局 - 使用grid实现更好的控制
        self.text_area.grid(row=0, column=0, sticky='nsew')
        v_scrollbar.grid(row=0, column=1, sticky='ns')
        h_scrollbar.grid(row=1, column=0, sticky='ew')
        
        # 配置权重
        text_container.grid_rowconfigure(0, weight=1)
        text_container.grid_columnconfigure(0, weight=1)
        
        self.text_area.focus_set()
        
        # 绑定文本变化事件以更新统计信息
        self.text_area.bind('<KeyRelease>', self.update_text_stats)
        self.text_area.bind('<ButtonRelease>', self.update_text_stats)
    
    def create_tooltip(self, widget, text):
        """创建工具提示"""
        def on_enter(event):
            # 创建工具提示窗口
            x, y, _, _ = widget.bbox("insert")
            x += widget.winfo_rootx() + 25
            y += widget.winfo_rooty() + 25
            
            # 创建提示窗口
            self.tooltip = tk.Toplevel(widget)
            self.tooltip.wm_overrideredirect(True)
            self.tooltip.wm_geometry(f"+{x}+{y}")
            
            label = tk.Label(self.tooltip, text=text, 
                           bg='yellow', fg='black',
                           font=('微软雅黑', 8))
            label.pack()
        
        def on_leave(event):
            if hasattr(self, 'tooltip') and self.tooltip:
                self.tooltip.destroy()
        
        widget.bind("<Enter>", on_enter)
        widget.bind("<Leave>", on_leave)
    
    def setup_bindings(self):
        """设置键盘绑定"""
        # 文件操作
        self.root.bind('<Control-n>', lambda e: self.new_file())
        self.root.bind('<Control-o>', lambda e: self.open_file())
        self.root.bind('<Control-s>', lambda e: self.save_file())
        self.root.bind('<Control-Shift-S>', lambda e: self.save_as_file())
        self.root.bind('<Control-p>', lambda e: self.print_file())
        
        # 编辑操作
        self.root.bind('<Control-z>', lambda e: self.undo_text())
        self.root.bind('<Control-y>', lambda e: self.redo_text())
        self.root.bind('<Control-x>', lambda e: self.cut_text())
        self.root.bind('<Control-c>', lambda e: self.copy_text())
        self.root.bind('<Control-v>', lambda e: self.paste_text())
        self.root.bind('<Control-a>', lambda e: self.select_all())
        self.root.bind('<Control-f>', lambda e: self.find_text())
        self.root.bind('<Control-h>', lambda e: self.replace_text())
        self.root.bind('<F3>', lambda e: self.find_next())
        self.root.bind('<Shift-F3>', lambda e: self.find_prev())
        self.root.bind('<F5>', lambda e: self.insert_datetime())
        
        # 缩放操作
        self.root.bind('<Control-plus>', lambda e: self.zoom_in())
        self.root.bind('<Control-minus>', lambda e: self.zoom_out())
        self.root.bind('<Control-0>', lambda e: self.zoom_reset())
        
        # 文本修改检测
        self.text_area.bind('<<Modified>>', self.on_text_modified)
        self.text_area.bind('<KeyRelease>', self.update_text_stats)
        self.text_area.bind('<ButtonRelease-1>', self.update_cursor_info)
        
        # 实时统计更新
        self.text_area.bind('<KeyRelease>', self.update_text_stats)
    
    # 文件操作功能
    def new_file(self):
        """新建文件"""
        if self.text_modified:
            if not self.confirm_save():
                return
        
        self.text_area.delete(1.0, tk.END)
        self.current_file = None
        self.text_modified = False
        self.update_title()
        self.status_bar.config(text="新建文件")
    
    def open_file(self):
        """打开文件"""
        if self.text_modified:
            if not self.confirm_save():
                return
        
        file_path = filedialog.askopenfilename(
            title="打开文件",
            filetypes=[
                ("文本文件", "*.txt"),
                ("所有文件", "*.*")
            ]
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as file:
                    content = file.read()
                
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(1.0, content)
                self.current_file = file_path
                self.text_modified = False
                self.update_title()
                self.status_bar.config(text=f"已打开: {os.path.basename(file_path)}")
                
            except Exception as e:
                messagebox.showerror("错误", f"无法打开文件: {str(e)}")
    
    def save_file(self):
        """保存文件"""
        if self.current_file:
            try:
                content = self.text_area.get(1.0, tk.END)
                with open(self.current_file, 'w', encoding='utf-8') as file:
                    file.write(content)
                
                self.text_modified = False
                self.update_title()
                self.status_bar.config(text=f"已保存: {os.path.basename(self.current_file)}")
                
            except Exception as e:
                messagebox.showerror("错误", f"无法保存文件: {str(e)}")
        else:
            self.save_as_file()
    
    def save_as_file(self):
        """另存为文件"""
        file_path = filedialog.asksaveasfilename(
            title="另存为",
            defaultextension=".txt",
            filetypes=[
                ("文本文件", "*.txt"),
                ("所有文件", "*.*")
            ]
        )
        
        if file_path:
            try:
                content = self.text_area.get(1.0, tk.END)
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write(content)
                
                self.current_file = file_path
                self.text_modified = False
                self.update_title()
                self.status_bar.config(text=f"已保存: {os.path.basename(file_path)}")
                
            except Exception as e:
                messagebox.showerror("错误", f"无法保存文件: {str(e)}")
    
    def confirm_save(self):
        """确认是否保存修改"""
        result = messagebox.askyesnocancel(
            "记事本",
            "文件已修改,是否保存更改?"
        )
        
        if result is None:  # 取消
            return False
        elif result:  # 是
            self.save_file()
            return not self.text_modified
        else:  # 否
            return True
    
    def page_setup(self):
        """页面设置"""
        messagebox.showinfo("页面设置", "页面设置功能开发中...")
    
    def print_file(self):
        """打印文件"""
        messagebox.showinfo("打印", "打印功能开发中...")
    
    def exit_app(self):
        """退出应用"""
        if self.text_modified:
            if not self.confirm_save():
                return
        
        self.root.quit()
    
    # 编辑功能
    def undo_text(self):
        """撤销"""
        try:
            self.text_area.edit_undo()
        except:
            pass
    
    def redo_text(self):
        """重做"""
        try:
            self.text_area.edit_redo()
        except:
            pass
    
    def cut_text(self):
        """剪切"""
        try:
            self.text_area.event_generate("<<Cut>>")
        except:
            pass
    
    def copy_text(self):
        """复制"""
        try:
            self.text_area.event_generate("<<Copy>>")
        except:
            pass
    
    def paste_text(self):
        """粘贴"""
        try:
            self.text_area.event_generate("<<Paste>>")
        except:
            pass
    
    def delete_text(self):
        """删除"""
        try:
            self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
        except:
            pass
    
    def find_text(self):
        """查找文本"""
        self.find_dialog = tk.Toplevel(self.root)
        self.find_dialog.title("查找")
        self.find_dialog.geometry("300x150")
        self.find_dialog.transient(self.root)
        self.find_dialog.resizable(False, False)
        
        tk.Label(self.find_dialog, text="查找内容:").pack(pady=5)
        
        self.find_entry = tk.Entry(self.find_dialog, width=30)
        self.find_entry.pack(pady=5)
        self.find_entry.focus()
        
        button_frame = tk.Frame(self.find_dialog)
        button_frame.pack(pady=10)
        
        tk.Button(button_frame, text="查找下一个", command=self.find_next).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="取消", command=self.find_dialog.destroy).pack(side=tk.LEFT, padx=5)
        
        self.find_entry.bind('<Return>', lambda e: self.find_next())
    
    def find_next(self):
        """查找下一个"""
        if hasattr(self, 'find_entry') and self.find_entry.get():
            search_text = self.find_entry.get()
            content = self.text_area.get(1.0, tk.END)
            
            start_pos = self.text_area.index(tk.INSERT)
            idx = content.find(search_text, int(start_pos.split('.')[1]))
            
            if idx != -1:
                line = content.count('\n', 0, idx) + 1
                col = idx - content.rfind('\n', 0, idx)
                self.text_area.tag_remove(tk.SEL, 1.0, tk.END)
                self.text_area.tag_add(tk.SEL, f"{line}.{col}", f"{line}.{col+len(search_text)}")
                self.text_area.mark_set(tk.INSERT, f"{line}.{col+len(search_text)}")
                self.text_area.see(tk.INSERT)
            else:
                messagebox.showinfo("查找", "找不到指定的文本")
    
    def find_prev(self):
        """查找上一个"""
        if hasattr(self, 'find_entry') and self.find_entry.get():
            search_text = self.find_entry.get()
            content = self.text_area.get(1.0, tk.INSERT)
            
            idx = content.rfind(search_text)
            
            if idx != -1:
                line = content.count('\n', 0, idx) + 1
                col = idx - content.rfind('\n', 0, idx)
                self.text_area.tag_remove(tk.SEL, 1.0, tk.END)
                self.text_area.tag_add(tk.SEL, f"{line}.{col}", f"{line}.{col+len(search_text)}")
                self.text_area.mark_set(tk.INSERT, f"{line}.{col}")
                self.text_area.see(tk.INSERT)
            else:
                messagebox.showinfo("查找", "找不到指定的文本")
    
    def replace_text(self):
        """替换文本"""
        self.replace_dialog = tk.Toplevel(self.root)
        self.replace_dialog.title("替换")
        self.replace_dialog.geometry("350x200")
        self.replace_dialog.transient(self.root)
        self.replace_dialog.resizable(False, False)
        
        tk.Label(self.replace_dialog, text="查找内容:").pack(pady=5)
        self.replace_find_entry = tk.Entry(self.replace_dialog, width=30)
        self.replace_find_entry.pack(pady=5)
        
        tk.Label(self.replace_dialog, text="替换为:").pack(pady=5)
        self.replace_with_entry = tk.Entry(self.replace_dialog, width=30)
        self.replace_with_entry.pack(pady=5)
        
        button_frame = tk.Frame(self.replace_dialog)
        button_frame.pack(pady=10)
        
        tk.Button(button_frame, text="查找下一个", command=self.replace_find_next).pack(side=tk.LEFT, padx=2)
        tk.Button(button_frame, text="替换", command=self.replace_current).pack(side=tk.LEFT, padx=2)
        tk.Button(button_frame, text="全部替换", command=self.replace_all).pack(side=tk.LEFT, padx=2)
        tk.Button(button_frame, text="取消", command=self.replace_dialog.destroy).pack(side=tk.LEFT, padx=2)
    
    def replace_find_next(self):
        """替换查找下一个"""
        self.find_next()
    
    def replace_current(self):
        """替换当前"""
        if hasattr(self, 'replace_find_entry') and self.replace_find_entry.get():
            if self.text_area.tag_ranges(tk.SEL):
                selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
                if selected_text == self.replace_find_entry.get():
                    self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                    self.text_area.insert(tk.SEL_FIRST, self.replace_with_entry.get())
            self.find_next()
    
    def replace_all(self):
        """全部替换"""
        if hasattr(self, 'replace_find_entry') and self.replace_find_entry.get():
            content = self.text_area.get(1.0, tk.END)
            new_content = content.replace(self.replace_find_entry.get(), self.replace_with_entry.get())
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, new_content)
    
    def select_all(self):
        """全选"""
        self.text_area.tag_add(tk.SEL, 1.0, tk.END)
        self.text_area.mark_set(tk.INSERT, 1.0)
        self.text_area.see(tk.INSERT)
    
    def insert_datetime(self):
        """插入日期时间"""
        from datetime import datetime
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.text_area.insert(tk.INSERT, now)
    
    # 格式功能
    def toggle_word_wrap(self):
        """切换自动换行"""
        current_wrap = self.text_area.cget("wrap")
        if current_wrap == tk.WORD:
            self.text_area.config(wrap=tk.NONE)
            self.status_bar.config(text="自动换行: 关闭")
        else:
            self.text_area.config(wrap=tk.WORD)
            self.status_bar.config(text="自动换行: 开启")
    
    def choose_font(self):
        """选择字体"""
        font_dialog = tk.Toplevel(self.root)
        font_dialog.title("字体")
        font_dialog.geometry("400x300")
        font_dialog.transient(self.root)
        
        # 字体选择
        tk.Label(font_dialog, text="字体:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        font_family = ttk.Combobox(font_dialog, values=['微软雅黑', '宋体', '黑体', '楷体', 'Arial', 'Times New Roman'])
        font_family.set('微软雅黑')
        font_family.grid(row=0, column=1, padx=5, pady=5)
        
        # 字号选择
        tk.Label(font_dialog, text="大小:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        font_size = ttk.Combobox(font_dialog, values=['8', '9', '10', '11', '12', '14', '16', '18', '20', '24'])
        font_size.set('12')
        font_size.grid(row=1, column=1, padx=5, pady=5)
        
        # 样式选择
        tk.Label(font_dialog, text="样式:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W)
        font_style = ttk.Combobox(font_dialog, values=['常规', '粗体', '斜体', '粗斜体'])
        font_style.set('常规')
        font_style.grid(row=2, column=1, padx=5, pady=5)
        
        # 示例文本
        sample_text = tk.Text(font_dialog, height=5, width=30)
        sample_text.insert(1.0, "这是一段示例文本")
        sample_text.grid(row=3, column=0, columnspan=2, padx=5, pady=10)
        
        def apply_font():
            family = font_family.get()
            size = font_size.get()
            style = font_style.get()
            
            weight = 'normal'
            slant = 'roman'
            
            if '粗体' in style:
                weight = 'bold'
            if '斜体' in style:
                slant = 'italic'
            
            new_font = (family, size, weight, slant)
            self.text_area.config(font=new_font)
            sample_text.config(font=new_font)
        
        def ok_clicked():
            apply_font()
            font_dialog.destroy()
        
        button_frame = tk.Frame(font_dialog)
        button_frame.grid(row=4, column=0, columnspan=2, pady=10)
        
        tk.Button(button_frame, text="确定", command=ok_clicked).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="取消", command=font_dialog.destroy).pack(side=tk.LEFT, padx=5)
        
        # 绑定事件
        font_family.bind('<<ComboboxSelected>>', lambda e: apply_font())
        font_size.bind('<<ComboboxSelected>>', lambda e: apply_font())
        font_style.bind('<<ComboboxSelected>>', lambda e: apply_font())
    
    def choose_bg_color(self):
        """选择背景颜色"""
        color = colorchooser.askcolor(title="选择背景颜色")
        if color[1]:
            self.text_area.config(bg=color[1])
    
    def choose_text_color(self):
        """选择文本颜色"""
        color = colorchooser.askcolor(title="选择文本颜色")
        if color[1]:
            self.text_area.config(fg=color[1])
    
    # 查看功能
    def zoom_in(self):
        """放大"""
        current_font = self.text_area.cget("font")
        size = int(current_font[1])
        if size < 72:
            self.text_area.config(font=(current_font[0], size + 2))
            self.update_zoom_info(size + 2)
    
    def zoom_out(self):
        """缩小"""
        current_font = self.text_area.cget("font")
        size = int(current_font[1])
        if size > 6:
            self.text_area.config(font=(current_font[0], size - 2))
            self.update_zoom_info(size - 2)
    
    def zoom_reset(self):
        """重置缩放"""
        self.text_area.config(font=('微软雅黑', 12))
        self.update_zoom_info(12)
    
    def update_zoom_info(self, size):
        """更新缩放信息"""
        zoom_percent = int((size / 12) * 100)
        self.zoom_var.set(f"{zoom_percent}%")
        self.status_bar.config(text=f"缩放: {zoom_percent}%")
    
    def toggle_statusbar(self):
        """切换状态栏显示"""
        if self.status_bar.winfo_ismapped():
            self.status_bar.pack_forget()
        else:
            self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    # 帮助功能
    def show_help(self):
        """显示帮助"""
        help_text = """作业五 - 记事本软件使用说明

文件操作:
- 新建: Ctrl+N
- 打开: Ctrl+O  
- 保存: Ctrl+S
- 另存为: Ctrl+Shift+S
- 打印: Ctrl+P

编辑操作:
- 撤销: Ctrl+Z
- 重做: Ctrl+Y
- 剪切: Ctrl+X
- 复制: Ctrl+C
- 粘贴: Ctrl+V
- 全选: Ctrl+A
- 查找: Ctrl+F
- 替换: Ctrl+H
- 插入时间: F5

格式设置:
- 自动换行
- 字体设置
- 颜色设置

查看功能:
- 放大: Ctrl++
- 缩小: Ctrl+-
- 重置缩放: Ctrl+0"""
        
        help_window = tk.Toplevel(self.root)
        help_window.title("记事本帮助")
        help_window.geometry("500x400")
        
        text_area = tk.Text(help_window, wrap=tk.WORD, font=('微软雅黑', 10))
        text_area.insert(1.0, help_text)
        text_area.config(state=tk.DISABLED)
        
        scrollbar = ttk.Scrollbar(help_window, orient=tk.VERTICAL, command=text_area.yview)
        text_area.config(yscrollcommand=scrollbar.set)
        
        text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    
    def show_about(self):
        """显示关于信息"""
        about_text = """作业五 - 记事本软件

版本: 1.0
作者: 软件构造作业
功能: 完整的文本编辑器

包含文件操作、编辑功能、格式设置等完整功能。"""
        
        messagebox.showinfo("关于记事本", about_text)
    
    # 辅助功能
    def update_title(self):
        """更新窗口标题"""
        if self.current_file:
            title = f"作业五 - 记事本软件 - {os.path.basename(self.current_file)}"
            if self.text_modified:
                title += " *"
        else:
            title = "作业五 - 记事本软件 - 无标题"
            if self.text_modified:
                title += " *"
        
        self.root.title(title)
    
    def on_text_modified(self, event=None):
        """文本修改事件"""
        self.text_modified = True
        self.update_title()
        self.text_area.edit_modified(False)
    
    def update_cursor_info(self, event=None):
        """更新光标位置信息"""
        cursor_pos = self.text_area.index(tk.INSERT)
        line, col = cursor_pos.split('.')
        self.cursor_info.config(text=f"第{line}行, 第{int(col)+1}列")
    
    def update_text_stats(self, event=None):
        """更新文本统计信息 - 改进版,准确识别中文字符"""
        content = self.text_area.get(1.0, tk.END)
        
        # 字符数(包括空格)
        self.char_count = len(content) - 1  # 减去末尾的换行符
        
        # 改进的字数统计:准确识别中文字符
        # 中文:每个汉字算一个字
        # 英文:按单词统计
        chinese_chars = re.findall(r'[\u4e00-\u9fff]', content)
        english_words = re.findall(r'[a-zA-Z]+', content)
        
        # 中文字数 + 英文单词数
        self.word_count = len(chinese_chars) + len(english_words)
        
        # 行数
        self.line_count = int(self.text_area.index('end-1c').split('.')[0])
        
        # 段落数(以空行分隔)
        paragraphs = re.split(r'\n\s*\n', content.strip())
        self.paragraph_count = len(paragraphs) if content.strip() else 0
        
        # 空格数
        self.space_count = content.count(' ')
        
        # 更新显示
        self.word_count_label.config(text=f"字数: {self.word_count}")
        self.char_count_label.config(text=f"字符: {self.char_count}")
        self.line_count_label.config(text=f"行数: {self.line_count}")
        
        # 更新光标位置
        self.update_cursor_info()
    
    def quick_search(self):
        """快速搜索功能"""
        search_text = self.search_var.get()
        if search_text:
            content = self.text_area.get(1.0, tk.END)
            
            # 清除之前的搜索结果
            self.text_area.tag_remove("search", 1.0, tk.END)
            
            # 高亮所有匹配项
            start_idx = "1.0"
            while True:
                start_idx = self.text_area.search(search_text, start_idx, tk.END)
                if not start_idx:
                    break
                end_idx = f"{start_idx}+{len(search_text)}c"
                self.text_area.tag_add("search", start_idx, end_idx)
                start_idx = end_idx
            
            # 设置高亮样式
            self.text_area.tag_config("search", background="yellow", foreground="black")
            
            # 跳转到第一个匹配项
            first_match = self.text_area.search(search_text, "1.0", tk.END)
            if first_match:
                self.text_area.see(first_match)
                self.text_area.mark_set(tk.INSERT, first_match)
    
    def show_statistics(self):
        """显示详细统计信息 - 增强版,包含文本分析"""
        content = self.text_area.get(1.0, tk.END)
        
        # 计算各种统计信息
        char_count = len(content) - 1
        
        # 改进的字数统计
        chinese_chars = re.findall(r'[\u4e00-\u9fff]', content)
        english_words = re.findall(r'[a-zA-Z]+', content)
        word_count = len(chinese_chars) + len(english_words)
        
        line_count = int(self.text_area.index('end-1c').split('.')[0])
        
        # 段落数(以空行分隔)
        paragraphs = re.split(r'\n\s*\n', content.strip())
        paragraph_count = len(paragraphs) if content.strip() else 0
        
        # 空格数
        space_count = content.count(' ')
        
        # 文本分析
        # 中文字符数
        chinese_count = len(chinese_chars)
        # 英文字符数
        english_chars = re.findall(r'[a-zA-Z]', content)
        english_char_count = len(english_chars)
        # 数字字符数
        digit_count = len(re.findall(r'\d', content))
        # 标点符号数
        punctuation_count = len(re.findall(r'[!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~]', content))
        
        # 创建统计对话框
        stats_window = tk.Toplevel(self.root)
        stats_window.title("文本统计与分析")
        stats_window.geometry("400x450")
        stats_window.resizable(False, False)
        
        # 居中显示
        stats_window.transient(self.root)
        stats_window.grab_set()
        
        # 创建滚动框架
        canvas = tk.Canvas(stats_window)
        scrollbar = ttk.Scrollbar(stats_window, orient="vertical", command=canvas.yview)
        scrollable_frame = tk.Frame(canvas)
        
        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )
        
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 统计信息框架
        stats_frame = tk.Frame(scrollable_frame, padx=20, pady=20)
        stats_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = tk.Label(stats_frame, text="📊 文本统计与分析", 
                              font=('微软雅黑', 16, 'bold'),
                              fg=self.colors['primary'])
        title_label.pack(pady=(0, 20))
        
        # 基础统计
        basic_frame = tk.LabelFrame(stats_frame, text="基础统计", font=('微软雅黑', 12, 'bold'))
        basic_frame.pack(fill=tk.X, pady=(0, 15))
        
        basic_items = [
            ("📝 字符数(含空格)", f"{char_count}"),
            ("🔤 字数", f"{word_count}"),
            ("📄 行数", f"{line_count}"),
            ("📑 段落数", f"{paragraph_count}"),
            ("␣ 空格数", f"{space_count}"),
        ]
        
        for label, value in basic_items:
            item_frame = tk.Frame(basic_frame)
            item_frame.pack(fill=tk.X, pady=3)
            
            tk.Label(item_frame, text=label, font=('微软雅黑', 10)).pack(side=tk.LEFT)
            tk.Label(item_frame, text=value, font=('微软雅黑', 10, 'bold'),
                    fg=self.colors['secondary']).pack(side=tk.RIGHT)
        
        # 字符分析
        char_frame = tk.LabelFrame(stats_frame, text="字符分析", font=('微软雅黑', 12, 'bold'))
        char_frame.pack(fill=tk.X, pady=(0, 15))
        
        char_items = [
            ("🔡 中文字符", f"{chinese_count}"),
            ("🔠 英文字符", f"{english_char_count}"),
            ("🔢 数字字符", f"{digit_count}"),
            ("❗ 标点符号", f"{punctuation_count}"),
        ]
        
        for label, value in char_items:
            item_frame = tk.Frame(char_frame)
            item_frame.pack(fill=tk.X, pady=3)
            
            tk.Label(item_frame, text=label, font=('微软雅黑', 10)).pack(side=tk.LEFT)
            tk.Label(item_frame, text=value, font=('微软雅黑', 10, 'bold'),
                    fg=self.colors['secondary']).pack(side=tk.RIGHT)
        
        # 文本分析建议
        if char_count > 0:
            analysis_frame = tk.LabelFrame(stats_frame, text="分析建议", font=('微软雅黑', 12, 'bold'))
            analysis_frame.pack(fill=tk.X, pady=(0, 15))
            
            # 计算比例
            chinese_ratio = chinese_count / char_count * 100
            english_ratio = english_char_count / char_count * 100
            
            suggestions = []
            if chinese_ratio > 70:
                suggestions.append("📖 主要使用中文")
            elif english_ratio > 70:
                suggestions.append("📖 主要使用英文")
            else:
                suggestions.append("📖 中英文混合")
            
            if line_count > 50:
                suggestions.append("📄 文档较长")
            elif line_count < 10:
                suggestions.append("📄 文档较短")
            
            if paragraph_count > 10:
                suggestions.append("📑 段落结构清晰")
            
            for suggestion in suggestions:
                tk.Label(analysis_frame, text=suggestion, font=('微软雅黑', 10),
                        fg=self.colors['success']).pack(anchor=tk.W, pady=2)
        
        # 关闭按钮
        close_btn = tk.Button(stats_frame, text="关闭", 
                            command=stats_window.destroy,
                            bg=self.colors['secondary'],
                            fg='white',
                            font=('微软雅黑', 10),
                            width=10)
        close_btn.pack(pady=(15, 0))
        
        # 布局滚动框架
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
    
    def find_replace(self):
        """查找和替换功能"""
        # 创建查找替换对话框
        find_window = tk.Toplevel(self.root)
        find_window.title("查找和替换")
        find_window.geometry("400x200")
        find_window.resizable(False, False)
        
        find_window.transient(self.root)
        find_window.grab_set()
        
        # 主框架
        main_frame = tk.Frame(find_window, padx=20, pady=20)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 查找部分
        tk.Label(main_frame, text="查找内容:", font=('微软雅黑', 10)).grid(row=0, column=0, sticky=tk.W, pady=5)
        find_var = tk.StringVar()
        find_entry = tk.Entry(main_frame, textvariable=find_var, width=30, font=('微软雅黑', 10))
        find_entry.grid(row=0, column=1, pady=5, padx=5)
        
        # 替换部分
        tk.Label(main_frame, text="替换为:", font=('微软雅黑', 10)).grid(row=1, column=0, sticky=tk.W, pady=5)
        replace_var = tk.StringVar()
        replace_entry = tk.Entry(main_frame, textvariable=replace_var, width=30, font=('微软雅黑', 10))
        replace_entry.grid(row=1, column=1, pady=5, padx=5)
        
        # 选项
        case_sensitive = tk.BooleanVar()
        case_check = tk.Checkbutton(main_frame, text="区分大小写", variable=case_sensitive,
                                   font=('微软雅黑', 9))
        case_check.grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=5)
        
        # 按钮框架
        btn_frame = tk.Frame(main_frame)
        btn_frame.grid(row=3, column=0, columnspan=2, pady=15)
        
        def find_next():
            """查找下一个匹配项"""
            search_text = find_var.get()
            if not search_text:
                return
            
            # 清除之前的搜索结果
            self.text_area.tag_remove("search", 1.0, tk.END)
            
            # 从当前光标位置开始查找
            start_idx = self.text_area.index(tk.INSERT)
            
            # 查找匹配项
            if case_sensitive.get():
                found_idx = self.text_area.search(search_text, start_idx, tk.END)
            else:
                found_idx = self.text_area.search(search_text.lower(), start_idx, tk.END, nocase=1)
            
            if found_idx:
                end_idx = f"{found_idx}+{len(search_text)}c"
                self.text_area.tag_add("search", found_idx, end_idx)
                self.text_area.tag_config("search", background="yellow", foreground="black")
                self.text_area.see(found_idx)
                self.text_area.mark_set(tk.INSERT, found_idx)
            else:
                messagebox.showinfo("查找", "未找到匹配项")
        
        def replace():
            """替换当前匹配项"""
            search_text = find_var.get()
            replace_text = replace_var.get()
            
            if not search_text:
                return
            
            # 获取当前选中的文本(应该是搜索高亮的部分)
            try:
                selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
                if selected == search_text or (not case_sensitive.get() and selected.lower() == search_text.lower()):
                    self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                    self.text_area.insert(tk.SEL_FIRST, replace_text)
                    find_next()  # 查找下一个
            except:
                messagebox.showinfo("替换", "请先使用查找功能定位到要替换的文本")
        
        def replace_all():
            """替换所有匹配项"""
            search_text = find_var.get()
            replace_text = replace_var.get()
            
            if not search_text:
                return
            
            content = self.text_area.get(1.0, tk.END)
            
            if case_sensitive.get():
                new_content = content.replace(search_text, replace_text)
            else:
                new_content = re.sub(re.escape(search_text), replace_text, content, flags=re.IGNORECASE)
            
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, new_content)
            messagebox.showinfo("替换", f"已替换所有匹配项")
        
        # 按钮
        tk.Button(btn_frame, text="查找下一个", command=find_next, 
                width=10).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="替换", command=replace, 
                width=10).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="全部替换", command=replace_all, 
                width=10).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="关闭", command=find_window.destroy, 
                width=10).pack(side=tk.LEFT, padx=5)
        
        # 焦点设置
        find_entry.focus_set()
    
    def format_text(self):
        """格式化文本功能"""
        # 获取当前选中的文本
        try:
            # 保存选择范围的起始和结束位置
            sel_start = self.text_area.index(tk.SEL_FIRST)
            sel_end = self.text_area.index(tk.SEL_LAST)
            selected_text = self.text_area.get(sel_start, sel_end)
            
            # 创建格式化选项对话框
            format_window = tk.Toplevel(self.root)
            format_window.title("文本格式化")
            format_window.geometry("400x300")
            format_window.resizable(False, False)
            
            format_window.transient(self.root)
            format_window.grab_set()
            
            # 格式化选项
            options_frame = tk.Frame(format_window, padx=20, pady=20)
            options_frame.pack(fill=tk.BOTH, expand=True)
            
            tk.Label(options_frame, text="选择格式化选项:", 
                    font=('微软雅黑', 12, 'bold')).pack(pady=(0, 15))
            
            # 格式化函数
            def to_upper():
                formatted = selected_text.upper()
                self.text_area.delete(sel_start, sel_end)
                self.text_area.insert(sel_start, formatted)
                # 重新选中格式化后的文本
                self.text_area.tag_add(tk.SEL, sel_start, f"{sel_start} + {len(formatted)}c")
                self.text_area.mark_set(tk.INSERT, sel_start)
                format_window.destroy()
            
            def to_lower():
                formatted = selected_text.lower()
                self.text_area.delete(sel_start, sel_end)
                self.text_area.insert(sel_start, formatted)
                # 重新选中格式化后的文本
                self.text_area.tag_add(tk.SEL, sel_start, f"{sel_start} + {len(formatted)}c")
                self.text_area.mark_set(tk.INSERT, sel_start)
                format_window.destroy()
            
            def capitalize_words():
                formatted = ' '.join(word.capitalize() for word in selected_text.split())
                self.text_area.delete(sel_start, sel_end)
                self.text_area.insert(sel_start, formatted)
                # 重新选中格式化后的文本
                self.text_area.tag_add(tk.SEL, sel_start, f"{sel_start} + {len(formatted)}c")
                self.text_area.mark_set(tk.INSERT, sel_start)
                format_window.destroy()
            
            def remove_extra_spaces():
                # 保留文本中的换行符,只处理连续的空格
                lines = selected_text.split('\n')
                formatted_lines = []
                for line in lines:
                    # 只替换连续的空格,保留单个空格
                    formatted_line = re.sub(r' +', ' ', line)
                    formatted_lines.append(formatted_line)
                formatted = '\n'.join(formatted_lines)
                
                self.text_area.delete(sel_start, sel_end)
                self.text_area.insert(sel_start, formatted)
                # 重新选中格式化后的文本
                self.text_area.tag_add(tk.SEL, sel_start, f"{sel_start} + {len(formatted)}c")
                self.text_area.mark_set(tk.INSERT, sel_start)
                format_window.destroy()
            
            # 按钮
            tk.Button(options_frame, text="转换为大写", command=to_upper, 
                    width=15).pack(pady=5)
            tk.Button(options_frame, text="转换为小写", command=to_lower, 
                    width=15).pack(pady=5)
            tk.Button(options_frame, text="单词首字母大写", command=capitalize_words, 
                    width=15).pack(pady=5)
            tk.Button(options_frame, text="去除多余空格", command=remove_extra_spaces, 
                    width=15).pack(pady=5)
            
            tk.Button(options_frame, text="取消", command=format_window.destroy, 
                    width=15).pack(pady=10)
            
        except:
            messagebox.showinfo("提示", "请先选择要格式化的文本")
    
    def insert_timestamp(self):
        """插入时间戳"""
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.text_area.insert(tk.INSERT, timestamp)
    
    def insert_date(self):
        """插入日期"""
        date = datetime.datetime.now().strftime("%Y年%m月%d日")
        self.text_area.insert(tk.INSERT, date)
    
    def auto_save(self):
        """自动保存功能"""
        if hasattr(self, 'current_file') and self.current_file:
            try:
                content = self.text_area.get(1.0, tk.END)
                with open(self.current_file, 'w', encoding='utf-8') as f:
                    f.write(content)
                # 在状态栏显示自动保存信息
                self.status_var.set(f"自动保存完成 - {datetime.datetime.now().strftime('%H:%M:%S')}")
                self.root.after(3000, lambda: self.status_var.set(""))  # 3秒后清除状态
            except Exception as e:
                messagebox.showerror("自动保存错误", f"自动保存失败: {str(e)}")
    
    def setup_auto_save(self):
        """设置自动保存定时器"""
        # 每5分钟自动保存一次
        self.root.after(300000, self.auto_save_timer)  # 5分钟 = 300000毫秒
        
        # 设置自动备份
        self.setup_auto_backup()
    
    def auto_save_timer(self):
        """自动保存定时器"""
        if hasattr(self, 'current_file') and self.current_file:
            self.auto_save()
        # 重新设置定时器
        self.root.after(300000, self.auto_save_timer)
    
    def setup_auto_backup(self):
        """设置自动备份功能"""
        self.auto_backup_enabled = True
        self.auto_backup_interval = 60000  # 60秒
        self.last_backup_time = time.time()
        self.backup_count = 0
        self.max_backups = 10  # 最多保留10个备份
        
        # 创建备份目录
        backup_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'backups')
        if not os.path.exists(backup_dir):
            os.makedirs(backup_dir)
        
        self.backup_dir = backup_dir
        self.auto_backup()
    
    def auto_backup(self):
        """自动备份功能"""
        if self.auto_backup_enabled and self.current_file:
            current_time = time.time()
            if current_time - self.last_backup_time >= self.auto_backup_interval / 1000:
                try:
                    content = self.text_area.get(1.0, tk.END)
                    if content.strip():  # 只在有内容时备份
                        # 生成备份文件名
                        timestamp = time.strftime("%Y%m%d_%H%M%S")
                        filename = os.path.basename(self.current_file)
                        backup_file = os.path.join(self.backup_dir, f"{filename}.backup_{timestamp}.txt")
                        
                        with open(backup_file, 'w', encoding='utf-8') as f:
                            f.write(content)
                        
                        self.backup_count += 1
                        self.last_backup_time = current_time
                        
                        # 清理旧备份
                        self.cleanup_old_backups()
                        
                        # 更新状态栏
                        self.status_var.set(f"自动备份完成 ({timestamp})")
                        self.root.after(3000, lambda: self.status_var.set(""))
                except Exception as e:
                    print(f"自动备份失败: {e}")
        
        # 继续定时器
        if self.auto_backup_enabled:
            self.root.after(self.auto_backup_interval, self.auto_backup)
    
    def cleanup_old_backups(self):
        """清理旧的备份文件"""
        try:
            backup_files = []
            for file in os.listdir(self.backup_dir):
                if file.endswith('.txt') and 'backup_' in file:
                    file_path = os.path.join(self.backup_dir, file)
                    backup_files.append((file_path, os.path.getmtime(file_path)))
            
            # 按修改时间排序
            backup_files.sort(key=lambda x: x[1])
            
            # 删除多余的备份文件
            while len(backup_files) > self.max_backups:
                oldest_file = backup_files.pop(0)[0]
                os.remove(oldest_file)
        except Exception as e:
            print(f"清理备份文件失败: {e}")
    
    def show_backup_manager(self):
        """显示备份管理器"""
        if not hasattr(self, 'backup_dir') or not os.path.exists(self.backup_dir):
            messagebox.showinfo("备份管理器", "暂无备份文件")
            return
        
        backup_window = tk.Toplevel(self.root)
        backup_window.title("备份管理器")
        backup_window.geometry("600x400")
        backup_window.resizable(True, True)
        
        # 居中显示
        backup_window.transient(self.root)
        backup_window.grab_set()
        
        # 标题
        title_label = tk.Label(backup_window, text="📂 备份管理器", 
                              font=('微软雅黑', 16, 'bold'),
                              fg=self.colors['primary'])
        title_label.pack(pady=10)
        
        # 备份文件列表框架
        list_frame = tk.Frame(backup_window)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
        
        # 列表标题
        header_frame = tk.Frame(list_frame)
        header_frame.pack(fill=tk.X)
        
        tk.Label(header_frame, text="备份文件", font=('微软雅黑', 12, 'bold')).pack(side=tk.LEFT)
        tk.Label(header_frame, text="大小", font=('微软雅黑', 12, 'bold')).pack(side=tk.RIGHT)
        
        # 备份文件列表
        listbox_frame = tk.Frame(list_frame)
        listbox_frame.pack(fill=tk.BOTH, expand=True)
        
        scrollbar = ttk.Scrollbar(listbox_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.backup_listbox = tk.Listbox(listbox_frame, yscrollcommand=scrollbar.set,
                                        font=('微软雅黑', 10), selectmode=tk.SINGLE)
        self.backup_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.backup_listbox.yview)
        
        # 填充备份文件列表
        backup_files = []
        for file in os.listdir(self.backup_dir):
            if file.endswith('.txt') and 'backup_' in file:
                file_path = os.path.join(self.backup_dir, file)
                file_size = os.path.getsize(file_path)
                backup_files.append((file, file_size, file_path))
        
        # 按文件名排序(时间戳排序)
        backup_files.sort(reverse=True)
        
        for file, size, file_path in backup_files:
            size_str = f"{size} bytes" if size < 1024 else f"{size/1024:.1f} KB"
            self.backup_listbox.insert(tk.END, f"{file} - {size_str}")
            self.backup_listbox.file_paths = getattr(self.backup_listbox, 'file_paths', [])
            self.backup_listbox.file_paths.append(file_path)
        
        # 按钮框架
        button_frame = tk.Frame(backup_window)
        button_frame.pack(pady=10)
        
        # 恢复按钮
        restore_btn = tk.Button(button_frame, text="恢复选中备份", 
                              command=self.restore_backup,
                              bg=self.colors['success'],
                              fg='white',
                              font=('微软雅黑', 10))
        restore_btn.pack(side=tk.LEFT, padx=5)
        
        # 删除按钮
        delete_btn = tk.Button(button_frame, text="删除选中备份", 
                             command=self.delete_backup,
                             bg=self.colors['danger'],
                             fg='white',
                             font=('微软雅黑', 10))
        delete_btn.pack(side=tk.LEFT, padx=5)
        
        # 关闭按钮
        close_btn = tk.Button(button_frame, text="关闭", 
                            command=backup_window.destroy,
                            bg=self.colors['secondary'],
                            fg='white',
                            font=('微软雅黑', 10))
        close_btn.pack(side=tk.LEFT, padx=5)
    
    def restore_backup(self):
        """恢复选中的备份"""
        selection = self.backup_listbox.curselection()
        if not selection:
            messagebox.showwarning("警告", "请先选择一个备份文件")
            return
        
        file_path = self.backup_listbox.file_paths[selection[0]]
        
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 确认恢复
            if messagebox.askyesno("确认恢复", "确定要恢复此备份吗?当前内容将被覆盖。"):
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(1.0, content)
                messagebox.showinfo("成功", "备份已恢复")
        except Exception as e:
            messagebox.showerror("错误", f"恢复备份失败: {e}")
    
    def delete_backup(self):
        """删除选中的备份"""
        selection = self.backup_listbox.curselection()
        if not selection:
            messagebox.showwarning("警告", "请先选择一个备份文件")
            return
        
        file_path = self.backup_listbox.file_paths[selection[0]]
        
        try:
            if messagebox.askyesno("确认删除", "确定要删除此备份文件吗?"):
                os.remove(file_path)
                self.backup_listbox.delete(selection[0])
                del self.backup_listbox.file_paths[selection[0]]
                messagebox.showinfo("成功", "备份文件已删除")
        except Exception as e:
            messagebox.showerror("错误", f"删除备份失败: {e}")
    
    def word_wrap(self):
        """切换自动换行"""
        current_wrap = self.text_area.cget("wrap")
        if current_wrap == "none":
            self.text_area.config(wrap="word")
            self.status_var.set("自动换行已开启")
        else:
            self.text_area.config(wrap="none")
            self.status_var.set("自动换行已关闭")
        self.root.after(2000, lambda: self.status_var.set(""))
    
    def zoom_in(self):
        """放大字体"""
        current_font = self.text_font
        current_size = int(current_font.split()[-1])
        new_size = min(current_size + 2, 36)  # 最大36号字体
        self.text_font = f"微软雅黑 {new_size}"
        self.text_area.config(font=(self.text_font))
        self.status_var.set(f"字体大小: {new_size}pt")
        self.root.after(2000, lambda: self.status_var.set(""))
    
    def zoom_out(self):
        """缩小字体"""
        current_font = self.text_font
        current_size = int(current_font.split()[-1])
        new_size = max(current_size - 2, 8)  # 最小8号字体
        self.text_font = f"微软雅黑 {new_size}"
        self.text_area.config(font=(self.text_font))
        self.status_var.set(f"字体大小: {new_size}pt")
        self.root.after(2000, lambda: self.status_var.set(""))
    
    def reset_zoom(self):
        """重置字体大小"""
        self.text_font = "微软雅黑 12"
        self.text_area.config(font=(self.text_font))
        self.status_var.set("字体大小已重置")
        self.root.after(2000, lambda: self.status_var.set(""))
    
    def change_theme(self, theme_name):
        """切换主题"""
        themes = {
            'light': {
                'primary': '#2c3e50',
                'secondary': '#3498db',
                'accent': '#e74c3c',
                'success': '#27ae60',
                'warning': '#f39c12',
                'light_bg': '#ecf0f1',
                'dark_bg': '#34495e',
                'text_light': '#ffffff',
                'text_dark': '#2c3e50',
                'text_area_bg': '#ffffff',
                'text_area_fg': '#2c3e50'
            },
            'dark': {
                'primary': '#3498db',
                'secondary': '#2c3e50',
                'accent': '#e74c3c',
                'success': '#27ae60',
                'warning': '#f39c12',
                'light_bg': '#2c3e50',
                'dark_bg': '#1a252f',
                'text_light': '#ffffff',
                'text_dark': '#ecf0f1',
                'text_area_bg': '#34495e',
                'text_area_fg': '#ecf0f1'
            },
            'eye_care': {
                'primary': '#2c3e50',
                'secondary': '#27ae60',
                'accent': '#e74c3c',
                'success': '#27ae60',
                'warning': '#f39c12',
                'light_bg': '#f0f8ff',
                'dark_bg': '#4682b4',
                'text_light': '#ffffff',
                'text_dark': '#2c3e50',
                'text_area_bg': '#f5f5f5',
                'text_area_fg': '#2c3e50'
            }
        }
        
        if theme_name in themes:
            self.colors = themes[theme_name]
            
            # 更新界面颜色
            self.root.configure(bg=self.colors['light_bg'])
            self.toolbar_frame.configure(bg=self.colors['dark_bg'])
            self.status_bar.configure(bg=self.colors['dark_bg'])
            
            # 更新文本区域
            self.text_area.configure(
                bg=self.colors['text_area_bg'],
                fg=self.colors['text_area_fg'],
                selectbackground=self.colors['secondary']
            )
            
            # 更新状态栏标签
            for widget in self.status_bar.winfo_children():
                if isinstance(widget, tk.Label):
                    widget.configure(bg=self.colors['dark_bg'], fg=self.colors['text_light'])
            
            # 更新工具栏按钮
            for widget in self.toolbar_frame.winfo_children():
                if isinstance(widget, tk.Button):
                    widget.configure(bg=self.colors['secondary'], fg=self.colors['text_light'])
            
            self.status_var.set(f"已切换到{theme_name}主题")
            self.root.after(2000, lambda: self.status_var.set(""))

def main():
    """主函数"""
    root = tk.Tk()
    app = NotepadApp(root)
    # 设置自动保存
    app.setup_auto_save()
    root.mainloop()

if __name__ == "__main__":
    main()
posted @ 2026-01-03 00:29  liu某人  阅读(9)  评论(0)    收藏  举报