[Python] 纯文本查看 复制代码
import os
import subprocess
import sys
import ctypes
import threading
import time
import math
from pathlib import Path
from datetime import datetime
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from queue import Queue
from typing import List, Optional, Tuple, Dict
from ctypes import wintypes
import shutil
import tempfile
import uuid
class 闪电删除专业版:
def __init__(self, 主窗口):
self.主窗口 = 主窗口
self.主窗口.title("⚡ 闪电删除专业版 v4.7")
self.主窗口.geometry("1000x750")
self.主窗口.resizable(True, True)
# 设置最小窗口大小
self.主窗口.minsize(900, 650)
# 设置现代化主题颜色
self.颜色 = {
'主色调': '#3498db',
'成功色': '#2ecc71',
'危险色': '#e74c3c',
'警告色': '#f39c12',
'深色': '#2c3e50',
'浅色': '#ecf0f1',
'灰色': '#95a5a6',
'深灰色': '#34495e'
}
# 设置图标
try:
self.主窗口.iconbitmap(default='icon.ico')
except:
pass
# 删除任务队列
self.删除队列 = Queue()
self.正在删除 = False
self.停止请求 = False
self.当前任务 = None
# 统计信息
self.重置统计()
# 设置窗口置顶选项变量
self.窗口置顶 = tk.BooleanVar(value=False)
# 存储实际路径的映射(解决显示截断问题)
self.路径映射 = {} # 显示文本 -> 实际路径
# 预加载Windows API函数
self.API可用 = self.初始化WindowsAPI()
# 创建UI
self.创建基础UI()
# 启动删除线程
self.删除线程 = threading.Thread(target=self.删除工作线程, daemon=True)
self.删除线程.start()
# 启动统计更新线程
self.统计线程 = threading.Thread(target=self.更新统计工作线程, daemon=True)
self.统计线程.start()
def 初始化WindowsAPI(self):
"""设置Windows API调用"""
try:
self.kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
self.shell32 = ctypes.WinDLL('shell32', use_last_error=True)
# 设置API函数原型
self.kernel32.DeleteFileW.argtypes = [wintypes.LPCWSTR]
self.kernel32.DeleteFileW.restype = wintypes.BOOL
self.kernel32.SetFileAttributesW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD]
self.kernel32.SetFileAttributesW.restype = wintypes.BOOL
# 获取长路径函数
self.kernel32.GetLongPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]
self.kernel32.GetLongPathNameW.restype = wintypes.DWORD
return True
except Exception as e:
print(f"Windows API初始化失败: {e}")
return False
def 获取长路径(self, 路径):
"""将短路径转换为长路径"""
try:
if not self.API可用:
return 路径
# 分配缓冲区
缓冲区大小 = 4096
缓冲区 = ctypes.create_unicode_buffer(缓冲区大小)
结果 = self.kernel32.GetLongPathNameW(路径, 缓冲区, 缓冲区大小)
if 结果 > 0 and 结果 < 缓冲区大小:
return 缓冲区.value
return 路径
except:
return 路径
def 重置统计(self):
"""重置统计信息"""
self.已删除文件数 = 0
self.已删除文件夹数 = 0
self.总大小 = 0
self.当前大小 = 0
self.开始时间 = None
self.当前速度 = 0
def 创建基础UI(self):
"""创建基础UI组件"""
# 创建主容器
主容器 = tk.Frame(self.主窗口)
主容器.pack(fill=tk.BOTH, expand=True, padx=0, pady=0)
# 标题栏
标题栏框架 = tk.Frame(主容器, bg=self.颜色['深色'], height=70)
标题栏框架.pack(fill=tk.X, padx=0, pady=0)
标题栏框架.pack_propagate(False)
标题内容 = tk.Frame(标题栏框架, bg=self.颜色['深色'])
标题内容.pack(fill=tk.BOTH, padx=20, pady=10)
标题标签 = tk.Label(标题内容,
text="⚡ 闪电删除专业版 v4.7",
font=('Microsoft YaHei', 20, 'bold'),
bg=self.颜色['深色'],
fg='white')
标题标签.pack(side=tk.LEFT)
# 强力删除标签
强力标签 = tk.Label(标题内容,
text="💪 超强模式",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['深色'],
fg=self.颜色['成功色'])
强力标签.pack(side=tk.LEFT, padx=(10, 0))
# 窗口置顶按钮
置顶按钮 = tk.Checkbutton(标题内容,
text="📌 窗口置顶",
variable=self.窗口置顶,
command=self.切换置顶,
font=('Microsoft YaHei', 9),
bg=self.颜色['深色'],
fg='white',
selectcolor=self.颜色['深色'],
activebackground=self.颜色['深色'],
activeforeground='white')
置顶按钮.pack(side=tk.RIGHT)
# 主内容区域
内容框架 = tk.Frame(主容器, bg='white')
内容框架.pack(fill=tk.BOTH, expand=True, padx=20, pady=15)
# 左侧面板
左侧面板 = tk.Frame(内容框架, bg='white')
左侧面板.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
# 右侧面板
右侧面板 = tk.Frame(内容框架, bg='white', width=350)
右侧面板.pack(side=tk.RIGHT, fill=tk.BOTH, padx=(10, 0))
右侧面板.pack_propagate(False)
# 添加项目卡片
添加卡片 = tk.Frame(左侧面板, bg='white', relief='solid', borderwidth=1)
添加卡片.pack(fill=tk.X, pady=(0, 15))
卡片标题 = tk.Label(添加卡片,
text="📂 添加要删除的项目",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['浅色'],
fg=self.颜色['深色'],
padx=15,
pady=10)
卡片标题.pack(fill=tk.X)
卡片内容 = tk.Frame(添加卡片, bg='white')
卡片内容.pack(fill=tk.X, padx=15, pady=15)
按钮行1 = tk.Frame(卡片内容, bg='white')
按钮行1.pack(fill=tk.X, pady=(0, 10))
添加文件夹按钮 = tk.Button(按钮行1,
text="📁 添加文件夹",
command=self.添加文件夹,
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['主色调'],
fg='white',
activebackground='#2980b9',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=15,
height=2)
添加文件夹按钮.pack(side=tk.LEFT, padx=(0, 10))
添加文件按钮 = tk.Button(按钮行1,
text="📄 添加文件",
command=self.添加文件,
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['主色调'],
fg='white',
activebackground='#2980b9',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=15,
height=2)
添加文件按钮.pack(side=tk.LEFT)
按钮行2 = tk.Frame(卡片内容, bg='white')
按钮行2.pack(fill=tk.X)
输入框架 = tk.Frame(按钮行2, bg='white')
输入框架.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
self.路径变量 = tk.StringVar()
路径输入框 = tk.Entry(输入框架,
textvariable=self.路径变量,
font=('Microsoft YaHei', 10),
relief='solid',
borderwidth=1)
路径输入框.pack(side=tk.LEFT, fill=tk.X, expand=True, ipady=5)
添加路径按钮 = tk.Button(输入框架,
text="添加",
command=self.从输入框添加路径,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=8)
添加路径按钮.pack(side=tk.RIGHT, padx=(5, 0))
# 从剪贴板添加按钮
剪贴板按钮 = tk.Button(按钮行2,
text="📋 从剪贴板",
command=self.从剪贴板添加,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=12)
剪贴板按钮.pack(side=tk.RIGHT, padx=(5, 0))
# 项目列表卡片
列表卡片 = tk.Frame(左侧面板, bg='white', relief='solid', borderwidth=1)
列表卡片.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
列表标题栏 = tk.Frame(列表卡片, bg='white')
列表标题栏.pack(fill=tk.X, padx=15, pady=(15, 10))
self.列表标题 = tk.Label(列表标题栏,
text=f"🗑️ 待删除项目 ({0})",
font=('Microsoft YaHei', 11, 'bold'),
bg='white',
fg=self.颜色['深色'])
self.列表标题.pack(side=tk.LEFT)
列表操作栏 = tk.Frame(列表标题栏, bg='white')
列表操作栏.pack(side=tk.RIGHT)
清空按钮 = tk.Button(列表操作栏,
text="清空全部",
command=self.清空列表,
font=('Microsoft YaHei', 10),
bg=self.颜色['警告色'],
fg='white',
activebackground='#e67e22',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
清空按钮.pack(side=tk.RIGHT, padx=(5, 0))
移除选中按钮 = tk.Button(列表操作栏,
text="移除选中项目",
command=self.移除选中,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=15)
移除选中按钮.pack(side=tk.RIGHT)
列表内容框架 = tk.Frame(列表卡片, bg='white')
列表内容框架.pack(fill=tk.BOTH, expand=True, padx=15, pady=(0, 15))
列表容器 = tk.Frame(列表内容框架, bg='white')
列表容器.pack(fill=tk.BOTH, expand=True)
滚动条 = tk.Scrollbar(列表容器)
滚动条.pack(side=tk.RIGHT, fill=tk.Y)
self.列表框 = tk.Listbox(列表容器,
yscrollcommand=滚动条.set,
selectmode=tk.EXTENDED,
font=('Microsoft YaHei', 10),
bg='white',
fg=self.颜色['深色'],
selectbackground=self.颜色['主色调'],
selectforeground='white',
borderwidth=1,
relief='solid',
highlightthickness=0)
self.列表框.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
滚动条.config(command=self.列表框.yview)
self.列表框.bind('<<ListboxSelect>>', self.更新列表标题)
# 添加上下文菜单
self.创建列表框右键菜单()
# 删除选项卡片
选项卡片 = tk.Frame(右侧面板, bg='white', relief='solid', borderwidth=1)
选项卡片.pack(fill=tk.X, pady=(0, 15))
选项标题 = tk.Label(选项卡片,
text="⚙️ 删除选项",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['浅色'],
fg=self.颜色['深色'],
padx=15,
pady=10)
选项标题.pack(fill=tk.X)
选项内容 = tk.Frame(选项卡片, bg='white')
选项内容.pack(fill=tk.X, padx=15, pady=15)
self.跳过回收站变量 = tk.BooleanVar(value=True)
self.显示详细信息变量 = tk.BooleanVar(value=True)
self.删除前确认变量 = tk.BooleanVar(value=True)
self.使用强力模式变量 = tk.BooleanVar(value=True)
self.处理长路径变量 = tk.BooleanVar(value=True)
选项列表 = [
("🗑️ 跳过回收站(永久删除)", self.跳过回收站变量),
("📊 显示详细信息", self.显示详细信息变量),
("⚠️ 删除前确认", self.删除前确认变量),
("💪 使用超强模式", self.使用强力模式变量),
("📏 启用长路径支持", self.处理长路径变量)
]
for i, (文本, 变量) in enumerate(选项列表):
框架 = tk.Frame(选项内容, bg='white')
框架.pack(fill=tk.X, pady=3)
复选框 = tk.Checkbutton(框架,
text=文本,
variable=变量,
font=('Microsoft YaHei', 10),
bg='white',
fg=self.颜色['深色'],
selectcolor=self.颜色['主色调'],
activebackground='white',
activeforeground=self.颜色['深色'])
复选框.pack(side=tk.LEFT, anchor='w')
# 控制按钮卡片
控制卡片 = tk.Frame(右侧面板, bg='white', relief='solid', borderwidth=1)
控制卡片.pack(fill=tk.X, pady=(0, 15))
控制标题 = tk.Label(控制卡片,
text="🎮 控制面板",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['浅色'],
fg=self.颜色['深色'],
padx=15,
pady=10)
控制标题.pack(fill=tk.X)
控制内容 = tk.Frame(控制卡片, bg='white')
控制内容.pack(fill=tk.X, padx=15, pady=15)
self.分析按钮 = tk.Button(控制内容,
text="📊 智能分析",
command=self.智能分析项目,
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['成功色'],
fg='white',
activebackground='#27ae60',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=20,
height=2)
self.分析按钮.pack(pady=(0, 10))
self.删除按钮 = tk.Button(控制内容,
text="💣 超强删除",
command=self.开始删除,
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['危险色'],
fg='white',
activebackground='#c0392b',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=20,
height=2)
self.删除按钮.pack(pady=(0, 10))
self.停止按钮 = tk.Button(控制内容,
text="⏹️ 停止",
command=self.停止删除,
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['警告色'],
fg='white',
activebackground='#e67e22',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=20,
height=2,
state='disabled')
self.停止按钮.pack()
# 进度条卡片
进度卡片 = tk.Frame(右侧面板, bg='white', relief='solid', borderwidth=1)
进度卡片.pack(fill=tk.X, pady=(0, 15))
进度标题 = tk.Label(进度卡片,
text="📈 进度状态",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['浅色'],
fg=self.颜色['深色'],
padx=15,
pady=10)
进度标题.pack(fill=tk.X)
进度内容 = tk.Frame(进度卡片, bg='white')
进度内容.pack(fill=tk.X, padx=15, pady=15)
进度条框架 = tk.Frame(进度内容, bg='white', height=20)
进度条框架.pack(fill=tk.X, pady=(0, 10))
进度条框架.pack_propagate(False)
self.进度条画布 = tk.Canvas(进度条框架,
bg=self.颜色['浅色'],
highlightthickness=0,
height=20)
self.进度条画布.pack(fill=tk.X)
self.进度条填充 = self.进度条画布.create_rectangle(0, 0, 0, 20,
fill=self.颜色['主色调'],
outline='')
self.状态变量 = tk.StringVar(value="🟢 就绪 - 添加项目以开始")
状态标签 = tk.Label(进度内容,
textvariable=self.状态变量,
font=('Microsoft YaHei', 10),
bg='white',
fg=self.颜色['深色'],
wraplength=300,
justify='left')
状态标签.pack(anchor='w')
# 统计信息卡片
统计卡片 = tk.Frame(右侧面板, bg='white', relief='solid', borderwidth=1)
统计卡片.pack(fill=tk.BOTH, expand=True)
统计标题 = tk.Label(统计卡片,
text="📊 实时统计",
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['浅色'],
fg=self.颜色['深色'],
padx=15,
pady=10)
统计标题.pack(fill=tk.X)
统计内容 = tk.Frame(统计卡片, bg='white')
统计内容.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
self.统计标签 = {}
统计数据 = [
("📁 已删除文件夹:", "已删除文件夹数", self.颜色['主色调']),
("📄 已删除文件:", "已删除文件数", self.颜色['成功色']),
("💾 已处理大小:", "已处理大小", self.颜色['警告色']),
("📦 总大小:", "总大小", self.颜色['深色']),
("⚡ 处理速度:", "处理速度", self.颜色['危险色']),
("⏱️ 剩余时间:", "剩余时间", self.颜色['深灰色']),
("🎯 当前项目:", "当前项目", self.颜色['深色'])
]
for 文本, 键, 颜色 in 统计数据:
框架 = tk.Frame(统计内容, bg='white')
框架.pack(fill=tk.X, pady=4)
标签 = tk.Label(框架,
text=文本,
font=('Microsoft YaHei', 9),
bg='white',
fg=self.颜色['深灰色'],
width=18,
anchor='w')
标签.pack(side=tk.LEFT)
值标签 = tk.Label(框架,
text="0",
font=('Microsoft YaHei', 9, 'bold'),
bg='white',
fg=颜色,
width=25,
anchor='w')
值标签.pack(side=tk.LEFT, padx=(5, 0))
self.统计标签[键] = 值标签
# 日志区域
日志卡片 = tk.Frame(主容器, bg='white', relief='solid', borderwidth=1)
日志卡片.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
日志标题栏 = tk.Frame(日志卡片, bg='white')
日志标题栏.pack(fill=tk.X, padx=15, pady=(15, 10))
日志标题 = tk.Label(日志标题栏,
text="📝 操作日志",
font=('Microsoft YaHei', 11, 'bold'),
bg='white',
fg=self.颜色['深色'])
日志标题.pack(side=tk.LEFT)
日志操作栏 = tk.Frame(日志标题栏, bg='white')
日志操作栏.pack(side=tk.RIGHT)
保存日志按钮 = tk.Button(日志操作栏,
text="💾 保存日志",
command=self.保存日志,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
保存日志按钮.pack(side=tk.RIGHT, padx=(5, 0))
清空日志按钮 = tk.Button(日志操作栏,
text="🗑️ 清空日志",
command=self.清空日志,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
清空日志按钮.pack(side=tk.RIGHT)
日志容器 = tk.Frame(日志卡片, bg='white')
日志容器.pack(fill=tk.BOTH, expand=True, padx=15, pady=(0, 15))
self.日志文本框 = scrolledtext.ScrolledText(日志容器,
height=8,
font=('Microsoft YaHei', 9),
wrap=tk.WORD,
bg='#f8f9fa',
fg=self.颜色['深色'],
insertbackground=self.颜色['主色调'],
borderwidth=1,
relief='solid')
self.日志文本框.pack(fill=tk.BOTH, expand=True)
# 初始化日志
self.记录日志("=" * 60)
self.记录日志("⚡ 闪电删除专业版 v4.7 超强模式")
self.记录日志("=" * 60)
self.记录日志("✅ 准备就绪,可以开始删除文件和文件夹")
self.记录日志("💪 超强模式:支持删除大文件夹、长路径文件")
self.记录日志("📏 智能分析:支持超长文件名和路径的分析")
self.记录日志("⚠️ 警告:跳过回收站将永久删除,不可恢复!")
def 创建列表框右键菜单(self):
"""创建列表框的右键菜单"""
self.列表框菜单 = tk.Menu(self.主窗口, tearoff=0)
self.列表框菜单.add_command(label="查看完整路径", command=self.查看完整路径)
self.列表框菜单.add_command(label="查看属性", command=self.查看项目属性)
self.列表框.bind("<Button-3>", self.显示列表框菜单)
def 显示列表框菜单(self, event):
"""显示列表框右键菜单"""
try:
选中项 = self.列表框.nearest(event.y)
self.列表框.selection_clear(0, tk.END)
self.列表框.selection_set(选中项)
self.列表框.activate(选中项)
self.列表框菜单.post(event.x_root, event.y_root)
except:
pass
def 查看完整路径(self):
"""查看选中项目的完整路径"""
选中项 = self.列表框.curselection()
if 选中项:
显示文本 = self.列表框.get(选中项[0])
实际路径 = self.获取实际路径(显示文本)
# 显示完整路径对话框
路径窗口 = tk.Toplevel(self.主窗口)
路径窗口.title("完整路径")
路径窗口.geometry("600x200")
路径窗口.resizable(False, False)
路径窗口.configure(bg='white')
标题 = tk.Label(路径窗口,
text="📁 完整路径信息",
font=('Microsoft YaHei', 12, 'bold'),
bg='white',
fg=self.颜色['深色'],
pady=10)
标题.pack(fill=tk.X)
路径框架 = tk.Frame(路径窗口, bg='white')
路径框架.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
路径标签 = tk.Label(路径框架,
text="路径:",
font=('Microsoft YaHei', 10),
bg='white',
fg=self.颜色['深色'],
anchor='w')
路径标签.pack(anchor='w')
路径文本框 = scrolledtext.ScrolledText(路径框架,
height=4,
font=('Microsoft YaHei', 9),
wrap=tk.WORD,
bg='#f8f9fa',
fg=self.颜色['深色'],
borderwidth=1,
relief='solid')
路径文本框.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
路径文本框.insert('1.0', 实际路径)
路径文本框.config(state='disabled')
按钮框架 = tk.Frame(路径窗口, bg='white')
按钮框架.pack(fill=tk.X, padx=20, pady=(0, 15))
复制按钮 = tk.Button(按钮框架,
text="📋 复制到剪贴板",
command=lambda: self.复制到剪贴板(实际路径),
font=('Microsoft YaHei', 10),
bg=self.颜色['主色调'],
fg='white',
activebackground='#2980b9',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2')
复制按钮.pack(side=tk.LEFT, padx=(0, 10))
关闭按钮 = tk.Button(按钮框架,
text="关闭",
command=路径窗口.destroy,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
关闭按钮.pack(side=tk.RIGHT)
def 查看项目属性(self):
"""查看选中项目的属性"""
选中项 = self.列表框.curselection()
if 选中项:
显示文本 = self.列表框.get(选中项[0])
实际路径 = self.获取实际路径(显示文本)
try:
if os.path.exists(实际路径):
import stat
是文件夹 = os.path.isdir(实际路径)
大小 = 0
创建时间 = ""
修改时间 = ""
属性 = ""
try:
stat_info = os.stat(实际路径)
创建时间 = datetime.fromtimestamp(stat_info.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
修改时间 = datetime.fromtimestamp(stat_info.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
# 获取文件属性
属性列表 = []
if stat.S_ISDIR(stat_info.st_mode):
属性列表.append("文件夹")
else:
属性列表.append("文件")
try:
大小 = os.path.getsize(实际路径)
except:
pass
if not stat_info.st_mode & stat.S_IWRITE:
属性列表.append("只读")
属性 = ", ".join(属性列表)
except:
pass
属性信息 = f"""
📊 项目属性
{'='*40}
名称: {os.path.basename(实际路径)}
路径: {实际路径}
类型: {'文件夹' if 是文件夹 else '文件'}
大小: {self.格式化大小(大小) if not 是文件夹 else '--'}
属性: {属性}
创建时间: {创建时间}
修改时间: {修改时间}
"""
messagebox.showinfo("项目属性", 属性信息)
else:
messagebox.showerror("错误", "项目不存在或无法访问")
except Exception as e:
messagebox.showerror("错误", f"获取属性失败:\n{str(e)}")
def 获取实际路径(self, 显示文本):
"""从显示文本获取实际路径"""
# 如果显示文本是完整的路径(非截断)
if os.path.exists(显示文本):
return 显示文本
# 从路径映射中查找
if 显示文本 in self.路径映射:
return self.路径映射[显示文本]
# 返回显示文本(可能是完整路径)
return 显示文本
def 复制到剪贴板(self, 文本):
"""复制文本到剪贴板"""
self.主窗口.clipboard_clear()
self.主窗口.clipboard_append(文本)
self.记录日志("📋 已复制到剪贴板")
def 切换置顶(self):
"""切换窗口置顶状态"""
self.主窗口.attributes('-topmost', self.窗口置顶.get())
def 更新列表标题(self, event=None):
"""更新列表标题中的项目数量"""
数量 = self.列表框.size()
self.列表标题.config(text=f"🗑️ 待删除项目 ({数量})")
def 从输入框添加路径(self):
"""从输入框添加路径"""
路径 = self.路径变量.get().strip()
if 路径:
# 尝试获取长路径
实际路径 = self.获取长路径(路径)
if os.path.exists(实际路径):
self.添加项目到列表(实际路径)
self.路径变量.set("")
self.记录日志(f"✅ 已添加: {os.path.basename(实际路径)}")
self.更新列表标题()
else:
messagebox.showerror("错误", "路径不存在!")
else:
messagebox.showwarning("警告", "请输入路径")
def 从剪贴板添加(self):
"""从剪贴板添加路径"""
try:
路径 = self.主窗口.clipboard_get().strip()
if 路径:
# 尝试获取长路径
实际路径 = self.获取长路径(路径)
if os.path.exists(实际路径):
if self.添加项目到列表(实际路径):
self.记录日志(f"📋 从剪贴板添加: {os.path.basename(实际路径)}")
self.更新列表标题()
return True
else:
messagebox.showwarning("警告", "剪贴板中的路径不存在")
except:
messagebox.showerror("错误", "无法访问剪贴板")
return False
def 添加项目到列表(self, 实际路径):
"""添加项目到列表,处理长路径显示"""
# 生成显示文本
if len(实际路径) > 60:
# 显示开头和结尾部分
显示文本 = f"{实际路径[:25]}...{实际路径[-30:]}"
else:
显示文本 = 实际路径
# 检查是否已存在
项目列表 = self.列表框.get(0, tk.END)
if 显示文本 not in 项目列表:
self.列表框.insert(tk.END, 显示文本)
# 存储映射关系
self.路径映射[显示文本] = 实际路径
# 设置颜色
if os.path.isdir(实际路径):
self.列表框.itemconfig(tk.END, {'fg': self.颜色['主色调']})
else:
self.列表框.itemconfig(tk.END, {'fg': self.颜色['成功色']})
return True
return False
def 添加文件夹(self):
"""添加文件夹"""
try:
文件夹 = filedialog.askdirectory(
title="选择要删除的文件夹",
mustexist=True
)
if 文件夹:
# 获取长路径
实际路径 = self.获取长路径(文件夹)
if self.添加项目到列表(实际路径):
self.记录日志(f"📁 已添加文件夹: {os.path.basename(实际路径)}")
self.更新列表标题()
except Exception as e:
messagebox.showerror("错误", f"添加文件夹失败:\n{str(e)}")
def 添加文件(self):
"""添加文件"""
try:
文件列表 = filedialog.askopenfilenames(
title="选择要删除的文件",
multiple=True
)
if 文件列表:
成功添加 = 0
for 文件 in 文件列表:
# 获取长路径
实际路径 = self.获取长路径(文件)
if self.添加项目到列表(实际路径):
成功添加 += 1
self.记录日志(f"📄 已添加 {成功添加} 个文件")
self.更新列表标题()
except Exception as e:
messagebox.showerror("错误", f"添加文件失败:\n{str(e)}")
def 移除选中(self):
"""移除选中的项目"""
选中项 = self.列表框.curselection()
if 选中项:
for 索引 in reversed(选中项):
显示文本 = self.列表框.get(索引)
# 从路径映射中移除
if 显示文本 in self.路径映射:
del self.路径映射[显示文本]
self.列表框.delete(索引)
self.记录日志(f"🗑️ 从列表中移除了 {len(选中项)} 个项目")
self.更新列表标题()
def 清空列表(self):
"""清空列表"""
if self.列表框.size() > 0:
if messagebox.askyesno("确认", "确定要清空列表中的所有项目吗?"):
数量 = self.列表框.size()
self.列表框.delete(0, tk.END)
self.路径映射.clear()
self.记录日志(f"🧹 已清空 {数量} 个项目")
self.更新列表标题()
def 获取完整路径列表(self):
"""获取列表框中的完整路径列表"""
路径列表 = []
for i in range(self.列表框.size()):
显示文本 = self.列表框.get(i)
实际路径 = self.获取实际路径(显示文本)
if os.path.exists(实际路径):
路径列表.append(实际路径)
return 路径列表
def 智能分析项目(self):
"""智能分析项目,支持长路径"""
项目列表 = self.获取完整路径列表()
if not 项目列表:
messagebox.showwarning("警告", "列表为空,请先添加项目。")
return
# 在新线程中进行分析
threading.Thread(target=self._执行智能分析, args=(项目列表,), daemon=True).start()
def _执行智能分析(self, 项目列表):
"""执行智能分析"""
try:
self.状态变量.set("🔍 正在智能分析项目...")
self.主窗口.update()
分析结果 = self.深度分析项目(项目列表)
self.主窗口.after(0, self._显示分析结果, 分析结果)
except Exception as e:
self.主窗口.after(0, lambda: self.记录日志(f"❌ 分析出错: {str(e)}"))
self.主窗口.after(0, lambda: messagebox.showerror("错误", f"分析失败:\n{str(e)}"))
self.主窗口.after(0, lambda: self.状态变量.set("❌ 分析失败"))
def 深度分析项目(self, 项目列表):
"""深度分析项目,支持长路径和大量文件"""
分析数据 = {
'项目总数': len(项目列表),
'文件夹数量': 0,
'文件数量': 0,
'总大小': 0,
'大文件列表': [],
'深层文件夹': [],
'错误项目': [],
'详细统计': {}
}
for 项目 in 项目列表:
try:
if not os.path.exists(项目):
分析数据['错误项目'].append(f"❌ 不存在: {项目}")
continue
if os.path.isdir(项目):
分析数据['文件夹数量'] += 1
# 深度分析文件夹
文件夹分析 = self.深度分析文件夹(项目)
分析数据['总大小'] += 文件夹分析['总大小']
分析数据['文件数量'] += 文件夹分析['文件数量']
分析数据['大文件列表'].extend(文件夹分析['大文件列表'])
if 文件夹分析['深度'] > 5:
分析数据['深层文件夹'].append(f"{os.path.basename(项目)} (深度: {文件夹分析['深度']}层)")
# 存储详细统计
分析数据['详细统计'][项目] = 文件夹分析
elif os.path.isfile(项目):
分析数据['文件数量'] += 1
try:
大小 = self.安全获取文件大小(项目)
分析数据['总大小'] += 大小
if 大小 > 100 * 1024 * 1024:
分析数据['大文件列表'].append((项目, 大小))
except Exception as e:
分析数据['错误项目'].append(f"❌ 文件大小获取失败: {项目} - {str(e)}")
except Exception as e:
分析数据['错误项目'].append(f"❌ 分析失败: {项目} - {str(e)}")
return 分析数据
def 深度分析文件夹(self, 文件夹路径, 最大深度=20):
"""深度分析文件夹,支持长路径"""
分析结果 = {
'总大小': 0,
'文件数量': 0,
'文件夹数量': 0,
'大文件列表': [],
'深度': 0,
'错误文件': []
}
try:
# 使用os.walk但限制深度
from collections import deque
待处理队列 = deque([(文件夹路径, 0)]) # (路径, 深度)
while 待处理队列:
当前路径, 当前深度 = 待处理队列.popleft()
分析结果['深度'] = max(分析结果['深度'], 当前深度)
try:
with os.scandir(当前路径) as 条目列表:
for 条目 in 条目列表:
try:
if 条目.is_file(follow_symlinks=False):
分析结果['文件数量'] += 1
try:
大小 = 条目.stat().st_size
分析结果['总大小'] += 大小
if 大小 > 100 * 1024 * 1024:
分析结果['大文件列表'].append((条目.path, 大小))
except:
分析结果['错误文件'].append(条目.path)
elif 条目.is_dir(follow_symlinks=False):
分析结果['文件夹数量'] += 1
if 当前深度 < 最大深度:
待处理队列.append((条目.path, 当前深度 + 1))
except Exception as e:
分析结果['错误文件'].append(f"{条目.path} - {str(e)}")
except Exception as e:
分析结果['错误文件'].append(f"{当前路径} - {str(e)}")
except Exception as e:
分析结果['错误文件'].append(f"扫描失败: {文件夹路径} - {str(e)}")
return 分析结果
def 安全获取文件大小(self, 文件路径):
"""安全获取文件大小,支持长路径"""
try:
# 尝试直接获取
return os.path.getsize(文件路径)
except:
try:
# 使用stat
return os.stat(文件路径).st_size
except:
try:
# 使用Windows API
if self.API可用:
stat_info = os.stat(文件路径)
return stat_info.st_size
except:
pass
return 0
def _显示分析结果(self, 分析结果):
"""显示分析结果"""
报告 = f"""
📊 智能分析报告
{'='*60}
• 项目总数: {分析结果['项目总数']}
• 文件夹数量: {分析结果['文件夹数量']}
• 文件数量: {分析结果['文件数量']}
• 总大小: {self.格式化大小(分析结果['总大小'])}
"""
if 分析结果['大文件列表']:
报告 += f"""• 大文件(>100MB): {len(分析结果['大文件列表'])}
"""
# 只显示前5个大文件
for 文件, 大小 in 分析结果['大文件列表'][:5]:
文件名 = os.path.basename(文件)
if len(文件名) > 40:
文件名 = 文件名[:37] + "..."
报告 += f" └ {文件名} ({self.格式化大小(大小)})\n"
if len(分析结果['大文件列表']) > 5:
报告 += f" └ ... 还有 {len(分析结果['大文件列表'])-5} 个大文件\n"
if 分析结果['深层文件夹']:
报告 += f"""• 深层文件夹(>5层): {len(分析结果['深层文件夹'])}
"""
for 文件夹 in 分析结果['深层文件夹'][:3]:
报告 += f" └ {文件夹}\n"
if len(分析结果['深层文件夹']) > 3:
报告 += f" └ ... 还有 {len(分析结果['深层文件夹'])-3} 个深层文件夹\n"
if 分析结果['错误项目']:
报告 += f"""• 分析错误: {len(分析结果['错误项目'])}个
"""
for 错误 in 分析结果['错误项目'][:3]:
if len(错误) > 60:
错误 = 错误[:57] + "..."
报告 += f" └ {错误}\n"
if len(分析结果['错误项目']) > 3:
报告 += f" └ ... 还有 {len(分析结果['错误项目'])-3} 个错误\n"
# 创建结果窗口
结果窗口 = tk.Toplevel(self.主窗口)
结果窗口.title("智能分析结果")
结果窗口.geometry("700x500")
结果窗口.resizable(True, True)
结果窗口.configure(bg='white')
标题框架 = tk.Frame(结果窗口, bg='white')
标题框架.pack(fill=tk.X, padx=20, pady=(20, 10))
标题 = tk.Label(标题框架,
text="📊 智能分析结果",
font=('Microsoft YaHei', 14, 'bold'),
bg='white',
fg=self.颜色['深色'])
标题.pack()
内容框架 = tk.Frame(结果窗口, bg='white')
内容框架.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
# 使用Notebook显示多个标签页
标签页 = ttk.Notebook(内容框架)
标签页.pack(fill=tk.BOTH, expand=True)
# 概述标签页
概述框架 = tk.Frame(标签页, bg='white')
概述文本框 = scrolledtext.ScrolledText(概述框架,
font=('Microsoft YaHei', 10),
wrap=tk.WORD,
bg='#f8f9fa',
fg=self.颜色['深色'],
borderwidth=1,
relief='solid')
概述文本框.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
概述文本框.insert('1.0', 报告)
概述文本框.config(state='disabled')
标签页.add(概述框架, text="概述")
# 详细统计标签页
if 分析结果['详细统计']:
统计框架 = tk.Frame(标签页, bg='white')
统计文本框 = scrolledtext.ScrolledText(统计框架,
font=('Microsoft YaHei', 9),
wrap=tk.WORD,
bg='#f8f9fa',
fg=self.颜色['深色'],
borderwidth=1,
relief='solid')
统计文本框.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
详细报告 = "📁 文件夹详细统计\n" + "="*50 + "\n"
for 路径, 统计 in 分析结果['详细统计'].items():
名称 = os.path.basename(路径)
if len(名称) > 30:
名称 = 名称[:27] + "..."
详细报告 += f"\n📂 {名称}\n"
详细报告 += f" 大小: {self.格式化大小(统计['总大小'])}\n"
详细报告 += f" 文件: {统计['文件数量']}个\n"
详细报告 += f" 子文件夹: {统计['文件夹数量']}个\n"
详细报告 += f" 深度: {统计['深度']}层\n"
统计文本框.insert('1.0', 详细报告)
统计文本框.config(state='disabled')
标签页.add(统计框架, text="详细统计")
# 按钮框架
按钮框架 = tk.Frame(结果窗口, bg='white')
按钮框架.pack(fill=tk.X, padx=20, pady=(0, 20))
导出按钮 = tk.Button(按钮框架,
text="📄 导出报告",
command=lambda: self.导出分析报告(分析结果),
font=('Microsoft YaHei', 10),
bg=self.颜色['主色调'],
fg='white',
activebackground='#2980b9',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=15)
导出按钮.pack(side=tk.LEFT, padx=(0, 10))
关闭按钮 = tk.Button(按钮框架,
text="关闭",
command=结果窗口.destroy,
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
关闭按钮.pack(side=tk.RIGHT)
self.记录日志(f"📊 智能分析完成: {分析结果['文件数量']}个文件, {分析结果['文件夹数量']}个文件夹, {self.格式化大小(分析结果['总大小'])}")
self.状态变量.set("✅ 分析完成")
def 导出分析报告(self, 分析结果):
"""导出分析报告"""
文件路径 = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
initialfile=f"删除分析_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
)
if 文件路径:
try:
with open(文件路径, 'w', encoding='utf-8') as f:
f.write("=" * 60 + "\n")
f.write("⚡ 闪电删除专业版 - 智能分析报告\n")
f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 60 + "\n\n")
f.write(f"项目总数: {分析结果['项目总数']}\n")
f.write(f"文件夹数量: {分析结果['文件夹数量']}\n")
f.write(f"文件数量: {分析结果['文件数量']}\n")
f.write(f"总大小: {self.格式化大小(分析结果['总大小'])}\n\n")
if 分析结果['大文件列表']:
f.write(f"大文件列表(>100MB, 共{len(分析结果['大文件列表'])}个):\n")
for 文件, 大小 in 分析结果['大文件列表']:
f.write(f" {文件} ({self.格式化大小(大小)})\n")
f.write("\n")
if 分析结果['错误项目']:
f.write(f"错误项目(共{len(分析结果['错误项目'])}个):\n")
for 错误 in 分析结果['错误项目']:
f.write(f" {错误}\n")
self.记录日志(f"📄 分析报告已导出到: {文件路径}")
messagebox.showinfo("导出成功", f"分析报告已保存到:\n{文件路径}")
except Exception as e:
messagebox.showerror("导出失败", f"保存报告失败:\n{str(e)}")
def 开始删除(self):
"""开始删除"""
项目列表 = self.获取完整路径列表()
if not 项目列表:
messagebox.showwarning("警告", "列表为空,请先添加项目。")
return
if self.删除前确认变量.get():
项目数量 = len(项目列表)
项目列表文本 = "\n".join([f"• {os.path.basename(项目)[:40]}" for 项目 in 项目列表[:3]])
if 项目数量 > 3:
项目列表文本 += f"\n• ... 还有 {项目数量-3} 个项目"
确认窗口 = tk.Toplevel(self.主窗口)
确认窗口.title("确认删除")
确认窗口.geometry("500x300")
确认窗口.resizable(False, False)
确认窗口.configure(bg='white')
确认窗口.transient(self.主窗口)
确认窗口.grab_set()
警告图标 = tk.Label(确认窗口,
text="💣",
font=('Microsoft YaHei', 40),
bg='white',
fg=self.颜色['危险色'])
警告图标.pack(pady=(20, 10))
警告文本 = tk.Label(确认窗口,
text="超强删除!",
font=('Microsoft YaHei', 16, 'bold'),
bg='white',
fg=self.颜色['危险色'])
警告文本.pack(pady=(0, 10))
确认文本 = tk.Label(确认窗口,
text=f"确定要超强删除 {项目数量} 个项目吗?\n\n{项目列表文本}",
font=('Microsoft YaHei', 10),
bg='white',
fg=self.颜色['深色'],
wraplength=450,
justify='center')
确认文本.pack(pady=(0, 20))
按钮框架 = tk.Frame(确认窗口, bg='white')
按钮框架.pack(pady=(0, 20))
取消按钮 = tk.Button(按钮框架,
text="取消",
command=lambda: [确认窗口.destroy(), None],
font=('Microsoft YaHei', 10),
bg=self.颜色['深色'],
fg='white',
activebackground='#1a252f',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=10)
取消按钮.pack(side=tk.LEFT, padx=(0, 10))
确认按钮 = tk.Button(按钮框架,
text="超强删除",
command=lambda: [确认窗口.destroy(), self._开始删除实现(项目列表)],
font=('Microsoft YaHei', 10, 'bold'),
bg=self.颜色['危险色'],
fg='white',
activebackground='#c0392b',
activeforeground='white',
relief='solid',
borderwidth=1,
cursor='hand2',
width=15)
确认按钮.pack(side=tk.LEFT)
确认窗口.wait_window()
return
self._开始删除实现(项目列表)
def _开始删除实现(self, 项目列表):
"""删除实现"""
self.重置统计()
for 项目 in 项目列表:
self.删除队列.put({
'类型': '项目',
'路径': 项目,
'跳过回收站': self.跳过回收站变量.get(),
'使用强力模式': self.使用强力模式变量.get(),
'处理长路径': self.处理长路径变量.get()
})
self.正在删除 = True
self.停止请求 = False
self.分析按钮.config(state='disabled')
self.删除按钮.config(state='disabled')
self.停止按钮.config(state='normal')
self.更新进度条(0)
self.状态变量.set("💣 正在超强删除...")
self.开始时间 = datetime.now()
self.记录日志(f"💣 开始超强删除 {len(项目列表)} 个项目")
模式描述 = []
if self.跳过回收站变量.get():
模式描述.append("跳过回收站")
else:
模式描述.append("使用回收站")
if self.使用强力模式变量.get():
模式描述.append("超强模式")
if self.处理长路径变量.get():
模式描述.append("长路径支持")
self.记录日志(f"⚙️ 删除模式: {', '.join(模式描述)}")
def 停止删除(self):
"""停止删除"""
self.停止请求 = True
self.状态变量.set("🟡 正在停止...")
self.记录日志("⏹️ 用户请求停止删除")
def 更新进度条(self, 百分比):
"""更新自定义进度条"""
if self.进度条画布.winfo_exists():
宽度 = self.进度条画布.winfo_width()
if 宽度 > 1:
新宽度 = int(宽度 * 百分比 / 100)
self.进度条画布.coords(self.进度条填充, 0, 0, 新宽度, 20)
def 删除工作线程(self):
"""删除工作线程"""
while True:
try:
任务 = self.删除队列.get(timeout=0.1)
if 任务['类型'] == '项目':
self.当前任务 = 任务
self.处理删除项目(任务)
self.删除队列.task_done()
if self.删除队列.empty() and self.正在删除:
self.主窗口.after(0, self.删除完成处理)
except:
continue
def 处理删除项目(self, 任务):
"""处理删除项目"""
if self.停止请求:
return
路径 = 任务['路径']
try:
# 获取实际路径(处理显示截断问题)
实际路径 = self.获取实际路径(路径) if 路径 in self.路径映射 else 路径
# 预处理路径(特别是对于大文件夹和长路径)
处理后的路径 = self.预处理路径(实际路径, 任务)
if os.path.isdir(处理后的路径):
self.强力删除文件夹(处理后的路径, 任务)
elif os.path.isfile(处理后的路径):
self.强力删除文件(处理后的路径, 任务)
else:
self.记录日志(f"❌ 路径不可访问: {路径}")
except Exception as e:
错误信息 = f"❌ 删除失败 {os.path.basename(路径)}: {str(e)}"
self.记录日志(错误信息)
def 预处理路径(self, 路径, 任务):
"""预处理路径,支持长路径和大文件夹"""
if not os.path.exists(路径):
return 路径
# 启用长路径支持
if 任务['处理长路径'] and len(路径) > 250:
if os.path.isabs(路径) and 路径[1] == ':':
# 添加长路径前缀
return r'\\?\\' + 路径
return 路径
def 强力删除文件(self, 文件路径, 任务):
"""强力删除文件"""
if self.停止请求:
return
try:
文件名 = os.path.basename(文件路径)
# 获取文件大小
try:
文件大小 = os.path.getsize(文件路径)
except:
文件大小 = 0
# 跳过回收站:永久删除
if 任务['跳过回收站']:
self.记录日志(f"💀 永久删除文件: {文件名}")
# 方法1: 使用del命令强制删除(永久删除)
if 任务['使用强力模式']:
try:
result = subprocess.run(['cmd', '/c', 'del', '/f', '/q', 文件路径],
capture_output=True,
timeout=30,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW)
if not os.path.exists(文件路径):
self.更新统计(文件大小, 1, 0)
self.记录日志(f"✅ 永久删除成功: {文件名}")
return
except Exception as e:
self.记录日志(f"❌ del命令永久删除失败 {文件名}: {str(e)}")
# 方法2: 直接删除
try:
os.remove(文件路径)
self.更新统计(文件大小, 1, 0)
self.记录日志(f"✅ 永久删除完成: {文件名}")
return
except Exception as e:
self.记录日志(f"❌ 永久删除失败 {文件名}: {str(e)}")
# 方法3: 使用Windows API
if self.API可用 and os.path.exists(文件路径):
try:
self.使用WindowsAPI删除(文件路径)
if not os.path.exists(文件路径):
self.更新统计(文件大小, 1, 0)
self.记录日志(f"🔧 API永久删除成功: {文件名}")
return
except Exception as e:
self.记录日志(f"❌ API永久删除失败 {文件名}: {str(e)}")
# 使用回收站:安全删除
else:
self.记录日志(f"🗑️ 移动到回收站: {文件名}")
删除成功 = self.安全删除(文件路径, 是文件夹=False)
if 删除成功:
self.更新统计(文件大小, 1, 0)
self.记录日志(f"✅ 已移动到回收站: {文件名}")
else:
self.记录日志(f"❌ 移动到回收站失败 {文件名}")
# 如果安全删除失败,尝试强制删除
try:
os.remove(文件路径)
self.更新统计(文件大小, 1, 0)
self.记录日志(f"✅ 强制删除完成: {文件名}")
except Exception as e:
self.记录日志(f"❌ 强制删除失败 {文件名}: {str(e)}")
except Exception as e:
self.记录日志(f"❌ 文件删除失败 {文件名}: {str(e)}")
def 强力删除文件夹(self, 文件夹路径, 任务):
"""超强删除文件夹 - 使用多种方法确保完全删除"""
if self.停止请求:
return
try:
文件夹名 = os.path.basename(文件夹路径)
if not os.path.exists(文件夹路径):
self.记录日志(f"❌ 文件夹不存在: {文件夹名}")
return
self.记录日志(f"💣 开始超强删除文件夹: {文件夹名}")
# 跳过回收站:永久删除
if 任务['跳过回收站']:
self.记录日志(f"💀 永久删除文件夹: {文件夹名}")
# 方法1: 使用robocopy镜像删除(最可靠)
if 任务['使用强力模式']:
try:
删除成功 = self.使用robocopy删除(文件夹路径)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ robocopy永久删除成功: {文件夹名}")
return
except Exception as e:
self.记录日志(f"❌ robocopy永久删除失败: {str(e)}")
# 方法2: 使用rd命令强制删除
try:
result = subprocess.run(['cmd', '/c', 'rd', '/s', '/q', 文件夹路径],
capture_output=True,
timeout=300,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW)
if not os.path.exists(文件夹路径):
self.更新统计(0, 0, 1)
self.记录日志(f"✅ rd命令永久删除成功: {文件夹名}")
return
except Exception as e:
self.记录日志(f"❌ rd命令永久删除失败: {str(e)}")
# 方法3: 使用PowerShell(处理长路径)
try:
删除成功 = self.使用PowerShell删除(文件夹路径)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ PowerShell永久删除成功: {文件夹名}")
return
except Exception as e:
self.记录日志(f"❌ PowerShell永久删除失败: {str(e)}")
# 方法4: 分批删除(处理超大文件夹)
self.记录日志(f"🔄 尝试分批永久删除: {文件夹名}")
删除成功 = self.分批删除文件夹(文件夹路径, 任务)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ 分批永久删除成功: {文件夹名}")
return
# 方法5: 使用Windows API强制删除
if self.API可用:
try:
删除成功 = self.使用WindowsAPI删除(文件夹路径)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ Windows API永久删除成功: {文件夹名}")
return
except Exception as e:
self.记录日志(f"❌ Windows API永久删除失败: {str(e)}")
# 如果所有方法都失败,回退到手动删除
self.记录日志(f"🔄 回退到手动永久删除: {文件夹名}")
self.手动删除文件夹(文件夹路径, 任务)
# 使用回收站:安全删除
else:
self.记录日志(f"🗑️ 安全删除文件夹到回收站: {文件夹名}")
删除成功 = self.安全删除(文件夹路径, 是文件夹=True)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ 文件夹已安全删除: {文件夹名}")
else:
self.记录日志(f"❌ 安全删除失败,尝试强制删除: {文件夹名}")
# 如果安全删除失败,回退到强制删除
self.记录日志(f"🔄 尝试强制删除: {文件夹名}")
删除成功 = self.使用robocopy删除(文件夹路径)
if 删除成功:
self.更新统计(0, 0, 1)
self.记录日志(f"✅ 强制删除成功: {文件夹名}")
else:
self.记录日志(f"❌ 所有删除方法都失败: {文件夹名}")
except Exception as e:
self.记录日志(f"❌ 文件夹删除失败 {文件夹路径}: {str(e)}")
def 使用robocopy删除(self, 文件夹路径):
"""使用robocopy镜像删除(最可靠的方法)"""
try:
import tempfile
# 创建一个空目录作为镜像源
空目录 = tempfile.mkdtemp()
# 使用robocopy镜像删除
robocopy命令 = [
'robocopy', 空目录, 文件夹路径,
'/MIR', '/R:1', '/W:1', '/NP', '/NJH', '/NJS', '/NDL'
]
self.记录日志(f"🔄 使用robocopy方法删除: {os.path.basename(文件夹路径)}")
result = subprocess.run(robocopy命令,
capture_output=True,
timeout=600, # 10分钟超时
text=True,
creationflags=subprocess.CREATE_NO_WINDOW)
# 清理空目录
try:
os.rmdir(空目录)
except:
pass
# robocopy成功返回0或1
if result.returncode <= 1:
# 尝试删除空文件夹
try:
import time
for _ in range(3): # 重试3次
try:
os.rmdir(文件夹路径)
break
except:
time.sleep(0.5)
except:
pass
if not os.path.exists(文件夹路径):
return True
return False
except Exception as e:
raise e
def 使用PowerShell删除(self, 文件夹路径):
"""使用PowerShell删除(支持长路径)"""
try:
# 使用PowerShell命令
powershell命令 = f'''
$folder = "{文件夹路径}"
if (Test-Path $folder) {{
# 尝试强制删除
Remove-Item -Path $folder -Recurse -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 1
# 检查是否删除成功
if (Test-Path $folder) {{
# 如果仍然存在,使用更强制的方法
try {{
cmd /c rmdir /s /q "$folder"
}} catch {{ }}
Start-Sleep -Seconds 1
# 最终检查
if (Test-Path $folder) {{
return $false
}}
}}
return $true
}}
return $false
'''
result = subprocess.run(['powershell', '-Command', powershell命令],
capture_output=True,
timeout=300,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW)
# 检查是否成功删除
import time
for _ in range(5):
if not os.path.exists(文件夹路径):
return True
time.sleep(0.5)
return False
except Exception as e:
raise e
def 分批删除文件夹(self, 文件夹路径, 任务, 批次大小=100):
"""分批删除超大文件夹"""
删除成功 = True
try:
# 先收集所有文件
文件列表 = []
for 根目录, 子目录, 文件 in os.walk(文件夹路径):
for 文件名 in 文件:
文件路径 = os.path.join(根目录, 文件名)
文件列表.append(文件路径)
# 计算总文件数
总文件数 = len(文件列表)
已处理文件 = 0
# 分批删除文件
for i in range(0, len(文件列表), 批次大小):
if self.停止请求:
return False
批次 = 文件列表[i:i+批次大小]
for 文件路径 in 批次:
if self.停止请求:
return False
try:
if os.path.exists(文件路径):
# 根据跳过回收站选项选择删除方式
if 任务['跳过回收站']:
# 永久删除
os.remove(文件路径)
else:
# 安全删除
self.安全删除(文件路径, 是文件夹=False)
# 更新统计
try:
大小 = os.path.getsize(文件路径)
except:
大小 = 0
self.更新统计(大小, 1, 0)
已处理文件 += 1
except Exception as e:
self.记录日志(f"❌ 批次删除文件失败 {os.path.basename(文件路径)}: {str(e)}")
删除成功 = False
# 更新进度
if 总文件数 > 0:
进度 = (已处理文件 / 总文件数) * 100
self.主窗口.after(0, lambda p=进度: self.更新进度条(p))
# 删除所有空文件夹(从最深层开始)
for 根目录, 子目录, 文件 in os.walk(文件夹路径, topdown=False):
for 子目录名 in 子目录:
子目录路径 = os.path.join(根目录, 子目录名)
try:
os.rmdir(子目录路径)
self.更新统计(0, 0, 1)
except Exception as e:
self.记录日志(f"❌ 删除子文件夹失败 {子目录名}: {str(e)}")
删除成功 = False
# 最后删除根目录
try:
import time
for _ in range(3): # 重试3次
try:
os.rmdir(文件夹路径)
self.更新统计(0, 0, 1)
break
except:
time.sleep(0.5)
except Exception as e:
self.记录日志(f"❌ 删除根目录失败: {str(e)}")
删除成功 = False
# 最终检查
import time
for _ in range(5):
if not os.path.exists(文件夹路径):
return True
time.sleep(0.5)
return False
except Exception as e:
self.记录日志(f"❌ 分批删除失败: {str(e)}")
return False
def 手动删除文件夹(self, 文件夹路径, 任务):
"""手动删除文件夹"""
文件夹名 = os.path.basename(文件夹路径)
删除的文件数 = 0
删除的文件夹数 = 0
try:
# 1. 先删除所有文件
for 根目录, 子目录, 文件列表 in os.walk(文件夹路径, topdown=False):
for 文件 in 文件列表:
if self.停止请求:
return
try:
文件路径 = os.path.join(根目录, 文件)
# 获取文件大小
try:
文件大小 = os.path.getsize(文件路径)
except:
文件大小 = 0
# 根据跳过回收站选项选择删除方式
if 任务['跳过回收站']:
# 永久删除
os.remove(文件路径)
else:
# 安全删除
self.安全删除(文件路径, 是文件夹=False)
删除的文件数 += 1
self.更新统计(文件大小, 1, 0)
except Exception as e:
self.记录日志(f"❌ 删除文件失败 {文件}: {str(e)}")
# 2. 删除子文件夹
for 子目录名 in 子目录:
if self.停止请求:
return
try:
子目录路径 = os.path.join(根目录, 子目录名)
os.rmdir(子目录路径)
删除的文件夹数 += 1
self.更新统计(0, 0, 1)
except Exception as e:
self.记录日志(f"❌ 删除子文件夹失败 {子目录名}: {str(e)}")
# 3. 最后删除根文件夹
try:
os.rmdir(文件夹路径)
删除的文件夹数 += 1
self.更新统计(0, 0, 1)
if 任务['跳过回收站']:
self.记录日志(f"✅ 手动永久删除完成: {文件夹名} (删除了{删除的文件数}个文件, {删除的文件夹数}个文件夹)")
else:
self.记录日志(f"✅ 手动安全删除完成: {文件夹名} (删除了{删除的文件数}个文件, {删除的文件夹数}个文件夹)")
except Exception as e:
self.记录日志(f"❌ 最终删除根文件夹失败 {文件夹名}: {str(e)}")
except Exception as e:
self.记录日志(f"❌ 手动删除失败 {文件夹名}: {str(e)}")
def 安全删除(self, 路径, 是文件夹=False):
"""安全删除(移动到回收站)"""
try:
# 使用Windows API移动到回收站
if self.API可用:
try:
# 准备参数
class SHFILEOPSTRUCT(ctypes.Structure):
_fields_ = [
("hwnd", ctypes.wintypes.HWND),
("wFunc", ctypes.wintypes.UINT),
("pFrom", ctypes.wintypes.LPCWSTR),
("pTo", ctypes.wintypes.LPCWSTR),
("fFlags", ctypes.wintypes.UINT),
("fAnyOperationsAborted", ctypes.wintypes.BOOL),
("hNameMappings", ctypes.wintypes.LPVOID),
("lpszProgressTitle", ctypes.wintypes.LPCWSTR),
]
shfos = SHFILEOPSTRUCT()
shfos.hwnd = 0
shfos.wFunc = 3 # FO_DELETE
shfos.pFrom = 路径 + '\0\0'
shfos.pTo = None
shfos.fFlags = 0x40 | 0x4 | 0x10 | 0x4000 # FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI | FOF_ALLOWUNDO
shfos.fAnyOperationsAborted = False
shfos.hNameMappings = 0
shfos.lpszProgressTitle = None
result = self.shell32.SHFileOperationW(ctypes.byref(shfos))
# 检查是否成功移动到回收站
import time
for _ in range(5):
if not os.path.exists(路径):
return True
time.sleep(0.5)
return False
except Exception as e:
self.记录日志(f"❌ API安全删除失败: {str(e)}")
# 如果API失败,使用send2trash库(如果可用)
try:
import send2trash
if 是文件夹:
send2trash.send2trash(路径)
else:
send2trash.send2trash(路径)
import time
for _ in range(5):
if not os.path.exists(路径):
return True
time.sleep(0.5)
return False
except ImportError:
self.记录日志("⚠️ 未安装send2trash库,使用临时目录方式")
except Exception as e:
self.记录日志(f"❌ send2trash失败: {str(e)}")
# 备选方案:移动到临时目录
return self._移动到临时目录(路径, 是文件夹)
except Exception as e:
self.记录日志(f"❌ 安全删除失败: {str(e)}")
return False
def _移动到临时目录(self, 路径, 是文件夹=False):
"""移动到临时目录(模拟回收站)"""
try:
# 创建临时目录
临时目录 = os.path.join(os.environ.get('TEMP', 'C:\\Windows\\Temp'), '闪电删除回收站')
os.makedirs(临时目录, exist_ok=True)
# 生成临时路径
时间戳 = datetime.now().strftime('%Y%m%d_%H%M%S')
文件名 = os.path.basename(路径)
临时路径 = os.path.join(临时目录, f"{时间戳}_{文件名}")
# 处理同名文件
计数器 = 1
while os.path.exists(临时路径):
临时路径 = os.path.join(临时目录, f"{时间戳}_{文件名}_{计数器}")
计数器 += 1
if 是文件夹:
shutil.move(路径, 临时路径)
self.记录日志(f"📁 文件夹已移动到临时目录: {临时路径}")
else:
shutil.move(路径, 临时路径)
self.记录日志(f"📄 文件已移动到临时目录: {临时路径}")
return True
except Exception as e:
self.记录日志(f"❌ 移动到临时目录失败: {str(e)}")
return False
def 使用WindowsAPI删除(self, 路径):
"""使用Windows API强制删除"""
try:
if not self.API可用 or not os.path.exists(路径):
return False
# 移除只读属性
try:
self.kernel32.SetFileAttributesW(路径, 128) # FILE_ATTRIBUTE_NORMAL
except:
pass
if os.path.isfile(路径):
# 删除文件
result = self.kernel32.DeleteFileW(路径)
elif os.path.isdir(路径):
# 删除文件夹
import ctypes.wintypes
# 使用SHFileOperation
class SHFILEOPSTRUCT(ctypes.Structure):
_fields_ = [
("hwnd", ctypes.wintypes.HWND),
("wFunc", ctypes.wintypes.UINT),
("pFrom", ctypes.wintypes.LPCWSTR),
("pTo", ctypes.wintypes.LPCWSTR),
("fFlags", ctypes.wintypes.UINT),
("fAnyOperationsAborted", ctypes.wintypes.BOOL),
("hNameMappings", ctypes.wintypes.LPVOID),
("lpszProgressTitle", ctypes.wintypes.LPCWSTR),
]
shfos = SHFILEOPSTRUCT()
shfos.hwnd = 0
shfos.wFunc = 2 # FO_DELETE
shfos.pFrom = 路径 + '\0\0'
shfos.pTo = None
shfos.fFlags = 0x40 | 0x4 | 0x10 # FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI
shfos.fAnyOperationsAborted = False
shfos.hNameMappings = 0
shfos.lpszProgressTitle = None
result = self.shell32.SHFileOperationW(ctypes.byref(shfos))
# 检查是否删除成功
import time
for _ in range(10):
if not os.path.exists(路径):
return True
time.sleep(0.5)
return False
except Exception as e:
return False
def 更新统计(self, 大小, 文件数=0, 文件夹数=0):
"""更新统计信息"""
self.总大小 += 大小
self.当前大小 += 大小
self.已删除文件数 += 文件数
self.已删除文件夹数 += 文件夹数
# 更新处理速度
if self.开始时间:
耗时 = (datetime.now() - self.开始时间).total_seconds()
if 耗时 > 0:
self.当前速度 = self.当前大小 / 耗时
总项目数 = self.列表框.size()
if 总项目数 > 0:
已处理 = self.已删除文件数 + self.已删除文件夹数
进度 = (已处理 / 总项目数) * 100
self.主窗口.after(0, lambda: self.更新进度条(进度))
def 更新统计工作线程(self):
"""更新统计显示的工作线程"""
while True:
try:
if self.正在删除 and self.开始时间:
耗时 = (datetime.now() - self.开始时间).total_seconds()
if 耗时 > 0:
速度 = self.当前速度
剩余项目数 = self.列表框.size() - (self.已删除文件数 + self.已删除文件夹数)
if 速度 > 0 and 剩余项目数 > 0:
平均文件大小 = self.当前大小 / max(1, self.已删除文件数)
剩余时间 = (剩余项目数 * 平均文件大小) / 速度
else:
剩余时间 = 0
self.主窗口.after(0, self.更新统计显示, 速度, 剩余时间)
time.sleep(0.5)
except:
time.sleep(1)
def 更新统计显示(self, 速度, 剩余时间):
"""更新统计显示"""
try:
self.统计标签['已删除文件数'].config(text=str(self.已删除文件数))
self.统计标签['已删除文件夹数'].config(text=str(self.已删除文件夹数))
self.统计标签['已处理大小'].config(text=self.格式化大小(self.当前大小))
self.统计标签['总大小'].config(text=self.格式化大小(self.总大小))
self.统计标签['处理速度'].config(text=f"{self.格式化大小(速度)}/秒")
if 剩余时间 > 0:
时间字符串 = self.格式化时间(剩余时间)
self.统计标签['剩余时间'].config(text=时间字符串)
else:
self.统计标签['剩余时间'].config(text="--")
if self.当前任务:
路径 = self.当前任务['路径']
名称 = os.path.basename(路径)
if os.path.isdir(路径):
名称 = f"📁 {名称}"
else:
名称 = f"📄 {名称}"
if len(名称) > 30:
名称 = 名称[:27] + "..."
self.统计标签['当前项目'].config(text=名称)
except Exception as e:
pass
def 删除完成处理(self):
"""删除完成后的处理"""
self.正在删除 = False
self.分析按钮.config(state='normal')
self.删除按钮.config(state='normal')
self.停止按钮.config(state='disabled')
self.更新进度条(100)
结束时间 = datetime.now()
耗时 = (结束时间 - self.开始时间).total_seconds()
结果信息 = f"""
✅ 删除完成!
{'='*40}
• 已删除文件夹: {self.已删除文件夹数}
• 已删除文件: {self.已删除文件数}
• 总大小: {self.格式化大小(self.总大小)}
• 耗时: {耗时:.2f}秒
"""
if 耗时 > 0:
速度 = self.总大小 / 耗时
结果信息 += f"• 平均速度: {self.格式化大小(速度)}/秒"
self.状态变量.set("✅ 删除完成!")
self.记录日志("=" * 60)
self.记录日志(结果信息)
for 键 in self.统计标签:
if 键 in ['已删除文件数', '已删除文件夹数']:
continue
self.统计标签[键].config(text="--")
self.主窗口.after(100, lambda: messagebox.showinfo("删除完成", 结果信息))
def 格式化大小(self, 字节数):
"""格式化文件大小"""
if 字节数 == 0:
return "0 B"
大小单位 = ["B", "KB", "MB", "GB", "TB"]
i = int(math.floor(math.log(字节数, 1024)))
p = math.pow(1024, i)
s = round(字节数 / p, 2)
return f"{s} {大小单位[i]}"
def 格式化时间(self, 秒数):
"""格式化时间"""
if 秒数 < 60:
return f"{int(秒数)}秒"
elif 秒数 < 3600:
分钟 = int(秒数 // 60)
秒 = int(秒数 % 60)
return f"{分钟}分{秒}秒"
else:
小时 = int(秒数 // 3600)
分钟 = int((秒数 % 3600) // 60)
return f"{小时}小时{分钟}分"
def 记录日志(self, 消息):
"""添加日志消息"""
时间戳 = datetime.now().strftime("%H:%M:%S")
日志条目 = f"[{时间戳}] {消息}\n"
self.主窗口.after(0, self._添加日志条目, 日志条目)
def _添加日志条目(self, 日志条目):
"""在UI线程中添加日志条目"""
if self.日志文本框.winfo_exists():
self.日志文本框.insert(tk.END, 日志条目)
self.日志文本框.see(tk.END)
# 限制日志行数
if self.日志文本框.index('end-1c').split('.')[0] > '1000':
self.日志文本框.delete(1.0, 2.0)
self.主窗口.update_idletasks()
def 清空日志(self):
"""清空日志"""
self.日志文本框.delete(1.0, tk.END)
self.记录日志("📝 日志已清空")
def 保存日志(self):
"""保存日志"""
文件路径 = filedialog.asksaveasfilename(
defaultextension=".log",
filetypes=[("日志文件", "*.log"), ("文本文件", "*.txt"), ("所有文件", "*.*")],
initialfile=f"闪电删除_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
)
if 文件路径:
try:
with open(文件路径, 'w', encoding='utf-8') as f:
f.write(self.日志文本框.get(1.0, tk.END))
self.记录日志(f"💾 日志已保存到: {文件路径}")
except Exception as e:
messagebox.showerror("错误", f"保存日志失败:\n{str(e)}")
def 主函数():
try:
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass
主窗口 = tk.Tk()
主窗口.title("闪电删除专业版 v4.7 超强模式")
应用 = 闪电删除专业版(主窗口)
def 关闭窗口():
if 应用.正在删除:
if messagebox.askokcancel("退出", "删除操作正在进行中,确定要退出吗?"):
主窗口.destroy()
else:
主窗口.destroy()
主窗口.protocol("WM_DELETE_WINDOW", 关闭窗口)
主窗口.update_idletasks()
宽度 = 主窗口.winfo_width()
高度 = 主窗口.winfo_height()
x = (主窗口.winfo_screenwidth() // 2) - (宽度 // 2)
y = (主窗口.winfo_screenheight() // 2) - (高度 // 2)
主窗口.geometry(f'{宽度}x{高度}+{x}+{y}')
主窗口.mainloop()
if __name__ == "__main__":
import os
if os.name == 'nt':
try:
import ctypes
是管理员 = ctypes.windll.shell32.IsUserAnAdmin() != 0
if not 是管理员:
print("提示:某些系统文件可能需要管理员权限才能删除。")
except:
pass
主函数()