本文详细记录了一个实用的文件行删除工具的完整开发过程,涵盖了Tkinter GUI编程、文件操作、用户体验设计等多个方面。
项目背景
在日常开发工作中,我们经常会遇到需要处理文本文件的情况。有时候需要删除日志文件的特定时间段记录,有时候需要清理数据文件的部分行,有时候需要移除配置文件中的某些设置。手动打开文本编辑器进行行删除不仅效率低下,而且容易出错。
为了解决这个问题,我决定开发一个专门的文件行删除工具,要求能够:
- 可视化选择文件
- 精确指定删除的行范围
- 提供操作前的预览功能
- 确保操作的安全性(备份机制)
- 用户友好的界面
技术选型:为什么选择Tkinter?
Python GUI库比较
在开始之前,我对比了Python的几个主流GUI库:
| 库名 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Tkinter | Python内置,无需安装,跨平台,API稳定 | 界面相对老旧,原生组件少 | 快速开发小型工具 |
| PyQt/PySide | 功能强大,界面美观,组件丰富 | 学习曲线陡峭,需要安装 | 需要复杂界面的桌面应用 |
| wxPython | 原生外观,跨平台 | 文档较少,社区较小 | 跨平台原生风格应用 |
| Kivy | 移动端友好,完全跨平台 | 传统桌面应用不适合 | 移动或触控应用 |
对于这个工具,我选择了Tkinter,因为:
- 零依赖:Python自带,用户无需安装额外库
- 快速开发:API简洁,原型开发快
- 足够功能:对于文件处理工具来说,功能完全足够
- 跨平台:Windows、macOS、Linux都能运行
架构设计
整体架构考虑
# 项目结构遵循MVC模式的思想
class FileLineDeleteApp:
def __init__(self): ...
# Model - 数据层(文件操作逻辑)
# View - 视图层(UI组件)
# Controller - 控制层(事件处理)
核心功能模块
- 文件选择模块:通过filedialog获取用户选择的文件
- 文件预览模块:显示文件信息,包括大小、行数、修改时间等
- 行号设置模块:提供方便的界面设置要删除的行范围
- 删除执行模块:安全的行删除,包含备份机制
- 日志显示模块:实时显示操作状态
关键技术实现
1. 现代化UI设计
主题和样式设置
def setup_style(self):
"""设置ttk样式"""
self.style = ttk.Style()
# 尝试设置不同主题
available_themes = self.style.theme_names()
print("可用主题:", available_themes)
# 根据平台选择合适的主题
if 'vista' in available_themes:
self.style.theme_use('vista')
elif 'clam' in available_themes:
self.style.theme_use('clam')
else:
self.style.theme_use('winnative')
# 自定义样式
self.style.configure('Title.TLabel',
font=('微软雅黑', 16, 'bold'))
self.style.configure('Danger.TButton',
font=('微软雅黑', 9),
background='#d9534f') # Bootstrap danger红色
布局管理
使用pack()和fill=tk.BOTH, expand=True实现自适应布局:
# 主容器使用自适应填充
main_container = ttk.Frame(self.root, padding="10")
main_container.pack(fill=tk.BOTH, expand=True)
# 文本区域带滚动条
self.info_text = tk.Text(info_frame, height=12, width=30)
info_scrollbar = ttk.Scrollbar(info_frame,
orient=tk.VERTICAL,
command=self.info_text.yview)
self.info_text.configure(yscrollcommand=info_scrollbar.set)
# 分割窗格实现灵活的布局
info_paned = ttk.PanedWindow(main_container, orient=tk.HORIZONTAL)
info_paned.pack(fill=tk.BOTH, expand=True, pady=10)
2. 文件信息获取与显示
获取文件元数据
def get_file_info(self, filename):
"""获取文件的详细信息"""
try:
# 获取文件大小(格式化显示)
file_size = os.path.getsize(filename)
# 获取修改时间(注意引号嵌套的处理)
mod_time = os.path.getmtime(filename)
# 外层单引号,内层双引号
time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mod_time))
# 读取文件行数
with open(filename, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
return {
'size': f"{file_size:,} 字节",
'mod_time': time_str,
'total_lines': total_lines,
'content_preview': lines[:5]
}
except Exception as e:
return {'error': str(e)}
3. 安全的文件操作
备份机制实现
def delete_lines_in_range(self, file_path, start_line, end_line):
"""删除指定文件中的行范围"""
try:
# 1. 读取原始文件
with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
# 2. 行号验证
if start_line < 1 or end_line > total_lines:
return False, f"行号超出范围 (1-{total_lines})"
# 3. 创建备份(非常重要!)
backup_path = file_path + '.bak'
import shutil
shutil.copy2(file_path, backup_path) # copy2会保留元数据
# 4. 执行删除操作
new_lines = []
deleted_lines = []
for index, line in enumerate(lines, start=1):
if start_line <= index <= end_line:
deleted_lines.append(line) # 保存被删除的行(可选)
else:
new_lines.append(line) # 保留的行
# 5. 写回新文件
with open(file_path, 'w', encoding='utf-8') as file:
file.writelines(new_lines)
# 6. 返回操作结果
deleted_count = end_line - start_line + 1
return True, f"已删除 {deleted_count} 行,原始文件已备份为: {backup_path}"
except PermissionError:
return False, "没有文件写入权限"
except FileNotFoundError:
return False, "文件未找到"
except Exception as e:
return False, f"发生错误: {str(e)}"
4. 用户体验优化
DPI自适应(针对高分辨率屏幕)
# 在Windows高DPI屏幕上自动调整
if sys.platform == 'win32':
try:
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass # 如果失败,继续执行
窗口居中显示
def center_window(window, width, height):
"""将窗口居中显示"""
# 先更新窗口信息,确保获取到正确的尺寸
window.update_idletasks()
# 获取屏幕尺寸
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
# 计算位置
x = (screen_width // 2) - (width // 2)
y = (screen_height // 2) - (height // 2)
# 设置窗口位置
window.geometry(f'{width}x{height}+{x}+{y}')
自动滚动
def update_status(self, message):
"""更新状态信息并自动滚动到底部"""
self.status_text.insert(tk.END, f"> {message}\n")
self.status_text.see(tk.END) # 关键:自动滚动到底部
5. 输入验证与错误处理
def execute_deletion(self):
"""执行删除操作前的完整验证"""
# 1. 验证文件存在
filename = self.file_path.get()
if not filename or not os.path.exists(filename):
self.show_error("请先选择一个有效的文件")
return
# 2. 验证行号格式
try:
start = int(self.start_line.get())
end = int(self.end_line.get())
except ValueError:
self.show_error("请输入有效的整数行号")
return
# 3. 验证逻辑关系
if start < 1 or end < 1:
self.show_error("行号必须大于0")
return
if start > end:
self.show_error("起始行不能大于结束行")
return
# 4. 验证文件实际行数
try:
with open(filename, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
except Exception as e:
self.show_error(f"无法读取文件: {str(e)}")
return
# 5. 超出范围的处理
if start > total_lines:
self.show_error(f"起始行号超出范围 (最大行数: {total_lines})")
return
if end > total_lines:
# 智能调整:询问用户是否调整到最大值
response = messagebox.askyesno(
"行号调整",
f"结束行号超出范围,是否调整为最大行数 {total_lines}?"
)
if response:
end = total_lines
else:
self.update_status("已取消操作")
return
# 6. 确认对话框
lines_to_delete = end - start + 1
confirm = messagebox.askyesno(
"确认删除",
f"确认删除行 {start} 到 {end} 吗?\n"
f"共 {lines_to_delete} 行\n\n"
"注意:此操作不可逆!",
icon='warning' # ⚠️ 警告图标
)
if confirm:
self.perform_deletion(filename, start, end, lines_to_delete)
扩展功能建议
这个基本版本已经相当完善,但还可以进一步扩展:
1. 批量处理模式
def batch_process_files(self):
"""批量处理多个文件"""
files = filedialog.askopenfilenames(title="选择多个文件")
if files:
for file in files:
# 对每个文件执行相同的删除操作
self.process_single_file(file, start_line, end_line)
2. 历史记录
class HistoryManager:
def __init__(self):
self.history = []
def add_record(self, operation):
"""添加历史记录"""
self.history.append({
'time': datetime.now(),
'operation': operation,
'file': operation['file'],
'lines_deleted': operation['lines_count']
})
def undo_last(self):
"""撤销最后一次操作"""
if self.history:
last_op = self.history.pop()
# ...撤销逻辑...
界面设计
布局层次
- 顶部:标题和主要操作按钮
- 中部:输入区域和设置区域
- 下部:信息显示和状态反馈
- 底部:状态栏
颜色与视觉线索
- 危险操作:使用红色按钮(Bootstrap Danger红色 #d9534f)
- 成功状态:绿色文字
- 错误状态:红色文字和图标
- 提示信息:灰色小字
交互设计原则
- 及时反馈:每个操作都有明确的状态反馈
- 安全第一:危险操作有确认对话框,自动备份
- 容错设计:错误输入有清晰的提示
- 逐步引导:界面按照操作步骤排列
测试注意事项
不同文件类型的测试
test_files = {
"文本文件": "test.txt",
"Python文件": "test.py",
"CSV文件": "data.csv",
"大型文件": "large.log",
"空文件": "empty.txt",
"只读文件": "readonly.txt",
"不存在的文件": "nonexist.txt"
}
边界情况处理
- 空文件处理
- 超大文件内存管理
- 特殊字符编码
- 行号越界
- 权限问题
- 磁盘空间不足
学习要点总结
对于Tkinter新手
- 了解常用组件:Label, Entry, Button, Text, Scrollbar, Frame
- 掌握布局管理器:pack, grid, place的选择与组合
- 使用ttk提高美观度:Tkinter的增强版组件
- 事件绑定:使用bind连接界面与逻辑
对于文件操作
- 安全第一:永远先备份再修改
- 编码处理:使用UTF-8编码,添加errors='ignore'处理异常
- 大文件处理:考虑分块读取时的情况
- 异常处理:详细的错误信息和用户友好的提示
对于项目结构
- 模块化设计:将功能拆分为独立方法
- 文档注释:清晰的函数说明和参数说明
- 版本控制:关键功能的版本备份
- 用户反馈:明确的操作结果反馈
性能优化建议
大文件处理
def process_large_file(self, filename, start_line, end_line):
"""处理大文件的优化版本"""
# 1. 只读取需要的行
with open(filename, 'r') as file:
# 跳过开头不需要的行
for _ in range(start_line - 1):
next(file, None)
# 2. 逐行处理,减少内存消耗
remaining_lines = []
for line_num, line in enumerate(file, start=start_line):
if line_num > end_line:
remaining_lines.append(line)
# 3. 写入剩余部分
# ... 写入逻辑 ...
调试技巧
添加调试输出
import logging
def setup_logging(self):
"""设置日志系统"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='file_deleter.log'
)
self.logger = logging.getLogger(__name__)
结语
这款文件行删除工具虽然功能简单,但涵盖了一个完整GUI应用的方方面面。从界面设计到文件操作,从用户体验到错误处理,每一个细节都体现了良好的软件开发实践。
通过这个项目,不仅掌握了Tkinter的基本用法,还深入理解了:
- 如何设计直观的用户界面
- 如何确保文件操作的安全性和可靠性
- 如何提供有意义的错误信息和反馈
- 如何优化代码结构和可维护性
希望这篇详细的开发记录对正在学习Python GUI编程的你有所帮助。工具的开发不仅仅是功能的实现,更是对用户体验、安全性和健壮性的全面考虑。 附上源码
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import time
import sys
class FileLineDeleteApp:
def __init__(self, root):
self.file_entry = None
self.style = None
self.root = root
# 变量初始化(必须在创建widgets之前!)
self.file_path = tk.StringVar()
self.start_line = tk.StringVar()
self.end_line = tk.StringVar()
self.root.title("文件行删除工具")
self.root.geometry("650x750")
# 设置主题,按钮,文字样式
self.setup_style()
# 创建UI
self.create_widgets()
def setup_style(self):
"""设置ttk样式"""
self.style = ttk.Style()
# 尝试设置不同主题
available_themes = self.style.theme_names()
print("可用主题:", available_themes)
# 选择可用的主题
if 'vista' in available_themes:
self.style.theme_use('vista')
elif 'clam' in available_themes:
self.style.theme_use('clam')
else:
self.style.theme_use('winnative')
# 自定义样式
self.style.configure('Title.TLabel', font=('微软雅黑', 16, 'bold'))
self.style.configure('Header.TLabel', font=('微软雅黑', 10, 'bold'))
self.style.configure('Normal.TLabel', font=('微软雅黑', 9))
# 为按钮创建样式
self.style.configure('Danger.TButton',
font=('微软雅黑', 9),
background='#d9534f'
)
# 为按钮创建样式
self.style.configure('Primary.TButton',
font=('微软雅黑', 9),
)
def create_widgets(self):
"""创建所有UI组件"""
# 主容器 框架会填满父容器的宽度和高度
main_container = ttk.Frame(self.root, padding="10")
main_container.pack(fill=tk.BOTH, expand=True)
# ========== 上半部分:标题和文件选择 ==========
# 标题
title_label = ttk.Label(main_container,
text="文件行删除工具",
style='Title.TLabel')
title_label.pack(pady=(0, 10))
# 分割线
separator = ttk.Separator(main_container, orient='horizontal')
separator.pack(fill=tk.X, pady=5)
# 文件选择部分
file_frame = ttk.LabelFrame(main_container,
text="1. 选择文件",
padding="10",
relief=tk.RIDGE)
file_frame.pack(fill=tk.X, pady=10)
file_row = ttk.Frame(file_frame)
file_row.pack(fill=tk.X, pady=5)
ttk.Label(file_row, text="文件路径:",
style='Header.TLabel',
width=10).pack(side=tk.LEFT, padx=(0, 5))
self.file_entry = ttk.Entry(file_row,
textvariable=self.file_path,
width=50,
font=('微软雅黑', 9))
self.file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
browse_btn = ttk.Button(file_row,
text="浏览...",
command=self.browse_file)
browse_btn.pack(side=tk.LEFT, padx=(5, 0))
# 行号输入部分
line_frame = ttk.LabelFrame(main_container,
text="2. 设置删除范围",
padding="10",
relief=tk.RIDGE)
line_frame.pack(fill=tk.X, pady=10)
# 起始行号
start_frame = ttk.Frame(line_frame)
start_frame.pack(fill=tk.X, pady=5)
ttk.Label(start_frame, text="起始行号:",
style='Normal.TLabel',
width=12).pack(side=tk.LEFT)
# 使用Combobox代替Spinbox,更稳定
self.start_combo = ttk.Combobox(start_frame,
textvariable=self.start_line,
width=10,
font=('微软雅黑', 9),
state='normal')
self.start_combo.pack(side=tk.LEFT, padx=5)
# 添加一些示例值
self.start_combo['values'] = tuple(range(1, 101))
# 结束行号
end_frame = ttk.Frame(line_frame)
end_frame.pack(fill=tk.X, pady=5)
ttk.Label(end_frame, text="结束行号:",
style='Normal.TLabel',
width=12).pack(side=tk.LEFT)
self.end_combo = ttk.Combobox(end_frame,
textvariable=self.end_line,
width=10,
font=('微软雅黑', 9),
state='normal')
self.end_combo.pack(side=tk.LEFT, padx=5)
# 添加一些示例值
self.end_combo['values'] = tuple(range(10, 201, 10))
# 行号提示
ttk.Label(line_frame, text="提示:输入要删除的行号范围(包含起止行)",
font=('微软雅黑', 8),
foreground='gray').pack(pady=(5, 0))
# ========== 中间部分:操作按钮 ==========
button_frame = ttk.Frame(main_container)
button_frame.pack(pady=20)
ttk.Button(button_frame,
text="预览文件信息",
command=self.preview_file_info,
style='Primary.TButton',
width=15).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame,
text="执行删除",
command=self.execute_deletion,
style='Danger.TButton',
).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame,
text="清除",
command=self.clear_all,
style='Primary.TButton',
width=15).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame,
text="退出",
command=self.root.quit,
style='Primary.TButton',
width=15).pack(side=tk.LEFT, padx=5)
# ========== 下半部分:信息显示 ==========
# 使用PanedWindow分割信息显示区域
info_paned = ttk.PanedWindow(main_container, orient=tk.HORIZONTAL)
info_paned.pack(fill=tk.BOTH, expand=True, pady=10)
# 文件信息框架
info_frame = ttk.LabelFrame(info_paned,
text="文件信息",
padding="5")
info_paned.add(info_frame, weight=1)
# 使用Text控件显示文件信息
self.info_text = tk.Text(info_frame,
height=12,
width=30,
font=('微软雅黑', 9),
bg='#f8f9fa',
relief=tk.FLAT,
wrap=tk.WORD)
info_scrollbar = ttk.Scrollbar(info_frame,
orient=tk.VERTICAL,
command=self.info_text.yview)
self.info_text.configure(yscrollcommand=info_scrollbar.set)
self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
info_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 状态信息框架
status_frame = ttk.LabelFrame(info_paned,
text="操作状态",
padding="5")
info_paned.add(status_frame, weight=1)
# 状态文本区域
self.status_text = tk.Text(status_frame,
height=12,
width=30,
font=('微软雅黑', 9),
bg='#f8f9fa',
relief=tk.FLAT,
wrap=tk.WORD)
status_scrollbar = ttk.Scrollbar(status_frame,
orient=tk.VERTICAL,
command=self.status_text.yview)
self.status_text.configure(yscrollcommand=status_scrollbar.set)
self.status_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
status_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 底部状态栏
self.status_bar = ttk.Label(self.root,
text="就绪",
relief=tk.SUNKEN,
anchor=tk.W,
font=('微软雅黑', 9),
padding=(5, 2))
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=5)
def browse_file(self):
"""浏览并选择文件"""
filename = filedialog.askopenfilename(
title="选择要处理的文件",
filetypes=[
("所有文件", "*.*"),
("Python 文件", "*.py"),
("文本文件", "*.txt"),
("CSV 文件", "*.csv"),
("日志文件", "*.log")
]
)
if filename:
self.file_path.set(filename)
self.update_status(f"已选择文件: {os.path.basename(filename)}")
def preview_file_info(self):
"""预览文件信息"""
filename = self.file_path.get()
if not filename or not os.path.exists(filename):
self.show_error("请选择一个有效的文件")
return
try:
# 清空文本区域
self.info_text.delete(1.0, tk.END)
with open(filename, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
# 获取文件信息
file_size = os.path.getsize(filename)
file_name = os.path.basename(filename)
file_dir = os.path.dirname(filename)
mod_time = os.path.getmtime(filename)
# 显示文件信息
self.info_text.insert(tk.END, "文件信息:\n")
self.info_text.insert(tk.END, "=" * 30 + "\n")
self.info_text.insert(tk.END, f"文件名: {file_name}\n")
self.info_text.insert(tk.END, f"大小: {file_size:,} 字节\n")
self.info_text.insert(tk.END, f"总行数: {total_lines:,} 行\n")
self.info_text.insert(tk.END, f"路径: {file_dir}\n")
self.info_text.insert(tk.END, f'修改时间: {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mod_time))}\n')
self.info_text.insert(tk.END, f"有效范围: 1 - {total_lines}\n")
self.info_text.insert(tk.END, "=" * 30 + "\n")
# 如果有内容,显示前几行预览
if lines:
self.info_text.insert(tk.END, "\n前5行预览:\n")
for i, line in enumerate(lines[:5], 1):
line_preview = line.strip()[:80]
if len(line.strip()) > 80:
line_preview += "..."
self.info_text.insert(tk.END, f"{i:3d}: {line_preview}\n")
# 更新状态
self.update_status("文件信息已加载")
self.status_bar.config(text=f"加载完成: {file_name}", foreground="green")
except Exception as e:
self.show_error(f"读取文件失败: {str(e)}")
def execute_deletion(self):
"""执行删除操作"""
filename = self.file_path.get()
# 验证输入
if not filename or not os.path.exists(filename):
self.show_error("请先选择一个有效的文件")
return
try:
start = int(self.start_line.get())
end = int(self.end_line.get())
except ValueError:
self.show_error("请输入有效的整数行号")
return
if start < 1 or end < 1:
self.show_error("行号必须大于0")
return
if start > end:
self.show_error("起始行不能大于结束行")
return
# 读取文件以验证行号范围
try:
with open(filename, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
except Exception as e:
self.show_error(f"无法读取文件: {str(e)}")
return
if start > total_lines:
self.show_error(f"起始行号超出范围 (最大行数: {total_lines})")
return
if end > total_lines:
# 自动调整到最大行数
response = messagebox.askyesno("行号调整",
f"结束行号超出范围,是否调整为最大行数 {total_lines}?")
if response:
end = total_lines
else:
self.update_status("已取消操作")
return
# 显示要删除的行数
lines_to_delete = end - start + 1
# 确认对话框
confirm = messagebox.askyesno(
"确认删除",
f"确认删除行 {start} 到 {end} 吗?\n"
f"共 {lines_to_delete} 行\n\n"
"注意:此操作不可逆!",
icon='warning'
)
if confirm:
try:
success, message = self.delete_lines_in_range(filename, start, end)
if success:
self.update_status(f"✓ {message}")
self.update_status(f"删除行: {start} - {end}")
self.update_status(f"删除数量: {lines_to_delete} 行")
# 清空输入框
self.start_line.set("")
self.end_line.set("")
self.status_bar.config(text="删除成功", foreground="green")
# 重新预览文件信息
self.preview_file_info()
# 显示成功对话框
messagebox.showinfo("成功",
f"成功删除 {lines_to_delete} 行!\n"
f"原始文件已备份为: {filename}.bak")
else:
self.show_error(message)
except Exception as e:
error_msg = f"删除过程中发生错误: {str(e)}"
self.show_error(error_msg)
def delete_lines_in_range(self, file_path, start_line, end_line):
"""删除指定文件中的行范围"""
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
total_lines = len(lines)
# 验证行号范围
if start_line < 1 or end_line > total_lines:
return False, f"行号超出范围 (1-{total_lines})"
if start_line > end_line:
return False, "起始行不能大于结束行"
# 创建备份文件
backup_path = file_path + '.bak'
import shutil
shutil.copy2(file_path, backup_path)
# 进行删除操作
new_lines = []
deleted_lines = []
for index, line in enumerate(lines, start=1):
if start_line <= index <= end_line:
deleted_lines.append(line)
else:
new_lines.append(line)
with open(file_path, 'w', encoding='utf-8') as file:
file.writelines(new_lines)
return True, f"已删除 {end_line - start_line + 1} 行"
except FileNotFoundError:
return False, "文件未找到"
except PermissionError:
return False, "没有文件写入权限"
except Exception as e:
return False, f"发生错误: {str(e)}"
def clear_all(self):
"""清除所有输入和显示"""
self.file_path.set("")
self.start_line.set("")
self.end_line.set("")
# 清空文本区域
self.info_text.delete(1.0, tk.END)
self.status_text.delete(1.0, tk.END)
# 更新状态
self.update_status("所有内容已清除")
self.status_bar.config(text="就绪", foreground="black")
def update_status(self, message):
"""更新状态信息"""
self.status_text.insert(tk.END, f"> {message}\n")
self.status_text.see(tk.END) # 自动滚动到底部
def show_error(self, message):
"""显示错误信息"""
self.status_bar.config(text=f"错误: {message}", foreground="red")
self.update_status(f"✗ {message}")
messagebox.showerror("错误", message)
def main():
"""主函数"""
root = tk.Tk()
# 设置DPI感知(Windows)
# 在Windows系统中,当程序运行在高分辨率的屏幕上(如4K显示器、Retina屏),可能会出现字体模糊、界面元素过小的问题。这段代码告诉Windows系统:我的程序支持高DPI缩放。
if sys.platform == 'win32':
try:
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass # 如果失败,继续执行
# 设置窗口图标
try:
if sys.platform == 'win32':
root.iconbitmap(default='icon.ico')
except:
pass
app = FileLineDeleteApp(root)
# 设置窗口居中
root.update_idletasks()
width = 700 # 固定宽度
height = 750 # 固定高度
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
# 绑定回车键触发浏览按钮
root.bind('<Return>', lambda e: app.browse_file())
root.mainloop()
if __name__ == "__main__":
main()