Python 使用tkinter复刻Windows记事本UI和菜单功能(三)

上一篇:Python 使用tkinter复刻Windows记事本UI和菜单功能(二)-CSDN博客

下一篇:敬请耐心等待,如发现BUG以及建议,请在评论区发表,谢谢!

本文章完成了记事本的新建、保存、另存、打开文件、状态栏显隐等部分基本功能,还设计还原了页面设置UI。很抱歉现阶段我没有精力再去完善和优化这个项目了,不止是能力受限,还耽搁太多时间了。

运行结果

代码实例

string = \
"""复刻Windows记事本BUG:1、快捷键:Ctrl+O 打开文件实现时发现光标处会插入换行'\\n'(现在我仍未知是否是为解释器BUG)未实现:1、文件的新窗口无法实现(未使用线程)2、无法实现单击菜单栏显示菜单项后与键盘交互(非快捷键),因为Menu无法与bind捆绑事件及交互3、文件的页面设置的具体功能交互还没完成,只完成UI和交互框架4、文件的打印还没实现(我不知道怎么连接外设)4、除了文件以外的菜单还没实现
"""# 通配符
__all__ = ['main']import tkinter as tk
from tkinter import ttk
from tkinter import font
import tkinter.messagebox as tkmb
import tkinter.filedialog as tkfd# 全局变量
# 初始化
FONT_SIZE = 12      # 默认字体大小
# 永久保存变量
PAPER_VAR = 'A4'
PAPER_ORIENT = 1
LEFT_VAR = 20
RIGHT_VAR = 20
TOP_VAR = 25
BOTTOM_VAR = 25
HEADER_VAR = None
FOOTER_VAR = None# (打印)页面设置UI
class PageSetup:orientVar = NoneleftVar = NonerightVar = NonetopVar = NonebottomVar = NoneheaderVar = NonefooterVar = None# (打印)页面设置@classmethoddef pageSetup(cls):set = tk.Toplevel()     # 页面设置顶级窗口set.title('页面设置')     # 窗口标题set.geometry(f'622x418+{set.winfo_screenwidth()//4+60}+{set.winfo_screenheight()//8+52}')set.focus_set()         # 设置窗口焦点set.resizable(0, 0)     # 禁止窗口的放大set.grab_set()          # 锁定父窗口# 窗口布局# (打印)纸张选择paperFrame = ttk.LabelFrame(set, text='纸张', padding=(191, 38))paperFrame.place(x=14, y=16)tk.Label(paperFrame).pack()# 大小size = tk.Label(set, text='大小(Z):')size.place(x=24, y=48)# 来源source = tk.Label(set, text='来源(S):', state='disable')source.place(x=24, y=92)# 纸张大小下拉菜单# 修改 OptionMenu 的样式style = ttk.Style()style.configure("my.TMenubutton", background='#DCDCDC', width=35)# 纸张大小下拉菜单paperVar = tk.StringVar(value=PAPER_VAR)paperOption = [paperVar.get(), f'A3{" "*55}', 'A4', 'A5', 'B4 (JIS)', 'B5 (JIS)', 'Executive', 'Statement', 'Tabloid', '法律专用纸', '信纸']paperMenu = ttk.OptionMenu(set, paperVar, *paperOption, style="my.TMenubutton", command=cls.paperOption)paperMenu.place(x=110, y=46)# 默认选择(打印纸张)cls.paperOption(paperVar.get())# 纸张来源下拉菜单# 修改 OptionMenu 的样式style.configure("my2.TMenubutton", background='#C0C0C0', width=35)# 纸张大小下拉菜单cls.sourceVar = tk.StringVar()sourceOption = [None, f'选项1{" " * 55}', '选项2', '选项3']sourceMenu = ttk.OptionMenu(set, cls.sourceVar, *sourceOption, style="my2.TMenubutton")sourceMenu.config(state="disabled")sourceMenu.place(x=110, y=90)# (打印纸张)方向选择orientFrame = ttk.LabelFrame(set, text='方向', padding=(50, 38))orientFrame.place(x=14, y=147)tk.Label(orientFrame).pack()cls.orientVar = tk.IntVar(value=PAPER_ORIENT)# (打印纸张)纵向lengthways = ttk.Radiobutton(set, text='纵向(O)', variable=cls.orientVar, value=1, command=cls.orientOption)lengthways.place(x=26, y=180)# (打印纸张)横向crosswise = ttk.Radiobutton(set, text='横向(A)', variable=cls.orientVar, value=2, command=cls.orientOption)crosswise.place(x=26, y=220)# 默认(打印纸张)纵向cls.orientOption()# (打印纸张)页边距(毫米)marginFrame = ttk.LabelFrame(set, text='页边距(毫米)', padding=(130, 38))marginFrame.place(x=136, y=147)tk.Label(marginFrame).pack()# 文字标签tk.Label(set, text='左(L):').place(x=148, y=180)tk.Label(set, text='右(R):').place(x=274, y=180)tk.Label(set, text='上(T):').place(x=148, y=220)tk.Label(set, text='下(B):').place(x=274, y=220)# 输入框cls.leftVar = tk.StringVar(value=LEFT_VAR)cls.rightVar = tk.StringVar(value=RIGHT_VAR)cls.topVar = tk.StringVar(value=TOP_VAR)cls.bottomVar = tk.StringVar(value=BOTTOM_VAR)leftEntry = ttk.Entry(set, width=6, textvariable=cls.leftVar)leftEntry.place(x=200, y=180)rightEntry = ttk.Entry(set, width=6, textvariable=cls.rightVar)rightEntry.place(x=326, y=180)topEntry = ttk.Entry(set, width=6, textvariable=cls.topVar)topEntry.place(x=200, y=220)bottomEntry = ttk.Entry(set, width=6, textvariable=cls.bottomVar)bottomEntry.place(x=326, y=220)# (打印纸张)预览previewFrame = ttk.LabelFrame(set, text='预览', padding=(88, 147))previewFrame.place(x=420, y=16)tk.Label(previewFrame).pack()image = tk.PhotoImage(file='.\\..\\photo\\微信余额.png')tk.Label(set, image=image).place(x=421, y=37)cls.headerVar = tk.StringVar(value=HEADER_VAR)cls.footerVar = tk.StringVar(value=FOOTER_VAR)# 页眉tk.Label(set, text='页眉(H):').place(x=14, y=288)headerEntry = ttk.Entry(set, width=42, textvariable=cls.headerVar)headerEntry.place(x=106, y=288)# 页脚tk.Label(set, text='页脚(F):').place(x=14, y=330)footerEntry = ttk.Entry(set, width=42, textvariable=cls.footerVar)footerEntry.place(x=106, y=330)# 页眉页脚输入值网页详情介绍# 修改 Button 的样式# style.configure("my.TButton", width=6, font=("Arial", 10, 'underline'), foreground="blue")# ttk.Button(set, text='输入值', style='my.TButton').place(x=106, y=360)headerFooterWeb = tk.Label(set, text='输入值', relief='flat', foreground="blue", font=("Arial", 10, 'underline'))headerFooterWeb.place(x=106, y=360)# 捆绑跳转网页事件import webbrowserheaderFooterWeb.bind('<Button-1>', lambda event: webbrowser.open('https://support.microsoft.com/zh-cn/windows/更改记事本中的页眉和页脚命令-c1b0e27b-497d-c478-c4c1-0da491cac148'))# 确定# 修改 Button 的样式style.map("my.TButton", background=[('!active', '!disabled', '#00BFFF')])confirm = ttk.Button(set, text='确定', width=13, style='my.TButton', command=lambda: cls.confirmCancel('确定', set))confirm.place(x=394, y=372)# 取消cancel = ttk.Button(set, text='取消', width=13, command=lambda: cls.confirmCancel('取消', set))cancel.place(x=506, y=372)# 捆绑获取输入框的数据事件set.bind('<KeyRelease>', cls.getEntry)set.mainloop()      # 窗口循环# (打印纸张)页面设置确定与取消@classmethoddef confirmCancel(cls, option, win=None):print(option)if option == '确定':# 修改的数值保存到文件# 发出警告声音win.bell()pass# 关闭当前窗口win.destroy()# 获取输入框的数据@classmethoddef getEntry(cls, event=None):# (打印纸张)设置页边距(毫米)print('页边距:',cls.leftVar.get(),cls.rightVar.get(),cls.topVar.get(),cls.bottomVar.get())global LEFT_VAR, RIGHT_VAR, TOP_VAR, BOTTOM_VARLEFT_VAR = cls.leftVar.get()RIGHT_VAR = cls.rightVar.get()TOP_VAR = cls.topVar.get()BOTTOM_VAR = cls.bottomVar.get()# (打印纸张)设置页眉页脚print('页眉/页脚:',cls.headerVar.get(),cls.footerVar.get())global HEADER_VAR, FOOTER_VARHEADER_VAR = cls.headerVar.get()FOOTER_VAR = cls.footerVar.get()# (打印纸张)方向选择@classmethoddef orientOption(cls):global PAPER_ORIENTPAPER_ORIENT = cls.orientVar.get()# (打印纸张)方向选择if PAPER_ORIENT == 1:print('方向:纵向')elif PAPER_ORIENT == 2:print('方向:横向')# 纸张选择@classmethoddef paperOption(cls, option):global PAPER_VARPAPER_VAR = option# 纸张设置if option == 'A3':print('大小:A3')elif option == 'A4':print('大小:A4')elif option == 'A5':print('大小:A5')elif option == 'B4 (JIS)':print('大小:B4 (JIS)')elif option == 'B5 (JIS)':print('大小:B5 (JIS)')elif option == 'Executive':print('大小:Executive')elif option == 'Statement':print('大小:Statement')elif option == 'Tabloid':print('大小:Tabloid')elif option == '法律专用纸':print('大小:法律专用纸')elif option == '信纸':print('大小:信纸')# 文本编辑器窗口UI
class WindowsUI(PageSetup):readText = ''  # 读取文本数据@classmethoddef __init__(cls, base):cls.base = base# cls.base = tk.Tk()              # 新建一个窗口cls.base.title('无标题 - 记事本')     # 窗口标题cls.base.geometry(f'750x550+{cls.base.winfo_screenwidth()//4}+{cls.base.winfo_screenheight()//8}')# 创建一级菜单栏(此时为空)cls.menubar = tk.Menu(cls.base)cls.base.config(menu=cls.menubar)# 文件菜单# 创建二级菜单栏(此时为空)cls.fileMenu = tk.Menu(cls.menubar, tearoff=0)# 向一级菜单栏添加 文件 项,并与二级菜单(fileMenu)建立级联关系(从属/上下级)cls.menubar.add_cascade(label='文件(F)', menu=cls.fileMenu)# 文件的二级菜单栏添加 ... 项cls.fileMenu.add_command(label=f'新建(N){" "*28}Ctrl+N', command=cls.newText)cls.fileMenu.add_command(label=f'新窗口(W){" "*16}Ctrl+Shift+N', command=newWindow)cls.fileMenu.add_command(label=f'打开(O)...{" "*26}Ctrl+O', command=cls.openFile)cls.fileMenu.add_command(label=f'保存(S){" "*29}Ctrl+S', command=cls.saveFile)cls.fileMenu.add_command(label=f'另存为(A)...{" "*15}Ctrl+Shift+S', command=cls.saveAsFile)cls.fileMenu.add_command(label=f'页面设置(U)...', command=cls.pageSetup)cls.fileMenu.add_command(label=f'打印(P)...{" "*27}Ctrl+P')cls.fileMenu.add_command(label=f'退出(X)', command=cls.base.destroy)# 菜单之间插入分隔线cls.fileMenu.insert_separator(5)cls.fileMenu.insert_separator(8)# 编辑菜单# 创建二级菜单栏(此时为空)cls.editMenu = tk.Menu(cls.menubar, tearoff=0)# 向一级菜单栏添加 编辑 项,并与二级菜单(editMenu)建立级联关系(从属/上下级)cls.menubar.add_cascade(label='编辑(E)', menu=cls.editMenu)# 编辑的二级菜单栏添加 ... 项cls.editMenu.add_command(label=f'撤销(U){" "*26}Ctrl+Z', command=cls.repealEdit)cls.editMenu.add_command(label=f'剪切(T){" "*26}Ctrl+X')cls.editMenu.add_command(label=f'复制(C){" "*26}Ctrl+C')cls.editMenu.add_command(label=f'粘贴(V){" "*26}Ctrl+V')cls.editMenu.add_command(label=f'删除(L){" "*27}Delete')cls.editMenu.add_command(label=f'使用 Bing 搜索...{" "*14}Ctrl+E')cls.editMenu.add_command(label=f'查找(F)...{" "*25}Ctrl+F')cls.editMenu.add_command(label=f'查找上一个(N){" "*23}F3')cls.editMenu.add_command(label=f'查找下一个(V){" "*15}Shift+F3')cls.editMenu.add_command(label=f'替换(R)...{" "*23}Ctrl+H')cls.editMenu.add_command(label=f'转到(G)...{" "*23}Ctrl+G')cls.editMenu.add_command(label=f'全选(A){" "*26}Ctrl+A')cls.editMenu.add_command(label=f'时间/日期(D){" "*25}F5')# 菜单之间插入分隔线cls.editMenu.insert_separator(1)cls.editMenu.insert_separator(6)cls.editMenu.insert_separator(13)# 格式菜单# 全局变量cls.wrap = tk.BooleanVar(value=True)# 创建二级菜单栏(此时为空)cls.formatMenu = tk.Menu(cls.menubar, tearoff=0)# 向一级菜单栏添加 格式 项,并与二级菜单(formatMenu)建立级联关系(从属/上下级)cls.menubar.add_cascade(label='格式(O)', menu=cls.formatMenu)# 格式的二级菜单栏添加 ... 项cls.formatMenu.add_checkbutton(label='自动换行(W)', variable=cls.wrap, onvalue=1, offvalue=0, command=cls.setWrap)cls.formatMenu.add_command(label='字体(F)...')# 查看菜单# 全局变量cls.state = tk.BooleanVar(value=True)# 创建二级菜单栏(此时为空)cls.viewMenu = tk.Menu(cls.menubar, tearoff=0)# 向一级菜单栏添加 查看 项,并与二级菜单(checkMenu)建立级联关系(从属/上下级)cls.menubar.add_cascade(label='查看(V)', menu=cls.viewMenu)# 创建三级菜单栏(此时为空)cls.threeViewMenu = tk.Menu(cls.viewMenu, tearoff=0)# 查看的二级菜单栏添加 ... 项cls.viewMenu.add_cascade(label='缩放(Z)', menu=cls.threeViewMenu)cls.viewMenu.add_checkbutton(label='状态栏(S)', variable=cls.state, onvalue=1, offvalue=0, command=cls.setState)# 缩放的三级菜单栏添加 ... 项cls.threeViewMenu.add_command(label=f'放大(I){" " * 14}Ctrl + 加号', command=lambda: cls.FontSizeEvent('放大'))cls.threeViewMenu.add_command(label=f'缩小(O){" " * 13}Ctrl + 减号', command=lambda: cls.FontSizeEvent('缩小'))cls.threeViewMenu.add_command(label=f'恢复默认缩放{" " * 11}Ctrl+0', command=lambda: cls.FontSizeEvent('默认缩放'))# 帮助菜单# 创建二级菜单栏(此时为空)cls.helpMenu = tk.Menu(cls.menubar, tearoff=0)# 向一级菜单栏添加 帮助 项,并与二级菜单(helpMenu)建立级联关系(从属/上下级)cls.menubar.add_cascade(label='帮助(H)', menu=cls.helpMenu)# 帮助的二级菜单栏添加 ... 项cls.helpMenu.add_command(label='查看帮助(H)')cls.helpMenu.add_command(label='发送反馈(F)')cls.helpMenu.add_command(label='关于文本编辑器(A)')# 菜单之间插入分隔线cls.helpMenu.insert_separator(2)# 右键菜单# 创建二级菜单栏(此时为空)cls.rightKeyMenu = tk.Menu(cls.base, tearoff=0)# 创建三级菜单栏(此时为空)cls.threeRightMenu = tk.Menu(cls.rightKeyMenu, tearoff=0)# 右键菜单的二级菜单栏添加 ... 项cls.rightKeyMenu.add_command(label='撤销(U)')cls.rightKeyMenu.add_command(label='剪切(T)')cls.rightKeyMenu.add_command(label='复制(C)')cls.rightKeyMenu.add_command(label='粘贴(P)')cls.rightKeyMenu.add_command(label='删除(D)')cls.rightKeyMenu.add_command(label='全选(A)')cls.rightKeyMenu.add_checkbutton(label='从右到左的阅读顺序(R)')cls.rightKeyMenu.add_checkbutton(label='显示 Unicode 控制字符(S)')cls.rightKeyMenu.add_cascade(label='插入 Unicode 控制字符(I)', menu=cls.threeRightMenu)cls.rightKeyMenu.add_command(label='关闭输入法(L)')cls.rightKeyMenu.add_command(label='汉字重选(R)')cls.rightKeyMenu.add_command(label='使用 Bing 搜索(B)...')# 插入 Unicode 控制字符(I)的三级菜单栏添加 ... 项cls.threeRightMenu.add_command(label='特殊字符1')cls.threeRightMenu.add('command', label='特殊字符2')cls.threeRightMenu.insert(3, 'command', label='特殊字符3')# ...# 菜单之间插入分隔线cls.rightKeyMenu.insert_separator(1)cls.rightKeyMenu.insert_separator(6)cls.rightKeyMenu.insert_separator(8)cls.rightKeyMenu.insert_separator(12)cls.rightKeyMenu.insert_separator(15)# 捆绑鼠标右键事件cls.base.bind('<Button-3>', lambda event: cls.rightKeyEvent(event, cls.rightKeyMenu))# 底行内容显示# 底部内容框架cls.bottomFrame = tk.Frame(cls.base)cls.bottomFrame.pack(side=tk.BOTTOM, fill='both')# 状态栏框架cls.stateFrame = tk.Frame(cls.bottomFrame, borderwidth=2, relief=tk.GROOVE)cls.stateFrame.pack(side=tk.BOTTOM, fill='both')# 字符编码cls.charCodeLabel = tk.Label(cls.stateFrame, text=' UTF-8', width=16, anchor='w', borderwidth=2, relief=tk.GROOVE)cls.charCodeLabel.pack(side=tk.RIGHT)# 换行方式(回车换行)cls.CRLFlabel = tk.Label(cls.stateFrame, text=' Windows (CRLF)', width=17, anchor='w', borderwidth=2, relief=tk.GROOVE)cls.CRLFlabel.pack(side=tk.RIGHT)# 字体大小cls.fontSizeLabel = tk.Label(cls.stateFrame, text='100%', width=6, borderwidth=2, relief=tk.GROOVE)cls.fontSizeLabel.pack(side=tk.RIGHT)# 光标位置cls.locationLabel = tk.Label(cls.stateFrame, text='  第 1 行,第 1 列', width=19, anchor='w', borderwidth=2, relief=tk.GROOVE)cls.locationLabel.pack(side=tk.RIGHT)# 空白填充(也可以按需显示内容)cls.blankLabel = tk.Label(cls.stateFrame, text='欢迎使用记事本', borderwidth=2, relief=tk.GROOVE)cls.blankLabel.pack(fill=tk.BOTH)# 右侧滚动条cls.rightScrollbar = tk.Scrollbar(cls.base, orient='vertical')cls.rightScrollbar.pack(side=tk.RIGHT, fill='both')# 底侧滚动条cls.bottomScrollbar = tk.Scrollbar(cls.bottomFrame, orient="horizontal")# 文本编辑区域cls.fontSize = tk.IntVar()cls.fontSize.set(FONT_SIZE)cls.setFont = font.Font(family='Tahoma', size=cls.fontSize.get())cls.text = tk.Text(cls.base, wrap="word", xscrollcommand=cls.bottomScrollbar.set, yscrollcommand=cls.rightScrollbar.set, font=cls.setFont)cls.text.pack(expand=True, fill='both')# 将焦点设置到Text控件上cls.text.focus_set()# 底侧滚动条与文本域关联cls.bottomScrollbar.config(command=cls.text.xview)# 右侧滚动条与文本域关联cls.rightScrollbar.config(command=cls.text.yview)# 修改窗口标题的图片cls.icon = tk.PhotoImage(file='.\\..\\photo\\记事本.png')cls.base.iconphoto(True, cls.icon)# cls.base.mainloop()             # 窗口主循环# 类里的变量base = Nonetext = NonefontSize = NonesetFont = NonefontSizeLabel = NonelocationLabel = Nonewrap = NonebottomScrollbar = NonerightScrollbar = Nonestate = NonebottomFrame = NonestateFrame = NoneeditMenu = NonetextGet = None@classmethoddef mainLoop(cls):cls.base.mainloop()  # 窗口主循环# 项目运行函数@classmethoddef workFunc(cls):# 捆绑事件,获取Text文本的光标位置cls.text.bind('<KeyPress>', cls.cursorPosition)  # 键盘按下触发cls.text.bind('<KeyRelease>', cls.cursorPosition)  # 键盘释放触发cls.text.bind('<ButtonPress>', cls.cursorPosition)  # 鼠标按下触发cls.text.bind('<ButtonRelease>', cls.cursorPosition)  # 鼠标释放触发# 自定义注册事件# cls.text.event_add('<<CursorEvent>>', *('<KeyPress>', '<KeyRelease>', '<ButtonPress>', '<ButtonRelease>'))# cls.text.bind('<<CursorEvent>>', cls.cursorPosition)# 自定义注册缩放事件('<<ZoomEvent>>')cls.base.event_add('<<ZoomEvent>>', *('<Control-MouseWheel>', '<Control-Key-=>', '<Control-Key-+>', '<Control-minus>', '<Control-Key-0>'))# 捆绑自定义注册缩放事件改变字体大小cls.base.bind('<<ZoomEvent>>', cls.FontSizeEvent)  # 鼠标上滚缩小,下滚放大# 捆绑按键按下编辑文本事件cls.base.bind('<KeyPress>', cls.editText)# 新建文本cls.base.bind('<Control-Key-n>', cls.newText)# 创建新窗口cls.base.bind('<Control-Shift-Key-N>', newWindow)# 打开文件(BUG)cls.base.bind('<Control-Key-o>', cls.openFile)# 保存文件cls.base.bind('<Control-Key-s>', cls.saveFile)# 文件另存为cls.base.bind('<Control-Shift-Key-S>', cls.saveAsFile)# 文件打印(未完成)# 撤销编辑cls.base.bind('<Control-Key-z>', cls.repealEdit)# 撤销返编辑cls.base.bind('<Control-Shift-Key-Z>', cls.repealEdit)# 编辑撤销菜单状态cls.base.bind('<Motion>', cls.repealState)# 编辑剪切# 编辑复制# 编辑粘贴# 编辑删除# 编辑撤销菜单状态@classmethoddef repealState(cls, event=None):if len(cls.editData) > 1 and cls.editIndex != -len(cls.editData):cls.editMenu.entryconfig(0, state='active', activebackground='#4169E1')elif cls.editIndex == -len(cls.editData):cls.editMenu.entryconfig(0, state='disable', activebackground='#DCDCDC')# 撤销编辑editData = [('\n', FONT_SIZE)]  # 编辑数据editIndex = -1@classmethoddef repealEdit(cls, event=None):# 菜单栏触发if not event:cls.base.event_generate('<Control-Key-z>')return# 快捷键触发elif event.keysym == 'z':# 限制条件if len(cls.editData) == 1 or -len(cls.editData) == cls.editIndex:returncls.editIndex -= 1# 快捷键触发elif event.keysym == 'Z':# 限制条件if cls.editIndex >= -1:returncls.editIndex += 1# 初始化文本cls.text.delete('1.0', 'end')# 插入上次编辑的文本数据cls.text.insert('1.0', cls.editData[cls.editIndex][0][:-1:])# 修改字体大小cls.fontSize.set(cls.editData[cls.editIndex][1])# 改变字体大小cls.setFont.config(size=cls.fontSize.get())# 改变底部显示字体大小百分比cls.fontSizeLabel.config(text='{:.0%}'.format(cls.fontSize.get() / FONT_SIZE))# 文件另存为@classmethoddef saveAsFile(cls, event=None):global openPath# 文件保存类型filetypes = [("文本文档", ".txt"), ("所有文件", ".*")]# 保存文件对话框savePath = tkfd.asksaveasfile(defaultextension=".txt", initialfile='*.txt', filetypes=filetypes)# 确定保存if savePath:# 把文本编辑的数据写入文件with open(savePath.name, 'w', encoding=savePath.encoding) as file:file.write(cls.text.get('1.0', 'end')[:-1:])# 窗口标题前去掉'*'cls.base.title(cls.base.title()[1::])# 修改标题cls.base.title(f'{savePath.name.split("/")[-1]} - 记事本')cls.readText = cls.text.get('1.0', 'end')[:-1:]openPath = savePath# 保存文件@classmethoddef saveFile(cls, event=None):global openPath# 判断文本是否编辑过if cls.base.title()[0] == '*':# 从程序打开进入的编辑if not cls.readText:# 消息对话框选择是否保存ifYes = tkmb.askyesnocancel('记事本', f'你想将更改保存到 无标题 吗?')# 如果选择是if ifYes:# 文件另存为cls.saveAsFile()# 打开文件后保存else:# 保存文本数据,写入文件with open(openPath.name, 'w', encoding=openPath.encoding) as file:file.write(cls.text.get('1.0', 'end')[:-1:])# 窗口标题前去掉'*'cls.base.title(cls.base.title()[1::])cls.readText = cls.text.get('1.0', 'end')[:-1:]# 打开文件@classmethoddef openFile(cls, keyTrigger=None):global openPathifYes = TruesavePath = True# 如果是键盘触发(必须)if keyTrigger:# Ctrl+O 触发,光标处会加入换行(不知道是否为BUG,求指教)cls.text.delete('1.0', 'end')cls.text.insert('1.0', cls.textGet[:-1:])# 判断文本是否编辑过if cls.base.title()[0] == '*':# 从程序打开进入的编辑if not cls.readText:fileName = f'你想将更改保存到 {cls.base.title().split(" ")[0][1::]} 吗?'# 从文件打开进入的编辑else:fileName = f'你想将更改保存到\n{openPath.name}\n吗?'# 在消息对话框选择是否保存当前编辑的文本ifYes = tkmb.askyesnocancel('记事本', fileName)# 确定if ifYes:# 判断保存的文件是否存在if not cls.readText:# 文件另存为cls.saveAsFile()else:# 保存文本数据,写入文件with open(openPath.name, 'w', encoding=openPath.encoding) as file:file.write(cls.text.get('1.0', 'end')[:-1:])# 窗口标题前去掉'*'cls.base.title(cls.base.title()[1::])cls.readText = cls.text.get('1.0', 'end')[:-1:]# 打开文件if ifYes != None and savePath:# 消息对话框打开文件# must be -defaultextension, -filetypes, -initialdir, -initialfile, -multiple, -parent, -title, or -typevariablefiletypes = [('文本文档', '.txt'), ('所有文件', '.*')]# 打开文件对话框cls.openPath = tkfd.askopenfile(filetypes=filetypes)# 确定打开文件if cls.openPath:openPath = cls.openPath# 窗口初始化cls.text.delete('1.0', 'end')cls.readText = ''# 打开文件读取数据插入到文本域with open(cls.openPath.name, 'r', encoding=cls.openPath.encoding) as file:for i in file:cls.text.insert('end', i)# 更新文本读取数据cls.readText += i# 修改窗口标题cls.base.title(f'{cls.openPath.name.split("/")[-1]} - 记事本')cls.base.event_generate('<Key>')# 数据更新cls.editData.clear()cls.editData.append((cls.textGet, cls.fontSize.get()))cls.editIndex = -1# 新建文本@classmethoddef newText(cls, event=None):ifYes = TruesavePath = True# 判断文本是否编辑过if cls.base.title()[0] == '*':# 从程序打开进入的编辑if not cls.readText:fileName = f'你想将更改保存到 {cls.base.title().split(" ")[0][1::]} 吗?'# 从文件打开进入的编辑else:fileName = f'你想将更改保存到\n{openPath.name}\n吗?'# 在消息对话框选择是否保存当前编辑的文本ifYes = tkmb.askyesnocancel('记事本', fileName)# 确定if ifYes:# 判断保存的文件是否存在if not cls.readText:# 文件保存类型filetypes = [("文本文档", ".txt"), ("所有文件", ".*")]# 保存文件对话框savePath = tkfd.asksaveasfile(defaultextension=".txt", initialfile='*.txt', filetypes=filetypes)# 确定保存if savePath:# 把文本编辑的数据写入文件with open(savePath.name, 'w', encoding=savePath.encoding) as file:file.write(cls.text.get('1.0', 'end')[:-1:])else:# 保存文本数据,写入文件with open(openPath.name, 'w', encoding=openPath.encoding) as file:file.write(cls.text.get('1.0', 'end')[:-1:])# 初始化窗口if ifYes != None and savePath:cls.base.title('无标题 - 记事本')cls.readText = ''cls.text.delete('1.0', 'end')cls.base.event_generate('<Key>')# 编辑Text文本@classmethoddef editText(cls, event=None):print('1editText:', [cls.text.get('1.0', 'end')])data = cls.text.get('1.0', 'end')# 文本编辑时窗口标题前加入'*'if data[:-1:] != cls.readText and data != '\n' and cls.base.title()[0] != '*':cls.base.title('*' + cls.base.title())# 编辑过文本未保存或与原文本相同时窗口标题前去掉'*'elif data[:-1:] == cls.readText or data == '\n':if cls.base.title()[0] == '*':cls.base.title(cls.base.title()[1::])# 如果是键盘Ctrl+O触发打开文件(必须)cls.textGet = cls.text.get('1.0', 'end')# 编辑撤销数据存储if cls.editIndex == -1:cls.editData.append((cls.textGet, cls.fontSize.get()))if len(cls.editData) != 1 and cls.textGet == cls.editData[cls.editIndex-1][0]:cls.editData.pop(-2)else:if cls.textGet != cls.editData[cls.editIndex][0]:cls.editData.insert(len(cls.editData) + cls.editIndex + 1, (cls.textGet, cls.fontSize.get()))buf = cls.editData[:len(cls.editData) + cls.editIndex + 1:]cls.editData.clear()cls.editData.extend(buf)cls.editIndex = -1else:cls.editData.pop(len(cls.editData) + cls.editIndex)cls.editData.insert(len(cls.editData) + cls.editIndex+1, (cls.textGet, cls.fontSize.get()))print('2editText:', [cls.text.get('1.0', 'end')])print('2readText:', [cls.readText])print('2editData:', cls.editData)# 状态栏:更新字体大小百分比@classmethoddef FontSizeEvent(cls, event):# 菜单调整字体大小if event == '放大':cls.base.event_generate('<Control-Key-+>')  # 引起键盘触发事件returnelif event == '缩小':cls.base.event_generate('<Control-minus>')  # 引起键盘触发事件returnelif event == '默认缩放':cls.base.event_generate('<Control-Key-0>')  # 引起键盘触发事件return# 快捷键调整字体大小# 向下滚动if event.delta < 0 or event.keysym == 'minus':# 字体大小范围if cls.fontSize.get() <= 1:return# 缩小字体cls.fontSize.set(cls.fontSize.get() - 1)print('向上滚动,字体大小:', cls.fontSize.get())# 向上滚动else:# 字体大小范围if cls.fontSize.get() >= FONT_SIZE * 5:return# 放大字体cls.fontSize.set(cls.fontSize.get() + 1)# 恢复默认缩放if event.keysym == '0':cls.fontSize.set(FONT_SIZE)print('向下滚动,字体大小:', cls.fontSize.get())# 改变字体大小cls.setFont.config(size=cls.fontSize.get())# 改变底部显示字体大小百分比cls.fontSizeLabel.config(text='{:.0%}'.format(cls.fontSize.get() / FONT_SIZE))# 状态栏:获取Text光标位置@classmethoddef cursorPosition(cls, event):row, column = event.widget.index("insert").split(".")print("光标位置:行", row, "列", int(column) + 1)cls.locationLabel.config(text=f'  第 {row} 行,第 {int(column) + 1} 列')# 勾选自动换行显示与否@classmethoddef setWrap(cls):# 设置自动换行if cls.wrap.get():# 自动换行设置cls.text.config(wrap='word')# 移除底部水平滑动条cls.bottomScrollbar.pack_forget()# 底部框架没有组件显示时移除if not cls.state.get():cls.bottomFrame.pack_forget()# 设置取消自动换行else:# 先移除右侧滚动条,再显示cls.rightScrollbar.pack_forget()# 先移除中间文本域,再显示cls.text.pack_forget()# 显示底部框架cls.bottomFrame.pack(side=tk.BOTTOM, fill='both')# 取消自动换行设置cls.text.config(wrap='none')# 显示底部水平滑动条cls.bottomScrollbar.pack(fill='both')# 再显示右侧滚动条cls.rightScrollbar.pack(side=tk.RIGHT, fill='both')# 再中间文本域cls.text.pack(expand=True, fill='both')# 勾选底部状态栏显示与否@classmethoddef setState(cls):# 底部显示状态栏if cls.state.get():# 先移除右侧滚动条,再显示cls.rightScrollbar.pack_forget()# 先移除中间文本域,再显示cls.text.pack_forget()# 显示底部框架cls.bottomFrame.pack(side=tk.BOTTOM, fill='both')# 显示状态栏cls.stateFrame.pack(side=tk.BOTTOM, fill='both')# 底部移除状态栏else:# 移除状态栏cls.stateFrame.pack_forget()# 底部框架没有组件显示时移除if cls.wrap.get():cls.bottomFrame.pack_forget()# 再显示右侧滚动条cls.rightScrollbar.pack(side=tk.RIGHT, fill='both')# 再中间文本域cls.text.pack(expand=True, fill='both')# Text文本鼠标右键菜单事件@classmethoddef rightKeyEvent(cls, event, object):object.post(event.x_root, event.y_root)# 创建顶级窗口后,根窗口会对其产生影响(比如:打开的对话框是对根窗口打开的,操作的却是顶级窗口)
# 创建新窗口(需要用到线程,否则前面创建的窗口不能运行)
def newWindow(event=None):newBase = tk.Toplevel()UI2 = WindowsUI(newBase)UI2.workFunc()  # 项目运行函数# 主窗口
def mainWindow():base = tk.Tk()  # 新建主窗口UI = WindowsUI(base)UI.workFunc()  # 项目运行函数WindowsUI.mainLoop()      # 窗口主循环# 全局变量
openPath = ''# 主函数
def main():mainWindow()              # 主窗口# 代码测试
if __name__ == '__main__':main()
else:print(f'导入{__name__}模块')

