zhong (钟鹏群) 2 dienas atpakaļ
vecāks
revīzija
48ea7c4d78
4 mainītis faili ar 435 papildinājumiem un 28 dzēšanām
  1. 156 28
      folder_tree_3d.py
  2. 1 0
      history.txt
  3. 102 0
      opengl3d-demo.py
  4. 176 0
      opengl3d.py

+ 156 - 28
folder_tree_3d.py

@@ -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()

+ 1 - 0
history.txt

@@ -0,0 +1 @@
+D:\zlgitlab\昊杰

+ 102 - 0
opengl3d-demo.py

@@ -0,0 +1,102 @@
+import pygame
+import math
+from pygame.locals import *
+from OpenGL.GL import *
+from OpenGL.GLU import *
+
+# 初始化 Pygame
+pygame.init()
+
+# 设置窗口大小
+width, height = 800, 600
+display = pygame.display.set_mode((width, height), DOUBLEBUF | OPENGL)
+pygame.display.set_caption('3D Sphere')
+
+# 启用深度测试
+glEnable(GL_DEPTH_TEST)
+
+# 设置光照
+glEnable(GL_LIGHTING)
+glEnable(GL_LIGHT0)
+
+# 设置光源位置
+light_position = [1.0, 1.0, 1.0, 0.0]  #  directional light
+glLightfv(GL_LIGHT0, GL_POSITION, light_position)
+
+# 设置材质属性
+material_ambient = [0.3, 0.7, 0.3, 1.0]
+material_specular = [0.3, 0.7, 0.3, 1.0]
+material_shininess = [50.0]
+
+glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient)
+glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular)
+glMaterialfv(GL_FRONT, GL_SHININESS, material_shininess)
+
+# 设置视角
+gluPerspective(45, (width/height), 0.1, 50.0)
+# 移动相机
+glTranslatef(0.0, 0.0, -5)
+
+# 初始化3个绿球的角度
+ball_angles = [0, 120, 240]  # 三个绿球的初始角度
+ball_radius = 0.5  # 绿球的半径
+distance = 2.5  # 绿球到黄球的距离
+
+# 主循环
+running = True
+while running:
+    # 处理事件
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            running = False
+    
+    # 清除屏幕
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+    
+    # 旋转整个场景
+    glRotatef(1, 3, 1, 1)
+    
+    # 绘制黄球(中心球)
+    glMaterialfv(GL_FRONT, GL_DIFFUSE, [1.0, 1.0, 0.0, 1.0])  # 黄色
+    quad = gluNewQuadric()
+    gluQuadricNormals(quad, GLU_SMOOTH)
+    gluSphere(quad, 1, 32, 32)
+    gluDeleteQuadric(quad)
+    
+    # 绘制3个绿球和连线
+    glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.0, 1.0, 0.0, 1.0])  # 绿色
+    for i, angle in enumerate(ball_angles):
+        # 计算绿球位置
+        angle_rad = math.radians(angle)
+        x = distance * math.cos(angle_rad)
+        y = distance * math.sin(angle_rad)
+        z = 0
+        
+        # 绘制连线(连接到中心黄球)
+        glBegin(GL_LINES)
+        glVertex3f(0, 0, 0)  # 中心黄球位置
+        glVertex3f(x, y, z)  # 绿球位置
+        glEnd()
+        
+        # 保存当前矩阵
+        glPushMatrix()
+        # 移动到绿球位置
+        glTranslatef(x, y, z)
+        # 绘制绿球
+        quad = gluNewQuadric()
+        gluQuadricNormals(quad, GLU_SMOOTH)
+        gluSphere(quad, ball_radius, 16, 16)
+        gluDeleteQuadric(quad)
+        # 恢复矩阵
+        glPopMatrix()
+        
+        # 更新绿球角度
+        ball_angles[i] = (angle + 1) % 360
+    
+    # 交换缓冲区
+    pygame.display.flip()
+    # 控制帧率
+    pygame.time.wait(10)
+
+# 退出 Pygame
+pygame.quit()

+ 176 - 0
opengl3d.py

