forked from Rowland/EG
1094 lines
42 KiB
Python
1094 lines
42 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
维修系统场景GUI界面
|
||
包括步骤显示、工具选择按钮和警告提示
|
||
"""
|
||
|
||
from direct.gui.DirectGui import *
|
||
from direct.gui import DirectGuiGlobals as DGG
|
||
from direct.showbase.DirectObject import DirectObject
|
||
from direct.task import Task
|
||
from direct.task.TaskManagerGlobal import taskMgr
|
||
from panda3d.core import TextNode, Vec3, Vec4
|
||
|
||
# 引入全局变量
|
||
import builtins
|
||
|
||
|
||
class MaintenanceGUI(DirectObject):
|
||
"""维修系统GUI管理器"""
|
||
|
||
def __init__(self, world):
|
||
DirectObject.__init__(self)
|
||
self.world = world
|
||
|
||
self.chinese_font = None
|
||
if hasattr(self.world, 'getChineseFont'):
|
||
self.chinese_font = self.world.getChineseFont()
|
||
|
||
# 获取GUI父节点引用
|
||
self.aspect2d = None
|
||
print(f"🔍 初始化维修GUI,world类型: {type(world).__name__}")
|
||
print(f"🔍 world是否有aspect2d: {hasattr(world, 'aspect2d')}")
|
||
|
||
if hasattr(world, 'aspect2d'):
|
||
self.aspect2d = world.aspect2d
|
||
print("✅ 从world获取aspect2d成功")
|
||
else:
|
||
print("❌ world没有aspect2d属性")
|
||
|
||
# GUI元素
|
||
self.step_text = None # 步骤显示文本
|
||
self.current_tool_text = None # 当前工具显示文本
|
||
self.warning_text = None # 警告文本
|
||
self.tool_buttons = [] # 工具按钮列表
|
||
self.stop_button = None # 停止按钮
|
||
|
||
# 状态
|
||
self.current_tool = None # 当前选择的工具,初始为无
|
||
self.available_tools = [] # 可用工具列表
|
||
self.current_step_info = "" # 当前步骤信息
|
||
self.mode = "training" # 模式:training 或 exam
|
||
|
||
# 警告显示任务
|
||
self.warning_task = None
|
||
|
||
print("✅ 维修系统GUI初始化完成")
|
||
|
||
# 添加测试快捷键
|
||
self.accept("t", self.test_tool_selection) # 按T键测试工具选择
|
||
self.accept("1", lambda: self.test_specific_button(0)) # 按1键测试第一个按钮
|
||
self.accept("2", lambda: self.test_specific_button(1)) # 按2键测试第二个按钮
|
||
self.accept("3", lambda: self.test_specific_button(2)) # 按3键测试第三个按钮
|
||
self.accept("p", self.print_button_positions) # 按P键打印按钮位置
|
||
|
||
# 添加鼠标监控任务
|
||
self.mouse_monitor_task = None
|
||
|
||
def setup_gui(self, tools_list, mode="training"):
|
||
"""设置GUI界面"""
|
||
try:
|
||
print(f"🎨 设置维修系统GUI,工具列表: {tools_list}, 模式: {mode}")
|
||
|
||
self.available_tools = tools_list if tools_list else ["手"]
|
||
self.mode = mode
|
||
|
||
# 重新尝试获取aspect2d引用
|
||
self.get_aspect2d_reference()
|
||
|
||
# 清理现有GUI
|
||
self.cleanup_gui()
|
||
|
||
# 创建步骤显示文本
|
||
self.create_step_text()
|
||
|
||
# 创建工具选择按钮(训练和考核模式都需要)
|
||
self.create_tool_buttons()
|
||
self.create_current_tool_text()
|
||
|
||
# 创建停止按钮(训练和考核模式都需要)
|
||
# self.create_stop_button()
|
||
|
||
# 创建警告文本(仅训练模式需要)
|
||
if mode == "training":
|
||
self.create_warning_text()
|
||
|
||
print("✅ 维修系统GUI设置完成")
|
||
|
||
# 启动鼠标监控任务(训练和考核模式都需要)
|
||
self.start_mouse_monitor()
|
||
|
||
except Exception as e:
|
||
print(f"❌ 设置维修系统GUI失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def start_mouse_monitor(self):
|
||
"""启动鼠标监控任务"""
|
||
try:
|
||
# 停止可能存在的旧任务
|
||
if self.mouse_monitor_task:
|
||
taskMgr.remove(self.mouse_monitor_task)
|
||
|
||
# 启动新的监控任务
|
||
self.mouse_monitor_task = taskMgr.add(self.monitor_mouse_clicks, "mouse_monitor")
|
||
print("✅ 鼠标监控任务已启动")
|
||
print(f"✅ 任务管理器中的任务: {[task.getName() for task in taskMgr.getAllTasks()]}")
|
||
|
||
# 测试一下任务是否能正常运行
|
||
self.test_mouse_monitor()
|
||
|
||
except Exception as e:
|
||
print(f"❌ 启动鼠标监控失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def test_mouse_monitor(self):
|
||
"""测试鼠标监控功能"""
|
||
try:
|
||
print("🧪 测试鼠标监控功能...")
|
||
|
||
# 尝试直接调用监控函数
|
||
result = self.monitor_mouse_clicks(None)
|
||
print(f"🧪 监控函数调用结果: {result}")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 测试鼠标监控失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def monitor_mouse_clicks(self, task):
|
||
"""监控鼠标点击并手动检测按钮"""
|
||
try:
|
||
# 添加计数器来减少日志输出
|
||
if not hasattr(self, '_monitor_counter'):
|
||
self._monitor_counter = 0
|
||
self._monitor_counter += 1
|
||
|
||
# 每100帧输出一次状态
|
||
if self._monitor_counter % 100 == 1:
|
||
print(f"🔄 鼠标监控运行中... (帧 {self._monitor_counter})")
|
||
|
||
# 检查鼠标状态
|
||
mouse_watcher = None
|
||
if hasattr(self.world, 'mouseWatcherNode'):
|
||
mouse_watcher = self.world.mouseWatcherNode
|
||
if self._monitor_counter % 100 == 1:
|
||
print(f"✅ 从world获取mouseWatcherNode: {mouse_watcher}")
|
||
else:
|
||
# 使用全局base对象
|
||
try:
|
||
import builtins
|
||
if hasattr(builtins, 'base') and hasattr(builtins.base, 'mouseWatcherNode'):
|
||
mouse_watcher = builtins.base.mouseWatcherNode
|
||
if self._monitor_counter % 100 == 1:
|
||
print(f"✅ 从base获取mouseWatcherNode: {mouse_watcher}")
|
||
except:
|
||
if self._monitor_counter % 100 == 1:
|
||
print("❌ 无法获取mouseWatcherNode")
|
||
|
||
if mouse_watcher:
|
||
has_mouse = mouse_watcher.hasMouse()
|
||
if has_mouse:
|
||
mouse_pos = mouse_watcher.getMouse()
|
||
|
||
# 检查左键状态
|
||
mouse_x = mouse_pos.getX()
|
||
mouse_y = mouse_pos.getY()
|
||
|
||
# 检查左键是否被按下
|
||
is_button_down = mouse_watcher.isButtonDown('mouse1')
|
||
|
||
# 如果没有记录当前状态,初始化
|
||
if not hasattr(self, '_last_button_state'):
|
||
self._last_button_state = False
|
||
|
||
# 检测按钮从未按下到按下的状态变化(按下瞬间)
|
||
if is_button_down and not self._last_button_state:
|
||
print(f"🖱️ 鼠标监控:检测到按钮按下,位置({mouse_x:.3f}, {mouse_y:.3f})")
|
||
|
||
# 检查这次点击是否在按钮区域
|
||
clicked_button = self.check_button_click(mouse_x, mouse_y)
|
||
if clicked_button is not None:
|
||
print(f"🎯 鼠标监控检测到按钮点击: {clicked_button}")
|
||
self.on_tool_selected(clicked_button)
|
||
|
||
# 更新按钮状态
|
||
self._last_button_state = is_button_down
|
||
else:
|
||
if self._monitor_counter % 100 == 1:
|
||
print("❌ mouseWatcher为空")
|
||
|
||
return task.cont if task else None
|
||
|
||
except Exception as e:
|
||
print(f"❌ 鼠标监控异常: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return task.cont if task else None
|
||
|
||
def check_button_click(self, mouse_x, mouse_y):
|
||
"""检查鼠标点击是否在按钮区域"""
|
||
try:
|
||
button_width = 0.4 # 与create_tool_buttons保持一致
|
||
button_height = 0.25 # 与create_tool_buttons保持一致
|
||
button_spacing = 0.45 # 与create_tool_buttons保持一致
|
||
start_x = -0.8 # 与create_tool_buttons保持一致
|
||
start_y = -0.75 # 与create_tool_buttons保持一致
|
||
|
||
print(f"🔍 检查按钮点击:鼠标({mouse_x:.3f}, {mouse_y:.3f})")
|
||
|
||
for i, tool_data in enumerate(self.available_tools):
|
||
# 获取工具名称
|
||
if isinstance(tool_data, dict):
|
||
tool_name = tool_data.get('name', str(tool_data))
|
||
else:
|
||
tool_name = str(tool_data)
|
||
|
||
# 计算按钮位置
|
||
button_x = start_x + i * button_spacing
|
||
button_y = start_y
|
||
|
||
# 计算按钮边界
|
||
left = button_x - button_width/2
|
||
right = button_x + button_width/2
|
||
bottom = button_y - button_height/2
|
||
top = button_y + button_height/2
|
||
|
||
print(f" 按钮{i} '{tool_name}': 中心({button_x:.3f}, {button_y:.3f}), 范围x[{left:.3f}, {right:.3f}], y[{bottom:.3f}, {top:.3f}]")
|
||
|
||
# 检查鼠标是否在按钮范围内
|
||
if (left <= mouse_x <= right and bottom <= mouse_y <= top):
|
||
print(f"✅ 点击在按钮{i} '{tool_name}' 范围内")
|
||
return tool_name
|
||
|
||
print("❌ 点击不在任何按钮范围内")
|
||
return None
|
||
|
||
except Exception as e:
|
||
print(f"❌ 检查按钮点击异常: {e}")
|
||
return None
|
||
|
||
def handle_mouse_click(self, qt_x, qt_y):
|
||
"""直接处理鼠标点击(从拆装交互系统调用)"""
|
||
try:
|
||
print(f"🖱️ 直接处理鼠标点击:Qt坐标({qt_x}, {qt_y})")
|
||
|
||
# 将Qt坐标转换为aspect2d坐标
|
||
# 获取窗口尺寸
|
||
if hasattr(self.world, 'qtWidget') and self.world.qtWidget:
|
||
widget_width = self.world.qtWidget.width()
|
||
widget_height = self.world.qtWidget.height()
|
||
else:
|
||
widget_width = 1380
|
||
widget_height = 750
|
||
|
||
# 坐标转换(与之前拆装交互系统中的算法一致)
|
||
aspect_x = (qt_x / widget_width) * 2.67 - 1.33
|
||
aspect_y = 1.0 - (qt_y / widget_height) * 2.0
|
||
|
||
print(f"🔄 坐标转换:Qt({qt_x}, {qt_y}) -> aspect2d({aspect_x:.3f}, {aspect_y:.3f})")
|
||
|
||
# 检查是否点击在按钮区域
|
||
clicked_button = self.check_button_click(aspect_x, aspect_y)
|
||
if clicked_button:
|
||
print(f"🎯 直接处理:检测到按钮点击 '{clicked_button}'")
|
||
self.on_tool_selected(clicked_button)
|
||
return True
|
||
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"❌ 直接处理鼠标点击失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def get_aspect2d_reference(self):
|
||
"""获取aspect2d引用"""
|
||
if self.aspect2d:
|
||
return # 已经有引用了
|
||
|
||
try:
|
||
print("🔍 重新尝试获取aspect2d引用...")
|
||
|
||
# 方法1: 直接使用world对象(推荐)
|
||
if hasattr(self.world, 'aspect2d'):
|
||
self.aspect2d = self.world.aspect2d
|
||
print("✅ 从world获取aspect2d成功")
|
||
return
|
||
|
||
# 方法2: 使用world对象作为父节点
|
||
# 在main.py的Panda3DWorld中,world对象本身就是ShowBase的实例
|
||
self.aspect2d = self.world.aspect2d if hasattr(self.world, 'aspect2d') else None
|
||
|
||
if self.aspect2d:
|
||
print("✅ 使用world.aspect2d成功")
|
||
else:
|
||
print("❌ 无法获取aspect2d引用")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 获取aspect2d引用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_step_text(self):
|
||
"""创建步骤显示文本"""
|
||
try:
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建步骤文本")
|
||
return
|
||
|
||
self.step_text = DirectLabel(
|
||
text="准备开始维修训练...",
|
||
text_align=TextNode.ALeft,
|
||
text_scale=0.08,
|
||
text_fg=(1, 1, 1, 1),
|
||
text_bg=(0, 0, 0, 0.7),
|
||
frameColor=(0, 0, 0, 0.5),
|
||
frameSize=(-1.8, 1.8, -0.3, 0.3),
|
||
pos=(-1.7, 0, 0.8),
|
||
parent=self.aspect2d
|
||
)
|
||
print("✅ 步骤显示文本创建成功")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建步骤文本失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_tool_buttons(self):
|
||
"""创建工具选择按钮"""
|
||
try:
|
||
print(f"🔧 开始创建工具按钮...")
|
||
print(f" aspect2d引用: {self.aspect2d}")
|
||
print(f" 可用工具: {self.available_tools}")
|
||
print(f" 模式: {self.mode}")
|
||
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建工具按钮")
|
||
print(" 尝试重新获取aspect2d引用...")
|
||
self.get_aspect2d_reference()
|
||
if not self.aspect2d:
|
||
print("❌ 重新获取aspect2d失败")
|
||
return
|
||
|
||
print(f"🔧 使用aspect2d创建工具按钮: {self.available_tools}")
|
||
|
||
# 按钮布局参数(优化为更容易点击)
|
||
button_width = 0.4 # 增加宽度
|
||
button_height = 0.25 # 增加高度,让按钮更容易点击
|
||
button_spacing = 0.45 # 增加间距
|
||
start_x = -0.8 # 进一步向右移动
|
||
start_y = -0.75 # 向上移动一点
|
||
|
||
for i, tool in enumerate(self.available_tools):
|
||
# 处理工具数据格式
|
||
if isinstance(tool, dict):
|
||
tool_name = tool.get('name', str(tool))
|
||
tool_description = tool.get('description', '')
|
||
else:
|
||
tool_name = str(tool)
|
||
tool_description = ''
|
||
|
||
print(f" 正在创建按钮 {i+1}/{len(self.available_tools)}: {tool_name}")
|
||
|
||
# 计算按钮位置(横向排列)
|
||
pos_x = start_x + i * button_spacing
|
||
pos_y = start_y
|
||
|
||
# 创建按钮
|
||
button = DirectButton(
|
||
text=tool_name,
|
||
text_scale=0.06,
|
||
text_fg=(1, 1, 1, 1),
|
||
frameColor=(0.3, 0.3, 0.8, 0.8),
|
||
frameSize=(-button_width/2, button_width/2, -button_height/2, button_height/2),
|
||
pos=(pos_x, 0, pos_y),
|
||
command=self.on_tool_selected,
|
||
extraArgs=[tool_name],
|
||
parent=self.aspect2d,
|
||
# 按钮交互设置
|
||
relief=DGG.RAISED,
|
||
borderWidth=(0.01, 0.01),
|
||
text_pos=(0, -0.02),
|
||
# 强制启用鼠标交互
|
||
enableEdit=1,
|
||
# 音效设置
|
||
clickSound=None,
|
||
rolloverSound=None,
|
||
# 确保按钮优先级和可见性
|
||
sortOrder=1000, # 更高的排序值
|
||
state=DGG.NORMAL, # 确保按钮状态正常
|
||
# 鼠标事件设置
|
||
suppressMouse=0
|
||
)
|
||
|
||
# 手动绑定额外的事件监听
|
||
button.bind(DGG.B1PRESS, self.button_press_handler, extraArgs=[tool_name])
|
||
button.bind(DGG.B1RELEASE, self.button_release_handler, extraArgs=[tool_name])
|
||
|
||
self.tool_buttons.append(button)
|
||
print(f" ✅ 成功创建工具按钮: {tool_name} at ({pos_x}, {pos_y})")
|
||
|
||
print(f"🎯 总共创建了 {len(self.tool_buttons)} 个工具按钮")
|
||
|
||
# 设置默认选择的工具样式
|
||
self.update_tool_button_styles()
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建工具按钮失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_current_tool_text(self):
|
||
"""创建当前工具显示文本"""
|
||
try:
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建当前工具文本")
|
||
return
|
||
|
||
self.current_tool_text = DirectLabel(
|
||
text=f"Tool: {self.current_tool}",
|
||
text_align=TextNode.ALeft,
|
||
text_scale=0.07,
|
||
text_fg=(1, 1, 0, 1),
|
||
text_bg=(0, 0, 0, 0.7),
|
||
frameColor=(0, 0, 0, 0.5),
|
||
frameSize=(-0.8, 0.8, -0.2, 0.2),
|
||
pos=(1.0, 0, 0.8),
|
||
parent=self.aspect2d
|
||
)
|
||
print("✅ 当前工具文本创建成功")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建当前工具文本失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_stop_button(self):
|
||
"""创建停止按钮"""
|
||
try:
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建停止按钮")
|
||
return
|
||
|
||
# 停止按钮位置:右下角
|
||
self.stop_button = DirectButton(
|
||
text="停止",
|
||
text_align=TextNode.ACenter,
|
||
text_scale=0.06,
|
||
text_fg=(1, 1, 1, 1),
|
||
text_bg=(0, 0, 0, 0),
|
||
frameColor=(0.8, 0.2, 0.2, 0.9), # 红色背景
|
||
frameSize=(-0.15, 0.15, -0.08, 0.08),
|
||
pos=(1.15, 0, -0.85), # 右下角位置
|
||
parent=self.aspect2d,
|
||
command=self.on_stop_clicked,
|
||
relief=DGG.RAISED,
|
||
borderWidth=(0.01, 0.01),
|
||
rolloverSound=None,
|
||
clickSound=None,
|
||
pressEffect=1
|
||
)
|
||
|
||
print("✅ 停止按钮创建成功")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建停止按钮失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_warning_text(self):
|
||
"""创建警告文本"""
|
||
try:
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建警告文本")
|
||
return
|
||
|
||
self.warning_text = DirectLabel(
|
||
text="",
|
||
text_align=TextNode.ACenter,
|
||
text_scale=0.09,
|
||
text_fg=(1, 0, 0, 1),
|
||
text_bg=(1, 1, 0, 0.8),
|
||
frameColor=(1, 0, 0, 0.8),
|
||
frameSize=(-1.5, 1.5, -0.3, 0.3),
|
||
pos=(0, 0, 0.3),
|
||
parent=self.aspect2d
|
||
)
|
||
# 默认隐藏警告文本
|
||
self.warning_text.hide()
|
||
print("✅ 警告文本创建成功")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建警告文本失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def button_press_handler(self, tool_name, event=None):
|
||
"""按钮按下事件处理"""
|
||
print(f"🖱️ 按钮按下事件:{tool_name}")
|
||
|
||
def button_release_handler(self, tool_name, event=None):
|
||
"""按钮释放事件处理"""
|
||
print(f"🖱️ 按钮释放事件:{tool_name}")
|
||
print(f"🔄 直接调用工具选择:{tool_name}")
|
||
self.on_tool_selected(tool_name)
|
||
|
||
def on_tool_selected(self, tool):
|
||
"""工具选择回调"""
|
||
try:
|
||
print(f"🎯 按钮点击事件触发!")
|
||
print(f"🔧 选择工具: {tool}")
|
||
|
||
# 考核模式下额外提示
|
||
if hasattr(self, 'mode') and self.mode == "exam":
|
||
print(f"📝 考核模式工具选择: {tool}")
|
||
|
||
print(f"📍 当前工具变更: {self.current_tool} -> {tool}")
|
||
|
||
self.current_tool = tool
|
||
|
||
# 更新当前工具显示
|
||
if self.current_tool_text:
|
||
self.current_tool_text['text'] = f"Tool: {tool}"
|
||
print(f"✅ 当前工具显示已更新")
|
||
|
||
# 更新按钮样式
|
||
self.update_tool_button_styles()
|
||
print(f"✅ 按钮样式已更新")
|
||
|
||
# 通知拆装交互系统工具变更
|
||
if hasattr(self.world, 'assembly_interaction') and self.world.assembly_interaction:
|
||
if hasattr(self.world.assembly_interaction, 'step_dialog') and self.world.assembly_interaction.step_dialog:
|
||
# 更新步骤对话框中的工具选择
|
||
step_dialog = self.world.assembly_interaction.step_dialog
|
||
if hasattr(step_dialog, 'tool_combo'):
|
||
# 同步工具选择
|
||
tool_index = -1
|
||
for i in range(step_dialog.tool_combo.count()):
|
||
if step_dialog.tool_combo.itemText(i) == tool:
|
||
tool_index = i
|
||
break
|
||
if tool_index >= 0:
|
||
step_dialog.tool_combo.setCurrentIndex(tool_index)
|
||
print(f"✅ Qt对话框工具选择已同步")
|
||
|
||
print(f"✅ 工具切换完成: {tool}")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 工具选择失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def on_stop_clicked(self):
|
||
"""停止按钮点击回调"""
|
||
try:
|
||
print("🛑 用户点击停止按钮")
|
||
|
||
# 通知拆装交互系统停止
|
||
if hasattr(self.world, 'assembly_interaction') and self.world.assembly_interaction:
|
||
self.world.assembly_interaction.stop_interaction()
|
||
else:
|
||
print("⚠️ 拆装交互系统不存在,无法停止")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 停止操作失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update_tool_button_styles(self):
|
||
"""更新工具按钮样式"""
|
||
try:
|
||
for i, button in enumerate(self.tool_buttons):
|
||
# 处理工具数据格式
|
||
tool_data = self.available_tools[i]
|
||
if isinstance(tool_data, dict):
|
||
tool_name = tool_data.get('name', str(tool_data))
|
||
else:
|
||
tool_name = str(tool_data)
|
||
|
||
if tool_name == self.current_tool:
|
||
# 选中状态:亮蓝色
|
||
button['frameColor'] = (0.2, 0.8, 1.0, 0.9)
|
||
button['text_fg'] = (0, 0, 0, 1)
|
||
else:
|
||
# 未选中状态:深蓝色
|
||
button['frameColor'] = (0.3, 0.3, 0.8, 0.8)
|
||
button['text_fg'] = (1, 1, 1, 1)
|
||
|
||
except Exception as e:
|
||
print(f"❌ 更新按钮样式失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update_step_info(self, step_text):
|
||
"""更新步骤信息"""
|
||
try:
|
||
print(f"📋 更新步骤信息: {step_text}")
|
||
|
||
self.current_step_info = step_text
|
||
|
||
if self.step_text:
|
||
self.step_text['text'] = step_text
|
||
|
||
except Exception as e:
|
||
print(f"❌ 更新步骤信息失败: {e}")
|
||
|
||
def show_warning(self, message, duration=2.0):
|
||
"""显示警告信息(仅训练模式)"""
|
||
try:
|
||
# 考核模式下不显示警告
|
||
if hasattr(self, 'mode') and self.mode == "exam":
|
||
print(f"📝 考核模式:隐藏警告 - {message}")
|
||
return
|
||
|
||
print(f"⚠️ 显示警告: {message}")
|
||
|
||
if not self.warning_text:
|
||
return
|
||
|
||
# 取消之前的警告任务
|
||
if self.warning_task:
|
||
taskMgr.remove(self.warning_task)
|
||
self.warning_task = None
|
||
|
||
# 显示警告文本
|
||
self.warning_text['text'] = message
|
||
self.warning_text.show()
|
||
|
||
# 设置自动隐藏任务
|
||
self.warning_task = taskMgr.doMethodLater(
|
||
duration,
|
||
self.hide_warning,
|
||
'hide_warning_task'
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"❌ 显示警告失败: {e}")
|
||
|
||
def hide_warning(self, task=None):
|
||
"""隐藏警告信息"""
|
||
try:
|
||
if self.warning_text:
|
||
self.warning_text.hide()
|
||
|
||
if self.warning_task:
|
||
self.warning_task = None
|
||
|
||
return Task.done
|
||
|
||
except Exception as e:
|
||
print(f"❌ 隐藏警告失败: {e}")
|
||
return Task.done
|
||
|
||
def test_tool_selection(self):
|
||
"""测试工具选择功能(按T键触发)"""
|
||
if self.available_tools:
|
||
# 循环选择工具进行测试
|
||
current_index = 0
|
||
for i, tool_data in enumerate(self.available_tools):
|
||
if isinstance(tool_data, dict):
|
||
tool_name = tool_data.get('name', str(tool_data))
|
||
else:
|
||
tool_name = str(tool_data)
|
||
|
||
if tool_name == self.current_tool:
|
||
current_index = i
|
||
break
|
||
|
||
# 选择下一个工具
|
||
next_index = (current_index + 1) % len(self.available_tools)
|
||
next_tool_data = self.available_tools[next_index]
|
||
|
||
if isinstance(next_tool_data, dict):
|
||
next_tool_name = next_tool_data.get('name', str(next_tool_data))
|
||
else:
|
||
next_tool_name = str(next_tool_data)
|
||
|
||
print(f"⌨️ 键盘测试:切换到工具 '{next_tool_name}'")
|
||
self.on_tool_selected(next_tool_name)
|
||
|
||
def test_specific_button(self, button_index):
|
||
"""测试特定按钮(通过索引)"""
|
||
if not hasattr(self, 'available_tools') or not self.available_tools:
|
||
print(f"⌨️ 按钮{button_index}: 没有可用工具")
|
||
return
|
||
|
||
if button_index >= len(self.available_tools):
|
||
print(f"⌨️ 按钮{button_index}: 索引超出范围(最大{len(self.available_tools)-1})")
|
||
return
|
||
|
||
# 获取工具名称
|
||
tool_data = self.available_tools[button_index]
|
||
if isinstance(tool_data, dict):
|
||
tool_name = tool_data.get('name', str(tool_data))
|
||
else:
|
||
tool_name = str(tool_data)
|
||
|
||
print(f"⌨️ 按钮{button_index}测试:切换到工具 '{tool_name}'")
|
||
self.on_tool_selected(tool_name)
|
||
|
||
def print_button_positions(self):
|
||
"""打印所有按钮的位置信息"""
|
||
try:
|
||
if not hasattr(self, 'available_tools') or not self.available_tools:
|
||
print("📍 没有可用工具按钮")
|
||
return
|
||
|
||
print("📍 ===== 按钮位置信息 =====")
|
||
|
||
button_width = 0.4
|
||
button_height = 0.25
|
||
button_spacing = 0.45
|
||
start_x = -0.8
|
||
start_y = -0.75
|
||
|
||
for i, tool_data in enumerate(self.available_tools):
|
||
# 获取工具名称
|
||
if isinstance(tool_data, dict):
|
||
tool_name = tool_data.get('name', str(tool_data))
|
||
else:
|
||
tool_name = str(tool_data)
|
||
|
||
# 计算按钮位置
|
||
button_x = start_x + i * button_spacing
|
||
button_y = start_y
|
||
|
||
# 计算按钮边界
|
||
left = button_x - button_width/2
|
||
right = button_x + button_width/2
|
||
bottom = button_y - button_height/2
|
||
top = button_y + button_height/2
|
||
|
||
print(f"📍 按钮{i} '{tool_name}':")
|
||
print(f" 中心: ({button_x:.3f}, {button_y:.3f})")
|
||
print(f" 范围: x[{left:.3f}, {right:.3f}], y[{bottom:.3f}, {top:.3f}]")
|
||
print(f" 尺寸: {button_width} x {button_height}")
|
||
|
||
print("📍 ========================")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 打印按钮位置失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def show_exam_results(self, exam_data):
|
||
"""显示考核结果(GUI界面)"""
|
||
try:
|
||
print("🎓 开始显示考核结果GUI...")
|
||
|
||
# 清理现有GUI元素
|
||
self.cleanup_gui()
|
||
|
||
# 创建考核结果界面
|
||
self.create_exam_result_gui(exam_data)
|
||
|
||
print("✅ 考核结果GUI显示完成")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 显示考核结果GUI失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_exam_result_gui(self, exam_data):
|
||
"""创建考核结果GUI界面"""
|
||
try:
|
||
if not self.aspect2d:
|
||
print("❌ aspect2d引用不可用,无法创建考核结果GUI")
|
||
return
|
||
|
||
# 主背景 - 黑色背景
|
||
from direct.gui.DirectGui import DirectFrame
|
||
self.exam_bg = DirectFrame(
|
||
frameColor=(0, 0, 0, 0.95), # 更深的黑色背景
|
||
frameSize=(-2.0, 2.0, -1.0, 1.0),
|
||
pos=(0, 0, 0),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 标题
|
||
self.exam_title = DirectLabel(
|
||
text="🎓 考核结果报告",
|
||
text_align=TextNode.ACenter,
|
||
text_scale=0.12,
|
||
text_fg=(1, 1, 0, 1), # 黄色文字
|
||
text_bg=(0, 0, 0, 0), # 透明背景
|
||
frameColor=(0, 0, 0, 0), # 透明边框
|
||
frameSize=(-1.5, 1.5, -0.15, 0.15),
|
||
pos=(0, 0, 0.8),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 总体成绩区域
|
||
score_text = f"📊 总得分: {exam_data['final_score']:.0f}/{exam_data['max_score']:.0f} 分 ({exam_data['score_rate']:.1f}%)\n"
|
||
score_text += f"🏅 评级: {exam_data['grade_icon']} {exam_data['grade']} - {exam_data['grade_desc']}\n"
|
||
score_text += f"🎯 操作准确率: {exam_data['accuracy_rate']:.1f}% | 完美步骤: {exam_data['perfect_steps']}/{exam_data['total_steps']}"
|
||
|
||
# 根据成绩选择文字颜色(在黑色背景下更突出)
|
||
if exam_data['score_rate'] >= 90:
|
||
score_fg_color = (0.2, 1.0, 0.2, 1) # 亮绿色
|
||
elif exam_data['score_rate'] >= 80:
|
||
score_fg_color = (0.8, 1.0, 0.2, 1) # 亮黄绿色
|
||
elif exam_data['score_rate'] >= 60:
|
||
score_fg_color = (1.0, 0.8, 0.2, 1) # 亮橙色
|
||
else:
|
||
score_fg_color = (1.0, 0.3, 0.3, 1) # 亮红色
|
||
|
||
self.exam_score = DirectLabel(
|
||
text=score_text,
|
||
text_align=TextNode.ACenter,
|
||
text_scale=0.07,
|
||
text_fg=score_fg_color, # 根据成绩动态变色
|
||
text_bg=(0, 0, 0, 0), # 透明背景
|
||
frameColor=(0, 0, 0, 0), # 透明边框
|
||
frameSize=(-1.9, 1.9, -0.3, 0.3),
|
||
pos=(0, 0, 0.35),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 步骤详情区域
|
||
steps_text = "📋 步骤详情:\n"
|
||
for step in exam_data['step_details']:
|
||
steps_text += f"{step['status']} {step['name']}: {step['current_score']:.0f}/{step['max_score']:.0f}分"
|
||
if step['tool_error']:
|
||
steps_text += " ❌工具错误"
|
||
steps_text += f" ({step['attempts']}次操作)\n"
|
||
|
||
self.exam_steps = DirectLabel(
|
||
text=steps_text,
|
||
text_align=TextNode.ALeft,
|
||
text_scale=0.06,
|
||
text_fg=(0.9, 0.9, 0.9, 1), # 浅灰色文字
|
||
text_bg=(0, 0, 0, 0), # 透明背景
|
||
frameColor=(0, 0, 0, 0), # 透明边框
|
||
frameSize=(-1.8, 1.8, -0.4, 0.4),
|
||
pos=(0, 0, -0.1),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 改进建议区域(如果有建议)
|
||
if exam_data['suggestions']:
|
||
suggestions_text = "💡 改进建议:\n"
|
||
for suggestion in exam_data['suggestions']:
|
||
suggestions_text += f"{suggestion}\n"
|
||
|
||
self.exam_suggestions = DirectLabel(
|
||
text=suggestions_text,
|
||
text_align=TextNode.ALeft,
|
||
text_scale=0.05,
|
||
text_fg=(1, 0.8, 0.2, 1), # 橙黄色文字
|
||
text_bg=(0, 0, 0, 0), # 透明背景
|
||
frameColor=(0, 0, 0, 0), # 透明边框
|
||
frameSize=(-1.8, 1.8, -0.25, 0.25),
|
||
pos=(0, 0, -0.7),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 倒计时显示
|
||
self.exam_countdown_text = DirectLabel(
|
||
text="10秒后自动关闭",
|
||
text_align=TextNode.ACenter,
|
||
text_scale=0.06,
|
||
text_fg=(0.8, 0.8, 0.8, 1), # 灰色文字
|
||
text_bg=(0, 0, 0, 0), # 透明背景
|
||
frameColor=(0, 0, 0, 0), # 透明边框
|
||
frameSize=(-0.8, 0.8, -0.1, 0.1),
|
||
pos=(0, 0, -0.8),
|
||
parent=self.aspect2d
|
||
)
|
||
|
||
# 启动10秒倒计时任务
|
||
self.start_countdown_timer()
|
||
|
||
print("✅ 考核结果GUI元素创建完成")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建考核结果GUI失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def close_exam_results(self):
|
||
"""关闭考核结果界面"""
|
||
try:
|
||
print("🔄 关闭考核结果界面...")
|
||
|
||
# 清理考核结果GUI元素
|
||
if hasattr(self, 'exam_bg') and self.exam_bg:
|
||
self.exam_bg.removeNode()
|
||
self.exam_bg = None
|
||
|
||
if hasattr(self, 'exam_title') and self.exam_title:
|
||
self.exam_title.removeNode()
|
||
self.exam_title = None
|
||
|
||
if hasattr(self, 'exam_score') and self.exam_score:
|
||
self.exam_score.removeNode()
|
||
self.exam_score = None
|
||
|
||
if hasattr(self, 'exam_steps') and self.exam_steps:
|
||
self.exam_steps.removeNode()
|
||
self.exam_steps = None
|
||
|
||
if hasattr(self, 'exam_suggestions') and self.exam_suggestions:
|
||
self.exam_suggestions.removeNode()
|
||
self.exam_suggestions = None
|
||
|
||
if hasattr(self, 'exam_countdown_text') and self.exam_countdown_text:
|
||
self.exam_countdown_text.removeNode()
|
||
self.exam_countdown_text = None
|
||
|
||
# 停止倒计时任务
|
||
if hasattr(self, 'exam_countdown_task') and self.exam_countdown_task:
|
||
taskMgr.remove(self.exam_countdown_task)
|
||
self.exam_countdown_task = None
|
||
|
||
print("✅ 考核结果界面已关闭")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 关闭考核结果界面失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def start_countdown_timer(self):
|
||
"""启动10秒倒计时"""
|
||
try:
|
||
self.countdown_seconds = 10
|
||
print(f"⏰ 启动考核结果倒计时:{self.countdown_seconds}秒")
|
||
|
||
# 启动倒计时任务
|
||
self.exam_countdown_task = taskMgr.doMethodLater(
|
||
1.0, # 每秒执行一次
|
||
self.update_countdown,
|
||
'exam_countdown_task'
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"❌ 启动倒计时失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update_countdown(self, task):
|
||
"""更新倒计时显示"""
|
||
try:
|
||
self.countdown_seconds -= 1
|
||
|
||
if self.countdown_seconds > 0:
|
||
# 更新倒计时显示
|
||
if hasattr(self, 'exam_countdown_text') and self.exam_countdown_text:
|
||
self.exam_countdown_text['text'] = f"{self.countdown_seconds}秒后自动关闭"
|
||
|
||
print(f"⏰ 倒计时:{self.countdown_seconds}秒")
|
||
|
||
# 继续倒计时
|
||
return task.again
|
||
else:
|
||
# 倒计时结束,自动关闭
|
||
print("⏰ 倒计时结束,自动关闭考核结果界面")
|
||
self.close_exam_results()
|
||
return task.done
|
||
|
||
except Exception as e:
|
||
print(f"❌ 更新倒计时失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return task.done
|
||
|
||
|
||
def get_current_tool(self):
|
||
"""获取当前选择的工具"""
|
||
return self.current_tool
|
||
|
||
def set_mode(self, mode):
|
||
"""设置模式"""
|
||
self.mode = mode
|
||
print(f"🎯 维修GUI模式设置为: {mode}")
|
||
|
||
def cleanup_gui(self):
|
||
"""清理GUI元素"""
|
||
try:
|
||
print("🧹 清理维修系统GUI")
|
||
|
||
# 清理步骤文本
|
||
if self.step_text:
|
||
self.step_text.destroy()
|
||
self.step_text = None
|
||
|
||
# 清理当前工具文本
|
||
if self.current_tool_text:
|
||
self.current_tool_text.destroy()
|
||
self.current_tool_text = None
|
||
|
||
# 清理警告文本
|
||
if self.warning_text:
|
||
self.warning_text.destroy()
|
||
self.warning_text = None
|
||
|
||
# 清理工具按钮
|
||
for button in self.tool_buttons:
|
||
if button:
|
||
button.destroy()
|
||
self.tool_buttons.clear()
|
||
|
||
# 清理停止按钮
|
||
if hasattr(self, 'stop_button') and self.stop_button:
|
||
self.stop_button.destroy()
|
||
self.stop_button = None
|
||
|
||
# 取消警告任务
|
||
if self.warning_task:
|
||
taskMgr.remove(self.warning_task)
|
||
self.warning_task = None
|
||
|
||
# 取消鼠标监控任务
|
||
if self.mouse_monitor_task:
|
||
taskMgr.remove(self.mouse_monitor_task)
|
||
self.mouse_monitor_task = None
|
||
|
||
# 清理考核结果界面
|
||
self.close_exam_results()
|
||
|
||
print("✅ 维修系统GUI清理完成")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 清理维修系统GUI失败: {e}")
|
||
|
||
def show_gui(self):
|
||
"""显示GUI"""
|
||
try:
|
||
print(f"🎨 显示维修系统GUI...")
|
||
print(f" 模式: {self.mode}")
|
||
print(f" 步骤文本: {self.step_text is not None}")
|
||
print(f" 当前工具文本: {self.current_tool_text is not None}")
|
||
print(f" 工具按钮数量: {len(self.tool_buttons)}")
|
||
|
||
if self.step_text:
|
||
self.step_text.show()
|
||
print(" ✅ 步骤文本已显示")
|
||
|
||
# 当前工具文本和工具按钮在训练和考核模式都需要显示
|
||
if self.current_tool_text:
|
||
self.current_tool_text.show()
|
||
print(" ✅ 当前工具文本已显示")
|
||
|
||
for i, button in enumerate(self.tool_buttons):
|
||
button.show()
|
||
print(f" ✅ 工具按钮 {i+1} 已显示")
|
||
|
||
# 停止按钮在训练和考核模式都需要显示
|
||
if self.stop_button:
|
||
self.stop_button.show()
|
||
print(" ✅ 停止按钮已显示")
|
||
|
||
print("✅ 维修系统GUI显示完成")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 显示维修系统GUI失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def hide_gui(self):
|
||
"""隐藏GUI"""
|
||
try:
|
||
if self.step_text:
|
||
self.step_text.hide()
|
||
|
||
if self.current_tool_text:
|
||
self.current_tool_text.hide()
|
||
|
||
if self.warning_text:
|
||
self.warning_text.hide()
|
||
|
||
for button in self.tool_buttons:
|
||
button.hide()
|
||
|
||
if self.stop_button:
|
||
self.stop_button.hide()
|
||
|
||
print("✅ 维修系统GUI隐藏")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 隐藏维修系统GUI失败: {e}") |