最近绞尽脑汁做了一个功能十分详细的文本编辑器,可能已经比得上microsoft的笔记本了,接下来我给你们介绍一下怎么做
本文章是作者自己的知识产权,可以让你们复制而且不付钱已经是很良心了,请多多点赞、收藏、阅读、评论,谢谢!
from tkinter import *
import windnd
from tkinter.messagebox import *
from tkinter.scrolledtext import *
from easygui import *
class gui():
def __init__(self):
self.win=Tk()
self.win.geometry('892x685')
self.win.title('文本编辑器')
self.ss=False
self.menu()
self.text()
self.tools()
def menu(self):
self.menubar=Menu(self.win,tearoff=0)
self.file=Menu(self.menubar)
self.file.add_command(label='新建',command=self.new,accelerator='ctrl+n')
self.win.bind('<Control-N>',self.new)
self.file.add_command(label='打开',command=self.op,accelerator='ctrl+o')
self.win.bind('<Control-O>',self.op)
self.file.add_command(label='保存',command=self.save,accelerator='ctrl+s')
self.win.bind("<Control-S>",self.save)
self.file.add_separator()
self.file.add_command(label='清空',command=self.clear,accelerator='alt+c')
self.win.bind("<Alt-C>",self.clear)
self.file.add_separator()
self.file.add_command(label='最小化',command=self.clo,accelerator='ctrl+q')
self.win.bind('<Control-Q>',self.clo)
self.file.add_command(label='关闭',command=self.ex,accelerator='alt+f4')
self.win.bind("<Alt-F4>",self.ex)
self.menubar.add_cascade(label='文件',menu=self.file)
self.edit=Menu(self.menubar)
self.edit.add_command(label='粘贴',command=self.pasts,accelerator='ctrl+v')
self.win.bind("<Control-V>",self.pasts)
self.edit.add_command(label='复制',command=self.copy,accelerator='ctrl+c')
self.win.bind("<Control-C>",self.copy)
self.edit.add_command(label='剪切',command=self.cut,accelerator='ctrl+x')
self.win.bind("<Control-X>",self.cut)
self.edit.add_separator()
self.edit.add_command(label='撤销',command=self.undo,accelerator='ctrl+u')
self.win.bind("<Control-U>",self.undo)
self.edit.add_command(label='恢复',command=self.redo,accelerator='ctrl+r')
self.win.bind("<Control-R>",self.redo)
self.menubar.add_cascade(label='编辑',menu=self.edit)
self.window=Menu(self.menubar)
self.window.add_command(label='修改字体颜色',command=self.edd,accelerator='ctrl+f')
self.win.bind("<Control-F>",self.edd)
self.window.add_command(label='搜索',command=self.find,accelerator='alt+f')
self.win.bind('<Alt-F>',self.find)
self.menubar.add_cascade(label='窗口',menu=self.window)
self.menubar.add_command(label='帮助',command=self.hlp)
self.win.bind("<Control-H>",self.hlp)
self.win['menu']=self.menubar
windnd.hook_dropfiles(self.win,func=self.eve)
def text(self):
self.test=ScrolledText(self.win,width=96,height=29,undo=True)
self.test.pack(expand=YES,fill=BOTH)
def tools(self):
self.frame=Frame(self.win,width=892,height=23)
self.frame.pack(side=BOTTOM,fill=X)
self.st=StringVar()
self.st.set('第1行,第1列')
self.l=Label(self.frame,textvariable=self.st)
self.l.pack(side=LEFT,fill=X)
self.win.bind("<Key>",self.pos)
self.win.bind("<Button-1>",self.pos)
self.win.bind("<Button-3>",self.me)
self.win.protocol('WM_DELETE_WINDOW',self.bc)
def new(self):
if self.ss:
a=buttonbox(msg='你是否要保存?',title='保存',choices=['是','否'])
if a=='是':
self.save()
self.clear()
self.ss=False
self.win.title('untitle')
def op(self):
try:
a=askopenfilename(defaultextension='.txt')
b=open(a,'r',encoding='utf-8')
c=b.read()
self.test.delete(1.0,END)
self.test.insert(END,c)
b.close()
except:
showerror(title='错误',message='文本编辑器不能打开此文件!')
def save(self):
a=asksaveasfilename()
fi=open(a,'w')
fi.write(self.test.get(1.0,END).strip())
fi.close()
self.ss=True
self.win.title('文本编辑器 '+str(a))
def clear(self):
self.test.delete(1.0,END)
def clo(self):
self.win.iconify()
def ex(self):
self.win.destroy()
def pasts(self):
self.test.event_generate("<<Paste>>")
def copy(self):
self.test.event_generate("<<Copy>>")
def cut(self):
self.test.event_generate("<<Cut>>")
def undo(self):
self.test.event_generate("<<Undo>>")
def redo(self):
self.test.event_generate("<<Redo>>")
def eve(self,files):
ss=files[0]
bd=ss.decode('gbk')
a=open(bd,'r')
msg=a.read()
self.test.insert(END,msg)
self.win.title('文本编辑器 '+str(bd))
def pos(self,event):
po=str(self.test.index('insert')).split('.')
msg='第'+str(po[0])+'行,第'+str(po[1])+'列'
self.st.set(msg)
def me(self,event):
self.edit.post(event.x_root,event.y_root)
def bc(self):
a=buttonbox(msg='你要保存吗?',title='保存',choices=['是','否'])
if a=='是':
self.save()
self.win.destroy()
else:
self.win.destroy()
def edd(self):
a=enterbox('请输入你要变化的颜色','字体颜色')
try:
tag=self.test.tag_add('tag1','1.0','end')
self.test.tag_config('tag1',foreground=a)
except:
showerror('错误','没有此颜色')
def find(self):
a=enterbox('请输入你要搜索的内容','搜索')
b=self.test.search(a, '1.0', stopindex=END)
print(b)
if not b:
showerror('错误','没有此内容!')
else:
showinfo('查找成功','在第%s行第%s列'%(b.split('.')[0],str(int(b.split('.')[1])+1)))
def hlp(self):
a=Tk()
b=Label(a,text='pythonitstream原创,不可转载!!')
b.pack()
if __name__=='__main__':
gui()
mainloop()
特长对不对?那好,我来分段解释
我这是python3.8.3,请不同版本的人不要误会
1)导入部分
可以看到前面我就用6行的篇幅导入了本程序需要的库,可见本程序不简单
tkinter:内置库,一个非常复杂的GUI库
tkinter.messagebox:内置库,属于一个tkinter下的库。导入tkinter时,会导入tkinter文件夹里的__init__文件(主文件),但还有几个文件没有被导入,哪个需要哪个导入。一个tkinter里的信息框库
tkinter.scrolledtext:内置库,原理同tkinter.messagebox。一个tkinter里的添加有滚动条的文本框的库
在此声明一下,我导入scrolledtext不是装B,由于Text和ttk.Scrollbar绑定太复杂,而且还需要导入另外一个库:tkinter.ttk,所以由于麻烦,我才导入的。请大家不要误解
easygui:外置库,需要自己下载。相当于tkinter.simpledialog的一个简单的GUI库
windnd:外置库,需要自己下载。用来检测Tk窗口的事件【普通事件可以用bind来检查,但是特殊事件(比如有文件拖入窗口)就得用windnd】
2)__init__部分
__init__初始函数我就不用介绍了吧?我相信大伙都知道
前三行创建窗口、设置大小、设置标题我就不用介绍了
值ss:正在打开的文件是否保存
方法menu:设置菜单并绑定关于菜单的事件
方法text:创建文本框
方法tools:绑定事件并添加工具栏
3)方法menu
方法menu好像是这个类里面最长的一个方法了……
首先我们回顾一下tkinter的菜单如何写
菜单基类.add_command(label=菜单文本,command=菜单被触发时的函数,accelerator=快捷键)
声明:accelerator快捷键只是标注一下,并没有绑定
所以我们还要绑定菜单的快捷键
基类.bind('<快捷键>',触发函数)
这是绑定事件的语法
鼠标也是这样绑定的
所以这个方法就是绑定菜单,绑定快捷键
最后一行我绑定了把文件拖拽到窗口内部时的事件,我们要把文件的路径获得,并打开文件、读取文件,之后把文件内容呈现到文本框里
4)方法text
这个好像不用我多说,这玩意只是创建一个文本框,undo=True是指可以撤销,默认为False
5)方法tools
这个方法用来添加文本框下面的工具栏(显示第几行,第几列),主要就是弄一个Frame,然后染色,然后在里面添加一个绑定过StringVar的Label,就是工具栏了!啦啦啦!
每当用户按下键盘或鼠标左键的时候,这时候意味着文本框中的光标的位置出现了变化,所以我们也要更新Label里面的内容啦!
Tk.protocol好像说起来有点复杂,它用来检测窗口的属性变化事件。WM_DELETE_WINDOW就是关闭窗口的事件,关闭窗口要询问用户保不保存
6)方法new
new就是新的的意思,在这里用于新建文档
首先判断用户是否保存,保存了才能新建
self.clear是清屏的函数,我自己后面写的
新建后,把self.ss调整为未保存,把标题调整为untitled,这和普通笔记本没啥区别
7)方法op
op在这里是一个简写,全写为open,但我怕和内置函数混了,所以用的缩写
askopenfilename是easygui库的一个函数,用来返回用户所选的路径
之后就是打开文件,读取文件,关闭文件,显示到文本框里
encoding是用来改变打开文件时文件的打开格式,如utf-8等,没有这个会报错
我还加了一个错误判断,用来识别用户是不是捣乱了,打开了笔记本无法打开的文件
8)方法save
save用来保存文件,准确的说是save as另存为
asksaveasfilename也是easygui中的一个函数,它用来返回用户想保存的路径
open(a,'w')中的w是指“写”形式打开文件,如果文件不存在,会自动创建。打开以后,会清空文件的所有内容,写出代码里想要有的内容
写完文件之后把self.ss改成True就行了
9)方法clear
清屏的方法
self.text.delete是用来清空文本框一部分的内容
第一个参数是几行,几列,第二个也是,但是可以填写'end',用来表示到最后
所以1.0到end就是清到最后了
10)方法clo
用来最小化窗口
Tk.iconify是最小化窗口的方法
11)方法ex
用来关闭窗口
Tk.destroy是关闭窗口的方法
12)方法pasts
其实我当时不想和paste重名,所以我就写了pasts
用来粘贴文本
13)方法copy
用来复制文本框选中的内容
14)方法cut
用来剪切文本框选中的内容
15)方法undo
用来撤销文本框中的操作
16)方法redo
用来恢复文本框中的操作
17)方法eve
之前我在menu方法的结尾写了这行代码:
windnd.hook_dropfiles(self.win,func=self.eve)
用来检测有没有文件被拖拽到窗口范围里,绑定的触发函数是self.eve
这个函数用来打开拖拽的文件,获取其内容,并呈现到文本框里
由于windnd.hook_dropfiles对绑定的触发函数会返回一个值,所以files就是那个值,用来当作拖拽文件的路径
感叹!windnd创始人牛!牛!再牛!鼓掌!
但是读文件前,要把这个one year搞成gbk格式的,不然读不了
读完之后,就可以把他显示到文本框里了
18)方法pos
我在前面绑定了pos方法,把它当作get文本框中光标位置的函数,并写出来
Text.index('insert')就是获取文本框里光标的位置的方法,再把它改到工具栏的Label里,就行了
注意,这里的光标不是鼠标箭头,而是文本框中一闪一闪,被视作写文本的地方的光标
19)方法me
Menu类有一个特别的方法,叫post,用来把菜单显示到某个位置,而event是bind提供综合信息的参数,event.x_root和event.y_root就是光标的xy位置
这个方法就是用来添加一个右键菜单(和edit菜单一样)
20)方法bc
这方法用来在关闭窗口时询问用户保不保存,保存就self.save(),不保存就直接关了
21)方法edd
用来改变文字颜色,这玩意的tag有点复杂,做出如下解释
Text.tag_add就是添加一个tag,第一个参数为tag名,第二个参数为tag颜色起始位置,第三个参数为tag颜色终止位置
Text.tag_config就是“激活”tag,将tag显示出来。第一个参数就是tag名,第二个参数是tag颜色
22)方法find
用来查找某个字符串的位置。其中,Text.search就是查找字符串在哪的函数
第一个参数是要查找的字符串,第二个参数是查找的起始范围,stopindex就是查找的终止范围,可以写'end'
23)方法hlp
此处是help的缩写,提供一个小窗口,写着作者信息
是不是很详细?好了,本文章的内容就到这里,感谢大家的收听,观众们洗洗睡吧!
非喜勿喷!!