ADD file via upload

main
plfxc6zkf 2 weeks ago
parent 6593efd7bf
commit 402bb18e32

@ -0,0 +1,316 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
GUI主界面模块
FreeNote应用的图形用户界面
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import datetime
from mod_conf import get_config_manager, get_setting, set_setting
from mod_tpl import get_template_help_text
from mod_file import save_note_content, FileSaveError
class FreeNoteApp:
def __init__(self, root):
self.root = root
self.root.title("FreeNote-随手记")
self.root.geometry("1000x800")
# 文本修改状态标志
self.text_modified = False
# 获取配置管理器
self.config_manager = get_config_manager()
# 创建界面
self.create_widgets()
# 绑定快捷键
self.bind_shortcuts()
def create_widgets(self):
"""创建界面组件"""
# 初始化界面变量
self.init_ui_variables()
# 创建主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 配置Grid布局权重
main_frame.grid_rowconfigure(0, weight=0) # 工具栏:固定高度
main_frame.grid_rowconfigure(1, weight=1) # 文本区域:可伸缩
main_frame.grid_rowconfigure(2, weight=0) # 状态栏:固定高度
main_frame.grid_columnconfigure(0, weight=1) # 列可伸缩
# 工具栏
self.create_toolbar(main_frame)
# 文本编辑区域
self.create_text_area(main_frame)
# 状态栏
self.create_status_bar(main_frame)
def init_ui_variables(self):
"""初始化界面变量"""
# 目录变量
self.folder_var = tk.StringVar(
value=get_setting('folder'))
# 文件名变量
self.filename_var = tk.StringVar(
value=get_setting('filename'))
# 扩展名变量
self.ext_var = tk.StringVar(
value=get_setting('ext'))
def create_toolbar(self, parent):
"""创建工具栏"""
toolbar = ttk.Frame(parent)
toolbar.grid(row=0, column=0, sticky=tk.EW, pady=(0, 5))
# 配置grid权重使目录框和文件名框可以缩放
toolbar.grid_columnconfigure(0, weight=1) # 目录框列
toolbar.grid_columnconfigure(2, weight=2) # 文件名框列权重为目录框的2倍
# 目录框
self.dir_entry = ttk.Entry(toolbar, textvariable=self.folder_var)
self.dir_entry.grid(row=0, column=0, sticky=tk.EW, padx=(0, 5))
# 绑定失去焦点事件
self.dir_entry.bind('<FocusOut>', self.on_folder_focus_out)
self.dir_entry.bind('<Return>', self.on_folder_focus_out)
# 选择目录按钮
browse_btn = ttk.Button(toolbar, text="选择", width=4, command=self.browse_folder)
browse_btn.grid(row=0, column=1, padx=(0, 10))
# 文件名框
self.filename_entry = ttk.Entry(
toolbar, textvariable=self.filename_var)
self.filename_entry.grid(row=0, column=2, sticky=tk.EW, padx=(0, 5))
# 绑定失去焦点事件
self.filename_entry.bind('<FocusOut>', self.on_filename_focus_out)
self.filename_entry.bind('<Return>', self.on_filename_focus_out)
# 模板按钮
template_btn = ttk.Button(toolbar, text="模板", width=4, command=self.show_template_help)
template_btn.grid(row=0, column=3, padx=(0, 10))
# 扩展名选择框
ext_combo = ttk.Combobox(toolbar, textvariable=self.ext_var, values=['.txt', '.md'], width=8, state='readonly')
ext_combo.bind('<<ComboboxSelected>>', self.on_ext_change)
ext_combo.grid(row=0, column=4, padx=(0, 10))
# 保存按钮
save_btn = ttk.Button(toolbar, text="保存", width=4, command=self.save_note)
save_btn.grid(row=0, column=5)
def create_text_area(self, parent):
"""创建文本编辑区域"""
text_frame = ttk.Frame(parent)
text_frame.grid(row=1, column=0, sticky=tk.NSEW)
# 配置文本框架的布局权重
text_frame.grid_rowconfigure(0, weight=1)
text_frame.grid_columnconfigure(0, weight=1)
# 文本框
self.text_area = tk.Text(text_frame, wrap=tk.WORD)
self.text_area.grid(row=0, column=0, sticky=tk.NSEW)
# 绑定文本修改事件
self.text_area.bind('<KeyPress>', self.on_text_modified)
self.text_area.bind('<KeyRelease>', self.on_text_modified)
self.text_area.bind('<Button-1>', self.on_text_modified)
self.text_area.bind('<Control-v>', self.on_text_modified) # 粘贴
self.text_area.bind('<Control-x>', self.on_text_modified) # 剪切
# 滚动条
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.text_area.yview)
scrollbar.grid(row=0, column=1, sticky=tk.NS)
self.text_area.config(yscrollcommand=scrollbar.set)
def create_status_bar(self, parent):
"""创建状态栏"""
status_frame = ttk.Frame(parent)
status_frame.grid(row=2, column=0, sticky=tk.EW, pady=(5, 0))
# 配置状态栏布局权重
status_frame.grid_columnconfigure(0, weight=1) # 左侧状态消息可伸缩
status_frame.grid_columnconfigure(1, weight=0) # 右侧尺寸信息固定
# 主状态消息
self.status_var = tk.StringVar()
status_label = ttk.Label(status_frame, textvariable=self.status_var)
status_label.grid(row=0, column=0, sticky=tk.W)
# 窗口尺寸信息
self.size_var = tk.StringVar()
size_label = ttk.Label(status_frame, textvariable=self.size_var)
size_label.grid(row=0, column=1, sticky=tk.E)
# 初始化窗口尺寸显示
self.update_window_size()
# 绑定窗口resize事件
self.root.bind('<Configure>', self.on_window_resize)
def bind_shortcuts(self):
"""绑定快捷键"""
self.root.bind('<Control-s>', lambda e: self.save_note())
self.root.bind('<Control-n>', lambda e: self.clear_text())
def on_text_modified(self, event=None):
"""文本内容修改时的回调"""
# 使用after方法延迟检查确保文本已经更新
self.root.after_idle(self.check_text_modified)
def check_text_modified(self):
"""检查文本是否被修改"""
current_content = self.text_area.get("1.0", tk.END).strip()
if current_content and not self.text_modified:
self.text_modified = True
self.update_title()
elif not current_content and self.text_modified:
self.text_modified = False
self.update_title()
def update_title(self):
"""更新窗口标题以显示修改状态"""
base_title = "FreeNote - 随手记录"
if self.text_modified:
self.root.title(f"{base_title} *")
else:
self.root.title(base_title)
def reset_modified_flag(self):
"""重置修改标志"""
self.text_modified = False
self.update_title()
def clear_text(self):
"""清空文本框内容"""
if self.text_modified:
# 如果文本已修改,询问用户是否确认清空
if not messagebox.askyesno("确认", "文本内容已修改但未保存,确定要清空吗?"):
return
# 清空文本框
self.text_area.delete("1.0", tk.END)
# 重置修改标志
self.reset_modified_flag()
# 更新状态栏
self.update_status("文本已清空")
def on_folder_focus_out(self, event=None):
"""目录框失去焦点时保存配置"""
new_val = self.folder_var.get().strip()
old_val = get_setting('folder')
if new_val and new_val != old_val:
# 更新配置
set_setting('folder', new_val)
self.update_status(f"保存文件夹{new_val}")
def on_filename_focus_out(self, event=None):
"""文件名框失去焦点时保存配置"""
new_val = self.filename_var.get().strip()
old_val = get_setting('filename')
if new_val and new_val != old_val:
# 更新配置
set_setting('filename', new_val)
self.update_status(f"文件名{new_val}")
def on_ext_change(self, event=None):
"""扩展名变化时保存配置"""
new_val = self.ext_var.get().strip()
old_val = get_setting('ext')
if new_val and new_val != old_val:
# 更新配置
set_setting('ext', new_val)
self.update_status(f"扩展名已更改为{new_val}")
def browse_folder(self):
"""浏览选择目录"""
folder = filedialog.askdirectory(initialdir=self.folder_var.get())
if folder:
self.folder_var.set(folder)
# 触发失去焦点事件来保存配置
self.on_folder_focus_out()
def save_note(self):
"""保存笔记"""
# 检查文本是否被修改
if not self.text_modified:
messagebox.showinfo("提示", "文本内容未修改,无需保存。")
return
content = self.text_area.get("1.0", tk.END)
try:
# 获取参数
filename_template = self.filename_var.get().strip()
folder = self.folder_var.get().strip()
ext = self.ext_var.get().strip()
# 定义覆盖确认回调
def overwrite_callback(filepath):
return messagebox.askyesno("确认", f"文件 {filepath} 已存在,是否覆盖?")
# 保存文件
filepath = save_note_content(
content, filename_template, folder, ext,
overwrite_callback=overwrite_callback
)
# 更新状态
self.update_status(f"已保存: {filepath}")
# 重置修改标志
self.reset_modified_flag()
except FileSaveError as e:
messagebox.showerror("错误", str(e))
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
def update_status(self, message):
"""更新状态栏"""
status_text = f"{datetime.datetime.now().strftime('%H:%M:%S')} - {message}"
self.status_var.set(status_text)
def update_window_size(self):
"""更新窗口尺寸显示"""
width = self.root.winfo_width()
height = self.root.winfo_height()
self.size_var.set(f"{width} x {height}")
def on_window_resize(self, event=None):
"""窗口尺寸变化事件处理"""
# 只响应主窗口的resize事件忽略子组件的事件
if event and event.widget == self.root:
self.update_window_size()
def show_template_help(self):
"""显示模板变量帮助"""
help_text = get_template_help_text()
messagebox.showinfo("文件名模板帮助", help_text)
def create_app():
"""创建并返回应用程序实例"""
root = tk.Tk()
app = FreeNoteApp(root)
return root, app
Loading…
Cancel
Save