import tkinter as tk from tkinter import ttk, messagebox, filedialog, simpledialog import DISCAPI import os import shutil import tempfile import subprocess import platform from tkinterdnd2 import * from datetime import datetime import hashlib import time import json class DISCManager: def __init__(self, root): self.root = root self.root.title("DISC文件管理器") self.root.geometry("1200x600") # 当前打开的DISC文件 self.current_DISC = None self.current_path = [] # 当前在DISC中的路径 self.selected_items = [] self.clipboard_items = [] # 用于复制/粘贴操作的剪贴板 self.clipboard_operation = None # 'copy' 或 'move' # 创建菜单栏 self.create_menu() # 创建工具栏 self.create_toolbar() # 创建导航栏 self.create_navigation() # 创建文件列表 self.create_file_list() # 创建状态栏 self.create_status_bar() # 绑定拖拽事件 self.setup_drag_drop() def create_menu(self): menubar = tk.Menu(self.root) self.root.config(menu=menubar) # 文件菜单 file_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="文件", menu=file_menu) file_menu.add_command(label="新建压缩包", command=self.new_archive) file_menu.add_command(label="打开压缩包", command=self.open_archive) file_menu.add_separator() file_menu.add_command(label="重命名", command=self.rename_selected) file_menu.add_command(label="删除", command=self.delete_selected) file_menu.add_separator() file_menu.add_command(label="解压到", command=self.extract_to) file_menu.add_command(label="CRC校验", command=self.check_crc) # 编辑菜单 edit_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="编辑", menu=edit_menu) edit_menu.add_command(label="全选", command=self.select_all) edit_menu.add_command(label="全部取消", command=self.deselect_all) edit_menu.add_command(label="反选", command=self.invert_selection) edit_menu.add_separator() edit_menu.add_command(label="选择(掩码)", command=self.select_by_mask) edit_menu.add_command(label="取消选择(掩码)", command=self.deselect_by_mask) edit_menu.add_separator() edit_menu.add_command(label="复制", command=self.copy_selected) edit_menu.add_command(label="粘贴", command=self.paste_items) # 工具菜单 tools_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="工具", menu=tools_menu) tools_menu.add_command(label="基准测试", command=self.benchmark) # 帮助菜单 help_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="帮助", menu=help_menu) help_menu.add_command(label="关于", command=self.show_about) def create_toolbar(self): toolbar = ttk.Frame(self.root) toolbar.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5) # 工具栏按钮 ttk.Button(toolbar, text="添加", command=self.add_files).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="解压", command=self.extract_selected).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="测试", command=self.test_archive).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="复制", command=self.copy_selected).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="移动", command=self.move_selected).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="删除", command=self.delete_selected).pack(side=tk.LEFT, padx=2) ttk.Button(toolbar, text="信息", command=self.show_info).pack(side=tk.LEFT, padx=2) def create_navigation(self): nav_frame = ttk.Frame(self.root) nav_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2) # 返回上层按钮 ttk.Button(nav_frame, text="↑", width=3, command=self.go_up).pack(side=tk.LEFT, padx=2) # 路径显示 self.path_label = ttk.Label(nav_frame, text="", anchor=tk.W) self.path_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) def create_file_list(self): # 创建一个主Frame来容纳Treeview和滚动条 self.tree_frame = ttk.Frame(self.root) self.tree_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建Treeview columns = ('名称', '大小', '类型', '修改时间') self.tree = ttk.Treeview(self.tree_frame, columns=columns, show='tree headings') # 设置列 self.tree.heading('#0', text='') self.tree.heading('名称', text='名称') self.tree.heading('大小', text='大小') self.tree.heading('类型', text='类型') self.tree.heading('修改时间', text='修改时间') # 设置列宽 self.tree.column('#0', width=0, stretch=False) self.tree.column('名称', width=200) self.tree.column('大小', width=100) self.tree.column('类型', width=100) self.tree.column('修改时间', width=150) # 滚动条 scrollbar = ttk.Scrollbar(self.tree_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=scrollbar.set) # 布局Treeview和滚动条 self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 绑定事件 self.tree.bind('', self.on_double_click) self.tree.bind('', self.on_right_click) # 创建右键菜单 self.create_context_menu() def create_context_menu(self): self.context_menu = tk.Menu(self.root, tearoff=0) self.context_menu.add_command(label="重命名", command=self.rename_selected) self.context_menu.add_command(label="删除", command=self.delete_selected) self.context_menu.add_command(label="查看属性", command=self.show_properties) self.context_menu.add_command(label="创建链接", command=self.create_link) self.context_menu.add_command(label="CRC校验", command=self.check_crc) self.context_menu.add_separator() self.context_menu.add_command(label="复制", command=self.copy_selected) self.context_menu.add_command(label="移动", command=self.move_selected) # 空白区域右键菜单 self.empty_context_menu = tk.Menu(self.root, tearoff=0) self.empty_context_menu.add_command(label="新建文件", command=self.new_file) self.empty_context_menu.add_command(label="新建文件夹", command=self.new_folder) self.empty_context_menu.add_separator() if self.clipboard_items and self.clipboard_operation: self.empty_context_menu.add_command(label="粘贴", command=self.paste_items) def create_status_bar(self): # 状态栏单独放在窗口底部 self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) def setup_drag_drop(self): # 注册拖放目标 self.tree.drop_target_register(DND_FILES) self.tree.dnd_bind('<>', self.on_drop) def on_drop(self, event): files = self.root.tk.splitlist(event.data) self.add_files_to_archive(files) def new_archive(self): filename = filedialog.asksaveasfilename( defaultextension=".DISC", filetypes=[("DISC文件", "*.DISC"), ("所有文件", "*.*")] ) if filename: try: # 使用最高压缩等级创建DISC文件 with DISCAPI.DISCFile(filename, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf: pass self.current_DISC = filename self.current_path = [] self.update_file_list() self.update_status(f"新建压缩包: {filename} (最高压缩等级)") except Exception as e: messagebox.showerror("错误", f"创建压缩包失败: {str(e)}") def open_archive(self): filename = filedialog.askopenfilename( filetypes=[("DISC文件", "*.DISC"), ("所有文件", "*.*")] ) if filename: try: with DISCAPI.DISCFile(filename, 'r') as zf: # 测试文件是否有效 zf.testDISC() self.current_DISC = filename self.current_path = [] self.update_file_list() self.update_status(f"打开压缩包: {filename}") except Exception as e: messagebox.showerror("错误", f"打开压缩包失败: {str(e)}") def update_file_list(self): # 清空列表 for item in self.tree.get_children(): self.tree.delete(item) if not self.current_DISC: return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: # 添加返回上层按钮 if self.current_path: self.tree.insert('', 'end', values=('..', '', '文件夹', ''), tags=('folder',)) # 获取当前路径的文件和文件夹 current_path_str = '/'.join(self.current_path) + '/' if self.current_path else '' folders = set() files = [] for info in zf.infolist(): filename = info.filename if filename.startswith(current_path_str): relative_path = filename[len(current_path_str):] if '/' in relative_path: # 这是一个文件夹 folder_name = relative_path.split('/')[0] folders.add(folder_name) else: # 这是一个文件 files.append((relative_path, info)) # 添加文件夹 for folder in sorted(folders): self.tree.insert('', 'end', values=(folder, '', '文件夹', ''), tags=('folder',)) # 添加文件 print(files) for filename, info in sorted(files): size = self.format_size(info.file_size) date = datetime(*info.date_time).strftime('%Y-%m-%d %H:%M:%S') file_type = self.get_file_type(filename) self.tree.insert('', 'end', values=(filename, size, file_type, date), tags=('file',)) except Exception as e: import traceback print(traceback.format_exc()) messagebox.showerror("错误", f"读取文件列表失败: {str(e)}") # 更新路径显示 path_str = '/'.join(self.current_path) self.path_label.config(text=path_str if path_str else '根目录') # 更新右键菜单,显示或隐藏粘贴选项 self.create_context_menu() def get_file_type(self, filename): """根据文件扩展名返回文件类型描述""" _, ext = os.path.splitext(filename.lower()) file_types = { '.txt': '文本文档', '.doc': 'Word文档', '.docx': 'Word文档', '.xls': 'Excel表格', '.xlsx': 'Excel表格', '.ppt': 'PowerPoint', '.pptx': 'PowerPoint', '.pdf': 'PDF文档', '.jpg': 'JPEG图像', '.jpeg': 'JPEG图像', '.png': 'PNG图像', '.gif': 'GIF图像', '.bmp': '位图', '.mp3': 'MP3音频', '.wav': 'WAV音频', '.mp4': 'MP4视频', '.avi': 'AVI视频', '.mkv': 'MKV视频', '.DISC': 'DISC压缩包', '.rar': 'RAR压缩包', '.7z': '7-DISC压缩包', '.exe': '应用程序', '.dll': '动态链接库', '.py': 'Python脚本', '.js': 'JavaScript文件', '.html': 'HTML文档', '.css': '样式表', '.json': 'JSON数据', '.xml': 'XML文档', } return file_types.get(ext, f'{ext}文件') def format_size(self, size): for unit in ['B', 'KB', 'MB', 'GB']: if size < 1024: return f"{size:.1f} {unit}" size /= 1024 return f"{size:.1f} TB" def on_double_click(self, event): selection = self.tree.selection() if not selection: return item = self.tree.item(selection[0]) name = item['values'][0] if name == '..': self.go_up() elif item['tags'] and 'folder' in item['tags']: # 进入文件夹 self.current_path.append(str(name)) self.update_file_list() else: # 打开文件 self.open_file(name) def go_up(self): if self.current_path: self.current_path.pop() self.update_file_list() def open_file(self, filename): if not self.current_DISC: return try: # 创建临时文件 temp_dir = tempfile.mkdtemp() temp_file = os.path.join(temp_dir, str(filename)) with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: file_path = '/'.join(self.current_path + [str(filename)]) with open(temp_file, 'wb') as f: f.write(zf.read(file_path)) # 用系统默认程序打开 if platform.system() == 'Windows': os.startfile(temp_file) elif platform.system() == 'Darwin': subprocess.run(['open', temp_file]) else: subprocess.run(['xdg-open', temp_file]) except Exception as e: messagebox.showerror("错误", f"打开文件失败: {str(e)}") def on_right_click(self, event): # 获取点击位置的项目 item = self.tree.identify('item', event.x, event.y) if item: # 右键点击文件/文件夹 self.tree.selection_set(item) self.context_menu.post(event.x_root, event.y_root) else: # 右键点击空白区域 self.empty_context_menu.post(event.x_root, event.y_root) def add_files(self): if not self.current_DISC: messagebox.showwarning("警告", "请先打开或创建一个压缩包") return filenames = filedialog.askopenfilenames() if filenames: self.add_files_to_archive(filenames) def add_files_to_archive(self, filenames): if not self.current_DISC: return try: # 使用最高压缩等级添加文件 with DISCAPI.DISCFile(self.current_DISC, 'a', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf: for filename in filenames: if os.path.isdir(filename): # 添加文件夹及其内容 self.add_folder_to_DISC(zf, filename, '') else: # 添加单个文件 arcname = '/'.join(self.current_path + [os.path.basename(filename)]) zf.write(filename, arcname) self.update_file_list() self.update_status(f"已添加 {len(filenames)} 个文件 (最高压缩等级)") except Exception as e: messagebox.showerror("错误", f"添加文件失败: {str(e)}") def add_folder_to_DISC(self, zf, folder_path, arcpath): for root, dirs, files in os.walk(folder_path): for file in files: file_path = os.path.join(str(root), str(file)) rel_path = os.path.relpath(file_path, folder_path) arcname = '/'.join(self.current_path + [str(arcpath), str(rel_path)]) zf.write(file_path, arcname) def extract_selected(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: return folder = filedialog.askdirectory() if not folder: return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: for item in selection: values = self.tree.item(item)['values'] name = values[0] if name == '..': continue if 'folder' in self.tree.item(item)['tags']: # 提取文件夹 folder_path = '/'.join(self.current_path + [str(name)]) for info in zf.infolist(): if info.filename.startswith(folder_path + '/'): zf.extract(info, folder) else: # 提取文件 file_path = '/'.join(self.current_path + [str(name)]) zf.extract(file_path, folder) self.update_status(f"已提取 {len(selection)} 个项目") except Exception as e: messagebox.showerror("错误", f"提取失败: {str(e)}") def extract_to(self): self.extract_selected() def delete_selected(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: return if not messagebox.askyesno("确认", "确定要删除选中的项目吗?"): return try: # 创建临时文件 temp_dir = tempfile.mkdtemp() temp_DISC = os.path.join(temp_dir, 'temp.DISC') with DISCAPI.DISCFile(self.current_DISC, 'r') as zf_in: # 使用最高压缩等级创建临时DISC with DISCAPI.DISCFile(temp_DISC, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf_out: # 复制不需要删除的文件 for info in zf_in.infolist(): should_delete = False for item in selection: values = self.tree.item(item)['values'] name = values[0] if name == '..': continue if 'folder' in self.tree.item(item)['tags']: # 检查是否在要删除的文件夹中 folder_path = '/'.join(self.current_path + [str(name)]) if info.filename.startswith(folder_path + '/'): should_delete = True break else: # 检查是否是要删除的文件 file_path = '/'.join(self.current_path + [str(name)]) if info.filename == file_path: should_delete = True break if not should_delete: zf_out.writestr(info, zf_in.read(info.filename)) # 替换原文件 shutil.move(temp_DISC, self.current_DISC) shutil.rmtree(temp_dir) self.update_file_list() self.update_status(f"已删除 {len(selection)} 个项目") except Exception as e: messagebox.showerror("错误", f"删除失败: {str(e)}") def rename_selected(self): if not self.current_DISC: return selection = self.tree.selection() if len(selection) != 1: messagebox.showwarning("警告", "请选择一个项目进行重命名") return item = selection[0] values = self.tree.item(item)['values'] old_name = values[0] if old_name == '..': return new_name = simpledialog.askstring("重命名", "请输入新名称:", initialvalue=old_name) if not new_name or new_name == old_name: return try: # 创建临时文件 temp_dir = tempfile.mkdtemp() temp_DISC = os.path.join(temp_dir, 'temp.DISC') with DISCAPI.DISCFile(self.current_DISC, 'r') as zf_in: # 使用最高压缩等级创建临时DISC with DISCAPI.DISCFile(temp_DISC, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf_out: for info in zf_in.infolist(): if 'folder' in self.tree.item(item)['tags']: # 重命名文件夹 old_path = '/'.join(self.current_path + [str(old_name)]) new_path = '/'.join(self.current_path + [str(new_name)]) if info.filename.startswith(old_path + '/'): new_filename = info.filename.replace(old_path + '/', new_path + '/') zf_out.writestr(new_filename, zf_in.read(info.filename)) else: zf_out.writestr(info, zf_in.read(info.filename)) else: # 重命名文件 old_path = '/'.join(self.current_path + [str(old_name)]) new_path = '/'.join(self.current_path + [str(new_name)]) if info.filename == old_path: zf_out.writestr(new_path, zf_in.read(info.filename)) else: zf_out.writestr(info, zf_in.read(info.filename)) # 替换原文件 shutil.move(temp_DISC, self.current_DISC) shutil.rmtree(temp_dir) self.update_file_list() self.update_status(f"已重命名: {old_name} -> {new_name}") except Exception as e: messagebox.showerror("错误", f"重命名失败: {str(e)}") def select_all(self): for item in self.tree.get_children(): self.tree.selection_add(item) def deselect_all(self): for item in self.tree.get_children(): self.tree.selection_remove(item) def invert_selection(self): for item in self.tree.get_children(): if item in self.tree.selection(): self.tree.selection_remove(item) else: self.tree.selection_add(item) def select_by_mask(self): mask = simpledialog.askstring("选择", "请输入选择掩码 (例如: *.txt):") if mask: for item in self.tree.get_children(): values = self.tree.item(item)['values'] name = values[0] if self.match_mask(name, mask): self.tree.selection_add(item) def deselect_by_mask(self): mask = simpledialog.askstring("取消选择", "请输入取消选择掩码 (例如: *.txt):") if mask: for item in self.tree.get_children(): values = self.tree.item(item)['values'] name = values[0] if self.match_mask(name, mask): self.tree.selection_remove(item) def match_mask(self, name, mask): import fnmatch return fnmatch.fnmatch(name, mask) def test_archive(self): if not self.current_DISC: messagebox.showwarning("警告", "请先打开一个压缩包") return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: bad_file = zf.testDISC() if bad_file: messagebox.showerror("测试结果", f"发现损坏的文件: {bad_file}") else: messagebox.showinfo("测试结果", "压缩包测试通过,没有发现损坏的文件") except Exception as e: messagebox.showerror("错误", f"测试失败: {str(e)}") def check_crc(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: results = [] for item in selection: values = self.tree.item(item)['values'] name = values[0] if name == '..' or 'folder' in self.tree.item(item)['tags']: continue file_path = '/'.join(self.current_path + [str(name)]) data = zf.read(file_path) crc = hashlib.crc32(data) & 0xffffffff results.append(f"{name}: CRC32 = {crc:08X}") if results: messagebox.showinfo("CRC校验结果", '\n'.join(results)) except Exception as e: messagebox.showerror("错误", f"CRC校验失败: {str(e)}") def show_properties(self): selection = self.tree.selection() if not selection: return item = selection[0] values = self.tree.item(item)['values'] name = values[0] if name == '..': return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: if 'folder' in self.tree.item(item)['tags']: # 文件夹属性 folder_path = '/'.join(self.current_path + [str(name)]) file_count = 0 total_size = 0 compressed_size = 0 for info in zf.infolist(): if info.filename.startswith(folder_path + '/'): file_count += 1 total_size += info.file_size compressed_size += info.compress_size props = f"名称: {name}\n" props += f"类型: 文件夹\n" props += f"文件数: {file_count}\n" props += f"总大小: {self.format_size(total_size)}\n" props += f"压缩后大小: {self.format_size(compressed_size)}\n" if total_size > 0: props += f"压缩率: {(1 - compressed_size/total_size)*100:.1f}%" else: # 文件属性 file_path = '/'.join(self.current_path + [str(name)]) info = zf.getinfo(file_path) props = f"名称: {name}\n" props += f"类型: 文件\n" props += f"大小: {self.format_size(info.file_size)}\n" props += f"压缩后: {self.format_size(info.compress_size)}\n" props += f"压缩率: {(1 - info.compress_size/info.file_size)*100:.1f}%\n" props += f"修改时间: {datetime(*info.date_time).strftime('%Y-%m-%d %H:%M:%S')}\n" props += f"CRC32: {info.CRC:08X}\n" props += f"压缩方法: {info.compress_type}" messagebox.showinfo("属性", props) except Exception as e: messagebox.showerror("错误", f"获取属性失败: {str(e)}") def create_link(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: messagebox.showwarning("警告", "请选择要创建链接的文件") return item = selection[0] values = self.tree.item(item)['values'] name = values[0] if name == '..' or 'folder' in self.tree.item(item)['tags']: messagebox.showwarning("警告", "只能为文件创建链接") return link_name = simpledialog.askstring("创建链接", "请输入链接名称:", initialvalue=f"链接到_{name}") if not link_name: return try: # 创建链接信息 file_path = '/'.join(self.current_path + [str(name)]) link_info = { "type": "link", "target": file_path, "created": datetime.now().isoformat() } # 创建临时文件 temp_dir = tempfile.mkdtemp() temp_DISC = os.path.join(temp_dir, 'temp.DISC') with DISCAPI.DISCFile(self.current_DISC, 'r') as zf_in: # 使用最高压缩等级创建临时DISC with DISCAPI.DISCFile(temp_DISC, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf_out: # 复制所有文件 for info in zf_in.infolist(): zf_out.writestr(info, zf_in.read(info.filename)) # 添加链接文件 link_path = '/'.join(self.current_path + [str(link_name)]) zf_out.writestr(link_path, json.dumps(link_info)) # 替换原文件 shutil.move(temp_DISC, self.current_DISC) shutil.rmtree(temp_dir) self.update_file_list() self.update_status(f"已创建链接: {link_name}") except Exception as e: messagebox.showerror("错误", f"创建链接失败: {str(e)}") def new_file(self): if not self.current_DISC: return filename = simpledialog.askstring("新建文件", "请输入文件名:") if filename: try: # 使用最高压缩等级 with DISCAPI.DISCFile(self.current_DISC, 'a', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf: file_path = '/'.join(self.current_path + [str(filename)]) zf.writestr(file_path, '') self.update_file_list() self.update_status(f"已创建文件: {filename}") except Exception as e: messagebox.showerror("错误", f"创建文件失败: {str(e)}") def new_folder(self): if not self.current_DISC: return foldername = simpledialog.askstring("新建文件夹", "请输入文件夹名:") if foldername: try: # 使用最高压缩等级 with DISCAPI.DISCFile(self.current_DISC, 'a', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf: folder_path = '/'.join(self.current_path + [str(foldername)]) + '/' zf.writestr(folder_path, '') self.update_file_list() self.update_status(f"已创建文件夹: {foldername}") except Exception as e: messagebox.showerror("错误", f"创建文件夹失败: {str(e)}") def copy_selected(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: return # 保存选中的项目到剪贴板 self.clipboard_items = [] self.clipboard_operation = 'copy' for item in selection: values = self.tree.item(item)['values'] name = values[0] if name == '..': continue is_folder = 'folder' in self.tree.item(item)['tags'] self.clipboard_items.append({ 'name': name, 'is_folder': is_folder, 'path': '/'.join(self.current_path + [str(name)]) }) self.update_status(f"已复制 {len(self.clipboard_items)} 个项目") # 更新右键菜单,显示粘贴选项 self.create_context_menu() def move_selected(self): if not self.current_DISC: return selection = self.tree.selection() if not selection: return # 保存选中的项目到剪贴板 self.clipboard_items = [] self.clipboard_operation = 'move' for item in selection: values = self.tree.item(item)['values'] name = values[0] if name == '..': continue is_folder = 'folder' in self.tree.item(item)['tags'] self.clipboard_items.append({ 'name': name, 'is_folder': is_folder, 'path': '/'.join(self.current_path + [str(name)]) }) self.update_status(f"已移动 {len(self.clipboard_items)} 个项目") # 更新右键菜单,显示粘贴选项 self.create_context_menu() def paste_items(self): if not self.current_DISC or not self.clipboard_items: return try: # 创建临时文件 temp_dir = tempfile.mkdtemp() temp_DISC = os.path.join(temp_dir, 'temp.DISC') with DISCAPI.DISCFile(self.current_DISC, 'r') as zf_in: # 使用最高压缩等级创建临时DISC with DISCAPI.DISCFile(temp_DISC, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf_out: # 复制所有文件,除了要移动的文件 for info in zf_in.infolist(): should_skip = False if self.clipboard_operation == 'move': for item in self.clipboard_items: if item['is_folder']: # 检查是否在要移动的文件夹中 if info.filename.startswith(item['path'] + '/'): should_skip = True break else: # 检查是否是要移动的文件 if info.filename == item['path']: should_skip = True break if not should_skip: zf_out.writestr(info, zf_in.read(info.filename)) # 粘贴剪贴板中的项目 for item in self.clipboard_items: new_path = '/'.join(self.current_path + [item['name']]) if item['is_folder']: # 粘贴文件夹 folder_path = item['path'] # 创建文件夹标记 zf_out.writestr(new_path + '/', '') # 复制文件夹内容 for info in zf_in.infolist(): if info.filename.startswith(folder_path + '/'): # 计算新的路径 rel_path = info.filename[len(folder_path)+1:] new_file_path = new_path + '/' + rel_path zf_out.writestr(new_file_path, zf_in.read(info.filename)) else: # 粘贴文件 zf_out.writestr(new_path, zf_in.read(item['path'])) # 替换原文件 shutil.move(temp_DISC, self.current_DISC) shutil.rmtree(temp_dir) # 如果是移动操作,清空剪贴板 if self.clipboard_operation == 'move': self.clipboard_items = [] self.clipboard_operation = None self.update_file_list() self.update_status(f"已粘贴 {len(self.clipboard_items)} 个项目") # 更新右键菜单 self.create_context_menu() except Exception as e: messagebox.showerror("错误", f"粘贴失败: {str(e)}") def show_info(self): if not self.current_DISC: messagebox.showwarning("警告", "请先打开一个压缩包") return try: with DISCAPI.DISCFile(self.current_DISC, 'r') as zf: file_count = 0 folder_count = 0 total_size = 0 compressed_size = 0 for info in zf.infolist(): if info.filename.endswith('/'): folder_count += 1 else: file_count += 1 total_size += info.file_size compressed_size += info.compress_size info_text = f"文件名: {os.path.basename(self.current_DISC)}\n" info_text += f"路径: {os.path.dirname(self.current_DISC)}\n" info_text += f"文件夹数: {folder_count}\n" info_text += f"文件数: {file_count}\n" info_text += f"原始大小: {self.format_size(total_size)}\n" info_text += f"压缩后大小: {self.format_size(compressed_size)}\n" if total_size > 0: info_text += f"压缩率: {(1 - compressed_size/total_size)*100:.1f}%" messagebox.showinfo("压缩包信息", info_text) except Exception as e: messagebox.showerror("错误", f"获取信息失败: {str(e)}") def benchmark(self): if not self.current_DISC: messagebox.showwarning("警告", "请先打开一个压缩包") return try: # 创建临时文件用于测试 temp_dir = tempfile.mkdtemp() temp_file = os.path.join(temp_dir, 'benchmark_test.txt') # 创建测试文件 with open(temp_file, 'w') as f: f.write('A' * 1000000) # 1MB测试文件 # 测试压缩速度 start_time = time.time() temp_DISC = os.path.join(temp_dir, 'benchmark_test.DISC') with DISCAPI.DISCFile(temp_DISC, 'w', compression=DISCAPI.DISC_DEFLATED, compresslevel=9) as zf: zf.write(temp_file, 'test.txt') compress_time = time.time() - start_time # 测试解压速度 start_time = time.time() with DISCAPI.DISCFile(temp_DISC, 'r') as zf: zf.extract('test.txt', temp_dir) extract_time = time.time() - start_time # 获取文件大小 original_size = os.path.getsize(temp_file) compressed_size = os.path.getsize(temp_DISC) # 计算速度 compress_speed = original_size / (1024 * 1024) / compress_time # MB/s extract_speed = original_size / (1024 * 1024) / extract_time # MB/s # 显示结果 result = f"基准测试结果:\n\n" result += f"原始大小: {self.format_size(original_size)}\n" result += f"压缩后大小: {self.format_size(compressed_size)}\n" result += f"压缩率: {(1 - compressed_size/original_size)*100:.1f}%\n\n" result += f"压缩速度: {compress_speed:.2f} MB/s\n" result += f"解压速度: {extract_speed:.2f} MB/s" messagebox.showinfo("基准测试结果", result) # 清理临时文件 shutil.rmtree(temp_dir) except Exception as e: messagebox.showerror("错误", f"基准测试失败: {str(e)}") def show_about(self): about_text = "DISC文件管理器 v1.0\n\n" about_text += "一个仿照7-DISC的DISC文件管理器\n" about_text += "使用Python tkinter开发\n\n" about_text += "功能包括:\n" about_text += "- 创建和打开DISC文件\n" about_text += "- 添加、删除、重命名文件\n" about_text += "- 文件夹浏览\n" about_text += "- 拖拽添加文件\n" about_text += "- CRC校验\n" about_text += "- 文件属性查看\n" about_text += "- 文件复制和移动\n" about_text += "- 创建链接\n" about_text += "- 基准测试" messagebox.showinfo("关于", about_text) def update_status(self, message): self.status_bar.config(text=message) if __name__ == "__main__": root = TkinterDnD.Tk() app = DISCManager(root) root.mainloop()