赞
踩
原项目:[Tkinter项目]Python Tkinter NotePad记事本项目实战
https://www.bilibili.com/video/BV1qk4y1k7N2/?p=4
整体的功能是模仿记事本的一些基本简单设计,但是增加了工具栏的快捷方式。名字为PNote,“P”是指单词“penguin”(企鹅),简而言之,该记事本叫“企鹅记事本”,其icon就是使用一张小企鹅的表情包制成的,增添界面的趣味性。界面功能思维导图如下
提前找好工具栏的图片,图片的格式都为gif,大小一般为32x32或16x16。icon图片来源网站:https://www.iconfont.cn/
,下载的时候把图片后缀改成gif就可以了。先建一个py文件,随意起一个记事本的名字,py文件和图片文件分开放,图片放在同目录下的img文件中。ico图片转化网站:https://www.uupoop.com/ico/。将小企鹅表情包.jpg图片转换成尺寸大小32x32的icon图
记录工具:CSDN官网上的Markdown编辑器
根据想法中的思维导图整个应用程序的图形化界面分为四个part:菜单栏、工具栏、文本输入区域、右键弹出菜单,将组件代码分别写在这个四个方法中,其他的功能实现方法则另外处理
PS:此处的代码展示和源码文件的排版并不一致
#导入tkinter类库
from tkinter import *
from tkinter import filedialog, messagebox
from tkinter.ttk import Scrollbar, Checkbutton,Label,Button #tkk里面的组件会有所优化
import os
import sys
class PNote(Tk): #工具栏所用图片的名称 icons = ["new_file","open_file","save","cut","copy","paste", "undo","redo","find_text"] icon_res = [] # 默认主题使用字典(背景色为白色,字体为黑色)、主题一(背景为企鹅灰,字体为深蓝色)、主题二(背景色为深蓝色、字体为白色) theme_color = {"Default":"#000000.#FFFFFF", "Penguin Gray":"#222b34.#e7e4e3", "Night Mode":"#FFFFFF.#222b34"} # 初始化操作 def __init__(self): super().__init__() #继承父类的构造 #调用方法 self.set_window() #调用界面窗口方法 self.create_menu_bar() #菜单栏 self.create_tool_bar() #工具栏 self.create_body() #文本输入区域 self.create_pop_menu() #弹出菜单
# 设置窗口界面 def set_window(self): self.title("PNote") #标题 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("./img/penguin.ico") #创建菜单栏 def create_menu_bar(self): menu_bar = Menu(self) self['menu']=menu_bar #添加菜单栏项目 #文件 file_menu = Menu(menu_bar,tearoff=0) 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='Ctrl+Shift+S',command=self.save_as) file_menu.add_separator() #分割符 file_menu.add_command(label='退出',accelerator='Alt+F4',command=self.exit_notepad) menu_bar.add_cascade(label='文件',menu=file_menu) #文件菜单功能实现 # 打开文件功能 def open_file(self,event=None): #打开文件并进行类型设置 input_file = filedialog.askopenfilename(filetypes=[("所有文件","*.*"),("文本文档","*.text")]) 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.showerror("错误","文件保存失败!") 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("新建---PNote") self.context_text.delete(1.0,END) self.file_name = None #另存为 def save_as(self): input_file = filedialog.askopenfilename(filetypes=[("所有文件","*.*"),("文本文档","*.text")]) if input_file: self.file_name= input_file self.write_to_file(self.file_name) #退出 def exit_notepad(self): if messagebox.askokcancel("退出","确定退出吗?"): self.destroy() if __name__ == '__main__': app = PNote() app.mainloop()
由于编辑的功能和工具栏差不多,所以会其方法放在body部分,但是“查找”功能在此单独说明
#在方法create_menu_bar(self)加入以下代码 #编辑 editor_menu = Menu(menu_bar,tearoff=0) """撤销,恢复,剪切,复制,粘贴,查找,全选""" menu_bar.add_cascade(label='编辑',menu=editor_menu) editor_menu.add_command(label='撤销',accelerator='Ctrl+Z',command=lambda:self.handle_menu_action('撤销')) editor_menu.add_command(label='恢复',accelerator='Ctrl+Y',command=lambda:self.handle_menu_action('恢复')) editor_menu.add_separator() editor_menu.add_command(label='剪切',accelerator='Ctrl+X',command=lambda:self.handle_menu_action('剪切')) editor_menu.add_command(label='复制',accelerator='Ctrl+C',command=lambda:self.handle_menu_action('复制')) editor_menu.add_command(label='粘贴',accelerator='Ctrl+V',command=lambda:self.handle_menu_action('粘贴')) editor_menu.add_separator() editor_menu.add_command(label='查找',accelerator='Ctrl+F',command=self.find_text_dialog) editor_menu.add_command(label='全选',accelerator='Ctrl+A',command=self.select_all)
查找文本功能
#设置查找对话框 def find_text_dialog(self): 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=1, pady=1) # 关闭查找文本对话框 def close_search_dialog(): self.context_text.tag_remove('match',1.0,END) search_dialog.destroy() search_dialog.protocol("WM_DELETE_WINDOW",close_search_dialog) return "break" # 查找的方法 def search_result(self,key,ignore_case,search_dialog,search_box): self.context_text.tag_remove('match',1.0,END) matches_found = 0 if key: start_pos = 1.0 while True: start_pos = self.context_text.search(key,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) matches_found +=1 start_pos = end_pos self.context_text.tag_config('match',foreground='white', background='green') search_box.focus_set() search_dialog.title("发现了%d个匹配项" % matches_found) # 全选 def select_all(self): self.context_text.tag_add('sel',1.0,END) return "break"
视图的下拉列表包括显示行号和显示高亮当前行、主题选择,前两者用checkbutton组件实现图形化界面,后者使用radiobutton组件实现图形化界面
# 在方法create_menu_bar(self)加入以下代码 #视图菜单 view_menu = Menu(menu_bar,tearoff=0) menu_bar.add_cascade(label='视图',menu=view_menu) """显示行号""" 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) """主题""" themes_menu = Menu(menu_bar,tearoff=0) view_menu.add_separator() view_menu.add_cascade(label="主题",menu=themes_menu) #主题选择 #默认主题 self.theme_choice = StringVar() self.theme_choice.set("Default") for k in sorted(self.theme_color): themes_menu.add_radiobutton(label=k, variable=self.theme_choice, command=self.change_theme) #关于菜单 about_menu = Menu(menu_bar,tearoff=0) about_menu.add_command(label="关于",command=lambda:self.show_messagebox("关于")) about_menu.add_command(label="帮助",command=lambda:self.show_messagebox("帮助")) menu_bar.add_cascade(label='关于',menu=about_menu)
“关于”菜单的下拉列表都是使用messagebox弄两个对话框,简单化,如图所示
# 主题的切换
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('.')
self.context_text.config(bg=bg_color,fg=fg_color)
# 关于菜单
def show_messagebox(self,type):
if type == "帮助":
messagebox.showinfo("帮助","这是帮助文档",icon="question")
else:
messagebox.showinfo("关于","这是一个简单的记事本程序")
工具栏的,设计上是新建、打开、保存、剪切、复制、粘贴、撤销、重做、查找文本的icon逐一排列,和弹出菜单、菜单栏的功能是有些重复,是常用的功能快捷方式
def create_tool_bar(self):
tool_bar = Frame(self,height=25,background="#ffffff") # 容器Frame,白色
# 填充x轴
tool_bar.pack(fill="x")
# 生成图片文件放到对应的位置
for icon in self.icons:
tool_icon = PhotoImage(file="./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 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 create_body(self): # 左:行号;右:滚动条;中:文本编辑区 # 行号区域 self.line_number_bar = Text(self,width=3, padx=3,takefocus=0,border=0, background="#f0f0f0",state="disable" ) #state="disable"不能编辑状态 #左边填充整个y轴 self.line_number_bar.pack(side='left',fill='y') # 文本编辑区 # undo=True是否具备文本取消功能,wrap:如何换行,word:按照单词自动换行,expand:可以拉伸 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('<Any-KeyPress>',lambda e:self.update_line_num()) self.context_text.pack(fill='both',expand="yes") # 设置文本输入区 self.context_text.tag_config("active_line",background="#ffffff") # 滚动条 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 update_line_num(self):
if self.is_show_line_num.get():
# 获取所有行
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='disable')
else:
self.line_number_bar.config(state='normal')
self.line_number_bar.delete(1.0,END)
self.line_number_bar.config(state='disable')
高亮的颜色默认是白色的,所以在
# 高亮当前行
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 create_pop_menu(self): pop_menu = Menu(self.context_text,tearoff=0) for item1,item2 in zip(['剪切','复制','粘贴','撤销','恢复'], ['cut','copy','paste','undo','redo',]): pop_menu.add_command(label=item1,compound='left',command=self.tool_bar_action(item2)) pop_menu.add_separator() #分割 pop_menu.add_command(label="全选",command=self.select_all) #绑定 self.context_text.bind("<Button-3>",lambda event:pop_menu.tk_popup(event.x_root,event.y_root)) #右键菜单的处理 def handle_menu_action(self,action_type): if action_type == "撤销": self.context_text.event_generate("<<Undo>>") elif action_type == "恢复": self.context_text.event_generate("<<Rndo>>") elif action_type == "剪切": self.context_text.event_generate("<<Cut>>") elif action_type == "复制": self.context_text.event_generate("<<Copy>>") elif action_type == "粘贴": self.context_text.event_generate("<<Paste>>") #防止事件传递 return "break"
版本如以下动图所示:
网上找的教程python打包生成exe可执行文件
打包使用的是auto -py-to-exe
先安装pip install auto-py-to-exe,安装成功后输入auto-py-to-exe回车会弹出一个窗口
根据网上的教程为了便于打包,修改了图片的位置,所以修改了代码中图片的路径表达,
self.iconbitmap("D:\\PNote\\img\\penguin.ico")
tool_icon = PhotoImage(file="D:\\PNote\\img\\%s.gif" % (icon,))
最后上传文件和图片
最中得到PNote.exe,测试使用无误
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。