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.mzph.cn/news/164980.shtml

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

相关文章

【技巧】前端开发技巧 增加前端的请求缓存 提高开发效率

定义变量 /*** 开发缓存 开关* 说明* 方便开发使用 提升开发效率* true 打开缓存* false 关闭缓存 这里上线的时候必须改为* type {boolean}*/ const cacheFlag true/*** 排除某个url 方便开发时的数据实时生效* 这里根据开发到哪个功能 实时变更&#xff0c; 比如开…

京东数据分析(京东大数据):2023年10月京东手机行业品牌销售排行榜

鲸参谋监测的京东平台10月份手机市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;今年10月份&#xff0c;京东平台手机行业的销量约340万&#xff0c;环比增长约11%&#xff0c;同比则下滑约2%&#xff1b;销售额为108亿&#xff0c;环比增长约17%&#x…

请你说一下Vue中v-if和v-for的优先级谁更高

v-if 与 v-for简介 v-ifv-forv-if & v-for使用 v-if 与 v-for优先级比较 vue2 中&#xff0c;v-for的优先级高于v-if 例子进行分析 vue3 v-if 具有比 v-for 更高的优先级 例子进行分析 总结 在vue2中&#xff0c;v-for的优先级高于v-if在vue3中&#xff0c;v-if的优先级高…

RubyMine 2023:提升Rails/Ruby开发效率的强大利器

在Rails/Ruby开发领域&#xff0c;JetBrains RubyMine一直以其强大的功能和优秀的性能而备受开发者的青睐。现如今&#xff0c;我们迎来了全新的RubyMine 2023版本&#xff0c;它将为开发者们带来更高效的开发体验和无可比拟的工具支持。 首先&#xff0c;RubyMine 2023提供了…

Java-使用poi-tl根据word模板动态生成word

作者wangsz&#xff0c;想写一些关于word的工具&#xff0c;所以就写了这篇文章 1.首先&#xff0c;先导入所需要的依赖&#xff08;poi相关依赖即可&#xff09; <!-- POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi&l…

【libGDX】使用Mesh绘制立方体

1 前言 本文主要介绍使用 Mesh 绘制立方体&#xff0c;读者如果对 Mesh 不太熟悉&#xff0c;请回顾以下内容&#xff1a; 使用Mesh绘制三角形使用Mesh绘制矩形使用Mesh绘制圆形 在绘制立方体的过程中&#xff0c;主要用到了 MVP &#xff08;Model View Projection&#xff0…

目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】计算机视觉(最终篇)

目录 知识储备 KITTI数据集 1.KITTI数据集概述 2.数据采集平台 3.Dataset详述 算法原理

GIT无效的源路径/URL

ssh-add /Users/haijunyan/.ssh/id_rsa ssh-add -K /Users/haijunyan/.ssh/id_rsa

windows11上enable WSL

Windows电脑上要配置linux&#xff08;这里指ubuntu&#xff09;开发环境&#xff0c;主要有三种方式&#xff1a; 1&#xff09;在windows上装个虚拟机&#xff08;比如vmware&#xff09;。缺点是vmware加载ubuntu后系统会变慢很多&#xff0c;而且需要通过samba来实现window…

git clone -mirror 和 git clone 的区别

目录 前言两则区别git clone --mirrorgit clone 获取到的文件有什么不同瘦身仓库如何选择结语开源项目 前言 Git是一款强大的版本控制系统&#xff0c;通过Git可以方便地管理代码的版本和协作开发。在使用Git时&#xff0c;常见的操作之一就是通过git clone命令将远程仓库克隆…

【vue2】axios请求与axios拦截器的使用详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;当我们在路由跳转前与后我们可实现触发的操作 【前言】ajax是一种在javaScript代码中发请…

低代码开发与IT开发的区别

目录 一、含义不同 二、开发门槛不同 三、两者之间的区别 1、从技术特征来看 2、从目标开发者来看 四、低代码平台使用感受&#xff1f; &#xff08;1&#xff09;自定义模块&#xff0c;满足不同的业务需求 &#xff08;2&#xff09;工作流引擎&#xff0c;简化复杂流程的管…

机器学习实战-第4章 基于概率论的分类方法: 朴素贝叶斯

朴素贝叶斯 概述 贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。本章首先介绍贝叶斯分类算法的基础——贝叶斯定理。最后,我们通过实例来讨论贝叶斯分类的中最简单的一种: 朴素贝叶斯分类。 贝叶斯理论 & 条件概率 贝叶斯理论 …

linux网络之网络层与数据链路层

文章目录 一、网络层 1.IP协议 2.IP协议头格式 3.网段划分 4.特殊ip地址 5.IP地址的数量限制 6.私有ip和公网IP 7.路由 二、数据链路层 1.以太网 2.以太网帧格式 3.MAC地址 4.对比理解MAC地址和IP地址 5.MTU 6.ARP协议 ARP协议的工作流程 ARP数据报的格式 7.DNS 8.ICMP协议 9.N…

839 - Not so Mobile (UVA)

题目链接如下&#xff1a; Online Judge 这道题刘汝佳的解法极其简洁&#xff0c;用了20来行就解决了问题。膜拜…… 他的解法如下&#xff1a;天平&#xff08;UVa839紫书p157&#xff09;_天平 uva 839_falldeep的博客-CSDN博客 我写了两个&#xff08;都很冗长&#xff…

浅谈电气设备的绝缘在线监测与状态维修探究

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;在线监测是控制好电气设备绝缘的重要方式&#xff0c;为电力系统稳定奠定重要基础。在线监测电气设备时&#xff0c;要利用检测技术促进电力系统运行效率提升&#xff0c;让电气设备在具体工作过程中发挥更大作…

升级jdk17过程中,原来的jdk8下的webservice客户端怎样处理

背景&#xff1a;之前jdk8环境下&#xff0c;使用的cxf框架&#xff0c;而且是动态加载解析作为客户端。大家一直相处的很愉快。但是最近升级jdk17&#xff0c;发现cxf不好用了。网上百度&#xff0c;大部分都是说升级cxf版本&#xff0c;并且添加jaxb的相关依赖就可以了。但是…

在线接口测试工具fastmock使用

1、fastmock线上数据模拟器 在平时的项目测试中&#xff0c;尤其是前后端分离的时候&#xff0c;前端人员需要测试调用后端的接口&#xff0c;这个时候会出现测试不方便的情况。此时我们可以使用fastmock平台在线上模拟出一个可以调用的接口&#xff0c;方便前端人员进行数据测…

C/C++---------------LeetCode第2540. 最小公共值

最小公共值 题目及要求哈希算法双指针 题目及要求 给你两个整数数组 nums1 和 nums2 &#xff0c;它们已经按非降序排序&#xff0c;请你返回两个数组的 最小公共整数 。如果两个数组 nums1 和 nums2 没有公共整数&#xff0c;请你返回 -1 。 如果一个整数在两个数组中都 至少…

categraf托管与自升级

categraf支持多种方式进行部署、托管&#xff0c;社区里部署和管理categraf也是五花八门&#xff0c;大家自己使用方便即可。 之前我们觉得大家通过ansible之类的工具批量下发/更新就能很简单地完成任务&#xff0c;最近很多用户咨询我们关于categraf有没有更方便的升级方式&am…