|
@@ -9,9 +9,65 @@ except ImportError:
|
|
|
clipboard_available = False
|
|
clipboard_available = False
|
|
|
print("警告: pyperclip 模块未安装,剪贴板功能不可用。请运行 'pip install pyperclip' 安装。")
|
|
print("警告: pyperclip 模块未安装,剪贴板功能不可用。请运行 'pip install pyperclip' 安装。")
|
|
|
|
|
|
|
|
|
|
+# 尝试导入 pika 库用于 RabbitMQ 通信
|
|
|
|
|
+try:
|
|
|
|
|
+ import pika
|
|
|
|
|
+ rabbitmq_available = True
|
|
|
|
|
+except ImportError:
|
|
|
|
|
+ rabbitmq_available = False
|
|
|
|
|
+ print("警告: pika 模块未安装,RabbitMQ 通信功能不可用。请运行 'pip install pika' 安装。")
|
|
|
|
|
+
|
|
|
from pygame.locals import *
|
|
from pygame.locals import *
|
|
|
from OpenGL.GL import *
|
|
from OpenGL.GL import *
|
|
|
from OpenGL.GLU import *
|
|
from OpenGL.GLU import *
|
|
|
|
|
+from OpenGL.GLUT import *
|
|
|
|
|
+
|
|
|
|
|
+EXCLUDED_DIR = ['.git', '.vscode', '.idea', 'node_modules', '__pycache__']
|
|
|
|
|
+
|
|
|
|
|
+# 初始化字体
|
|
|
|
|
+pygame.font.init()
|
|
|
|
|
+# 使用默认字体,增大字体大小
|
|
|
|
|
+font = pygame.font.Font(None, 24)
|
|
|
|
|
+
|
|
|
|
|
+# 初始化 GLUT
|
|
|
|
|
+glutInit()
|
|
|
|
|
+
|
|
|
|
|
+# RabbitMQ 配置
|
|
|
|
|
+RABBITMQ_HOST = '101.201.78.54'
|
|
|
|
|
+RABBITMQ_PORT = 5672
|
|
|
|
|
+RABBITMQ_QUEUE = 'file_matches'
|
|
|
|
|
+RABBITMQ_USERNAME = 'admin' # 默认用户名
|
|
|
|
|
+RABBITMQ_PASSWORD = 'zpq123456' # 默认密码
|
|
|
|
|
+
|
|
|
|
|
+# 发送消息到 RabbitMQ
|
|
|
|
|
+def send_to_rabbitmq(message):
|
|
|
|
|
+ """发送消息到 RabbitMQ"""
|
|
|
|
|
+ if not rabbitmq_available:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 建立连接
|
|
|
|
|
+ credentials = pika.PlainCredentials(RABBITMQ_USERNAME, RABBITMQ_PASSWORD)
|
|
|
|
|
+ connection = pika.BlockingConnection(pika.ConnectionParameters(
|
|
|
|
|
+ host=RABBITMQ_HOST, port=RABBITMQ_PORT, credentials=credentials
|
|
|
|
|
+ ))
|
|
|
|
|
+ channel = connection.channel()
|
|
|
|
|
+
|
|
|
|
|
+ # 声明队列(使用持久化队列,避免弃用警告)
|
|
|
|
|
+ channel.queue_declare(queue=RABBITMQ_QUEUE, durable=True)
|
|
|
|
|
+
|
|
|
|
|
+ # 发送消息(不使用 BasicProperties,简化发送方式)
|
|
|
|
|
+ channel.basic_publish(
|
|
|
|
|
+ exchange='',
|
|
|
|
|
+ routing_key=RABBITMQ_QUEUE,
|
|
|
|
|
+ body=message.encode('utf-8')
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # 关闭连接
|
|
|
|
|
+ connection.close()
|
|
|
|
|
+ print(f"消息发送成功: {message}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"发送消息到 RabbitMQ 失败: {e}")
|
|
|
|
|
|
|
|
# 模拟模式开关(True: 使用模拟数据,False: 扫描真实目录)
|
|
# 模拟模式开关(True: 使用模拟数据,False: 扫描真实目录)
|
|
|
# USE_MOCK_DATA = True
|
|
# USE_MOCK_DATA = True
|
|
@@ -156,7 +212,7 @@ def build_directory_tree(root_path, max_depth=3, max_children_per_node=30):
|
|
|
filtered_entries = []
|
|
filtered_entries = []
|
|
|
for entry in entries:
|
|
for entry in entries:
|
|
|
# 过滤常见的版本控制和IDE目录
|
|
# 过滤常见的版本控制和IDE目录
|
|
|
- if entry in ['.git', '.vscode', '.idea', 'node_modules', '__pycache__']:
|
|
|
|
|
|
|
+ if entry in EXCLUDED_DIR:
|
|
|
continue
|
|
continue
|
|
|
filtered_entries.append(entry)
|
|
filtered_entries.append(entry)
|
|
|
|
|
|
|
@@ -209,19 +265,26 @@ def build_directory_tree(root_path, max_depth=3, max_children_per_node=30):
|
|
|
|
|
|
|
|
# 计算当前节点位置
|
|
# 计算当前节点位置
|
|
|
if node_id == 0: # 根节点
|
|
if node_id == 0: # 根节点
|
|
|
- positions[node_id] = [0.0, max_depth_found * 0.4, 0.0] # 顶部,高度减半
|
|
|
|
|
|
|
+ # positions[node_id] = [0.0, max_depth_found * 0.4, 0.0] # 顶部,高度减半
|
|
|
|
|
+ positions[node_id] = [0.0, max_depth_found * 0.6, 0.0] # 顶部,高度减半
|
|
|
else:
|
|
else:
|
|
|
parent_id = nodes[node_id][2]
|
|
parent_id = nodes[node_id][2]
|
|
|
if parent_id >= 0:
|
|
if parent_id >= 0:
|
|
|
# 子节点围绕父节点在圆上分布
|
|
# 子节点围绕父节点在圆上分布
|
|
|
child_index = children[parent_id].index(node_id)
|
|
child_index = children[parent_id].index(node_id)
|
|
|
|
|
+ # 用于表示某个节点的兄弟节点数量
|
|
|
num_siblings = len(children[parent_id])
|
|
num_siblings = len(children[parent_id])
|
|
|
|
|
|
|
|
# 计算角度(在父节点周围的圆上)
|
|
# 计算角度(在父节点周围的圆上)
|
|
|
|
|
+ # 这行代码确保多个子节点在指定的角度范围内均匀分布,避免它们在3D空间中重叠或拥挤。例如,如果起始角度是0度,结束角度是360度,有4个子节点,那么它们的角度会分别是0度、90度、180度和270度,均匀分布在圆周上
|
|
|
angle = start_angle + (end_angle - start_angle) * (child_index / max(num_siblings, 1))
|
|
angle = start_angle + (end_angle - start_angle) * (child_index / max(num_siblings, 1))
|
|
|
|
|
|
|
|
# 半径随深度减小
|
|
# 半径随深度减小
|
|
|
- radius = 0.75 * (0.75 ** depth) # 深度越大,半径越小,基础半径减半
|
|
|
|
|
|
|
+ # radius = 0.75 * (0.75 ** depth) # 深度越大,半径越小,基础半径减半
|
|
|
|
|
+ if depth == 1:
|
|
|
|
|
+ radius = 1.5
|
|
|
|
|
+ else:
|
|
|
|
|
+ radius = 0.75 * (0.75 ** depth) # 深度越大,半径越小,基础半径减半
|
|
|
|
|
|
|
|
# 计算位置
|
|
# 计算位置
|
|
|
px, py, pz = positions[parent_id]
|
|
px, py, pz = positions[parent_id]
|
|
@@ -255,6 +318,40 @@ root_path = "E:\\agricultural_research_platform"
|
|
|
|
|
|
|
|
tree_nodes, tree_edges, node_positions, tree_children = build_directory_tree(root_path)
|
|
tree_nodes, tree_edges, node_positions, tree_children = build_directory_tree(root_path)
|
|
|
|
|
|
|
|
|
|
+def draw_text(x, y, z, text, color=(1.0, 1.0, 1.0)):
|
|
|
|
|
+ """在3D空间中绘制文本"""
|
|
|
|
|
+ glDisable(GL_LIGHTING)
|
|
|
|
|
+ glColor3f(*color) # 使用指定的颜色
|
|
|
|
|
+
|
|
|
|
|
+ # 保存当前矩阵
|
|
|
|
|
+ glPushMatrix()
|
|
|
|
|
+
|
|
|
|
|
+ # 移动到指定位置
|
|
|
|
|
+ glTranslatef(x, y, z)
|
|
|
|
|
+
|
|
|
|
|
+ # 旋转文本,使其面向相机
|
|
|
|
|
+ glRotatef(rotation_angle, 0.0, 1.0, 0.0)
|
|
|
|
|
+
|
|
|
|
|
+ # 缩小文本,使其适合3D空间
|
|
|
|
|
+ scale = 0.001
|
|
|
|
|
+ glScalef(scale, scale, scale)
|
|
|
|
|
+
|
|
|
|
|
+ # 计算文本宽度,以便居中显示
|
|
|
|
|
+ text_width = 0
|
|
|
|
|
+ for char in text:
|
|
|
|
|
+ text_width += glutBitmapWidth(GLUT_BITMAP_HELVETICA_12, ord(char))
|
|
|
|
|
+
|
|
|
|
|
+ # 设置文本位置,使其居中
|
|
|
|
|
+ glRasterPos2f(-text_width / 2, 0)
|
|
|
|
|
+
|
|
|
|
|
+ # 绘制文本
|
|
|
|
|
+ for char in text:
|
|
|
|
|
+ glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, ord(char))
|
|
|
|
|
+
|
|
|
|
|
+ # 恢复矩阵
|
|
|
|
|
+ glPopMatrix()
|
|
|
|
|
+ glEnable(GL_LIGHTING)
|
|
|
|
|
+
|
|
|
# 初始化选中节点(根节点)
|
|
# 初始化选中节点(根节点)
|
|
|
selected_node_index = 0
|
|
selected_node_index = 0
|
|
|
|
|
|
|
@@ -268,14 +365,17 @@ blink_start_time = pygame.time.get_ticks()
|
|
|
# 旋转控制变量
|
|
# 旋转控制变量
|
|
|
rotation_angle = 0.0
|
|
rotation_angle = 0.0
|
|
|
|
|
|
|
|
|
|
+# 相机控制变量
|
|
|
|
|
+camera_z = -5 # 相机的 Z 轴位置,初始值为 -5
|
|
|
|
|
+
|
|
|
# 主循环
|
|
# 主循环
|
|
|
running = True
|
|
running = True
|
|
|
while running:
|
|
while running:
|
|
|
# 获取当前时间(用于闪烁效果)
|
|
# 获取当前时间(用于闪烁效果)
|
|
|
current_time = pygame.time.get_ticks()
|
|
current_time = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
- # 更新旋转角度(每10秒旋转一圈)
|
|
|
|
|
- rotation_angle = (current_time * 0.036) % 360.0
|
|
|
|
|
|
|
+ # 更新旋转角度(每30秒旋转一圈)
|
|
|
|
|
+ rotation_angle = (current_time * 0.012) % 360.0
|
|
|
|
|
|
|
|
# 检查剪贴板内容是否变化(如果剪贴板功能可用)
|
|
# 检查剪贴板内容是否变化(如果剪贴板功能可用)
|
|
|
if clipboard_available:
|
|
if clipboard_available:
|
|
@@ -299,6 +399,8 @@ while running:
|
|
|
file_content = file_content_cache[i]
|
|
file_content = file_content_cache[i]
|
|
|
if clipboard_content in file_content:
|
|
if clipboard_content in file_content:
|
|
|
blinking_nodes.add(i)
|
|
blinking_nodes.add(i)
|
|
|
|
|
+ # 发送文件名称到 RabbitMQ
|
|
|
|
|
+ send_to_rabbitmq(name)
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
pass # 忽略读取错误
|
|
pass # 忽略读取错误
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -336,17 +438,25 @@ while running:
|
|
|
# 移动到后一个兄弟,如果当前是最后一个则移动到第一个
|
|
# 移动到后一个兄弟,如果当前是最后一个则移动到第一个
|
|
|
new_index = (current_index_in_siblings + 1) % len(siblings)
|
|
new_index = (current_index_in_siblings + 1) % len(siblings)
|
|
|
selected_node_index = siblings[new_index]
|
|
selected_node_index = siblings[new_index]
|
|
|
|
|
+ elif event.type == pygame.MOUSEBUTTONDOWN:
|
|
|
|
|
+ if event.button == 4: # 鼠标滚轮向上滚动,放大
|
|
|
|
|
+ camera_z += 0.5 # 相机向Z轴正方向移动,放大视图
|
|
|
|
|
+ elif event.button == 5: # 鼠标滚轮向下滚动,缩小
|
|
|
|
|
+ camera_z -= 0.5 # 相机向Z轴负方向移动,缩小视图
|
|
|
|
|
+
|
|
|
|
|
+ # 限制相机的Z轴范围,防止过度放大或缩小
|
|
|
|
|
+ camera_z = max(-10, min(-2, camera_z))
|
|
|
|
|
|
|
|
# 清除屏幕
|
|
# 清除屏幕
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
|
|
|
|
|
|
|
# 重置模型视图矩阵并设置相机位置
|
|
# 重置模型视图矩阵并设置相机位置
|
|
|
glLoadIdentity()
|
|
glLoadIdentity()
|
|
|
- glTranslatef(0.0, 0.0, -5) # 相机向后移动
|
|
|
|
|
|
|
+ glTranslatef(0.0, 0.0, camera_z) # 相机位置,可通过鼠标滚轮调整
|
|
|
glRotatef(rotation_angle, 0.0, 1.0, 0.0) # 绕竖直中线/Y轴旋转
|
|
glRotatef(rotation_angle, 0.0, 1.0, 0.0) # 绕竖直中线/Y轴旋转
|
|
|
|
|
|
|
|
# 目录树可视化
|
|
# 目录树可视化
|
|
|
- ball_radius = 0.0667 # 节点球的半径(原来的1/3)
|
|
|
|
|
|
|
+ ball_radius = 0.0667 # 节点球的半径(默认大小)
|
|
|
|
|
|
|
|
# 绘制所有连线(目录树边)
|
|
# 绘制所有连线(目录树边)
|
|
|
glDisable(GL_LIGHTING) # 禁用光照以使用纯色
|
|
glDisable(GL_LIGHTING) # 禁用光照以使用纯色
|
|
@@ -407,11 +517,20 @@ while running:
|
|
|
glTranslatef(x, y, z)
|
|
glTranslatef(x, y, z)
|
|
|
quad = gluNewQuadric()
|
|
quad = gluNewQuadric()
|
|
|
gluQuadricNormals(quad, GLU_SMOOTH)
|
|
gluQuadricNormals(quad, GLU_SMOOTH)
|
|
|
- # 根据深度调整球体大小:深度越大,球体越小
|
|
|
|
|
- current_radius = ball_radius * (0.85 ** depth)
|
|
|
|
|
|
|
+ # 根据深度和节点类型调整球体大小:深度越大,球体越小;文件节点的半径缩小为原来的1/3,文件夹节点的半径缩小为原来的1/2
|
|
|
|
|
+ if is_dir:
|
|
|
|
|
+ current_radius = (ball_radius / 2) * (0.85 ** depth) # 文件夹节点的半径缩小为原来的1/2
|
|
|
|
|
+ else:
|
|
|
|
|
+ current_radius = (ball_radius / 3) * (0.85 ** depth) # 文件节点的半径缩小为原来的1/3
|
|
|
gluSphere(quad, current_radius, 32, 32)
|
|
gluSphere(quad, current_radius, 32, 32)
|
|
|
gluDeleteQuadric(quad)
|
|
gluDeleteQuadric(quad)
|
|
|
glPopMatrix()
|
|
glPopMatrix()
|
|
|
|
|
+
|
|
|
|
|
+ # 绘制文件名
|
|
|
|
|
+ if i in blinking_nodes: # 闪烁节点使用红色文本
|
|
|
|
|
+ draw_text(x, y + current_radius + 0.05, z, name, color=(1.0, 0.0, 0.0)) # 红色文本
|
|
|
|
|
+ else:
|
|
|
|
|
+ draw_text(x, y + current_radius + 0.05, z, name) # 默认白色文本
|
|
|
|
|
|
|
|
# 交换缓冲区
|
|
# 交换缓冲区
|
|
|
pygame.display.flip()
|
|
pygame.display.flip()
|