Compare commits
	
		
			6 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 898d47f799 | |||
| afd038dbdf | |||
| f191ef6a1e | |||
| e7f68cb442 | |||
| cad17f9bdf | |||
| 322cf7335c | 
							
								
								
									
										430
									
								
								SpaceSniffer.py
									
									
									
									
									
								
							
							
						
						
									
										430
									
								
								SpaceSniffer.py
									
									
									
									
									
								
							| @@ -1,100 +1,74 @@ | |||||||
| import os | import os | ||||||
| import threading | import threading | ||||||
| import tkinter as tk | import tkinter as tk | ||||||
| from tkinter import ttk, messagebox | from tkinter import ttk, filedialog, messagebox | ||||||
| import tkinter.filedialog |  | ||||||
| from queue import Queue | from queue import Queue | ||||||
|  | from send2trash import send2trash | ||||||
| import subprocess | import subprocess | ||||||
| import send2trash |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class FolderScanner(threading.Thread): | class FolderScanner(threading.Thread): | ||||||
|     def __init__(self, start_path, queue): |     def __init__(self, start_path, queue, progress_queue): | ||||||
|         """ |         threading.Thread.__init__(self) | ||||||
|         初始化函数,创建一个线程并设置起始路径和队列。 |         self.start_path = start_path  # 扫描路径 | ||||||
|  |         self.queue = queue  # 用于存储扫描结果 | ||||||
|  |         self.progress_queue = progress_queue  # 用于存储扫描进度 | ||||||
|  |         self.total_items = 0  # 总共扫描的文件/目录数量 | ||||||
|  |  | ||||||
|         Args: |     def run(self):  # 扫描文件/目录 | ||||||
|             start_path (str): 起始路径,表示要遍历的文件夹的根目录。 |         self.total_items = sum( | ||||||
|             queue (Queue): 队列对象,用于线程间的通信,存放待处理或已处理过的文件或文件夹路径。 |             [len(files) + len(dirs) for r, dirs, files in os.walk(self.start_path)] | ||||||
|  |         )  # 计算总共扫描的文件/目录数量 | ||||||
|  |         self.scan_folder(self.start_path)  # 扫描文件/目录 | ||||||
|  |         self.queue.put(None)  # 扫描结束 | ||||||
|  |  | ||||||
|         Returns: |     def scan_folder(self, folder):  # 扫描文件/目录 | ||||||
|             None |         scanned_items = 0  # 已扫描的文件/目录数量 | ||||||
|         """ |         for dirpath, dirnames, filenames in os.walk(folder):  # 遍历文件/目录 | ||||||
|         # 使用super()调用父类threading.Thread的__init__方法 |             for dirname in dirnames:  # 遍历目录 | ||||||
|         super().__init__() |  | ||||||
|         self.start_path = start_path |  | ||||||
|         self.queue = queue |  | ||||||
|  |  | ||||||
|     def run(self): |  | ||||||
|         """ |  | ||||||
|         运行扫描文件夹并获取文件信息的操作。 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             无参数。 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             无返回值。 |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         self.scan_folder(self.start_path) |  | ||||||
|  |  | ||||||
|     def scan_folder(self, folder): |  | ||||||
|         """ |  | ||||||
|         遍历指定文件夹下的所有子文件夹和文件,并将它们的路径、大小、父文件夹路径和类型(文件夹或文件)放入队列中。 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             folder (str): 需要遍历的文件夹路径。 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             None. |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         for dirpath, dirnames, filenames in os.walk(folder): |  | ||||||
|             for dirname in dirnames: |  | ||||||
|                 folder_path = os.path.join(dirpath, dirname) |                 folder_path = os.path.join(dirpath, dirname) | ||||||
|                 size = self.get_size(folder_path) |                 size = self.get_size(folder_path) | ||||||
|                 self.queue.put((folder_path, size, dirpath, "folder")) |                 self.queue.put((folder_path, size, dirpath, "folder")) | ||||||
|             for filename in filenames: |                 scanned_items += 1 | ||||||
|  |                 self.update_progress(scanned_items, folder_path) | ||||||
|  |  | ||||||
|  |             for filename in filenames:  # 遍历文件 | ||||||
|                 file_path = os.path.join(dirpath, filename) |                 file_path = os.path.join(dirpath, filename) | ||||||
|                 size = os.path.getsize(file_path) |                 size = os.path.getsize(file_path) | ||||||
|                 self.queue.put((file_path, size, dirpath, "file")) |                 self.queue.put((file_path, size, dirpath, "file")) | ||||||
|  |                 scanned_items += 1 | ||||||
|  |                 self.update_progress(scanned_items, file_path) | ||||||
|  |  | ||||||
|     def get_size(self, path): |     def get_size(self, path):  # 获取文件/目录大小 | ||||||
|         # 初始化总大小为0 |  | ||||||
|         total_size = 0 |         total_size = 0 | ||||||
|         # 遍历指定路径下的所有文件和目录 |  | ||||||
|         for dirpath, dirnames, filenames in os.walk(path): |         for dirpath, dirnames, filenames in os.walk(path): | ||||||
|             # 遍历当前目录下的所有文件 |  | ||||||
|             for f in filenames: |             for f in filenames: | ||||||
|                 # 拼接文件的完整路径 |  | ||||||
|                 fp = os.path.join(dirpath, f) |                 fp = os.path.join(dirpath, f) | ||||||
|                 # 将文件大小累加到总大小中 |  | ||||||
|                 total_size += os.path.getsize(fp) |                 total_size += os.path.getsize(fp) | ||||||
|         return total_size |         return total_size | ||||||
|  |  | ||||||
|  |     def update_progress(self, scanned_items, current_path):  # 更新扫描进度 | ||||||
|  |         progress = (scanned_items / self.total_items) * 100 | ||||||
|  |         self.progress_queue.put( | ||||||
|  |             (progress, scanned_items, self.total_items, current_path) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class App(tk.Tk): | class App(tk.Tk): | ||||||
|     def __init__(self, start_path): |     def __init__(self, start_path):  # 初始化 | ||||||
|         """ |  | ||||||
|         初始化目录扫描器窗口 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             start_path (str): 开始扫描的目录路径 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             None |  | ||||||
|         """ |  | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.title("目录扫描器 V1.0") |         self.withdraw() | ||||||
|  |         self.title("目录扫描器 V1.3") | ||||||
|         self.start_path = start_path |         self.start_path = start_path | ||||||
|         self.queue = Queue() |         self.queue = Queue() | ||||||
|         self.scanner = FolderScanner(start_path, self.queue) |         self.progress_queue = Queue() | ||||||
|  |         self.progress_window = ProgressWindow(self) | ||||||
|  |  | ||||||
|  |         self.scanner = FolderScanner(start_path, self.queue, self.progress_queue) | ||||||
|         self.scanner.start() |         self.scanner.start() | ||||||
|  |  | ||||||
|         self.style = ttk.Style(self) |         self.style = ttk.Style(self) | ||||||
|         self.style.configure( |         self.style.configure("Treeview", font=("Helvetica", 10), rowheight=25) | ||||||
|             "Treeview", font=("Helvetica", 10), rowheight=25 |  | ||||||
|         )  # 设置行高 |  | ||||||
|         self.style.configure("Treeview.Heading", font=("Helvetica", 12, "bold")) |         self.style.configure("Treeview.Heading", font=("Helvetica", 12, "bold")) | ||||||
|  |  | ||||||
|         self.tree = ttk.Treeview(self, style="Treeview") |         self.tree = ttk.Treeview(self, style="Treeview") | ||||||
| @@ -105,27 +79,14 @@ class App(tk.Tk): | |||||||
|  |  | ||||||
|         self.folder_first_var = tk.BooleanVar() |         self.folder_first_var = tk.BooleanVar() | ||||||
|         self.folder_first_checkbutton = tk.Checkbutton( |         self.folder_first_checkbutton = tk.Checkbutton( | ||||||
|             self, text="目录优先显示", variable=self.folder_first_var |             self, | ||||||
|  |             text="目录优先显示", | ||||||
|  |             variable=self.folder_first_var, | ||||||
|  |             command=self.update_folder_first_var, | ||||||
|         ) |         ) | ||||||
|         self.folder_first_checkbutton.pack(side=tk.LEFT, fill=tk.X) |  | ||||||
|  |  | ||||||
|         self.folder_first_checkbutton.config(command=self.update_folder_first_var) |  | ||||||
|  |  | ||||||
|         self.sort_by_size_button = tk.Button( |  | ||||||
|             self, text="根据目录/文件大小排序", command=self.sort_by_size |  | ||||||
|         ) |  | ||||||
|         self.sort_by_size_button.pack(side=tk.LEFT, fill=tk.X) |  | ||||||
|  |  | ||||||
|         self.sort_by_name_button = tk.Button( |  | ||||||
|             self, text="根据目录/文件名排序", command=self.sort_by_name |  | ||||||
|         ) |  | ||||||
|         self.sort_by_name_button.pack(side=tk.LEFT, fill=tk.X) |  | ||||||
|  |  | ||||||
|         self.refresh_button = tk.Button( |  | ||||||
|             self, text="刷新列表", command=self.refresh_tree |  | ||||||
|         ) |  | ||||||
|         self.refresh_button.pack(side=tk.LEFT, fill=tk.X) |  | ||||||
|  |  | ||||||
|  |         self.tree.bind("<Double-1>", self.on_double_click) | ||||||
|  |         self.tree.bind("<Return>", self.on_enter_press) | ||||||
|         self.tree.bind("<Button-3>", self.show_context_menu) |         self.tree.bind("<Button-3>", self.show_context_menu) | ||||||
|  |  | ||||||
|         self.size_sort_order = True |         self.size_sort_order = True | ||||||
| @@ -134,46 +95,121 @@ class App(tk.Tk): | |||||||
|  |  | ||||||
|         self.populate_root() |         self.populate_root() | ||||||
|         self.update_tree() |         self.update_tree() | ||||||
|  |         self.update_progress() | ||||||
|  |  | ||||||
|         self.protocol("WM_DELETE_WINDOW", self.on_close) |         self.protocol("WM_DELETE_WINDOW", self.on_close) | ||||||
|         self.set_window_size() |         self.set_window_size() | ||||||
|  |         self.create_menu() | ||||||
|  |  | ||||||
|     def populate_root(self): |     def create_menu(self): | ||||||
|         """ |         menu_bar = tk.Menu(self) | ||||||
|         在树形控件中填充根节点。 |         self.config(menu=menu_bar) | ||||||
|  |  | ||||||
|         Args: |         file_menu = tk.Menu(menu_bar, tearoff=0) | ||||||
|             无参数。 |         menu_bar.add_cascade(label="文件", menu=file_menu) | ||||||
|  |         file_menu.add_command(label="选择目录", command=self.reselect_directory) | ||||||
|  |         file_menu.add_separator() | ||||||
|  |         file_menu.add_command(label="退出", command=self.on_close) | ||||||
|  |  | ||||||
|         Returns: |         view_menu = tk.Menu(menu_bar, tearoff=0) | ||||||
|             无返回值。 |         menu_bar.add_cascade(label="视图", menu=view_menu) | ||||||
|  |         view_menu.add_checkbutton( | ||||||
|  |             label="目录优先显示", | ||||||
|  |             variable=self.folder_first_var, | ||||||
|  |             command=self.update_folder_first_var, | ||||||
|  |         ) | ||||||
|  |         view_menu.add_command( | ||||||
|  |             label="根据目录/文件大小排序", | ||||||
|  |             command=self.sort_by_size, | ||||||
|  |         ) | ||||||
|  |         view_menu.add_command( | ||||||
|  |             label="根据目录/文件名排序", | ||||||
|  |             command=self.sort_by_size, | ||||||
|  |         ) | ||||||
|  |         view_menu.add_command( | ||||||
|  |             label="刷新列表", | ||||||
|  |             command=self.refresh_tree, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|         """ |         help_menu = tk.Menu(menu_bar, tearoff=0) | ||||||
|         size = self.scanner.get_size(self.start_path) / 1024 / 1024 |         menu_bar.add_cascade(label="帮助", menu=help_menu) | ||||||
|  |         help_menu.add_command(label="帮助", command=self.show_help) | ||||||
|  |         help_menu.add_command(label="更新日志", command=self.show_update_log) | ||||||
|  |         help_menu.add_command(label="关于", command=self.show_about) | ||||||
|  |  | ||||||
|  |     def show_help(self): | ||||||
|  |         messagebox.showinfo( | ||||||
|  |             "帮助", | ||||||
|  |             "1. 双击文件夹可以展开/折叠\n", | ||||||
|  |             "2. 双击文件可以直接打开\n", | ||||||
|  |             "3. 右键单击文件/文件夹可以打开、删除文件/文件夹、进入所在目录\n", | ||||||
|  |             "4. 点击左上角文件菜单可以重新选择目录进行扫描\n", | ||||||
|  |             "5. 点击左上角视图菜单可以根据目录/文件大小、目录/文件名排序、刷新列表\n", | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def show_update_log(self): | ||||||
|  |         messagebox.showinfo( | ||||||
|  |             "更新日志", | ||||||
|  |             "V1.0 实现了目录扫描功能,文件/目录大小统计功能\n", | ||||||
|  |             "\n", | ||||||
|  |             "V1.1 实现了重新选择目录扫描功能,新增了进度条显示\n", | ||||||
|  |             "\n", | ||||||
|  |             "V1.2 实现了双击打开目录、文件的功能,实现了右键菜单\n", | ||||||
|  |             "\n", | ||||||
|  |             "V1.3 优化了界面UI\n", | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def show_about(self): | ||||||
|  |         messagebox.showinfo("关于", "目录扫描器 V1.3\n作者: ahdoawhfo") | ||||||
|  |  | ||||||
|  |     def on_double_click(self, event):  # V1.2 Update:当双击的是文件时,打开文件 | ||||||
|  |         item = self.tree.identify_row(event.y) | ||||||
|  |         if item: | ||||||
|  |             if os.path.isfile(item): | ||||||
|  |                 self.open_item(item) | ||||||
|  |  | ||||||
|  |     def on_enter_press(self, event):  # V1.2 Update:当回车的是文件时,打开文件 | ||||||
|  |         item = self.tree.selection()[0] | ||||||
|  |         if os.path.isfile(item): | ||||||
|  |             self.open_item(item) | ||||||
|  |  | ||||||
|  |     def reselect_directory(self):  # V1.2 Update:重新选择目录 | ||||||
|  |         new_path = filedialog.askdirectory() | ||||||
|  |         if new_path: | ||||||
|  |             self.start_path = new_path | ||||||
|  |             self.refresh_tree() | ||||||
|  |  | ||||||
|  |     def format_size(self, size):  # 格式化文件/目录大小 | ||||||
|  |         units = ["B", "KB", "MB", "GB", "TB"] | ||||||
|  |         index = 0 | ||||||
|  |         while size >= 1024 and index < len(units) - 1: | ||||||
|  |             size /= 1024.0 | ||||||
|  |             index += 1 | ||||||
|  |         return f"{size:.2f} {units[index]}" | ||||||
|  |  | ||||||
|  |     def populate_root(self):  # 初始化根目录 | ||||||
|  |         size = self.scanner.get_size(self.start_path) | ||||||
|  |         formatted_size = self.format_size(size) | ||||||
|         self.tree.insert( |         self.tree.insert( | ||||||
|             "", |             "", | ||||||
|             "end", |             "end", | ||||||
|             iid=self.start_path, |             iid=self.start_path, | ||||||
|             text=f"{os.path.basename(self.start_path)} ({size:.2f} MB)", |             text=f"{os.path.basename(self.start_path)} ({formatted_size})", | ||||||
|             tags=("folder",), |             tags=("folder",), | ||||||
|             open=True, |             open=True, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def update_tree(self): |     def update_tree(self):  # 更新树状图 | ||||||
|         """ |  | ||||||
|         更新树形结构,将磁盘文件信息更新到树形视图中。 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             无 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             无 |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         if not self.is_sorting: |         if not self.is_sorting: | ||||||
|             while not self.queue.empty(): |             while not self.queue.empty(): | ||||||
|                 path, size, parent, tag = self.queue.get() |                 item = self.queue.get() | ||||||
|                 size_mb = size / 1024 / 1024 |                 if item is None:  # Scanning is done | ||||||
|  |                     self.progress_window.destroy() | ||||||
|  |                     self.deiconify() | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |                 path, size, parent, tag = item | ||||||
|  |                 formatted_size = self.format_size(size) | ||||||
|                 parent_iid = self.get_iid(parent) |                 parent_iid = self.get_iid(parent) | ||||||
|                 if parent == self.start_path: |                 if parent == self.start_path: | ||||||
|                     parent_iid = self.start_path |                     parent_iid = self.start_path | ||||||
| @@ -182,35 +218,32 @@ class App(tk.Tk): | |||||||
|                         parent_iid, |                         parent_iid, | ||||||
|                         "end", |                         "end", | ||||||
|                         iid=self.get_iid(path), |                         iid=self.get_iid(path), | ||||||
|                         text=f"{os.path.basename(path)} ({size_mb:.2f} MB)", |                         text=f"{os.path.basename(path)} ({formatted_size})", | ||||||
|                         tags=(tag,), |                         tags=(tag,), | ||||||
|                         open=False, |                         open=False, | ||||||
|                     ) |                     ) | ||||||
|             self.after(100, self.update_tree) |             self.after(100, self.update_tree) | ||||||
|  |  | ||||||
|     def get_iid(self, path): |     def update_progress(self):  # 更新扫描进度 | ||||||
|         """ |         if not self.progress_window.winfo_exists():  # Check if the window still exists | ||||||
|         获取指定路径的iid值 |             return | ||||||
|  |         while not self.progress_queue.empty(): | ||||||
|  |             progress, scanned_items, total_items, current_path = ( | ||||||
|  |                 self.progress_queue.get() | ||||||
|  |             ) | ||||||
|  |             self.progress_window.progress_var.set(progress) | ||||||
|  |             self.progress_window.progress_bar["value"] = progress | ||||||
|  |             self.progress_window.progress_label.config( | ||||||
|  |                 text=f"扫描进度: {scanned_items} / {total_items}" | ||||||
|  |             ) | ||||||
|  |             self.progress_window.path_label.config(text=f"正在扫描: {current_path}") | ||||||
|  |  | ||||||
|         Args: |         self.after(100, self.update_progress) | ||||||
|             path (str): 文件或目录的路径 |  | ||||||
|  |  | ||||||
|         Returns: |     def get_iid(self, path):  # 获取目录/文件的 iid | ||||||
|             str: 路径本身作为iid值返回 |  | ||||||
|         """ |  | ||||||
|         return path |         return path | ||||||
|  |  | ||||||
|     def sort_by_size(self): |     def sort_by_size(self):  # 根据目录/文件大小排序 | ||||||
|         """ |  | ||||||
|         根据文件或文件夹的大小对文件列表进行排序。 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             无参数。 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             无返回值,直接修改当前实例的文件列表。 |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         self.sort_items( |         self.sort_items( | ||||||
|             lambda x: ( |             lambda x: ( | ||||||
|                 0 if x[1] == "folder" and self.folder_first_var.get() else 1, |                 0 if x[1] == "folder" and self.folder_first_var.get() else 1, | ||||||
| @@ -224,17 +257,7 @@ class App(tk.Tk): | |||||||
|         ) |         ) | ||||||
|         self.size_sort_order = not self.size_sort_order |         self.size_sort_order = not self.size_sort_order | ||||||
|  |  | ||||||
|     def sort_by_name(self): |     def sort_by_name(self):  # 根据目录/文件名排序 | ||||||
|         """ |  | ||||||
|         根据名称对列表进行排序 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             无 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             无返回值,但会改变对象的内部状态 |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         self.sort_items( |         self.sort_items( | ||||||
|             lambda x: ( |             lambda x: ( | ||||||
|                 0 if x[1] == "folder" and self.folder_first_var.get() else 1, |                 0 if x[1] == "folder" and self.folder_first_var.get() else 1, | ||||||
| @@ -244,18 +267,7 @@ class App(tk.Tk): | |||||||
|         ) |         ) | ||||||
|         self.name_sort_order = not self.name_sort_order |         self.name_sort_order = not self.name_sort_order | ||||||
|  |  | ||||||
|     def sort_items(self, key, reverse): |     def sort_items(self, key, reverse):  # 排序 | ||||||
|         """ |  | ||||||
|         对树形控件中的指定目录进行排序。 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             key (callable): 用于排序的key函数。 |  | ||||||
|             reverse (bool): 是否降序排序。 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             None |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         if self.folder_first_var.get(): |         if self.folder_first_var.get(): | ||||||
|             selected = self.tree.selection() |             selected = self.tree.selection() | ||||||
|             if not selected: |             if not selected: | ||||||
| @@ -315,24 +327,17 @@ class App(tk.Tk): | |||||||
|                 for index, item in enumerate(sorted_items): |                 for index, item in enumerate(sorted_items): | ||||||
|                     self.tree.move(item[2], parent, index) |                     self.tree.move(item[2], parent, index) | ||||||
|  |  | ||||||
|     def show_context_menu(self, event): |     def show_context_menu(self, event):  # 显示右键菜单 | ||||||
|         """ |  | ||||||
|         显示上下文菜单 |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             event (tkinter.Event): 鼠标事件对象 |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             None |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         item = self.tree.identify_row(event.y) |         item = self.tree.identify_row(event.y) | ||||||
|         if item: |         if item: | ||||||
|             self.tree.selection_set(item) |             self.tree.selection_set(item) | ||||||
|             menu = tk.Menu(self, tearoff=0) |             menu = tk.Menu(self, tearoff=0) | ||||||
|             menu.add_command(label="打开文件", command=lambda: self.open_item(item)) |  | ||||||
|             menu.add_command( |             menu.add_command( | ||||||
|                 label="打开所在目录", command=lambda: self.open_directory(item) |                 label="打开文件", command=lambda: self.open_item(item, "Open File") | ||||||
|  |             ) | ||||||
|  |             menu.add_command( | ||||||
|  |                 label="打开所在目录", | ||||||
|  |                 command=lambda: self.open_directory(item, "Open Containing Folder"), | ||||||
|             ) |             ) | ||||||
|             menu.add_command( |             menu.add_command( | ||||||
|                 label="删除文件/目录(放入回收站)", |                 label="删除文件/目录(放入回收站)", | ||||||
| @@ -340,53 +345,63 @@ class App(tk.Tk): | |||||||
|             ) |             ) | ||||||
|             menu.post(event.x_root, event.y_root) |             menu.post(event.x_root, event.y_root) | ||||||
|  |  | ||||||
|     def open_item(self, item): |     def open_item(self, item, context_menu_action=None):  # 打开文件/目录 | ||||||
|         path = self.tree.item(item, "text").split(" ")[0] |         name = self.tree.item(item, "text").split(" ")[0] | ||||||
|         full_path = os.path.join(self.start_path, path) |  | ||||||
|         try: |         try: | ||||||
|             if os.path.isdir(full_path): |             if os.path.isdir(item): | ||||||
|                 subprocess.Popen(f'explorer "{os.path.realpath(full_path)}"') |                 if context_menu_action == "Open File": | ||||||
|  |                     subprocess.Popen(f'explorer "{os.path.realpath(item)}"') | ||||||
|  |                 elif context_menu_action == "Open Containing Folder": | ||||||
|  |                     parent_folder = os.path.dirname(item) | ||||||
|  |                     subprocess.Popen(f'explorer "{os.path.realpath(parent_folder)}"') | ||||||
|             else: |             else: | ||||||
|                 os.startfile(full_path) |                 os.startfile(item) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             messagebox.showerror("错误", f"无法打开 {full_path}\n{e}") |             messagebox.showerror("错误", f"无法打开 {name}\n{e}") | ||||||
|  |  | ||||||
|     def open_directory(self, item): |     def open_directory(self, item, context_menu_action=None):  # 打开所在目录 | ||||||
|         path = self.tree.item(item, "text").split(" ")[0] |         name = self.tree.item(item, "text").split(" ")[0] | ||||||
|         full_path = os.path.join(self.start_path, path) |         if item == self.start_path: | ||||||
|         if not os.path.isdir(full_path): |             parent_path = item | ||||||
|             full_path = os.path.dirname(full_path) |         else: | ||||||
|  |             parent_path = os.path.dirname(item) | ||||||
|         try: |         try: | ||||||
|             subprocess.Popen(f'explorer "{os.path.realpath(full_path)}"') |             subprocess.Popen(f'explorer "{os.path.realpath(parent_path)}"') | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             messagebox.showerror("错误", f"无法打开 {full_path}\n{e}") |             messagebox.showerror("错误", f"无法打开 {name}\n{e}") | ||||||
|  |  | ||||||
|     def delete_item(self, item): |     def delete_item(self, item):  # 删除文件/目录 | ||||||
|         path = self.tree.item(item, "text").split(" ")[0] |         path = self.tree.item(item, "text").split(" ")[0] | ||||||
|         full_path = os.path.normpath( |         full_path = os.path.normpath(os.path.join(self.start_path, path)) | ||||||
|             os.path.join(self.start_path, path) |  | ||||||
|         )  # 使用os.path.normpath来规范化路径 |  | ||||||
|         confirm = messagebox.askyesno("提示", f"你确定要删除 {full_path} 吗?") |         confirm = messagebox.askyesno("提示", f"你确定要删除 {full_path} 吗?") | ||||||
|         if confirm: |         if confirm: | ||||||
|             try: |             try: | ||||||
|                 send2trash.send2trash(full_path) |                 send2trash(full_path) | ||||||
|                 self.tree.delete(item) |                 self.tree.delete(item) | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 messagebox.showerror("错误", f"无法删除 {full_path}\n{e}") |                 messagebox.showerror("错误", f"无法删除 {full_path}\n{e}") | ||||||
|             self.refresh_tree() |             self.refresh_tree() | ||||||
|  |  | ||||||
|     def refresh_tree(self): |     def refresh_tree(self):  # 刷新树状图 | ||||||
|         for item in self.tree.get_children(): |         self.is_sorting = False | ||||||
|             self.tree.delete(item) |         self.queue.queue.clear() | ||||||
|  |         self.progress_queue.queue.clear() | ||||||
|  |         self.tree.delete(*self.tree.get_children()) | ||||||
|         self.populate_root() |         self.populate_root() | ||||||
|         self.scanner = FolderScanner(self.start_path, self.queue) |         self.scanner = FolderScanner(self.start_path, self.queue, self.progress_queue) | ||||||
|  |         self.progress_window = ProgressWindow(self) | ||||||
|  |         self.withdraw() | ||||||
|         self.scanner.start() |         self.scanner.start() | ||||||
|  |         self.update_tree() | ||||||
|  |         self.update_progress() | ||||||
|  |  | ||||||
|     def on_close(self): |     def on_close(self):  # 关闭程序 | ||||||
|  |         if self.scanner.is_alive(): | ||||||
|             self.scanner.join() |             self.scanner.join() | ||||||
|         self.destroy() |         self.destroy() | ||||||
|  |         root.destroy() | ||||||
|  |  | ||||||
|     def set_window_size(self): |     def set_window_size(self):  # 设置窗口大小 | ||||||
|         screen_width = self.winfo_screenwidth() |         screen_width = self.winfo_screenwidth() | ||||||
|         screen_height = self.winfo_screenheight() |         screen_height = self.winfo_screenheight() | ||||||
|         window_width = int(screen_width * 0.6) |         window_width = int(screen_width * 0.6) | ||||||
| @@ -396,7 +411,7 @@ class App(tk.Tk): | |||||||
|         ) |         ) | ||||||
|         self.update_font_size() |         self.update_font_size() | ||||||
|  |  | ||||||
|     def update_font_size(self): |     def update_font_size(self):  # 更新字体大小 | ||||||
|         current_font = self.style.lookup("Treeview", "font") |         current_font = self.style.lookup("Treeview", "font") | ||||||
|         font_size = int(current_font.split()[1]) |         font_size = int(current_font.split()[1]) | ||||||
|         screen_width = self.winfo_screenwidth() |         screen_width = self.winfo_screenwidth() | ||||||
| @@ -409,23 +424,38 @@ class App(tk.Tk): | |||||||
|                 font=(current_font.split()[0], new_font_size + 2, "bold"), |                 font=(current_font.split()[0], new_font_size + 2, "bold"), | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     def sort_root_items(self, by="size"): |     def update_folder_first_var(self):  # 更新目录优先显示变量 | ||||||
|         if by == "size": |         self.folder_first_var.set(not self.folder_first_var.get()) | ||||||
|             self.sort_by_size() |  | ||||||
|         elif by == "name": |  | ||||||
|             self.sort_by_name() |  | ||||||
|  |  | ||||||
|     def update_folder_first_var(self): |  | ||||||
|         if self.folder_first_var.get(): | class ProgressWindow(tk.Toplevel):  # 进度窗口 | ||||||
|             self.folder_first_var.set(False) |     def __init__(self, parent):  # 初始化 | ||||||
|         else: |         super().__init__(parent) | ||||||
|             self.folder_first_var.set(True) |         self.title("扫描进度") | ||||||
|  |         self.geometry("500x200") | ||||||
|  |         self.label = tk.Label( | ||||||
|  |             self, text="扫描中,请稍等", fg="deepskyblue", font=("Helvetica", 16) | ||||||
|  |         ) | ||||||
|  |         self.label.pack(pady=20) | ||||||
|  |         self.progress_var = tk.DoubleVar() | ||||||
|  |         self.progress_bar = ttk.Progressbar( | ||||||
|  |             self, variable=self.progress_var, maximum=100 | ||||||
|  |         ) | ||||||
|  |         self.progress_bar.pack(fill=tk.X, padx=20, pady=10) | ||||||
|  |         self.progress_label = tk.Label(self, text="扫描进度: 0 / 0") | ||||||
|  |         self.progress_label.pack(pady=5) | ||||||
|  |         self.path_label = tk.Label(self, text="正在扫描: ") | ||||||
|  |         self.path_label.pack(pady=5) | ||||||
|  |         self.protocol("WM_DELETE_WINDOW", self.on_close) | ||||||
|  |  | ||||||
|  |     def on_close(self): | ||||||
|  |         pass  # 禁止关闭进度窗口 | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     root = tk.Tk() |     root = tk.Tk() | ||||||
|     root.withdraw() |     root.withdraw() | ||||||
|     start_path = tkinter.filedialog.askdirectory() |     start_path = filedialog.askdirectory() | ||||||
|     if start_path: |     if start_path: | ||||||
|         app = App(start_path) |         app = App(start_path) | ||||||
|         app.mainloop() |         app.mainloop() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user