【实战项目】基于 PyQt5 开发 Windows 磁盘分析与清理工具 作者donoot | 版本v0.11项目概述SysDriMan 是一款基于 Python 和 PyQt5 开发的 Windows 磁盘分析与清理工具旨在帮助用户快速了解磁盘空间使用情况管理大文件和已安装软件清理系统垃圾文件。项目地址https://github.com/donoot/SysDriMan功能特性1. 磁盘空间分析饼图可视化直观展示磁盘空间分布情况递归浏览双击饼图扇区可进入子目录查看详细分布空间信息显示实时显示总空间、已用空间、剩余空间2. 大文件管理自动扫描扫描并列出大于指定阈值的大文件文件详情显示文件路径、大小、修改时间、创建时间列宽可调表格列宽支持鼠标拖动调节3. 已安装软件管理注册表读取从 Windows 注册表读取所有已安装软件快速操作右键菜单支持卸载和修改软件4. 垃圾文件清理自动扫描扫描系统临时目录、回收站等常见垃圾位置一键清理支持批量清理选中的垃圾文件移至回收站5. 流光进度条滚动渐变参考经典工具溯雪的流光效果实时百分比中间显示扫描进度百分比技术架构设计整体架构​编辑模块划分模块职责文件主窗口UI 界面构建和功能集成main.py工具函数管理员权限检查、文件大小格式化utils.py扫描线程后台磁盘扫描、多线程并行处理scan_thread.py软件管理注册表读取、软件卸载software_manager.py垃圾清理垃圾文件扫描、清理garbage_cleaner.py核心技术实现1. 多线程并行扫描问题背景磁盘扫描是一个耗时操作如果在主线程中执行会导致 UI 卡顿。解决方案使用 Pythonthreading模块结合 Qt 的QThread实现多线程并行扫描。class ScanWorker(threading.Thread): 扫描工作线程类 def __init__(self, task_queue, result_queue, size_threshold, stop_event): super().__init__() self.task_queue task_queue self.result_queue result_queue self.size_threshold size_threshold self.stop_event stop_event ​ def run(self): while not self.stop_event.is_set(): try: dirpath, dirnames, filenames self.task_queue.get(timeout1) except: break dir_size 0 big_files [] for f in filenames: try: fp os.path.join(dirpath, f) if os.path.isfile(fp): st os.stat(fp) size st.st_size dir_size size if size self.size_threshold: big_files.append((fp, size, ...)) except (PermissionError, OSError): continue self.result_queue.put((dirpath, dir_size, big_files)) self.task_queue.task_done()关键设计使用Queue实现任务分发和结果收集多个ScanWorker线程并行处理目录扫描通过stop_event实现优雅停止使用 Qt 信号机制向主线程传递进度2. 流光进度条实现问题背景用户希望进度条具有流动的视觉效果类似于经典工具溯雪中的流光效果。解决方案使用 QTimer 定时更新 QProgressBar 的样式表实现滚动渐变效果。def setup_flow_progress_style(self): 设置流光进度条样式 self.flow_gradient_pos 0 self.flow_timer QTimer(self) self.flow_timer.timeout.connect(self.update_flow_animation) base_style QProgressBar { border: 1px solid #666666; border-radius: 4px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4a4a4a, stop:0.5 #3d3d3d, stop:1 #333333); text-align: center; color: #00ffff; font-weight: bold; font-size: 12px; } QProgressBar::chunk { border-radius: 3px; margin: 1px; } self.progress_bar.setStyleSheet(base_style) ​ def update_flow_animation(self): 更新流光动画 self.flow_gradient_pos (self.flow_gradient_pos 1) % 100 p self.flow_gradient_pos / 100.0 style f QProgressBar::chunk {{ background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:{(p - 0.3) % 1} transparent, stop:{(p - 0.2) % 1} rgba(0, 200, 255, 0.3), stop:{(p - 0.1) % 1} rgba(0, 220, 255, 0.6), stop:{p % 1} rgba(0, 255, 255, 1), stop:{(p 0.1) % 1} rgba(0, 220, 255, 0.6), stop:{(p 0.2) % 1} rgba(0, 200, 255, 0.3), stop:{(p 0.3) % 1} transparent); }} self.progress_bar.setStyleSheet(style)关键设计使用rgba颜色值实现透明度渐变通过取模运算实现无缝循环50ms 定时器间隔保证动画流畅3. Matplotlib 饼图嵌入 Qt 界面问题背景需要在 PyQt5 界面中嵌入交互式饼图支持双击进入子目录。解决方案使用FigureCanvasQTAgg将 Matplotlib 图表嵌入 Qt 界面并绑定鼠标事件。from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure ​ class MainWindow(QMainWindow): def __init__(self): ... self.fig Figure(figsize(5, 5)) self.canvas FigureCanvas(self.fig) self.left_layout.addWidget(self.canvas) self.canvas.mpl_connect(button_press_event, self.on_pie_click) def on_pie_click(self, event): 处理饼图双击事件 if event.dblclick and hasattr(self, wedges): for i, wedge in enumerate(self.wedges): if wedge.contains(event)[0]: label self.pie_labels[i] if label 其他目录: return new_path os.path.join(self.current_path, label) if os.path.isdir(new_path): self.path_history.append(self.current_path) self.current_path new_path self.update_pie_chart() break关键设计使用FigureCanvasQTAgg作为 Qt 控件通过mpl_connect绑定鼠标事件使用wedge.contains(event)判断点击位置维护路径历史支持返回上层4. 管理员权限自动提升问题背景访问系统目录和注册表需要管理员权限普通用户运行会遇到权限不足问题。解决方案在程序启动时检查权限若无管理员权限则自动提升。import ctypes import sys ​ def is_admin(): 检查当前进程是否以管理员身份运行 try: return ctypes.windll.shell32.IsUserAnAdmin() except: return False ​ def run_as_admin(): 以管理员身份重新启动程序 if sys.platform win32: script os.path.abspath(sys.argv[0]) params .join([ arg for arg in sys.argv[1:]]) ctypes.windll.shell32.ShellExecuteW( None, runas, sys.executable, f{script} {params}, None, 1 ) ​ if __name__ __main__: if not is_admin(): run_as_admin() sys.exit(0) app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())关键设计使用IsUserAnAdmin()检查权限使用ShellExecuteW以runas方式启动保留命令行参数传递5. 中文字体配置问题背景Matplotlib 默认不支持中文中文标签会显示为方块或乱码。解决方案检测系统可用字体并配置 Matplotlib 使用中文字体。import matplotlib.font_manager as fm ​ def setup_matplotlib_font(): 设置matplotlib中文字体 available_fonts [f.name for f in fm.fontManager.ttflist] preferred_fonts [ SimHei, Microsoft YaHei, SimSun, KaiTi, Heiti SC, STHeiti ] selected_font None for font in preferred_fonts: if font in available_fonts: selected_font font break if selected_font: matplotlib.rcParams[font.sans-serif] [selected_font] matplotlib.rcParams[font.family] sans-serif matplotlib.rcParams[axes.unicode_minus] False ​ setup_matplotlib_font()关键设计枚举系统可用字体列表按优先级选择最合适的中文字体关闭 Unicode 负号替换遇到的问题与解决方案问题 1QProgressBar 样式频繁更新导致闪烁现象每次更新进度条样式表时界面会出现短暂闪烁。原因频繁调用setStyleSheet会触发控件重绘。解决方案初始化时设置基础样式动画更新时只修改::chunk部分使用QTimer控制更新频率50ms问题 2多线程资源竞争现象多个线程同时写入结果字典时可能出现数据不一致。原因Python 的字典不是线程安全的。解决方案使用Queue收集结果在主线程中汇总避免多个线程直接修改共享数据结构问题 3Windows 控制台中文乱码现象程序输出的中文路径在控制台显示为乱码。原因Windows 控制台默认使用 GBK 编码而 Python 使用 UTF-8。解决方案if sys.platform win32: ctypes.windll.kernel32.SetConsoleCP(65001) ctypes.windll.kernel32.SetConsoleOutputCP(65001) os.environ[PYTHONUTF8] 1问题 4Matplotlib 饼图标签重叠现象当目录名称较长时饼图标签会重叠。解决方案设置labeldistance1.15增加标签距离使用较小的字体大小8-10pt限制显示的目录数量前 10 个 其他项目运行环境要求依赖安装命令PyQt5pip install PyQt5Matplotlibpip install matplotlibpsutilpip install psutilsend2trashpip install send2trash启动方式cd src python main.py注意程序会自动检查管理员权限若无权限会弹出 UAC 提示。总结与展望已实现功能✅ 磁盘空间分析与可视化✅ 多线程并行扫描✅ 流光进度条动画✅ 大文件管理✅ 软件管理注册表读取、卸载✅ 垃圾文件清理✅ 管理员权限自动提升✅ 中文路径支持未来计划 添加磁盘健康检测功能 支持导出分析报告PDF/Excel 添加文件类型统计 优化扫描算法支持增量扫描 添加快捷键支持 支持深色/浅色主题切换结语SysDriMan 项目展示了如何使用 PyQt5 构建功能完善的桌面应用涵盖了多线程编程、数据可视化、系统权限管理等多个技术要点。希望本文能对正在学习 PyQt5 开发的小伙伴有所帮助如果您对项目有任何建议或问题欢迎在 GitHub 仓库中提交 Issue 或 Pull Request。项目地址https://github.com/donoot/SysDriMan原创不易如果本文对您有帮助请点赞支持