整个文本编辑器可以分解为8个部分,如下图
① → 程序主窗口
② → 菜单栏
③ → 快捷菜单栏
④ → 关联选项栏
⑤ → 子关联选项栏
⑥ → 行号栏
⑦ → 文本栏
⑧ → 滚动条栏
主要需要两个,一个是主题配色的参数设置,还有一个就是图标(9个快捷菜单栏内容+ 1个主窗口 + 1个系统提示)
① 主题参数设置如下:(以'.'
点分割的前面属于前景色,后面属于背景色)
theme_color = {
'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',
}
#这里将快捷菜单栏上图表对应的名称存放列表中,和主题颜色参数放在一个py文件中,后面使用时候直接调用
ICONS = ['new_file', 'open_file', 'save', 'cut', 'copy', 'paste',
'undo', 'redo', 'find_text']
② 图标文件(除了editor.ico
和about.gif
两个文件,其余的都是属于快捷菜单栏上所对应的图标文件)
③ 首先将上面的参数放置在一个py文件中,比如命名为editor_style.py(自己的命名,这里可以自由命名),其次是使放置图标的文件夹与该py文件处于同一路径下(也就是同一个文件夹下面)
为了创建方便,选择直接继承Tk类。具体的操作就是在继承Tk类的基础上(使用Tk中的方法),然后利用它的方法创建自己的东西,这里程序主窗口设置的有:标题、位置、图标和窗口退出提醒
import os #系统模块
from editor_style import theme_color, ICONS #这里就是从之前命名的文件中导入两个参数
from tkinter import * #导入tkinter中所有的方法
from tkinter import filedialog, messagebox #这两个需要单独导入
from tkinter.ttk import Scrollbar, Checkbutton, Label, Button #导入ttk模块中的指定几个组件
#继承Tk这个类
class EditorPlus(Tk):
def __init__(self): #初始化自己的创建的EditorPlus类
super().__init__() #在继承Tk这个类的基础上在自己的类中添加内容
self._set_window_() #设置程序运行主窗口
#设置初始化窗口的属性
def _set_window_(self):
self.title("EditorPlus") #窗口名称
scn_width, scn_height = self.maxsize() #获得程序运行机器的分辨率(屏幕的长和宽)
wm_val = '750x450+{}+{}'.format((scn_width - 750) // 2, (scn_height - 450) // 2)
self.geometry(wm_val) #将窗口设置在屏幕的中间
self.iconbitmap("img/editor.ico") #加载一下窗口的左上方显示的图片
self.protocol('WM_DELETE_WINDOW', self.exit_editor) #设置窗口关闭提醒
def exit_editor(self):
if messagebox.askokcancel('退出?','确定退出吗?'): #设置文本提示框
self.destroy() #满足条件的话主窗口退出
if __name__ == '__main__': #代码分块
app = EditorPlus() #类的实例化
app.mainloop() #程序运行
→ 输出的结果为:(可以看到窗口的左上方有了图标,在点击退出按钮时候会有消息提醒)
要添加的内容直接通过封装一个函数,然后把这个函数放在自己创建的类的初始化函数中,然后就是对这个函数进行详细参数的设置
def __init__(self):
super().__init__()
self._set_window_()
self._create_menu_bar_() #创建菜单组件
def _create_menu_bar_(self):
menu_bar = Menu(self) #继承原来Tk的Menu的对象
menu_bar.add_cascade(label='文件') #创建文件的菜单栏
menu_bar.add_cascade(label='编辑') #创建编辑的菜单栏
menu_bar.add_cascade(label='视图') #创建视图的菜单栏
menu_bar.add_cascade(label='关于') #创建关于的菜单栏
self['menu'] = menu_bar #将设置好的菜单栏放在程序的主窗口上
→ 输出的结果为:(出现的窗口中已经显示的有菜单栏相关的内容了)
关联选项栏就是当鼠标点击菜单栏选项的时候会弹出一个功能选项卡,比如点击文件菜单栏选项的时候会有新建文件,打开文件,保存文件,另存为等等,如下
这时候也是相当于创建菜单栏,只不过这里设置的位置不是程序主窗口,下面就是“文件”菜单栏中进行关联选项栏内容的设置,需要注意两点
① 这个关联选项栏是基于菜单栏的,所以进行关联选项栏实例化应该是在刚刚实例化菜单栏的基础上,故有了这行代码file_menu = Menu(menu_bar, tearoff = 0)
,注意这里有一个参数tearoff
,当tearoff=0
时候,表示关联选项栏与菜单栏不可以脱离(可以把这个参数设置为1看一下效果),而且最后在设置“文件”菜单栏时候也需要指定这个关联选项对应的位置,即menu_bar.add_cascade(label='文件',menu = file_menu)
中的menu = file_menu
参数的设置
② 添加具体的内容这里用到了file_menu.add_command()
和file_menu.add_separator()
两个方法,其中第一个方法中label就是内容的名称,accelerator
是对应的快捷操作按键的说明,还有一个要使用的参数command
就是该操作按键对应的事件回调(也就是点击了这个快捷键要有反应的,而不是单单有个快捷键的显示,之后会介绍,这里先把UI框架搭建起来,后面再做功能的附加);第二个方法就是单纯的创建一个命令分割线,提醒注意分割线下面的操作,一般回放退出或者警示性的命令
def _create_menu_bar_(self):
menu_bar = Menu(self)
file_menu = Menu(menu_bar, tearoff = 1)
file_menu.add_command(label='新建',accelerator = 'Ctrl+N')
file_menu.add_command(label='打开',accelerator = 'Ctrl+O')
file_menu.add_command(label='保存',accelerator = 'Ctrl+S')
file_menu.add_command(label='另存为',accelerator = 'Ctrl+Shift+S')
file_menu.add_separator() #设置分割线
file_menu.add_command(label='退出',accelerator = 'Alt+F4')
menu_bar.add_cascade(label='文件',menu = file_menu)
menu_bar.add_cascade(label='编辑')
menu_bar.add_cascade(label='视图')
menu_bar.add_cascade(label='关于')
self['menu'] = menu_bar
可以基于上述的知识点对之后的“编辑”,“视图”和“关于”菜单栏进行关联选项栏中内容的设定。这一部分注意事项如下:
① “视图”菜单栏下除了关联选项栏之外,还有子关联选项栏(多级的关联选项栏的设置),就是放置有关的菜单栏对象不同
② 多选选项和单选选项的设置(add_checkbutton()
和 add_radiobutton()
方法的使用,两个方法分别对应多选与单选)
def _create_menu_bar_(self):
menu_bar = Menu(self) #实例化菜单栏对象
#文件菜单
file_menu = Menu(menu_bar, tearoff = 0) #基于菜单栏实例化“文件”关联选项栏对象
file_menu.add_command(label='新建',accelerator = 'Ctrl+N')
file_menu.add_command(label='打开',accelerator = 'Ctrl+O')
file_menu.add_command(label='保存',accelerator = 'Ctrl+S')
file_menu.add_command(label='另存为',accelerator = 'Ctrl+Shift+S')
file_menu.add_separator()
file_menu.add_command(label='退出',accelerator = 'Alt+F4')
menu_bar.add_cascade(label='文件',menu = file_menu) #将“文件”关联选项栏放在“文件”菜单栏上
#编辑菜单
edit_menu = Menu(menu_bar,tearoff = 0) #基于菜单栏实例化“编辑”关联选项栏对象
edit_menu.add_command(label='撤销',accelerator = 'Ctrl+Z')
edit_menu.add_command(label='恢复',accelerator = 'Ctrl+Y')
edit_menu.add_separator()
edit_menu.add_command(label='剪切',accelerator = 'Ctrl+X')
edit_menu.add_command(label='复制',accelerator = 'Ctrl+C')
edit_menu.add_command(label='粘贴',accelerator = 'Ctrl+V')
edit_menu.add_separator()
edit_menu.add_command(label='查找',accelerator = 'Ctrl+F')
edit_menu.add_separator()
edit_menu.add_command(label='全选',accelerator = 'Ctrl+A')
menu_bar.add_cascade(label='编辑',menu=edit_menu) #将“编辑”关联选项栏放在“编辑”菜单栏上
#视图菜单
view_menu = Menu(menu_bar, tearoff = 0) #将“视图”关联选项栏放在“视图”菜单栏上
self.show_line_number = IntVar() #为了方便定义的这个变量在类中其他的函数中使用,这里将变量变成类中的实例属性
self.show_line_number.set(1)
view_menu.add_checkbutton(label='显示行号',variable=self.show_line_number)
self.highlight_line = IntVar() #这里也是和上面一样,因为后面要用到这个变量
view_menu.add_checkbutton(label='高亮当前行', onvalue = 1, offvalue = 0, variable = self.highlight_line)
#在主题菜单中再添加一个子菜单
theme_menu = Menu(menu_bar, tearoff = 0)
self.theme_choice = StringVar()
self.theme_choice.set('Default')
for k in sorted(theme_color): #这里的theme_color就是之前从editor_style.py文件中导入的参数内容
theme_menu.add_radiobutton(label=k,variable=self.theme_choice)
view_menu.add_cascade(label='主题',menu=theme_menu) #注意这里添加的菜单栏指定的对象
menu_bar.add_cascade(label='视图', menu=view_menu) #注意这里添加的菜单栏指定的对象
#关于菜单
about_menu = Menu(menu_bar, tearoff= 0)
about_menu.add_command(label = '关于') #这里暂时未设置快捷键
about_menu.add_command(label = '帮助') #一般是全局的快捷键,比如帮助的快捷键一般是F1
menu_bar.add_cascade(label='关于',menu=about_menu)
self['menu'] = menu_bar
→ 输出的结果为:(至此就完成了菜单栏以及关联选项栏以及子关联选项栏的设置了)
快捷菜单栏就是存放关联选项栏中内容的指令按钮内容,在初始化函数中定一个创建快捷菜单栏的函数,然后在这个函数中进行详细内容的设置,注意事项如下
① 需要先创建一个类属性,也就是在class语句下面添加一个变量赋值语句icon_res = []
,用来保存所有的图标
② 为了避免敲九行类似的代码,这里使用了遍历循环进行创建快捷键的按钮(这也就是最初要添加ICONS这个变量的原因)
#在__init__函数下面要先添加这个函数,和之前创建_create_menu_bar_函数一样
def _create_shortcut_bar_(self):
shortcut_bar = Frame(self, height=25, background='#20b2aa') #创建一个Frame放置快捷按钮
shortcut_bar.pack(fill='x') #x轴方向填充
for icon in ICONS: #遍历循环设置快捷按钮
tool_icon = PhotoImage(file='img/{}.gif'.format(icon))
tool_btn = Button(shortcut_bar, image=tool_icon)
tool_btn.pack(side='left')
self.icon_res.append(tool_icon)#可以试试如果不设置类属性,运行后图标排列会是什么样子的
→ 输出的结果为:(快捷菜单栏设置完毕)
行号栏就是文本数据输入后会按照自动换行的方式,显示出每行数据所对应的行数,这里先把界面设置出来,之后在进行功能设计,takefocus=0
表示屏蔽焦点再配合state='disabled'
,就是对这个行号栏无法选定的意思(因为是个Text对象,默认的要是可以向里面进行文字输入的,所以这里要设定无法选定,只是用来显示行号的,就像sublime里面的行号栏一样)
也就是创建一个文本输入框,允许文字输入,同时要有随着窗口大小进行自动换行的功能(wrap = 'word'
),文本输入框的位置是向左紧贴着行号栏,其次是要向xy两边填充(expand='yes'
,fill='both'
)
为了配合文本输入框中文字内容的输入,应该配备的有滚动条进行内容滑动的显示,注意滚动条的创建,首先是要将滚动条与文本内容数据结合起来(设置滚动条的位置),还要把文字与滚动条结合起来,也就是说在滑动滚动条时候文字内容也会发生变化,在进行文本数据输入的过程中,滚动条也会发生变化(双向绑定)
#在__init__函数下面要先添加这个函数
def _create_body_(self):
#创建行号栏(takefocus屏蔽焦点)
self.line_number_bar = Text(self,width=4,padx =3,takefocus=0,border=0,
background='#F0E68C',state='disabled') #之后会使用这个变量,所以将其转换为实例属性
self.line_number_bar.pack(side='left',fill='y')
#创建文本输入框
self.content_text = Text(self, wrap = 'word')
self.content_text.pack(expand='yes',fill='both')
#创建滚动条
scroll_bar = Scrollbar(self.content_text)
scroll_bar['command'] = self.content_text.yview
self.content_text['yscrollcommand'] = scroll_bar.set
scroll_bar.pack(side='right',fill='y')
→ 输出的结果为:(UI界面及操作演示如下,至此整个基本界面的搭建完成)