简易IDE

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

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

功能:

1、菜单栏

2、编辑功能 open save...

3、快捷键  ctrl + O...

4、主题

5、右键功能

目录:

tkinter 写一个简易的ide Python 第1张

# bg 背景图 已完毕

# ico 图标

tkinter 写一个简易的ide Python 第2张   tkinter 写一个简易的ide Python 第3张   tkinter 写一个简易的ide Python 第4张    tkinter 写一个简易的ide Python 第5张   tkinter 写一个简易的ide Python 第6张   tkinter 写一个简易的ide Python 第7张  tkinter 写一个简易的ide Python 第8张   tkinter 写一个简易的ide Python 第9张   tkinter 写一个简易的ide Python 第10张

# 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 Python 第11张

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

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

 

 

 

 

 

 

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