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.pswp.cn/news/164980.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164980.shtml
英文地址,請注明出處:http://en.pswp.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…