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()
12-18
1052
12-25
09-01