文件菜单选项栏事件功能实现
1. 文件菜单选项栏事件功能实现步骤
上一个博客中已经完成了文本编辑器基本页面的UI设计,这里接着完善上一个博客中菜单栏及关联选项栏内容出现的功能(事件回调)。首先就是对“文件”菜单栏下的关联内容进行功能设置如下
1.1 新建文件
新建文件,就是直接清除当前文本的内容,然后创建一个新的画布(存在着窗口标题的重命名问题),其余的几乎是没有发生变化,因此这里就定义个new_file
函数,进行新建文件,然后将这个函数添加command
参数中
需要注意的一点就是:新建的文件一般是没有文件名称的,所以会有self.file_name = None
,同时为了更方便的调用这个变量(进行保存或者另存为时候需要用到),可以将其声明为全局变量(和之前的icon_res
一样转化为类属性)
def new_file(self,event=None):
self.title('New - EditorPlus')
self.content_text.delete(1.0,END)
self.file_name = None
#函数封装完毕后,需要把函数名称添加到command参数之后
file_menu.add_command(label='新建',accelerator = 'Ctrl+N',command=self.new_file)
→ 输出的结果为:(新建文件功能实现)
1.2 打开文件
打开文件就是在当前编辑器窗口界面上加载一个本地的文件(默认是txt),这时候就需要调用文件对话框了,设定要打开的文件类型。注意如果用户只是打开文件对话框并没有选择任何本地的文件,这时候不应执行打开文件的命令(也就是存在着条件判断,只用当用户选择了某一个文件的时候时,才进行文件的打开),整个过程封装在open_file
函数中,然后将这个函数添加command
参数中,如下
注意三点:① 弹出文件对话框中设置要选择打开的文件的类型
② 要对用户的操作进行判断,是否真实地点击了要打开的文件,如果点击了,才会打开文件
③ 这里的打开文件,实际上就是,将原来程序窗口的标题重新命名,然后把里面的文本内容全部删除,接着对这个文件名称进行赋值(这步操作就是为了后面的文件保存),最后再将与打开的文件内容写到文本栏中,这里要加上.read()
,否则写入的只是一个文本对象,写入的方式很简单就是在最开始的地方开始插入欲打开的文本数据
def open_file(self,event=None):
input_file = filedialog.askopenfilename(
filetypes=[('所有文件','*.*'),('文本文档','*.txt')]) #弹出文件对话框,设置选择文件的类型
if input_file: #如果用户选择了文本,则进行打开
#print(input_file) #这里可以调试,看一下选中文本的路径的形式(绝对路径)
self.title('{} - EditorPlus'.format(os.path.basename(input_file))) #以文件的名称进行窗口标题的命名
self.file_name = input_file #将这个打开的文件对象命名为其原来文件的名称
self.content_text.delete(1.0,END) #删除当前文本内容中的数据
with open(input_file, 'r') as _file:
self.content_text.insert(1.0,_file.read()) #将要打开文件中的数据写入到文本内容中
#函数封装完毕后,需要把函数名称添加到command参数之后
file_menu.add_command(label='打开',accelerator = 'Ctrl+O',command=self.open_file)
→ 输出的结果为:(打开文件的功能实现)
1.3 保存文件
保存文件涉及到条件判断,也就是上面提及到的file_name
(文件名称变量),如果这个文件是已经打开(有了文件名称变量的赋值),说明可以直接保存文件,如果文件file_name=None
(比如新建的文件,文件的名称一般是没有的),这时候保存文件实际上就是将文件另存为,需要指定文件的路径和要保存的文件名称,因此整个过程封装save
函数之中,然后将这个函数添加command
参数中,如下
def save(self, event=None):
if not self.file_name: #这里就体现出来之前设置的self.file_name全局变量的作用了
self.save_as() #没有有文件名称的另保存
else:
self._write_to_file(self.file_name) #有文件名称的直接写入文件(保存本地)
#函数封装完毕后,需要把函数名称添加到command参数之后
file_menu.add_command(label='保存',accelerator = 'Ctrl+S',command=self.save)
1.4 另存为文件
有了之前打开文件和保存文件的操作,另存为文件的就变得较为简单,这里就是调用文件对话框,然后判断用户有没有进行文件名称的输入,如果有的话就执行文件的写入(类似之前的打开文件),整个过程封装为save_as
函数,然后将这个函数添加command
参数中,如下
def save_as(self,event=None):
input_file = filedialog.asksaveasfilename( #注意这里弹出的是文件保存对话框
filetypes = [('所有文件','*.*'),('文本文档','*.txt')]
)
if input_file: #还是要对用户操作进行判定
self.file_name = input_file #设置文件名称
self._write_to_file(self.file_name) #写入本地
#函数封装完毕后,需要把函数名称添加到command参数之后
file_menu.add_command(label='另存为',accelerator = 'Ctrl+Shift+S',command=self.save_as)
1.5 文本内容写入
通过上面的两个函数可以发现,最后都是直接调用_write_to_file
函数,将多个函数中都要使用的步骤封装起来再调用可以明显的较少代码量,也可以提高程序的运行效率。现实中会存在一个问题就是,文本在保存中或者另存为的过程中可能出现保存失败,为了复现这个功能需要使用到try-except
进行异常处理。整个过程封装如下
注意:这里是保存和另存为函数下来的内容,只是将相同的内容封装成一个函数,减少代码量,不需要将这部分函数添加到command
参数中
def _write_to_file(self, file_name):
try:
content = self.content_text.get(1.0, 'end') #先获取文本框中的所有数据
with open(file_name, 'w') as the_file:
the_file.write(content) #将数据写入到本地的文件中
self.title("%s - EditorPlus" % os.path.basename(file_name)) #这一步就是显示当前窗口的标题不变,可以尝试注释一下这行代码
except IOError:
messagebox.showwarning("保存", "保存失败!") #如果保存失败的话,会弹出消息对话框
→ 输出的结果为:(刚开始在新建的文件窗口上输入“保存文件”,这时候没有文件的名称,所以点击保存就相当于另存为文件(命名为2.txt),然后将该文件的内容修改为“另保存文件”,点击另存为3.txt,这时候再在3.txt文件上随机打入文字,点击保存,后重新打开2.txt和3.txt来检测保存文件和另存为文件的功能正常,至此文件保存和另存为功能实现)
1.6 退出程序
这里可以直接调用之前已经设置好的主窗口退出的函数,然后将这个函数添加command
参数中,如下
def exit_editor(self):
if messagebox.askokcancel('退出?','确定退出吗?'):
self.destroy()
#函数封装完毕后,需要把函数名称添加到command参数之后
file_menu.add_command(label='退出',accelerator = 'Alt+F4', command=self.exit_editor)
→ 输出的结果为:(至此,“文件”菜单栏所有的功能全部实现)
2. 快捷键绑定
在菜单栏上设置的有快捷键操作的提示,但是真正要实现快捷键调用函数的功能还需要进行文本内容的绑定,也就是说快捷键的使用是要在文本栏存在的情况下设置(只要主程序窗口打开,文本栏默认是存在的)。在_create_body_
函数下设置相应的内容,如下
self.content_text.bind('<Control-N>', self.new_file)
self.content_text.bind('<Control-n>', self.new_file)
self.content_text.bind('<Control-O>', self.open_file)
self.content_text.bind('<Control-o>', self.open_file)
self.content_text.bind('<Control-S>', self.save)
self.content_text.bind('<Control-s>', self.save)
self.content_text.bind('<Control-Shift-S>', self.save_as)
self.content_text.bind('<Control-Shift-s>', self.save_as)
self.content_text.bind('<Alt-F4>', self.exit_editor)
→ 输出的结果为:(分别进行了新建,另存为,打开,保存和关闭的快捷键操作)
3. 全部代码
实现该部分功能的全部代码如下
def _create_body_(self):
#创建文本输入框
self.content_text = Text(self, wrap='word')
self.content_text.pack(expand='yes',fill='both')
self.content_text.bind('<Control-N>', self.new_file)
self.content_text.bind('<Control-n>', self.new_file)
self.content_text.bind('<Control-O>', self.open_file)
self.content_text.bind('<Control-o>', self.open_file)
self.content_text.bind('<Control-S>', self.save)
self.content_text.bind('<Control-s>', self.save)
self.content_text.bind('<Control-Shift-S>', self.save_as)
self.content_text.bind('<Control-Shift-s>', self.save_as)
self.content_text.bind('<Alt-F4>', self.exit_editor)
def _create_menu_bar_(self):
menu_bar = Menu(self) #实例化菜单栏对象
#文件菜单
file_menu = Menu(menu_bar, tearoff = 0) #基于菜单栏实例化“文件”关联选项栏对象
file_menu.add_command(label='新建',accelerator = 'Ctrl+N',command=self.new_file)
file_menu.add_command(label='打开',accelerator = 'Ctrl+O',command=self.open_file)
file_menu.add_command(label='保存',accelerator = 'Ctrl+S',command=self.save)
file_menu.add_command(label='另存为',accelerator = 'Ctrl+Shift+S',command=self.save_as)
file_menu.add_separator()
file_menu.add_command(label='退出',accelerator = 'Alt+F4', command=self.exit_editor)
menu_bar.add_cascade(label='文件',menu = file_menu) #将“文件”关联选项栏放在“文件”菜单栏上
def new_file(self, event=None):
self.title("New - EditorPlus")
self.content_text.delete(1.0, END)
self.file_name = None
def open_file(self, event=None):
input_file = filedialog.askopenfilename(
filetypes=[("所有文件", "*.*"), ("文本文档", "*.txt")])
if input_file:
self.title("%s - EditorPlus" % os.path.basename(input_file))
self.file_name = input_file
self.content_text.delete(1.0, END)
with open(input_file, 'r') as _file:
self.content_text.insert(1.0, _file.read())
def save(self, event=None):
if not self.file_name:
self.save_as()
else:
self._write_to_file(self.file_name)
def save_as(self, event=None):
input_file = filedialog.asksaveasfilename(
filetypes=[("All Files", "*.*"), ("文本文档", "*.txt")])
if input_file:
self.file_name = input_file
self._write_to_file(self.file_name)
def _write_to_file(self, file_name):
try:
content = self.content_text.get(1.0, 'end')
with open(file_name, 'w') as the_file:
the_file.write(content)
self.title("%s - EditorPlus" % os.path.basename(file_name))
except IOError:
messagebox.showwarning("保存", "保存失败!")
def exit_editor(self):
if messagebox.askokcancel("退出?", "确定退出吗?"):
self.destroy()