@@ -0,0 +1,176 @@
+import pygame
+import math
+import os
+from pygame.locals import *
+from OpenGL.GL import *
+from OpenGL.GLU import *
+
+# 初始化 Pygame
+pygame.init()
+
+# 设置窗口大小
+width, height = 800, 600
+display = pygame.display.set_mode((width, height), DOUBLEBUF | OPENGL)
+pygame.display.set_caption('3D Sphere')
+
+# 初始化剪贴板系统
+pygame.scrap.init()
+
+# 启用深度测试
+glEnable(GL_DEPTH_TEST)
+
+# 设置光照
+glEnable(GL_LIGHTING)
+glEnable(GL_LIGHT0)
+
+# 设置光源位置
+light_position = [1.0, 1.0, 1.0, 0.0]  #  directional light
+glLightfv(GL_LIGHT0, GL_POSITION, light_position)
+
+# 设置材质属性
+material_ambient = [0.3, 0.7, 0.3, 1.0]
+material_diffuse = [0.0, 1.0, 0.0, 1.0]
+material_specular = [0.3, 0.7, 0.3, 1.0]
+material_shininess = [50.0]
+
+glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient)
+glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse)
+glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular)
+glMaterialfv(GL_FRONT, GL_SHININESS, material_shininess)
+
+# 设置视角
+gluPerspective(45, (width/height), 0.1, 50.0)
+# 移动相机
+glTranslatef(0.0, 0.0, -5)
+
+# 文件夹结构
+folder_structure = []
+show_structure = False
+
+# 检查剪贴板内容
+def check_clipboard():
+    clipboard = pygame.scrap.get('text/plain')
+    if clipboard:
+        path = clipboard.decode('utf-8').strip()
+        if os.path.exists(path) and os.path.isdir(path):
+            return path
+    return None
+
+# 扫描文件夹函数(递归)
+def scan_folder(path, parent_distance=2, parent_angle=0, depth=0, max_depth=3):
+    structure = []
+    if depth >= max_depth:
+        return structure
+    
+    if os.path.exists(path) and os.path.isdir(path):
+        items = os.listdir(path)
+        for i, item in enumerate(items):
+            item_path = os.path.join(path, item)
+            is_dir = os.path.isdir(item_path)
+            # 计算位置
+            angle = parent_angle + (i / len(items)) * 360
+            distance = parent_distance + 1
+            radius = 0.2 if is_dir else 0.1
+            
+            # 添加当前项目
+            structure.append({
+                'name': item,
+                'path': item_path,
+                'is_dir': is_dir,
+                'angle': angle,
+                'distance': distance,
+                'radius': radius,
+                'parent_distance': parent_distance,
+                'parent_angle': parent_angle
+            })
+            
+            # 递归扫描子文件夹
+            if is_dir:
+                structure.extend(scan_folder(item_path, distance, angle, depth + 1, max_depth))
+    return structure
+
+# 使用默认路径 E:\myaliyun\IamHere
+default_path = "E:\\myaliyun\\IamHere"
+if os.path.exists(default_path) and os.path.isdir(default_path):
+    folder_structure = scan_folder(default_path)
+    show_structure = True
+else:
+    # 启动时检查剪贴板
+    clipboard_path = check_clipboard()
+    if clipboard_path:
+        folder_structure = scan_folder(clipboard_path)
+        show_structure = True
+
+# 主循环
+running = True
+while running:
+    # 处理事件
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            running = False
+    
+    # 清除屏幕
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+    
+    # 旋转大球
+    glRotatef(1, 3, 1, 1)
+    
+    # 绘制大球
+    quad = gluNewQuadric()
+    gluQuadricNormals(quad, GLU_SMOOTH)
+    gluSphere(quad, 1, 32, 32)
+    gluDeleteQuadric(quad)
+    
+
+    print(folder_structure)
+
+    # 绘制文件夹结构
+    if show_structure:
+        for item in folder_structure:
+            # 计算当前项目位置
+            angle_rad = math.radians(item['angle'])
+            x = item['distance'] * math.cos(angle_rad)
+            y = item['distance'] * math.sin(angle_rad)
+            z = 0
+            
+            # 计算父项目位置
+            parent_angle_rad = math.radians(item['parent_angle'])
+            parent_x = item['parent_distance'] * math.cos(parent_angle_rad)
+            parent_y = item['parent_distance'] * math.sin(parent_angle_rad)
+            parent_z = 0
+            
+            # 绘制连线(连接到父项目)
+            glBegin(GL_LINES)
+            glVertex3f(parent_x, parent_y, parent_z)  # 父项目位置
+            glVertex3f(x, y, z)  # 当前项目位置
+            glEnd()
+            
+            # 保存当前矩阵
+            glPushMatrix()
+            # 移动到当前项目位置
+            glTranslatef(x, y, z)
+            # 设置材质颜色(文件夹为绿色,文件为蓝色)
+            if item['is_dir']:
+                glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.0, 1.0, 0.0, 1.0])
+            else:
+                glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.0, 0.0, 1.0, 1.0])
+            # 绘制小球
+            quad = gluNewQuadric()
+            gluQuadricNormals(quad, GLU_SMOOTH)
+            gluSphere(quad, item['radius'], 16, 16)
+            gluDeleteQuadric(quad)
+            # 恢复材质颜色
+            glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse)
+            # 恢复矩阵
+            glPopMatrix()
+            
+            # 更新角度
+            item['angle'] = (item['angle'] + 1) % 360
+    
+    # 交换缓冲区
+    pygame.display.flip()
+    # 控制帧率
+    pygame.time.wait(10)
+
+# 退出 Pygame
+pygame.quit()