tkinter 写一个简易的ide

2018-10-29 15:29:59来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

简易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()

运行效果

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

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

 

 

 

 

 

 

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:第九天- 文件操作 r w a 文件复制/修改

下一篇:pycharm社区版新建django文件不友好操作