美文网首页大数据 爬虫Python AI SqlPython小哥哥
Python Tkinter实战——自制文本编辑器 !

Python Tkinter实战——自制文本编辑器 !

作者: 14e61d025165 | 来源:发表于2019-04-26 15:11 被阅读1次

    本篇为视频课程 tkinter入门详解 章节对应文档,请结合视频学习,地址见文末

    4.1 自制编辑器

    <tt-image data-tteditor-tag="tteditorTag" contenteditable="false" class="syl1556262654416" data-render-status="finished" data-syl-blot="image" style="box-sizing: border-box; cursor: text; color: rgb(34, 34, 34); font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: block;"> image

    <input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>

    编辑器预览

    完整项目地址 可以添加QQ源码群1004391443,有飞机大战、颜值打分器、打砖块小游戏、红包提醒神器、小姐姐表白神器等具体的实训项目,有清晰源码,有相应的文件

    项目实战要点

    在之前章节中已经学习了tkinter的大量知识,但仍然有部分知识是没有覆盖到的,换句话说,本教程并不是一本事无巨细的帮助文档,未提到的知识,我将在项目实战中列出,根据具体使用场景来学习。

    • 覆写窗口关闭事件

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">1self.protocol('WM_DELETE_WINDOW', self.exit_editor)
    </pre>

    • 实现鼠标右键菜单

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1def _create_right_popup_menu(self):
    2 popup_menu = Menu(self.content_text, tearoff=0)
    3 for it1, it2 in zip(['剪切', '复制', '粘贴', '撤销', '恢复'],
    4 ['cut', 'copy', 'paste', 'undo', 'redo']):
    5 popup_menu.add_command(label=it1, compound='left',
    6 command=self._shortcut_action(it2))
    7 popup_menu.add_separator()
    8 popup_menu.add_command(label='全选', command=self.select_all)
    9 self.content_text.bind('<Button-3>',
    10 lambda event: popup_menu.tk_popup(event.x_root, event.y_root))
    </pre>

    • 使用闭包处理回调事件

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 def _shortcut_action(self, type):
    2 def handle():
    3 if type == "new_file":
    4 self.new_file()
    5 elif type == "open_file":
    6 self.open_file()
    7 elif type == "save":
    8 self.save()
    9 elif type == "cut":
    10 # ………… 省略部分条件判断
    11 if type != "copy" and type != "save":
    12 self._update_line_num()
    13 return handle
    </pre>

    • 使用tkinter中的定时回调

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">1self.content_text.after(200, self._toggle_highlight)
    </pre>

    • 实现文本搜索功能

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">1start_pos = self.content_text.search(key, start_pos, nocase=ignore_case, stopindex="end")
    </pre>

    遗留待完善问题

    • 处理文本文件编码
    • 自制的编辑器目前无法打开不同编码格式的文本文件,需要能自动识别文本文件的编码
    • 建议考虑使用chardet 模块来识别编码

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">1import chardet
    2
    3with open('xxx.py', 'rb') as file:
    4 print(chardet.detect(file.read(1024)))
    </pre>

    • 搜索框定位
    • 搜索框应当根据当前编辑器的相对位置来显示

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">1# 获取根窗口的绝对位置,依据根窗口的位置计算搜索框的显示位置
    2print(self.winfo_rootx(), self.winfo_rooty())
    </pre>

    • 使用自定义的选项卡控件重构编辑器
    • 我们之前已经自定义了一个选项卡控件,实现了双创建选项卡,删除选项卡等功能,使用该控件重构编辑器,使编辑器更加实用

    搭建 UI 框架

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1from tkinter import *
    2from tkinter.ttk import Scrollbar
    3
    4theme_color = {
    5 'Default': '#000000.#FFFFFF',
    6 'Greygarious': '#83406A.#D1D4D1',
    7 'Aquamarine': '#5B8340.#D1E7E0',
    8 'Bold Beige': '#4B4620.#FFF0E1',
    9 'Cobalt Blue': '#ffffBB.#3333aa',
    10 'Olive Green': '#D1E7E0.#5B8340',
    11 'Night Mode': '#FFFFFF.#000000',
    12}
    13
    14
    15class EditorPlus(Tk):
    16 def init(self):
    17 super().init()
    18 self.set_window()
    19 self.create_menu_bar()
    20 self.create_shortcut_bar()
    21 self.create_body()
    22
    23 # 设置初始窗口的属性
    24 def set_window(self):
    25 self.title("EditorPlus")
    26 self.geometry('650x450')
    27
    28 # 创建整个菜单栏
    29 def create_menu_bar(self):
    30 menu_bar = Menu(self)
    31 # 创建文件的联级菜单
    32 file_menu = Menu(menu_bar, tearoff=0)
    33 file_menu.add_command(label='新建', accelerator='Ctrl+N')
    34 file_menu.add_command(label='打开', accelerator='Ctrl+O')
    35 file_menu.add_command(label='保存', accelerator='Ctrl+S')
    36 file_menu.add_command(label='另存为', accelerator='Shift+Ctrl+S')
    37 file_menu.add_separator()
    38 file_menu.add_command(label='退出', accelerator='Alt+F4')
    39
    40 # 在菜单栏上添加菜单标签,并将该标签与相应的联级菜单关联起来
    41 menu_bar.add_cascade(label='文件', menu=file_menu)
    42
    43 # 创建编辑的联级菜单
    44 edit_menu = Menu(menu_bar, tearoff=0)
    45 edit_menu.add_command(label='撤销', accelerator='Ctrl+Z')
    46 edit_menu.add_command(label='恢复', accelerator='Ctrl+Y')
    47 edit_menu.add_separator()
    48 edit_menu.add_command(label='剪切', accelerator='Ctrl+X')
    49 edit_menu.add_command(label='复制', accelerator='Ctrl+C')
    50 edit_menu.add_command(label='粘贴', accelerator='Ctrl+V')
    51 edit_menu.add_separator()
    52 edit_menu.add_command(label='查找', accelerator='Ctrl+F')
    53 edit_menu.add_separator()
    54 edit_menu.add_command(label='全选', accelerator='Ctrl+A')
    55 menu_bar.add_cascade(label='编辑', menu=edit_menu)
    56
    57 # 视图菜单
    58 view_menu = Menu(menu_bar, tearoff=0)
    59 show_line_number = IntVar()
    60 show_line_number.set(1)
    61 view_menu.add_checkbutton(label='显示行号', variable=show_line_number)
    62
    63 highlight_line = IntVar()
    64 view_menu.add_checkbutton(label='高亮当前行', onvalue=1, offvalue=0, variable=highlight_line)
    65
    66 # 在主题菜单中再添加一个子菜单列表
    67 themes_menu = Menu(menu_bar, tearoff=0)
    68 view_menu.add_cascade(label='主题', menu=themes_menu)
    69
    70 theme_choice = StringVar()
    71 theme_choice.set('Default')
    72 for k in sorted(theme_color):
    73 themes_menu.add_radiobutton(label=k, variable=theme_choice)
    74
    75 menu_bar.add_cascade(label='视图', menu=view_menu)
    76
    77 about_menu = Menu(menu_bar, tearoff=0)
    78 about_menu.add_command(label='关于')
    79 about_menu.add_command(label='帮助')
    80 menu_bar.add_cascade(label='关于', menu=about_menu)
    81 self["menu"] = menu_bar
    82
    83 # 创建快捷菜单栏
    84 def create_shortcut_bar(self):
    85 shortcut_bar = Frame(self, height=25, background='#20b2aa')
    86 shortcut_bar.pack(fill='x')
    87
    88 # 创建程序主体
    89 def create_body(self):
    90 # 创建行号栏 (takefocus=0 屏蔽焦点)
    91 line_number_bar = Text(self, width=4, padx=3, takefocus=0, border=0,
    92 background='#F0E68C', state='disabled')
    93 line_number_bar.pack(side='left', fill='y')
    94
    95 # 创建文本输入框
    96 content_text = Text(self, wrap='word')
    97 content_text.pack(expand='yes', fill='both')
    98
    99 # 创建滚动条
    100 scroll_bar = Scrollbar(content_text)
    101 scroll_bar["command"] = content_text.yview
    102 content_text["yscrollcommand"] = scroll_bar.set
    103 scroll_bar.pack(side='right', fill='y')
    104
    105
    106if "main" == name:
    107 app = EditorPlus()
    108 app.mainloop()
    </pre>

    完善编辑器的功能

    实现了基本的界面框架之后,只需将相应的功能一个一个添加上去即可。

    相关文章

      网友评论

        本文标题:Python Tkinter实战——自制文本编辑器 !

        本文链接:https://www.haomeiwen.com/subject/gutqnqtx.html