python文字编辑器1.0(AI做)
2026-08-28 20:39:26
发布于:浙江
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font
import os
import codecs
import base64
import sys
import ctypes
from ctypes import wintypes
SCRIPT_FOLDER = os.path.dirname(os.path.realpath(sys.argv[0]))
BACKUP_FULL_PATH = os.path.join(SCRIPT_FOLDER, "上一次备份.txt")
LOW_BATTERY_THRESHOLD = 10
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
RUN_AS_ADMIN = False
if is_admin():
RUN_AS_ADMIN = True
else:
try:
res = ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
if res > 32:
sys.exit(0)
else:
RUN_AS_ADMIN = False
except Exception:
RUN_AS_ADMIN = False
class SYSTEM_POWER_STATUS(ctypes.Structure):
fields = [
("ACLineStatus", ctypes.c_ubyte),
("BatteryFlag", ctypes.c_ubyte),
("BatteryLifePercent", ctypes.c_ubyte),
("Reserved1", ctypes.c_ubyte),
("BatteryLifeTime", wintypes.DWORD),
("BatteryFullLifeTime", wintypes.DWORD),
]
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
GetSystemPowerStatus = kernel32.GetSystemPowerStatus
GetSystemPowerStatus.argtypes = [ctypes.POINTER(SYSTEM_POWER_STATUS)]
def is_laptop_with_battery():
try:
sps = SYSTEM_POWER_STATUS()
if not GetSystemPowerStatus(ctypes.byref(sps)):
return False
if sps.BatteryFlag & 128:
return False
return True
except:
return False
def get_battery_info():
if not is_laptop_with_battery():
return None
sps = SYSTEM_POWER_STATUS()
GetSystemPowerStatus(ctypes.byref(sps))
percent = sps.BatteryLifePercent
ac_plugged = (sps.ACLineStatus == 1)
if percent == 255:
percent = 100
return (percent, ac_plugged)
def simple_encrypt(text: str, password: str) -> bytes:
key_bytes = password.encode("utf-8")
data_bytes = text.encode("utf-8")
out = bytearray()
for i, b in enumerate(data_bytes):
out.append(b ^ key_bytes[i % len(key_bytes)])
return base64.b64encode(out)
def simple_decrypt(cipher_data: bytes, password: str) -> str:
key_bytes = password.encode("utf-8")
raw = base64.b64decode(cipher_data)
out = bytearray()
for i, b in enumerate(raw):
out.append(b ^ key_bytes[i % len(key_bytes)])
return out.decode("utf-8")
ENCODING_LIST = [
("UTF‑8", "utf-8"),
("ANSI(GBK)", "gbk"),
("Unicode(UTF‑16LE)", "utf-16-le"),
("Unicode big‑endian(UTF‑16BE)", "utf-16-be")
]
NAME_TO_CODE = {n: c for n, c in ENCODING_LIST}
class TextEditor:
def init(self, root):
self.root = root
self.permission_tag = "【管理员模式】" if RUN_AS_ADMIN else "【普通用户模式】"
self.root.title(f"简易文本编辑器 {self.permission_tag}")
self.root.geometry("800x620")
self.file_path = None
self.is_saved = True
self.base_font_size = 12
self.current_font_size = self.base_font_size
self.current_font_family = "Consolas"
self.text_font = (self.current_font_family, self.current_font_size)
self.current_encoding = "utf-8"
self.low_battery_warn_shown = False
self.has_battery = is_laptop_with_battery()
self.main_frame = tk.Frame(self.root)
self.main_frame.pack(fill=tk.BOTH, expand=True)
self.status_bar = tk.Label(self.root, text=self.permission_tag, anchor="w", bg="#eeeeee")
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
self.current_line_digits = 2
self.line_numbers = tk.Text(self.main_frame, width=3, padx=2, state=tk.DISABLED,
background="#f0f0f0", font=self.text_font, wrap=tk.NONE)
self.line_numbers.pack(side=tk.LEFT, fill=tk.Y)
self.text_area = tk.Text(self.main_frame, wrap=tk.NONE, undo=True, font=self.text_font)
self.v_scrollbar = ttk.Scrollbar(self.main_frame, orient=tk.VERTICAL, command=self.on_vertical_scroll)
self.h_scrollbar = ttk.Scrollbar(self.root, orient=tk.HORIZONTAL, command=self.text_area.xview)
self.text_area.configure(yscrollcommand=self.v_scrollbar.set, xscrollcommand=self.h_scrollbar.set)
self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
self.right_menu = tk.Menu(self.root, tearoff=0)
self.right_menu.add_command(label="剪切", command=lambda: self.text_area.event_generate("<<Cut>>"))
self.right_menu.add_command(label="复制", command=lambda: self.text_area.event_generate("<<Copy>>"))
self.right_menu.add_command(label="粘贴", command=lambda: self.text_area.event_generate("<<Paste>>"))
self.right_menu.add_command(label="删除", command=self.delete_selected)
self.right_menu.add_separator()
self.right_menu.add_command(label="全选", command=lambda: self.text_area.tag_add(tk.SEL, "1.0", tk.END))
self.text_area.bind("<Button-3>", self.show_right_menu)
self.text_area.bind("<<Modified>>", self.on_text_modified)
self.text_area.bind("<Control-MouseWheel>", self.on_zoom)
self.text_area.bind("<KeyRelease>", self.update_line_numbers)
self.text_area.bind("<MouseWheel>", self.update_line_numbers)
self.text_area.bind("<ButtonRelease-1>", self.update_line_numbers)
self.root.bind("<Configure>", self.update_line_numbers)
self.root.protocol("WM_DELETE_WINDOW", self.exit_app)
self.create_menu()
self.find_window = None
self.find_entry = None
self.replace_entry = None
self.current_find_index = tk.END
self.update_line_numbers()
if self.has_battery:
self.battery_monitor_loop()
def battery_monitor_loop(self):
bat_info = get_battery_info()
if bat_info is not None:
percent, plugged = bat_info
if percent <= LOW_BATTERY_THRESHOLD and (not plugged):
if not self.low_battery_warn_shown:
messagebox.showwarning("⚠低电量警告",
f"当前电量 {percent}% ,电量不足!\n请尽快手动保存你的文档并插上电源!")
self.low_battery_warn_shown = True
else:
self.low_battery_warn_shown = False
self.root.after(30000, self.battery_monitor_loop)
def on_vertical_scroll(self, action, value):
self.text_area.yview(action, value)
self.line_numbers.yview_moveto(self.text_area.yview()[0])
def show_right_menu(self, event):
self.right_menu.tk_popup(event.x_root, event.y_root)
def delete_selected(self):
try:
self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
except tk.TclError:
pass
def create_menu(self):
menubar = tk.Menu(self.root)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="新建", command=self.new_file)
file_menu.add_command(label="打开", command=self.open_file)
file_menu.add_command(label="保存", command=self.save_file)
file_menu.add_command(label="另存为", command=self.save_as_file)
file_menu.add_separator()
file_menu.add_command(label="加密存档保存", command=self.save_encrypted)
file_menu.add_command(label="打开加密存档", command=self.open_encrypted)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.exit_app)
menubar.add_cascade(label="文件", menu=file_menu)
edit_menu = tk.Menu(menubar, tearoff=0)
edit_menu.add_command(label="撤销", command=lambda: self.text_area.event_generate("<<Undo>>"))
edit_menu.add_separator()
edit_menu.add_command(label="剪切", command=lambda: self.text_area.event_generate("<<Cut>>"))
edit_menu.add_command(label="复制", command=lambda: self.text_area.event_generate("<<Copy>>"))
edit_menu.add_command(label="粘贴", command=lambda: self.text_area.event_generate("<<Paste>>"))
edit_menu.add_command(label="删除", command=self.delete_selected)
edit_menu.add_separator()
edit_menu.add_command(label="查找替换", command=self.show_find_replace)
edit_menu.add_separator()
edit_menu.add_command(label="放大字体", command=lambda: self.adjust_font_size(1))
edit_menu.add_command(label="缩小字体", command=lambda: self.adjust_font_size(-1))
edit_menu.add_command(label="恢复默认字体", command=self.reset_font_size)
edit_menu.add_separator()
edit_menu.add_command(label="更改字体...", command=self.change_font)
menubar.add_cascade(label="编辑", menu=edit_menu)
encode_menu = tk.Menu(menubar, tearoff=0)
for name, code in ENCODING_LIST:
encode_menu.add_command(label=name, command=lambda c=code: self.set_encoding(c))
menubar.add_cascade(label="编码", menu=encode_menu)
self.root.config(menu=menubar)
def set_encoding(self, enc):
self.current_encoding = enc
messagebox.showinfo("提示", f"当前文件编码已切换为:{enc}")
def new_file(self):
if not self.is_saved:
response = messagebox.askyesnocancel("提示", "当前文件有未保存的修改,是否保存?")
if response:
self.save_file()
elif response is None:
return
self.text_area.delete(1.0, tk.END)
self.file_path = None
self.is_saved = True
self.current_encoding = "utf-8"
self.update_title()
self.update_line_numbers()
def on_text_modified(self, event):
if self.is_saved:
self.is_saved = False
self.update_title()
self.text_area.edit_modified(False)
def update_title(self):
title = f"简易文本编辑器 {self.permission_tag}"
if self.file_path:
title = "{} - {}".format(os.path.basename(self.file_path), title)
if not self.is_saved:
title = "*{}".format(title)
self.root.title(title)
def open_file(self):
if not self.is_saved:
response = messagebox.askyesnocancel("提示", "当前文件有未保存的修改,是否保存?")
if response:
self.save_file()
elif response is None:
return
file_path = filedialog.askopenfilename(
defaultextension=".txt",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_path:
content = None
try_order = ["utf-8", "gbk", "utf-16-le", "utf-16-be"]
ok_enc = None
for enc in try_order:
try:
with codecs.open(file_path, "r", encoding=enc) as f:
content = f.read()
ok_enc = enc
break
except Exception:
continue
if content is None:
messagebox.showerror("错误", "无法识别文件编码,打开失败!")
return
self.text_area.delete(1.0, tk.END)
self.text_area.insert(1.0, content)
self.file_path = file_path
self.current_encoding = ok_enc
self.is_saved = True
self.update_title()
self.update_line_numbers()
def save_file(self):
if self.file_path is None:
self.save_as_file()
return
try:
text_data = self.text_area.get(1.0, tk.END)
with codecs.open(self.file_path, "w", encoding=self.current_encoding) as f:
f.write(text_data)
self.is_saved = True
self.update_title()
messagebox.showinfo("提示", "文件保存成功!")
except Exception as e:
messagebox.showerror("错误", "保存失败:" + str(e))
def save_as_file(self):
#【修复1:先弹出文件路径选择,后编码弹窗】
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("文本文件(*.txt)", "*.txt"), ("所有文件", "*.*")]
)
if not file_path:
return
sel_win = tk.Toplevel(self.root)
sel_win.title("选择保存编码")
sel_win.geometry("380x260")
sel_win.transient(self.root)
sel_win.grab_set()
tk.Label(sel_win, text="请选择文件保存编码:", font=("SimSun", 12)).pack(pady=8)
save_enc_var = tk.StringVar()
for disp_name,code in ENCODING_LIST:
if code == self.current_encoding:
save_enc_var.set(disp_name)
break
for disp_name,code in ENCODING_LIST:
tk.Radiobutton(sel_win, text=disp_name, variable=save_enc_var, value=disp_name).pack(anchor="w", padx=40)
def do_save():
selected_name = save_enc_var.get()
target_enc = NAME_TO_CODE[selected_name]
text_data = self.text_area.get(1.0, tk.END)
try:
with codecs.open(file_path, "w", encoding=target_enc) as f:
f.write(text_data)
self.file_path = file_path
self.current_encoding = target_enc
self.is_saved = True
self.update_title()
messagebox.showinfo("提示", "文件保存成功!")
except Exception as e:
messagebox.showerror("错误", "保存失败:" + str(e))
sel_win.destroy()
ttk.Button(sel_win, text="保存", command=do_save).pack(pady=12)
def save_encrypted(self):
#【修复2:加密存档 先弹出保存位置】
save_path = filedialog.asksaveasfilename(
initialdir=SCRIPT_FOLDER,
defaultextension=".sec",
filetypes=[("加密存档文件 *.sec", "*.sec"), ("全部文件", "*.*")]
)
if not save_path:
return
text_content = self.text_area.get("1.0", tk.END)
pwd_win = tk.Toplevel(self.root)
pwd_win.title("设置存档密码")
pwd_win.geometry("320x180")
pwd_win.resizable(False, False)
pwd_win.transient(self.root)
pwd_win.grab_set()
tk.Label(pwd_win, text="输入密码:", font=("SimSun", 12)).grid(row=0, column=0, columnspan=2, pady=(15, 5))
pass1 = tk.Entry(pwd_win, show="*", width=35, font=("SimSun", 11))
pass1.grid(row=1, column=0, columnspan=2, padx=10)
tk.Label(pwd_win, text="确认密码:", font=("SimSun", 12)).grid(row=2, column=0, columnspan=2, pady=(12, 5))
pass2 = tk.Entry(pwd_win, show="*", width=35, font=("SimSun", 11))
pass2.grid(row=3, column=0, columnspan=2, padx=10)
def ok():
pw1 = pass1.get()
pw2 = pass2.get()
if pw1 != pw2:
messagebox.showerror("错误", "两次密码不一致!")
return
if len(pw1.strip()) == 0:
messagebox.showerror("错误", "密码不能为空!")
return
try:
enc_data = simple_encrypt(text_content, pw1)
with open(save_path, "wb") as f:
f.write(enc_data)
messagebox.showinfo("提示", "加密存档保存成功")
except Exception as e:
messagebox.showerror("错误", "加密失败:" + str(e))
pwd_win.destroy()
ttk.Button(pwd_win, text="保存加密存档", command=ok).grid(row=4, column=0, columnspan=2, pady=18)
def open_encrypted(self):
if not self.is_saved:
res = messagebox.askyesnocancel("提示", "当前有未保存内容,是否先保存?")
if res:
self.save_file()
elif res is None:
return
filepath = filedialog.askopenfilename(filetypes=[("加密存档 *.sec", "*.sec"), ("所有文件", "*.*")])
if not filepath:
return
pwd_win = tk.Toplevel(self.root)
pwd_win.title("输入解密密码")
pwd_win.geometry("320x130")
pwd_win.resizable(False, False)
pwd_win.transient(self.root)
pwd_win.grab_set()
tk.Label(pwd_win, text="请输入存档密码:", font=("SimSun", 12)).grid(row=0, column=0, columnspan=2, pady=(15, 8))
pwd_entry = tk.Entry(pwd_win, show="*", width=35, font=("SimSun", 11))
pwd_entry.grid(row=1, column=0, columnspan=2, padx=10)
def decrypt_action():
pw = pwd_entry.get()
try:
with open(filepath, "rb") as f:
data = f.read()
plain = simple_decrypt(data, pw)
self.text_area.delete("1.0", tk.END)
self.text_area.insert("1.0", plain)
self.is_saved = False
self.update_title()
self.update_line_numbers()
messagebox.showinfo("成功", "加密文件已解密打开")
except Exception:
messagebox.showerror("错误", "密码错误或者文件损坏!")
pwd_win.destroy()
ttk.Button(pwd_win, text="解密打开", command=decrypt_action).grid(row=2, column=0, columnspan=2, pady=15)
def save_last_backup_next_to_script(self):
text_data = self.text_area.get("1.0", tk.END)
win = tk.Toplevel(self.root)
win.title("备份编码选择")
win.geometry("380x260")
win.transient(self.root)
win.grab_set()
tk.Label(win, text="选择「上一次备份.txt」保存编码:", font=("SimSun", 12)).pack(pady=8)
var = tk.StringVar(value="UTF‑8")
for disp_name,code in ENCODING_LIST:
tk.Radiobutton(win, text=disp_name, variable=var, value=disp_name).pack(anchor="w", padx=40)
def confirm():
enc_name = var.get()
enc_code = NAME_TO_CODE[enc_name]
try:
with codecs.open(BACKUP_FULL_PATH, "w", encoding=enc_code) as f:
f.write(text_data)
except Exception:
pass
win.destroy()
ttk.Button(win, text="确认保存并退出", command=confirm).pack(pady=12)
self.root.wait_window(win)
def update_line_numbers(self, event=None):
#【修复3:移除延时锁,实时刷新行号】
end_str = self.text_area.index(tk.END)
line_count = int(end_str.split('.')[0]) - 1
new_digits = len(str(max(line_count, 1)))
if new_digits != self.current_line_digits:
self.current_line_digits = new_digits
new_width = new_digits + 1
self.line_numbers.config(width=new_width)
self.line_numbers.config(state=tk.NORMAL)
self.line_numbers.delete(1.0, tk.END)
for i in range(1, line_count + 1):
self.line_numbers.insert(tk.END, "{:0{}d}\n".format(i, self.current_line_digits))
if line_count == 0:
self.line_numbers.insert(tk.END, "01\n")
self.line_numbers.config(state=tk.DISABLED)
self.line_numbers.yview_moveto(self.text_area.yview()[0])
def show_find_replace(self):
if self.find_window is not None and self.find_window.winfo_exists():
self.find_window.lift()
return
self.find_window = tk.Toplevel(self.root)
self.find_window.title("查找替换")
self.find_window.geometry("400x150")
self.find_window.resizable(False, False)
self.find_window.transient(self.root)
self.find_window.grab_set()
tk.Label(self.find_window, text="查找:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
self.find_entry = tk.Entry(self.find_window, width=30)
self.find_entry.grid(row=0, column=1, padx=5, pady=5)
self.find_entry.focus()
tk.Label(self.find_window, text="替换为:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
self.replace_entry = tk.Entry(self.find_window, width=30)
self.replace_entry.grid(row=1, column=1, padx=5, pady=5)
btn_frame = tk.Frame(self.find_window)
btn_frame.grid(row=2, column=1, padx=5, pady=5, sticky="w")
tk.Button(btn_frame, text="查找下一个", command=self.find_next).grid(row=0, column=0, padx=2)
tk.Button(btn_frame, text="替换", command=self.replace_current).grid(row=0, column=1, padx=2)
tk.Button(btn_frame, text="全部替换", command=self.replace_all).grid(row=0, column=2, padx=2)
tk.Button(btn_frame, text="取消", command=self.find_window.destroy).grid(row=0, column=3, padx=2)
def find_next(self):
search_text = self.find_entry.get()
if not search_text:
return
self.text_area.tag_remove("find", 1.0, tk.END)
start_index = self.current_find_index if self.current_find_index != tk.END else "1.0"
pos = self.text_area.search(search_text, start_index, stopindex=tk.END, nocase=True)
if pos:
end_pos = "{}+{}c".format(pos, len(search_text))
self.text_area.tag_add("find", pos, end_pos)
self.text_area.tag_config("find", background="yellow")
self.text_area.see(pos)
self.text_area.mark_set(tk.INSERT, end_pos)
self.current_find_index = end_pos
else:
pos = self.text_area.search(search_text, "1.0", stopindex=tk.END, nocase=True)
if pos:
end_pos = "{}+{}c".format(pos, len(search_text))
self.text_area.tag_add("find", pos, end_pos)
self.text_area.tag_config("find", background="yellow")
self.text_area.see(pos)
self.text_area.mark_set(tk.INSERT, end_pos)
self.current_find_index = end_pos
else:
messagebox.showinfo("提示", "未找到 '{}'".format(search_text))
self.current_find_index = tk.END
def replace_current(self):
search_text = self.find_entry.get()
replace_text = self.replace_entry.get()
if not search_text or self.current_find_index == tk.END:
self.find_next()
return
sel_ranges = self.text_area.tag_ranges(tk.SEL)
start_index = None
if len(sel_ranges) > 0:
start_index = self.text_area.index(tk.SEL_FIRST)
if start_index is not None:
end_index = self.text_area.index(tk.SEL_LAST)
self.text_area.delete(start_index, end_index)
self.text_area.insert(start_index, replace_text)
self.current_find_index = "{}+{}c".format(start_index, len(replace_text))
self.find_next()
def replace_all(self):
search_text = self.find_entry.get()
replace_text = self.replace_entry.get()
if not search_text:
return
if self.find_window is not None:
self.find_window.destroy()
self.text_area.tag_remove("find", 1.0, tk.END)
count = 0
start_index = "1.0"
while True:
pos = self.text_area.search(search_text, start_index, stopindex=tk.END, nocase=True)
if not pos:
break
end_pos = "{}+{}c".format(pos, len(search_text))
self.text_area.delete(pos, end_pos)
self.text_area.insert(pos, replace_text)
count += 1
start_index = "{}+{}c".format(pos, len(replace_text))
messagebox.showinfo("提示", "已替换 {} 处匹配项".format(count))
self.update_line_numbers()
def exit_app(self):
self.save_last_backup_next_to_script()
if not self.is_saved:
response = messagebox.askyesnocancel("提示", "当前文件有未保存的修改,是否保存?")
if response:
self.save_file()
elif response is None:
return
self.root.quit()
def on_zoom(self, event):
if event.delta > 0:
self.adjust_font_size(1)
else:
self.adjust_font_size(-1)
def adjust_font_size(self, delta):
new_size = self.current_font_size + delta
if 6 <= new_size <= 72:
self.current_font_size = new_size
self.text_font = (self.current_font_family, self.current_font_size)
self.text_area.config(font=self.text_font)
self.line_numbers.config(font=self.text_font)
self.update_line_numbers()
def reset_font_size(self):
self.current_font_size = self.base_font_size
self.text_font = (self.current_font_family, self.current_font_size)
self.text_area.config(font=self.text_font)
self.line_numbers.config(font=self.text_font)
self.update_line_numbers()
def change_font(self):
font_window = tk.Toplevel(self.root)
font_window.title("更改字体")
font_window.geometry("450x300")
font_window.resizable(False, False)
font_window.transient(self.root)
font_window.grab_set()
tk.Label(font_window, text="字体:").grid(row=0, column=0, padx=10, pady=10, sticky="ne")
tk.Label(font_window, text="字号:").grid(row=1, column=0, padx=10, pady=10, sticky="ne")
font_families = sorted(font.families())
font_frame = tk.Frame(font_window)
font_frame.grid(row=0, column=1, padx=10, pady=10)
font_listbox = tk.Listbox(font_frame, width=30, height=8, exportselection=False)
font_scrollbar = ttk.Scrollbar(font_frame, orient=tk.VERTICAL, command=font_listbox.yview)
font_listbox.configure(yscrollcommand=font_scrollbar.set)
font_listbox.pack(side=tk.LEFT, fill=tk.BOTH)
font_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
for f in font_families:
font_listbox.insert(tk.END, f)
try:
current_index = font_families.index(self.current_font_family)
font_listbox.see(current_index)
font_listbox.selection_set(current_index)
except ValueError:
font_listbox.selection_set(0)
size_var = tk.StringVar(value=str(self.current_font_size))
size_spinbox = tk.Spinbox(font_window, from_=6, to=72, textvariable=size_var,
width=10, increment=1)
size_spinbox.grid(row=1, column=1, padx=10, pady=10, sticky="nw")
def apply_font():
try:
cur_sel = font_listbox.curselection()
selected_font = font_listbox.get(cur_sel[0])
selected_size = int(size_var.get())
if 6 <= selected_size <= 72:
self.current_font_family = selected_font
self.current_font_size = selected_size
self.text_font = (self.current_font_family, self.current_font_size)
self.text_area.config(font=self.text_font)
self.line_numbers.config(font=self.text_font)
self.update_line_numbers()
font_window.destroy()
else:
messagebox.showwarning("警告", "字号必须在6‑72之间!")
except Exception:
messagebox.showwarning("警告", "请选择有效的字体和字号!")
btn_frame = tk.Frame(font_window)
btn_frame.grid(row=2, column=0, columnspan=2, pady=20)
tk.Button(btn_frame, text="确定", command=apply_font, width=10).pack(side=tk.LEFT, padx=20)
tk.Button(btn_frame, text="取消", command=font_window.destroy, width=10).pack(side=tk.LEFT)
if name == "main":
root = tk.Tk()
app = TextEditor(root)
root.mainloop()
这里空空如也












有帮助,赞一个