tkinter 写一个简易的ide

时间:2023-03-10 06:09:24
tkinter 写一个简易的ide

简易IDE

基于tkinter的简易ide,参考文档和Baidu的资料制作的,过程中遇到了很多问题,也学到了很多知识。

功能:

1、菜单栏

2、编辑功能 open save...

3、快捷键  ctrl + O...

4、主题

5、右键功能

目录:

tkinter 写一个简易的ide

# bg 背景图 已完毕

# ico 图标

tkinter 写一个简易的ide   tkinter 写一个简易的ide   tkinter 写一个简易的ide    tkinter 写一个简易的ide   tkinter 写一个简易的ide   tkinter 写一个简易的ide  tkinter 写一个简易的ide   tkinter 写一个简易的ide   tkinter 写一个简易的ide

# 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()

运行效果

tkinter 写一个简易的ide

还有其它的小功能,运行之后就知道了。

问题:本来还说增加设置背景图片的功能,遇到了问题,资料也不足。