作者:周华

创作日期:2023/11/23

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/212089.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 执行Excel VBA宏工具类

写在前面 在Excel文档的自动化处理流程中&#xff0c;有部分值需要通过已定义的宏来求解&#xff0c;所以延伸出了用C# 调用Excel中的宏代码的需求。 首先要从NuGet中引入Microsoft.Office.Interop.Excel 类库 using Excel Microsoft.Office.Interop.Excel; 代码实现 /// &l…

Python---把函数的返回值作为另外一个函数的参数

def test1():return 50def test2(num):print(num)# 1. 保存函数test1的返回值 result test1()# 2.将函数返回值所在变量作为参数传递到test2函数 test2(result) # 50

【Redisson】基于自定义注解的Redisson分布式锁实现

前言 在项目中&#xff0c;经常需要使用Redisson分布式锁来保证并发操作的安全性。在未引入基于注解的分布式锁之前&#xff0c;我们需要手动编写获取锁、判断锁、释放锁的逻辑&#xff0c;导致代码重复且冗长。为了简化这一过程&#xff0c;我们引入了基于注解的分布式锁&…

走迷宫(BFS宽度优先搜索)

给定一个 nm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0 表示可以走的路&#xff0c;1 表示不可通过的墙壁。 最初&#xff0c;有一个人位于左上角 (1,1)处&#xff0c;已知该人每次可以向上、下、左、右任意一个方向移动…

