简易IDE
基于tkinter的简易ide,参考文档和Baidu的资料制作的,过程中遇到了很多问题,也学到了很多知识。
功能:
1、菜单栏
2、编辑功能 open save...
3、快捷键 ctrl + O...
4、主题
5、右键功能
目录:
# bg 背景图 已完毕
# ico 图标
# conf.ini 配置文件
使用 configparser库进行处理, 这里只用到了读取和写入,其它详细操作Baidu一下即可。
conf.ini
[window] program_name = Footprint Editor [icon] newfile = ico/newFile.png openfile = ico/open.png savefile = ico/save.png undofile = ico/undo.png redofile = ico/redo.png cutfile = ico/cut.png copyfile = ico/copy.png pastefile = ico/paste.png findfile = ico/find.png [theme] color_schemes = { 'Default': '#000000.#FFFFFF', 'Greygarious': '#83406A.#D1D4D1', 'Aquamarine': '#5B8340.#D1E7E0', 'Bold Beige': '#4B4620.#FFF0E1', 'Cobalt Blue': '#ffffBB.#3333aa', 'Olive Green': '#D1E7E0.#5B8340', 'Night Mode': '#FFFFFF.#000000'} select_themes = Default [common] bgstartsetting = False bgdir = bg bgname = default_bg.png
# textEditor.py 主要程序
textEditor.py
# -*- coding: utf-8 -*- # fengshunagzi # 菜单栏 # 编辑功能 打开 保存.. # 基本快捷键 剪贴 保存... # 主题 # 右键功能 from tkinter.filedialog import * from tkinter import * from pathlib import Path from PIL import Image, ImageTk import tkinter.messagebox as tmb import shutil from configparser import ConfigParser d = os.path.dirname(__file__) def exitText(event=None): # 保存 不保存 if tmb.askokcancel("Quit?", "Really quit?"): ## 退出前自动保存文件 save() root.destroy() def cut(): '''剪切''' content_text.event_generate("<<Cut>>") def copy(): '''复制''' content_text.event_generate("<<Copy>>") def paste(): '''粘贴''' content_text.event_generate("<<Paste>>") def undo(event=None): '''向前撤销''' content_text.event_generate("<<Undo>>") return 'break' def redo(event=None): '''向后返回''' content_text.event_generate("<<Redo>>") return 'break' def select_all(event=None): '''全选''' content_text.tag_add('sel', '1.0', 'end') return "break" def find_text(event=None): '''搜索字段''' search_toplevel = Toplevel(root) search_toplevel.title('Find Text') search_toplevel.transient(root) search_toplevel.resizable(False, False) Label(search_toplevel, text="Find All:").grid(row=0, column=0, sticky='e') search_entry_widget = Entry(search_toplevel, width=25) search_entry_widget.grid(row=0, column=1, padx=2, pady=2, sticky='we') search_entry_widget.focus_set() ignore_case_value = IntVar() Checkbutton(search_toplevel, text='IgnoreCase',variable=ignore_case_value).grid(row=1, column=1, sticky='e', padx=2, pady=2) Button(search_toplevel, text="Find All", underline=0, command=lambda: search_output( search_entry_widget.get(), ignore_case_value.get(), content_text, search_toplevel,search_entry_widget)).grid(row=0, column=2, sticky='e' + 'w', padx=2, pady=2) def close_search_window(): '''关闭选框''' content_text.tag_remove('match', '1.0', END) search_toplevel.destroy() search_toplevel.protocol('WM_DELETE_WINDOW', close_search_window) return "break" def search_output(needle, if_ignore_case, content_text,search_toplevel, search_box): '''匹配文字''' content_text.tag_remove('match', '1.0', END) matches_found = 0 if needle: start_pos = '1.0' while True: start_pos = content_text.search(needle, start_pos, nocase=if_ignore_case, stopindex=END) if not start_pos: break end_pos = '{}+{}c'.format(start_pos, len(needle)) content_text.tag_add('match', start_pos, end_pos) matches_found += 1 start_pos = end_pos content_text.tag_config('match', foreground='red', background='yellow') search_box.focus_set() search_toplevel.title('{} matches found'.format(matches_found)) def open_file(event=None): '''打开文件''' print(1) input_file_name = askopenfilename(defaultextension=".txt", filetypes=[("All Files", "*.*"), ("Text Documents", "*.txt")]) if input_file_name: global file_name file_name = input_file_name root.title('{} - {}'.format(os.path.basename(file_name),PROGRAM_NAME)) content_text.delete(1.0, END) with open(file_name) as _file: content_text.insert(1.0, _file.read()) update_line_numbers() def save(event=None): '''save文件''' global file_name try: if not file_name: save_as() else: write_to_file(file_name) except: pass return "break" def save_as(event=None): '''另存为''' input_file_name = asksaveasfilename(defaultextension=".txt", filetypes=[("All Files", "*.*"),("Text Documents", "*.txt")]) if input_file_name: global file_name file_name = input_file_name write_to_file(file_name) root.title('{} - {}'.format(os.path.basename(file_name),PROGRAM_NAME)) return "break" def write_to_file(file_name): '''写入文件''' try: content = content_text.get(1.0, 'end') with open(file_name, 'w') as the_file:the_file.write(content) except IOError: pass def new_file(event=None): '''新建文件''' root.title("Untitled") global file_name file_name = None content_text.delete(1.0,END) def display_about_messagebox(event=None): '''about messagebox''' tmb.showinfo( "About", "{}{}".format(PROGRAM_NAME, "\nTkinter GUIApplication\n Development Blueprints")) def display_help_messagebox(event=None): '''help messagebox''' tmb.showinfo("Help", "Fuck You: \nTkinter GUI Application\n Development Blueprints", icon='question') def on_content_changed(event=None): '''输入框发送改变时''' update_line_numbers() update_cursor_info_bar() def get_line_numbers(): '''获取行号''' output = '' if show_line_number.get(): row, col = content_text.index("end").split('.') for i in range(1, int(row)): output += str(i)+ '\n' return output def update_line_numbers(event = None): '''更新行号''' line_numbers = get_line_numbers() line_number_bar.config(state='normal') line_number_bar.delete('1.0', 'end') line_number_bar.insert('1.0', line_numbers) line_number_bar.config(state='disabled') def highlight_line(interval=100): '''高亮显示当前行''' content_text.tag_remove("active_line", 1.0, "end") content_text.tag_add("active_line", "insert linestart", "insert lineend+1c") content_text.after(interval, toggle_highlight) def undo_highlight(): '''取消高亮''' content_text.tag_remove("active_line", 1.0, "end") def toggle_highlight(event=None): '''高亮切换''' if to_highlight_line.get(): highlight_line() else: undo_highlight() def show_cursor_info_bar(): '''显示底部行列信息''' show_cursor_info_checked = show_cursor_info.get() if show_cursor_info_checked: cursor_info_bar.pack(expand='no', fill=None, side='right', anchor='se') else: cursor_info_bar.pack_forget() def update_cursor_info_bar(event=None): '''更新底部行列信息''' row, col = content_text.index(INSERT).split('.') line_num, col_num = str(int(row)), str(int(col)+1) # col starts at 0 infotext = "Line: {0} | Column: {1}".format(line_num, col_num) cursor_info_bar.config(text=infotext) def change_theme(event=None): '''修改主题''' selected_theme = themes_choices.get() # 写入配置文件 conf.set('theme', 'select_themes', selected_theme) saveConf() fg_bg_colors = color_schemes.get(selected_theme) foreground_color, background_color = fg_bg_colors.split('.') content_text.config(background=background_color, fg=foreground_color) def select_background(event=None): '''选择背景图片,已经取消,因为tkinter透明度不好用''' conf.set('common', 'bgstartsetting', 'true') saveConf() bgStartChecked = bgStart.get() print('[*] 是否启用背景设置', bgStartChecked) if bgStartChecked: print('弹出选择文件框') imgSelect = askopenfilename(defaultextension=".txt", filetypes=[( ".png", ".jpg"), ("Text Documents", "*.txt")]) if imgSelect: global imgSelectName imgSelectName = imgSelect print(imgSelectName) # C:/Users/hz/Desktop/我的文件/我的图片/11.jpg # 将图片拷贝到当前文件夹 修改配置参数 shutil.copyfile(imgSelectName, Path(d) / bgDir / bgName ) else: print('隐藏图片') print('使用主题选择的颜色') def set_background(): '''设置背景''' pass def readSettings(): '''读取配置文件''' global PROGRAM_NAME PROGRAM_NAME = conf.get('window', 'PROGRAM_NAME') global newFile newFile = conf.get('icon', 'newFile') global openFile openFile = conf.get('icon', 'openFile') global saveFile saveFile = conf.get('icon', 'saveFile') global undoFile undoFile = conf.get('icon', 'undoFile') global redoFile redoFile = conf.get('icon', 'redoFile') global cutFile cutFile = conf.get('icon', 'cutFile') global copyFile copyFile = conf.get('icon', 'copyFile') global pasteFile pasteFile = conf.get('icon', 'pasteFile') global findFile findFile = conf.get('icon', 'findFile') global color_schemes color_schemes_str = conf.get('theme', 'color_schemes') color_schemes = eval(color_schemes_str) global select_themes select_themes = conf.get('theme', 'select_themes') global bgstartsetting bgstartsetting = conf.get('common', 'bgstartsetting') global bgDir bgDir = conf.get('common', 'bgdir') global bgName bgName = conf.get('common', 'bgname') def saveConf(): '''保存配置文件''' fp = Path(d) / 'conf.ini' with open(fp, 'w') as fw: conf.write(fw) def show_popup_menu(event): '''显示右键菜单''' popup_menu.tk_popup(event.x_root, event.y_root) if __name__ == '__main__': ## 配置文件路径 fp = Path(d) / 'conf.ini' ## 实例化 conf = ConfigParser() ## 读取配置文件 conf.read(fp, encoding='utf8') ## 配置文件编码是utf-8 ## 读取里面的内容 readSettings() ## 开始窗体部分 root = Tk() # 引入菜单 menu_bar = Menu(root) file_menu = Menu(menu_bar, tearoff=0) edit_menu = Menu(menu_bar, tearoff=0) view_menu = Menu(menu_bar, tearoff=0) about_menu = Menu(menu_bar, tearoff=0) themes_menu = Menu(menu_bar, tearoff=0) # 添加菜单列表 menu_bar.add_cascade(label='File', menu=file_menu) menu_bar.add_cascade(label='Edit', menu=edit_menu) menu_bar.add_cascade(label='View', menu=view_menu) menu_bar.add_cascade(label='About', menu=about_menu) # File菜单 imageNew = ImageTk.PhotoImage(Image.open(Path(d) / newFile)) file_menu.add_command(label='New', accelerator='Ctrl+N', compound='left', image=imageNew, command=lambda: new_file()) imageOpen = ImageTk.PhotoImage(Image.open(Path(d) / openFile)) file_menu.add_command(label='Open', accelerator='Ctrl+O', compound='left', image=imageOpen, command=lambda: open_file()) imageSave = ImageTk.PhotoImage(Image.open(Path(d) / saveFile)) file_menu.add_command(label='Save', accelerator='Ctrl+S', compound='left', image=imageSave, command=lambda: save()) file_menu.add_command(label='Save as', accelerator='Ctrl+shift+s', compound='left', image='', command=lambda: save_as()) file_menu.add_separator() file_menu.add_command(label='Exit', accelerator='Alt+F4', compound='left', image='', command=lambda: exitText()) # Edit菜单 imageUndo = ImageTk.PhotoImage(Image.open(Path(d) / undoFile)) edit_menu.add_command(label='Undo', accelerator='Ctrl+Z', compound='left', image=imageUndo, command=lambda: undo()) imageRedo = ImageTk.PhotoImage(Image.open(Path(d) / undoFile)) edit_menu.add_command(label='Redo', accelerator='Ctrl+Y', compound='left', image=imageRedo, command=lambda: redo()) imageCut = ImageTk.PhotoImage(Image.open(Path(d) / cutFile)) edit_menu.add_command(label='Cut', accelerator='Ctrl+X', compound='left', image=imageCut, command=lambda: cut()) imageCopy = ImageTk.PhotoImage(Image.open(Path(d) / copyFile)) edit_menu.add_command(label='Copy', accelerator='Ctrl+C', compound='left', image=imageCopy, command=lambda: copy()) imagePaste= ImageTk.PhotoImage(Image.open(Path(d) / pasteFile)) edit_menu.add_command(label='Paste', accelerator='Ctrl+V', compound='left', image=imagePaste, command=lambda: paste()) edit_menu.add_separator() edit_menu.add_command(label='Find', accelerator='Ctrl+F', compound='left', image='', command=lambda: find_text()) edit_menu.add_separator() edit_menu.add_command(label='Select All', accelerator='Ctrl+A', compound='left', image='',command=lambda: select_all()) # About菜单 about_menu.add_command(label='About', accelerator='', compound='left', image='', command=lambda: display_about_messagebox()) about_menu.add_command(label='Help', accelerator='', compound='left', image='', command=lambda: display_help_messagebox()) # 添加横向Frame shortcut_bar = Frame(root, height=25, background='light seagreen') shortcut_bar.pack(expand='no', fill='x') # 添加纵向Frame line_number_bar = Text(root, width=4, padx=3, takefocus=0, border=0, background='khaki', state='disabled', wrap='none') line_number_bar.pack(side='left', fill='y') # 添加文本框 content_text = Text(root, wrap='word', undo=True) ## undo True 可以无限撤销 False 不能撤销 ## 给文本框保定鼠标事件 同时绑定执行函数 content_text.bind('<Control-y>', redo) # handling Ctrl + small-case y content_text.bind('<Control-Y>', redo) # handling Ctrl + upper-case Y content_text.bind('<Control-a>', select_all) # handling Ctrl + upper-case a content_text.bind('<Control-A>', select_all) # handling Ctrl + upper-case A content_text.bind('<Control-f>', find_text) #ctrl + f content_text.bind('<Control-F>', find_text) #ctrl + F content_text.bind('<Control-N>', new_file) #ctrl + N content_text.bind('<Control-n>', new_file) #ctrl + n content_text.bind('<Control-O>', open_file) #ctrl + O content_text.bind('<Control-o>', open_file) #ctrl + o content_text.bind('<Control-S>', save) #ctrl + S content_text.bind('<Control-s>', save) #ctrl + s content_text.bind('<Control-Shift-S>', save_as) #ctrl + shift + S content_text.bind('<Control-Shift-s>', save_as) #ctrl + sgift + s content_text.bind('<KeyPress-F1>', display_help_messagebox) content_text.bind('<Any-KeyPress>', on_content_changed) ## 切换行号 content_text.bind('<Button-1>', on_content_changed) content_text.tag_configure('active_line', background='ivory2') # 增加右键功能 popup_menu = Menu(content_text) popup_menu.add_command(label='Cut', compound='left', image=imageCut, command=lambda: cut()) popup_menu.add_command(label='Copy', compound='left', image=imageCopy, command=lambda: copy()) popup_menu.add_command(label='Paste', compound='left', image=imagePaste, command=lambda: paste()) popup_menu.add_command(label='Undo', compound='left', image=imageUndo, command=lambda: undo()) popup_menu.add_command(label='Redo', compound='left', image=imageRedo, command=lambda: redo()) popup_menu.add_separator() popup_menu.add_command(label='Select All', underline=7, command=select_all) ## 文本框绑定右键事件 content_text.bind('<Button-3>', show_popup_menu) ## 显示文本框 content_text.pack(expand='yes', fill='both') ## 增加滚动条 scroll_bar = Scrollbar(content_text) content_text.configure(yscrollcommand=scroll_bar.set) scroll_bar.config(command=content_text.yview) scroll_bar.pack(side='right', fill='y') # views 添加下拉选项 show_line_number = IntVar() show_line_number.set(1) view_menu.add_checkbutton(label="Show Line Number", variable=show_line_number, command=update_line_numbers) show_cursor_info = IntVar() show_cursor_info.set(1) view_menu.add_checkbutton(label="Show Cursor Location at Bottom", variable=show_cursor_info, command=show_cursor_info_bar) to_highlight_line = BooleanVar() to_highlight_line.set(1) view_menu.add_checkbutton(label="HighLight Current Line", variable=to_highlight_line, command=toggle_highlight) toggle_highlight() ## 增加分割线 view_menu.add_cascade(label="Themes", menu=themes_menu) # theme ## 增加theme菜单 themes_choices = StringVar() themes_choices.set(select_themes) for k,themes_choice in color_schemes.items(): themes_menu.add_radiobutton(label=k, variable=themes_choices, command=change_theme, value=k) # 添加设置背景功能 # bgStart = IntVar() # bgStart.set(0) # view_menu.add_checkbutton(label="Set Background", variable=bgStart, command=select_background) ## 添加快捷图标 icons = [(newFile, 'new_file'), (openFile, 'open_file'), (saveFile , 'save'),(cutFile, 'cut'), (copyFile, 'copy'), (pasteFile, 'paste'), (undoFile, 'undo'), (redoFile, 'redo'), (findFile, 'find_text')] for i, icon in enumerate(icons): tool_bar_icon = ImageTk.PhotoImage(Image.open(Path(d) / icon[0])) # cmd = eval(icon) tool_bar = Button(shortcut_bar, image=tool_bar_icon, command=eval(icon[1])) tool_bar.image = tool_bar_icon tool_bar.pack(side='left') ## 添加底部显示行号 cursor_info_bar = Label(content_text, text='Line: 1 | Column: 1') cursor_info_bar.pack(expand=NO, fill=None, side=RIGHT, anchor='se') ## 配置menu root.config(menu=menu_bar) ## 设置rootname root.title(PROGRAM_NAME) ## 设置最小size root.minsize(1000, 600) ## 设置居中显示 root.geometry('%dx%d+%d+%d' % ( 1000, 600, (root.winfo_screenwidth() - 1000) / 2, (root.winfo_screenheight() - 600) / 2)) ## 配置默认主题 change_theme() mainloop()
运行效果
还有其它的小功能,运行之后就知道了。
问题:本来还说增加设置背景图片的功能,遇到了问题,资料也不足。