|
|
@@ -1,5 +1,5 @@
|
|
|
import tkinter as tk
|
|
|
-from tkinter import filedialog, ttk
|
|
|
+from tkinter import filedialog, ttk, messagebox
|
|
|
import os
|
|
|
import math
|
|
|
from tkinter import Canvas
|
|
|
@@ -64,6 +64,28 @@ class FolderTree3D:
|
|
|
self.max_depth = 0
|
|
|
self.tree_data = [] # 存储所有节点数据,用于缩放重绘
|
|
|
|
|
|
+ # 历史记录
|
|
|
+ self.history = []
|
|
|
+ self.max_history = 3
|
|
|
+ self.history_file = "history.txt"
|
|
|
+
|
|
|
+ # 加载历史记录
|
|
|
+ self.load_history()
|
|
|
+
|
|
|
+ # 历史记录显示区域
|
|
|
+ history_frame = ttk.Frame(main_frame)
|
|
|
+ history_frame.pack(fill=tk.X, pady=(10, 0))
|
|
|
+
|
|
|
+ ttk.Label(history_frame, text="历史记录:").pack(side=tk.LEFT, padx=(0, 10))
|
|
|
+ self.history_buttons = []
|
|
|
+ for i in range(self.max_history):
|
|
|
+ btn = ttk.Button(history_frame, text="", width=30, command=lambda idx=i: self.select_history(idx))
|
|
|
+ btn.pack(side=tk.LEFT, padx=(0, 5))
|
|
|
+ self.history_buttons.append(btn)
|
|
|
+
|
|
|
+ # 更新历史记录显示
|
|
|
+ self.update_history_display()
|
|
|
+
|
|
|
def browse_folder(self):
|
|
|
folder_path = filedialog.askdirectory()
|
|
|
if folder_path:
|
|
|
@@ -246,7 +268,7 @@ class FolderTree3D:
|
|
|
if node_data["type"] == "folder":
|
|
|
current_radius = radius
|
|
|
else:
|
|
|
- current_radius = radius - 20
|
|
|
+ current_radius = radius - 40
|
|
|
|
|
|
x = root_x + current_radius * math.cos(angle)
|
|
|
y = root_y + current_radius * math.sin(angle)
|
|
|
@@ -345,34 +367,77 @@ class FolderTree3D:
|
|
|
y2 = node["y"] * self.scale + self.offset_y
|
|
|
self.canvas.create_line(x1, y1, x2, y2, fill="#666666", width=1)
|
|
|
|
|
|
- # 在文件所在圆上标记根节点与文件节点连线和圆的交点
|
|
|
+ # 绘制从圆心到文件夹的红色射线,向外射出
|
|
|
root_node = self.tree_data[0]
|
|
|
- root_x = root_node["x"] * self.scale + self.offset_x
|
|
|
- root_y = root_node["y"] * self.scale + self.offset_y
|
|
|
+ # 使用未缩放的坐标进行计算
|
|
|
+ root_x_unscaled = root_node["x"]
|
|
|
+ root_y_unscaled = root_node["y"]
|
|
|
|
|
|
for node in self.tree_data:
|
|
|
- if node["type"] == "file":
|
|
|
- # 计算根节点与文件节点连线和文件所在圆的交点
|
|
|
- file_x = node["x"] * self.scale + self.offset_x
|
|
|
- file_y = node["y"] * self.scale + self.offset_y
|
|
|
-
|
|
|
- # 计算文件所在圆的半径
|
|
|
- file_depth = node["depth"]
|
|
|
- file_radius = (file_depth * 100 - 20) * self.scale
|
|
|
-
|
|
|
- # 计算交点
|
|
|
- dx = file_x - root_x
|
|
|
- dy = file_y - root_y
|
|
|
- distance = math.sqrt(dx * dx + dy * dy)
|
|
|
- if distance == 0:
|
|
|
- continue
|
|
|
+ if node["type"] == "folder" and node["depth"] > 0: # 排除根节点
|
|
|
+ # 使用未缩放的坐标进行计算
|
|
|
+ folder_x_unscaled = node["x"]
|
|
|
+ folder_y_unscaled = node["y"]
|
|
|
|
|
|
- # 计算交点坐标
|
|
|
- intersection_x = root_x + (dx / distance) * file_radius
|
|
|
- intersection_y = root_y + (dy / distance) * file_radius
|
|
|
+ # 计算射线方向向量(未缩放)
|
|
|
+ dx_unscaled = folder_x_unscaled - root_x_unscaled
|
|
|
+ dy_unscaled = folder_y_unscaled - root_y_unscaled
|
|
|
|
|
|
- # 标记交点
|
|
|
- self.canvas.create_oval(intersection_x - 3, intersection_y - 3, intersection_x + 3, intersection_y + 3, fill="#FF0000", outline="#FF0000", width=1)
|
|
|
+ # 计算向量长度(未缩放)
|
|
|
+ distance_unscaled = math.sqrt(dx_unscaled * dx_unscaled + dy_unscaled * dy_unscaled)
|
|
|
+ if distance_unscaled > 0:
|
|
|
+ # 归一化方向向量(未缩放)
|
|
|
+ unit_dx = dx_unscaled / distance_unscaled
|
|
|
+ unit_dy = dy_unscaled / distance_unscaled
|
|
|
+
|
|
|
+ # 计算射线延伸的终点(未缩放)
|
|
|
+ extension_length_unscaled = 100 # 延伸长度,100像素(未缩放)
|
|
|
+ end_x_unscaled = folder_x_unscaled + unit_dx * extension_length_unscaled
|
|
|
+ end_y_unscaled = folder_y_unscaled + unit_dy * extension_length_unscaled
|
|
|
+
|
|
|
+ # 应用缩放和偏移,准备绘制
|
|
|
+ root_x = root_x_unscaled * self.scale + self.offset_x
|
|
|
+ root_y = root_y_unscaled * self.scale + self.offset_y
|
|
|
+ folder_x = folder_x_unscaled * self.scale + self.offset_x
|
|
|
+ folder_y = folder_y_unscaled * self.scale + self.offset_y
|
|
|
+ end_x = end_x_unscaled * self.scale + self.offset_x
|
|
|
+ end_y = end_y_unscaled * self.scale + self.offset_y
|
|
|
+
|
|
|
+ # 绘制红色射线,从圆心到延伸的终点
|
|
|
+ self.canvas.create_line(root_x, root_y, end_x, end_y, fill="#FF0000", width=1)
|
|
|
+
|
|
|
+ # 计算红色射线与文件所在圆圈的交点,并绘制绿色点
|
|
|
+ # 文件所在圆圈的半径(基于文件的深度,未缩放)
|
|
|
+ # 文件的深度是文件夹深度 + 1
|
|
|
+ folder_depth = node["depth"]
|
|
|
+ file_depth = folder_depth + 1
|
|
|
+ file_circle_radius_unscaled = file_depth * 100 - 40
|
|
|
+
|
|
|
+ # 计算射线与文件所在圆圈的交点(使用未缩放的坐标)
|
|
|
+ # 使用勾股定理计算交点
|
|
|
+ a = dx_unscaled * dx_unscaled + dy_unscaled * dy_unscaled
|
|
|
+ b = 2 * (root_x_unscaled * dx_unscaled + root_y_unscaled * dy_unscaled)
|
|
|
+ c = root_x_unscaled * root_x_unscaled + root_y_unscaled * root_y_unscaled - file_circle_radius_unscaled * file_circle_radius_unscaled
|
|
|
+
|
|
|
+ # 计算判别式
|
|
|
+ discriminant = b * b - 4 * a * c
|
|
|
+ if discriminant >= 0:
|
|
|
+ # 计算两个交点参数
|
|
|
+ t1 = (-b + math.sqrt(discriminant)) / (2 * a)
|
|
|
+ t2 = (-b - math.sqrt(discriminant)) / (2 * a)
|
|
|
+
|
|
|
+ # 找到在射线方向上的交点(t>0)
|
|
|
+ for t in [t1, t2]:
|
|
|
+ if t > 0:
|
|
|
+ # 计算交点(未缩放)
|
|
|
+ intersection_x_unscaled = root_x_unscaled + t * dx_unscaled
|
|
|
+ intersection_y_unscaled = root_y_unscaled + t * dy_unscaled
|
|
|
+ # 应用缩放和偏移
|
|
|
+ intersection_x = intersection_x_unscaled * self.scale + self.offset_x
|
|
|
+ intersection_y = intersection_y_unscaled * self.scale + self.offset_y
|
|
|
+ # 绘制绿色点
|
|
|
+ self.canvas.create_oval(intersection_x - 3, intersection_y - 3, intersection_x + 3, intersection_y + 3, fill="#00FF00", outline="#00FF00", width=1)
|
|
|
+
|
|
|
# 绘制同心圆
|
|
|
max_level = max(node["depth"] for node in self.tree_data)
|
|
|
# Get root node position after scale and offset
|
|
|
@@ -382,8 +447,8 @@ class FolderTree3D:
|
|
|
for level in range(1, max_level + 1):
|
|
|
radius = level * 100 * self.scale
|
|
|
self.canvas.create_oval(root_x - radius, root_y - radius, root_x + radius, root_y + radius, outline="#333333", width=1)
|
|
|
- # 在内侧20px处添加小同心圆
|
|
|
- inner_radius = radius - 20 * self.scale
|
|
|
+ # 在内侧40px处添加小同心圆
|
|
|
+ inner_radius = radius - 40 * self.scale
|
|
|
if inner_radius > 0:
|
|
|
self.canvas.create_oval(root_x - inner_radius, root_y - inner_radius, root_x + inner_radius, root_y + inner_radius, outline="#666666", width=1)
|
|
|
|
|
|
@@ -433,13 +498,76 @@ class FolderTree3D:
|
|
|
def show_3d_tree(self):
|
|
|
folder_path = self.path_var.get()
|
|
|
if not folder_path or not os.path.isdir(folder_path):
|
|
|
- tk.messagebox.showerror("错误", "请选择有效的文件夹路径")
|
|
|
+ messagebox.showerror("错误", "请选择有效的文件夹路径")
|
|
|
return
|
|
|
+
|
|
|
+ # 添加到历史记录
|
|
|
+ self.add_to_history(folder_path)
|
|
|
|
|
|
self.folder_data = {}
|
|
|
self.max_depth = 0
|
|
|
self.scan_folder(folder_path)
|
|
|
self.draw_3d_tree()
|
|
|
+
|
|
|
+ def add_to_history(self, folder_path):
|
|
|
+ # 如果路径已在历史记录中,先移除
|
|
|
+ if folder_path in self.history:
|
|
|
+ self.history.remove(folder_path)
|
|
|
+
|
|
|
+ # 添加到历史记录开头
|
|
|
+ self.history.insert(0, folder_path)
|
|
|
+
|
|
|
+ # 限制历史记录数量
|
|
|
+ if len(self.history) > self.max_history:
|
|
|
+ self.history = self.history[:self.max_history]
|
|
|
+
|
|
|
+ # 更新历史记录显示
|
|
|
+ self.update_history_display()
|
|
|
+
|
|
|
+ # 保存历史记录到文件
|
|
|
+ self.save_history()
|
|
|
+
|
|
|
+ def update_history_display(self):
|
|
|
+ # 更新历史记录按钮
|
|
|
+ for i in range(self.max_history):
|
|
|
+ if i < len(self.history):
|
|
|
+ # 显示路径的最后部分
|
|
|
+ path_parts = self.history[i].split(os.sep)
|
|
|
+ display_text = os.sep.join(path_parts[-3:])
|
|
|
+ if len(display_text) > 28:
|
|
|
+ display_text = "..." + display_text[-25:]
|
|
|
+ self.history_buttons[i].config(text=display_text, state=tk.NORMAL)
|
|
|
+ else:
|
|
|
+ self.history_buttons[i].config(text="", state=tk.DISABLED)
|
|
|
+
|
|
|
+ def select_history(self, index):
|
|
|
+ if index < len(self.history):
|
|
|
+ folder_path = self.history[index]
|
|
|
+ self.path_var.set(folder_path)
|
|
|
+ # 重新显示树状图
|
|
|
+ self.show_3d_tree()
|
|
|
+
|
|
|
+ def load_history(self):
|
|
|
+ """加载历史记录"""
|
|
|
+ try:
|
|
|
+ if os.path.exists(self.history_file):
|
|
|
+ with open(self.history_file, 'r', encoding='utf-8') as f:
|
|
|
+ lines = f.readlines()
|
|
|
+ self.history = [line.strip() for line in lines if line.strip()]
|
|
|
+ # 限制历史记录数量
|
|
|
+ if len(self.history) > self.max_history:
|
|
|
+ self.history = self.history[:self.max_history]
|
|
|
+ except Exception as e:
|
|
|
+ print(f"加载历史记录出错: {e}")
|
|
|
+
|
|
|
+ def save_history(self):
|
|
|
+ """保存历史记录"""
|
|
|
+ try:
|
|
|
+ with open(self.history_file, 'w', encoding='utf-8') as f:
|
|
|
+ for path in self.history:
|
|
|
+ f.write(path + '\n')
|
|
|
+ except Exception as e:
|
|
|
+ print(f"保存历史记录出错: {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
root = tk.Tk()
|