使用【画图】软件修改图片像素、比例和大小

打开电脑画图软件&#xff0c;点击开始 windows附件 画图 在画图软件里选择需要调整的照片&#xff0c;点击文件 打开 在弹出窗口中选择照片后点击打开 照片在画图软件中打开后&#xff0c;对照片进行调整。按图中顺序进行 确定后照片会根据设定的值自动调整 保存…

劲松中西医医院HPV诊疗中心分享7个预防HPV的小技巧

同居男女是HPV感染的高危人群&#xff0c;因为HPV主要通过性传播&#xff0c;而且对健康影响很大。为了有效避免男女在同居时感染HPV&#xff0c;以下是一些建议&#xff1a; 1. 同居时正确使用安全套&#xff1a;正确使用安全套可以有效地避免同居过程中被感染HPV。同时要把控…

字母异位词分组

给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [[“bat”],[“nat”,“tan…

二十三、RestClient操作索引库

目录 例&#xff1a;利用JavaRestClient实现创建、删除索引库&#xff0c;判断索引库是否存在 1、编写mapping映射 2、初始化JavaRestClient &#xff08;1&#xff09;导入elasticsearch的依赖 &#xff08;2&#xff09;修改elasticsearch的版本 &#xff08;3&#xf…

阅读记录【arXiv2020】 Adaptive Personalized Federated Learning

Adaptive Personalized Federated Learning 论文地址&#xff1a; https://arxiv.org/abs/2003.13461 摘要 对联邦学习算法个性化程度的研究表明&#xff0c;只有最大化全局模型的性能才会限制局部模型的个性化能力。在本文中&#xff0c;我们提倡自适应个性化联合学习&…

鸿蒙 ark ui 轮播图实现教程

前言&#xff1a; 各位同学有段时间没有见面 因为一直很忙所以就没有去更新博客。最近有在学习这个鸿蒙的ark ui开发 因为鸿蒙不是发布了一个鸿蒙next的测试版本 明年会启动纯血鸿蒙应用 所以我就想提前给大家写一些博客文章 效果图 具体实现 我们在鸿蒙的ark ui 里面列表使…

为什么 x86 操作系统从 0x7c00 处开始

0x00&#xff1a;x86 架构 BIOS 引导加载程序中的"0x7C00"之谜 你知道 x86 操作系统中的"0x7C00"这个神奇数字吗 ? "0x7C00" 是BIOS加载MBR&#xff08;主引导记录&#xff0c;磁盘中的第一个扇区&#xff09;的内存地址。操作系统或引导加载…

【Java并发】聊聊线程池原理以及实际应用

线程其实对于操作系统来说是宝贵的资源&#xff0c;java层面的线程其实本质还是依赖于操作系统内核的线程进行处理任务&#xff0c;如果频繁的创建、使用、销毁线程&#xff0c;那么势必会非常浪费资源以及性能不高&#xff0c;所以池化技术&#xff08;数据库连接池、线程池&a…