Python 代码:【复制】import tkinter as tk from tkinter import * from tkinter import filedialog, messagebox, END import os import sys class NotePad(tk.Tk): icons = ["new_file", "open_file", "save", "cut", "copy", "paste", "undo", "redo", "find_text"] icon_res = [] theme_color = {"Default": "#000000.#FFFFFF", "Olive Green": "#D1E7E0.#5B8340", "Night Mode": "#FFFFFF.#000000"} # 初始化操作 def __init__(self): super().__init__() self.file_name = None self.set_window() self.create_menu_bar() self.create_tool_bar() self.create_body() self.create_pop_menu() # 设置窗口界面 def set_window(self): self.title("NotePad") max_width, max_height = self.maxsize() align_center = "800x600+%d+%d" % ((max_width-800)/2, (max_height-600)/2) self.geometry(align_center) # 设置居中 self.iconbitmap("D:\notepad\img\editor.ico") # 创建菜单项目 def create_menu_bar(self): menu_bar = Menu(self) # 添加菜单项目 file_menu = Menu(menu_bar, tearoff=False) file_menu.add_command( label="新建", accelerator="Ctrl+N", command=self.new_file) file_menu.add_command( label="打开", accelerator="Ctrl+O", command=self.open_file) file_menu.add_command( label="保存", accelerator="Ctrl+S", command=self.save_file) file_menu.add_command( label="另存为", accelerator="Alt+S", command=self.save_as) file_menu.add_separator() file_menu.add_command( label="关闭", command=self.new_file) file_menu.add_command( label="退出", accelerator="Alt+E", command=self.exit_notepad) menu_bar.add_cascade(label="文件", menu=file_menu) edit_menu = Menu(menu_bar, tearoff=False) edit_menu.add_command(label="撤销", accelerator="Ctrl+Z", command=lambda: self.handle_menu_action("撤销")) edit_menu.add_command(label="恢复", accelerator="Ctrl+Y", command=lambda: self.handle_menu_action("恢复")) edit_menu.add_separator() edit_menu.add_command(label="剪切", accelerator="Ctrl+X", command=lambda: self.handle_menu_action("剪切")) edit_menu.add_command(label="复制", accelerator="Ctrl+C", command=lambda: self.handle_menu_action("复制")) edit_menu.add_command(label="粘贴", accelerator="Ctrl+V", command=lambda: self.handle_menu_action("粘贴")) edit_menu.add_separator() edit_menu.add_command(label="查找", accelerator="Ctrl+F", command=self.find_text_dialog) edit_menu.add_command(label="全选", accelerator="Ctrl+A", command=self.select_all) menu_bar.add_cascade(label="编辑", menu=edit_menu) view_menu = Menu(menu_bar, tearoff=False) # 显示行号 self.is_show_line_num = IntVar() self.is_show_line_num.set(1) view_menu.add_checkbutton(label="显示行号", onvalue=0, offvalue=1, variable=self.is_show_line_num, command=self.update_line_num) # 高亮当前行 self.is_heighlight_line = IntVar() view_menu.add_checkbutton(label="高亮当前行", variable=self.is_heighlight_line, command=self.toggle_highlight) # 主题 theme_menu = Menu(menu_bar, tearoff=False) self.theme_choice = StringVar() self.theme_choice.set("Default") for k in sorted(self.theme_color): theme_menu.add_radiobutton( label=k, variable=self.theme_choice, command=self.change_theme) view_menu.add_cascade(label="主题", menu=theme_menu) menu_bar.add_cascade(label="视图", menu=view_menu) about_menu = Menu(menu_bar, tearoff=False) about_menu.add_cascade( label="关于", command=lambda: self.show_message("关于")) about_menu.add_cascade( label="帮助", command=lambda: self.show_message("帮助")) menu_bar.add_cascade(label="关于", menu=about_menu) self['menu'] = menu_bar # 工具栏 def create_tool_bar(self): tool_bar = Frame(self, height=25, bg="#FFFFFF") # 填充x轴 tool_bar.pack(fill="x") for icon in self.icons: tool_icon = PhotoImage(file="D:\notepad\img\%s.gif" % (icon, )) tool_btn = Button(tool_bar, image=tool_icon, command=self.tool_bar_action(icon)) tool_btn.pack(side="left") # 将tool_icon添加到icon_res self.icon_res.append(tool_icon) # 界面操作主体 def create_body(self): # 左边行号,中间是文本编辑区, 右边是滚动条 # 行号区域 self.line_number_bar = Text(self, width=4, takefocus=0, border=0, background="#F0E68C", state="disabled") self.line_number_bar.pack(side="left", fill="y") # 文本编辑区 # wrap如何换行, word表示自动换行 # undo =True开启撤销功能 self.context_text = Text(self, wrap="word", undo=True) # 热键绑定 self.context_text.bind("<Control-o>", self.open_file) self.context_text.bind("<Control-O>", self.open_file) self.context_text.bind("<Control-s>", self.save_file) self.context_text.bind("<Control-S>", self.save_file) self.context_text.bind("<Control-n>", self.new_file) self.context_text.bind("<Control-N>", self.new_file) self.context_text.bind("<Alt-s>", self.save_as) self.context_text.bind("<Alt-S>", self.save_as) self.context_text.bind("<Alt-e>", self.exit_notepad) self.context_text.bind("<Alt-E>", self.exit_notepad) self.context_text.bind("<Control-F>", self.find_text_dialog) self.context_text.bind("<Control-f>", self.find_text_dialog) self.context_text.bind( "<Any-KeyPress>", lambda e: self.update_line_num()) self.context_text.pack(fill="both", expand=1) # 设置文本输入区 self.context_text.tag_config("active_line", background="#EEEEE0") # 滚动条 scroll_bar = Scrollbar(self.context_text) scroll_bar['command'] = self.context_text.yview self.context_text["yscrollcommand"] = scroll_bar.set scroll_bar.pack(side="right", fill="y") # 打开文件 def open_file(self, event=None): # 对打开文件的类型进行设置 input_file = filedialog.askopenfilename( filetypes=[("所有文件", "*.*"), ("文本文档", "*.txt")]) if input_file: self.title("{}***NotePad".format(os.path.basename(input_file))) self.file_name = input_file self.context_text.delete(1.0, END) with open(input_file, 'r') as _file: self.context_text.insert(1.0, _file.read()) # 保存文件 def write_to_file(self, file_name): try: content = self.context_text.get(1.0, END) with open(file_name, 'w') as _file: _file.write(content) self.title("{}---NotePad".format(os.path.basename(file_name))) except IOError: messagebox.showinfo("提示", "文件保存失败!") def save_file(self, event=None): if not self.file_name: self.save_as() else: self.write_to_file(self.file_name) # 新建文件 def new_file(self, event=None): self.title("新建---NotePad") self.context_text.delete(1.0, END) self.file_name = None # 另存为 def save_as(self, event=None): input_file = filedialog.asksaveasfilename( filetypes=[("所有文件", "*.*"), ("文本文档", "*.txt")], defaultextension="txt") if input_file: self.file_name = input_file self.write_to_file(self.file_name) # 退出 def exit_notepad(self, event=None): if messagebox.askokcancel("退出", "确定退出吗?"): self.destroy() # 右键弹出菜单 def create_pop_menu(self): pop_nemu = Menu(self.context_text, tearoff=False) for item1, item2 in zip(['复制', '粘贴', '剪切', '撤销', '恢复'], ['copy', 'paste', 'cut', 'undo', 'redo']): pop_nemu.add_command(label=item1, compound='left', command=self.tool_bar_action(item2)) pop_nemu.add_separator() pop_nemu.add_command(label="全选", command=self.select_all) # 绑定 self.context_text.bind("<Button-3>", lambda event: pop_nemu.tk_popup(event.x_root, event.y_root)) # 右键菜单的处理 def handle_menu_action(self, action_type): if action_type == "复制": self.context_text.event_generate("<<Copy>>") elif action_type == "粘贴": self.context_text.event_generate("<<Paste>>") elif action_type == "剪切": self.context_text.event_generate("<<Cut>>") elif action_type == "撤销": self.context_text.event_generate("<<Undo>>") elif action_type == "恢复": self.context_text.event_generate("<<Redo>>") # 防止事件传递 return "break" # 工具栏命令处理 def tool_bar_action(self, action_type): def handle(): if action_type == 'open_file': self.open_file() elif action_type == 'save': self.save_file() elif action_type == "new_file": self.new_file() elif action_type == "cut": self.handle_menu_action("剪切") elif action_type == "copy": self.handle_menu_action("复制") elif action_type == "paste": self.handle_menu_action("粘贴") elif action_type == "undo": self.handle_menu_action("撤销") elif action_type == "redo": self.handle_menu_action("恢复") elif action_type == "find_text": self.find_text_dialog() # handle返回处理 return handle # 全选 def select_all(self): self.context_text.tag_add('sel', 1.0, END) return "break" # 行号处理 def update_line_num(self): if self.is_show_line_num.get() == 0: # 获取所有行 row, col = self.context_text.index(END).split('.') # 列出每行的行号 line_num_content = "n".join([str(i) for i in range(1, int(row))]) self.line_number_bar.config(state="normal") self.line_number_bar.delete(1.0, END) self.line_number_bar.insert(1.0, line_num_content) self.line_number_bar.config(state='disabled') else: self.line_number_bar.config(state='normal') self.line_number_bar.delete(1.0, END) self.line_number_bar.config(state='disabled') # 高亮当前行 def toggle_highlight(self): if self.is_heighlight_line.get(): self.context_text.tag_remove('active_line', 1.0, END) # 设置高亮 self.context_text.tag_add( 'active_line', "insert linestart", "insert lineend+1c") # 通过轮询递归的方式进行处理 self.context_text.after(200, self.toggle_highlight) else: self.context_text.tag_remove("active_line", 1.0, END) # 文本查找功能 def find_text_dialog(self, event=None): search_dialog = Toplevel(self) search_dialog.title('查找文本') max_width, max_height = self.maxsize() align_center = "300x80+%d+%d" % ((max_width-300)/2, (max_height-80)/2) search_dialog.geometry(align_center) # 设置居中 search_dialog.resizable(False, False) Label(search_dialog, text='查找全部').grid(row=0, column=0, sticky='e') search_text = Entry(search_dialog, width=25) search_text.grid(row=0, column=1, padx=2, pady=2, sticky="we") search_text.focus_set() # 忽略大小写 ignore_case_value = IntVar() Checkbutton(search_dialog, text="忽略大小写", variable=ignore_case_value).grid( row=1, column=1, sticky='e', padx=2, pady=2) Button(search_dialog, text='查找', command=lambda: self.search_result(search_text.get(), ignore_case_value.get(), search_dialog, search_text)).grid( row=0, column=2, sticky="w"+"e", padx=2, pady=2) def close_serach_dialog(): self.context_text.tag_remove('match', 1.0, END) search_dialog.destroy() search_dialog.protocol("WM_DELETE_WINDOW", close_serach_dialog) return "break" # 查找方法 def search_result(self, key, ignore_case, search_dialog, search_box): self.context_text.tag_remove('match', 1.0, END) match_found = 0 if key: start_pos = 1.0 while True: start_pos = self.context_text.search( key, index=start_pos, nocase=ignore_case, stopindex=END) if not start_pos: break end_pos = "{}+{}c".format(start_pos, len(key)) self.context_text.tag_add('match', start_pos, end_pos) match_found += 1 start_pos = end_pos self.context_text.tag_config( 'match', foreground='red', background='yellow') search_box.focus_set() search_dialog.title("发现了%d个匹配" % match_found) # 主题切换 def change_theme(self): selected_theme = self.theme_choice.get() fg_bg = self.theme_color.get(selected_theme) fg_color, bg_color = fg_bg.split(".") print(fg_color, bg_color) self.context_text.config(bg=bg_color, fg=fg_color) # 关于菜单 def show_message(self, type): if type == "帮助": messagebox.showinfo("帮助", "这是帮助文档") else: messagebox.showinfo("关于", "这是一个简单的记事本程序") if __name__ == "__main__": app = NotePad() app.mainloop()