1.虚拟维修基本流程
This commit is contained in:
parent
d32993b903
commit
64df3107fe
@ -11,6 +11,13 @@ from PyQt5.QtWidgets import QMessageBox, QFileDialog, QDialog, QVBoxLayout, QHBo
|
||||
from PyQt5.QtWidgets import QLabel, QPushButton, QTextEdit, QComboBox, QGroupBox
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 导入维修系统GUI
|
||||
try:
|
||||
from core.maintenance_gui import MaintenanceGUI
|
||||
except ImportError as e:
|
||||
print(f"⚠️ 导入维修GUI失败: {e}")
|
||||
MaintenanceGUI = None
|
||||
|
||||
|
||||
class AssemblyInteractionManager(DirectObject):
|
||||
"""拆装交互管理器(优化版)"""
|
||||
@ -38,6 +45,11 @@ class AssemblyInteractionManager(DirectObject):
|
||||
# --- UI组件 ---
|
||||
self.step_dialog = None
|
||||
|
||||
# --- 维修系统GUI ---
|
||||
self.maintenance_gui = None
|
||||
if MaintenanceGUI:
|
||||
self.maintenance_gui = MaintenanceGUI(world)
|
||||
|
||||
# --- 操作权限控制 ---
|
||||
self.operation_enabled = True # 是否允许进行操作
|
||||
|
||||
@ -345,19 +357,29 @@ class AssemblyInteractionManager(DirectObject):
|
||||
step_data = self.config_data['steps'][self.current_step]
|
||||
required_tool = step_data.get('required_tool', '无')
|
||||
|
||||
# 获取当前选择的工具
|
||||
# 获取当前选择的工具(优先从维修GUI获取)
|
||||
current_tool = "无"
|
||||
if hasattr(self, 'step_dialog') and self.step_dialog and self.step_dialog.current_tool_combo:
|
||||
current_tool = self.step_dialog.current_tool_combo.currentText()
|
||||
if self.maintenance_gui:
|
||||
current_tool = self.maintenance_gui.get_current_tool()
|
||||
elif hasattr(self, 'step_dialog') and self.step_dialog and hasattr(self.step_dialog, 'tool_combo'):
|
||||
current_tool = self.step_dialog.tool_combo.currentText()
|
||||
|
||||
print(f"🔧 工具检查: 需要'{required_tool}',当前'{current_tool}'")
|
||||
|
||||
# 检查工具是否匹配
|
||||
tool_matches = self.check_tool_match(current_tool, required_tool)
|
||||
|
||||
if self.mode == "training":
|
||||
# 训练模式:显示提示并阻止错误操作
|
||||
# 训练模式:显示GUI警告并阻止错误操作
|
||||
if not tool_matches:
|
||||
print("⚠️ 训练模式 - 工具不匹配,无法进行操作")
|
||||
if hasattr(self, 'step_dialog') and self.step_dialog:
|
||||
|
||||
# 在维修GUI中显示警告
|
||||
if self.maintenance_gui:
|
||||
warning_msg = f"请先选择 '{required_tool}' 工具!"
|
||||
self.maintenance_gui.show_warning(warning_msg, 2.0)
|
||||
# 也在Qt对话框中显示警告(仅在非维修系统模式下)
|
||||
elif hasattr(self, 'step_dialog') and self.step_dialog:
|
||||
QMessageBox.warning(self.step_dialog, "工具不匹配",
|
||||
f"当前步骤需要使用 '{required_tool}' 工具,请先选择正确的工具!")
|
||||
return False
|
||||
@ -413,8 +435,10 @@ class AssemblyInteractionManager(DirectObject):
|
||||
self.mode = mode
|
||||
print(f"🎯 启动拆装交互模式: {self.mode}")
|
||||
|
||||
if not self.load_configuration():
|
||||
return False
|
||||
# 如果没有配置数据,尝试加载
|
||||
if not self.config_data:
|
||||
if not self.load_configuration():
|
||||
return False
|
||||
|
||||
if not self.config_data or not self.config_data.get('steps'):
|
||||
QMessageBox.warning(None, "警告", "没有找到有效的配置步骤!\n请先在'拆装配置'中设置步骤。")
|
||||
@ -424,9 +448,21 @@ class AssemblyInteractionManager(DirectObject):
|
||||
self.current_step = 0
|
||||
self.is_active = True
|
||||
|
||||
# 设置维修系统GUI
|
||||
if self.maintenance_gui:
|
||||
tools_list = self.config_data.get('tools', ['手'])
|
||||
self.maintenance_gui.setup_gui(tools_list, mode)
|
||||
self.maintenance_gui.show_gui()
|
||||
print(f"✅ 维修系统GUI已启动,模式: {mode}")
|
||||
|
||||
# 如果是考核模式,初始化考核相关数据
|
||||
if self.mode == "exam":
|
||||
self.init_exam_mode()
|
||||
# 显示考核开始提示
|
||||
if self.maintenance_gui:
|
||||
exam_start_msg = f"📝 考核模式已启动\n\n总分: {self.exam_max_score} 分\n步骤数: {self.total_steps}\n\n✅ 可以选择和切换工具\n❌ 不会显示步骤描述和语音提示\n❌ 使用错误工具会扣分"
|
||||
self.maintenance_gui.update_step_info(exam_start_msg)
|
||||
print("📝 考核开始提示已在GUI中显示")
|
||||
|
||||
# 设置碰撞检测
|
||||
self.setup_picking()
|
||||
@ -470,8 +506,9 @@ class AssemblyInteractionManager(DirectObject):
|
||||
# 调试:列出场景中的所有模型
|
||||
self.list_scene_models()
|
||||
|
||||
# 显示步骤指引界面
|
||||
self.show_step_dialog()
|
||||
# 显示步骤指引界面(维修系统使用GUI界面,跳过Qt对话框)
|
||||
if not self.maintenance_gui:
|
||||
self.show_step_dialog()
|
||||
|
||||
# 开始第一步
|
||||
self.start_current_step()
|
||||
@ -706,7 +743,26 @@ class AssemblyInteractionManager(DirectObject):
|
||||
# 播放步骤音频(如果有配置)
|
||||
self.play_step_audio(step_data)
|
||||
|
||||
if self.step_dialog:
|
||||
# 更新维修系统GUI的步骤信息
|
||||
if self.maintenance_gui:
|
||||
step_name = step_data.get('name', f'步骤 {self.current_step + 1}')
|
||||
step_description = step_data.get('description', '')
|
||||
|
||||
if self.mode == "exam":
|
||||
# 考核模式:只显示步骤编号和名称,不显示描述
|
||||
step_info = f"考核步骤 {self.current_step + 1}/{self.total_steps}: {step_name}"
|
||||
print(f"📝 考核模式步骤信息: {step_info}")
|
||||
else:
|
||||
# 训练模式:显示完整信息
|
||||
step_info = f"步骤 {self.current_step + 1}/{self.total_steps}: {step_name}"
|
||||
if step_description:
|
||||
step_info += f"\n{step_description}"
|
||||
print(f"📚 训练模式步骤信息: {step_info}")
|
||||
|
||||
self.maintenance_gui.update_step_info(step_info)
|
||||
|
||||
# 更新Qt步骤对话框(仅在非维修系统模式下)
|
||||
if self.step_dialog and not self.maintenance_gui:
|
||||
self.step_dialog.update_step_info(step_data, self.current_step + 1, self.total_steps)
|
||||
|
||||
# 准备交互(主要是确保目标模型可以被拾取)
|
||||
@ -872,38 +928,211 @@ class AssemblyInteractionManager(DirectObject):
|
||||
print("\n✅ 所有模型都处于正确状态")
|
||||
|
||||
def qt_mouse_press_event(self, event):
|
||||
"""Qt鼠标按下事件处理(只拦截左键)"""
|
||||
"""Qt鼠标按下事件处理(选择性拦截版)"""
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 只拦截左键,其他按键使用原有处理方式
|
||||
if event.button() == Qt.LeftButton:
|
||||
print('🖱️ 左键被拦截处理')
|
||||
print(f'🖱️ 左键点击位置: ({event.x()}, {event.y()})')
|
||||
# 调用我们的处理逻辑
|
||||
|
||||
# 检查是否点击在GUI区域
|
||||
if self.maintenance_gui and self.is_click_in_gui_area(event.x(), event.y()):
|
||||
print('🎯 点击在GUI区域,直接处理工具按钮点击')
|
||||
# 直接处理工具按钮点击,不依赖DirectGUI事件
|
||||
button_clicked = self.handle_gui_button_click(event.x(), event.y())
|
||||
if button_clicked:
|
||||
print(f'🎯 工具按钮点击已处理: {button_clicked}')
|
||||
event.accept()
|
||||
return
|
||||
|
||||
# 如果不是按钮点击,让原有事件处理器处理
|
||||
if hasattr(self, 'original_mouse_press_event'):
|
||||
self.original_mouse_press_event(event)
|
||||
else:
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
# 点击在3D区域,进行拦截处理
|
||||
print('🎯 点击在3D区域,进行拦截处理')
|
||||
self.handle_qt_mouse_click(event.x(), event.y())
|
||||
# 接受左键事件,阻止进一步传播
|
||||
event.accept()
|
||||
else:
|
||||
# 其他按键(右键、中键等)使用原有处理方式
|
||||
# print(f'🖱️ 其他按键({event.button()})使用原有处理方式') # 静默处理
|
||||
# 其他按键使用原有处理方式
|
||||
if hasattr(self, 'original_mouse_press_event'):
|
||||
self.original_mouse_press_event(event)
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def is_click_in_gui_area(self, click_x, click_y):
|
||||
"""检查鼠标点击是否在GUI区域(高精度版本)"""
|
||||
try:
|
||||
if not self.maintenance_gui:
|
||||
return False
|
||||
|
||||
# 获取窗口尺寸用于坐标转换
|
||||
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
|
||||
|
||||
# 使用与maintenance_gui完全相同的坐标转换算法
|
||||
aspect_x = (click_x / widget_width) * 2.67 - 1.33
|
||||
aspect_y = 1.0 - (click_y / widget_height) * 2.0
|
||||
|
||||
# 额外的坐标验证(与Panda3D标准坐标系对比)
|
||||
# Panda3D aspect2d坐标系: x范围[-1.33, 1.33], y范围[-1.0, 1.0]
|
||||
normalized_x = (click_x / widget_width) * 2.0 - 1.0 # 标准化到[-1, 1]
|
||||
normalized_y = 1.0 - (click_y / widget_height) * 2.0 # 标准化到[-1, 1],Y轴翻转
|
||||
aspect_ratio = widget_width / widget_height
|
||||
panda_x = normalized_x * aspect_ratio # 应用宽高比
|
||||
panda_y = normalized_y
|
||||
|
||||
print(f"🔍 精确GUI区域检查: Qt({click_x}, {click_y}) -> aspect2d({aspect_x:.3f}, {aspect_y:.3f})")
|
||||
print(f"🔍 验证坐标转换: 标准化({normalized_x:.3f}, {normalized_y:.3f}) -> Panda3D({panda_x:.3f}, {panda_y:.3f})")
|
||||
print(f"📏 窗口信息: 尺寸({widget_width}x{widget_height}), 宽高比({aspect_ratio:.3f})")
|
||||
|
||||
# 快速检查工具按钮区域
|
||||
if hasattr(self.maintenance_gui, 'available_tools') and self.maintenance_gui.available_tools:
|
||||
# 工具按钮区域(底部条带)
|
||||
tools_area_left = -1.1
|
||||
tools_area_right = 0.5
|
||||
tools_area_bottom = -1.0
|
||||
tools_area_top = -0.5
|
||||
|
||||
if (tools_area_left <= aspect_x <= tools_area_right and
|
||||
tools_area_bottom <= aspect_y <= tools_area_top):
|
||||
print(f"✅ 点击在工具按钮区域内")
|
||||
return True
|
||||
|
||||
# 检查其他GUI区域(步骤显示、当前工具、警告等)
|
||||
other_gui_areas = []
|
||||
|
||||
# 步骤显示区域(顶部)
|
||||
if hasattr(self.maintenance_gui, 'step_text') and self.maintenance_gui.step_text:
|
||||
other_gui_areas.append({
|
||||
'name': '步骤显示区域',
|
||||
'left': -1.8, 'right': 1.8,
|
||||
'bottom': 0.5, 'top': 1.1
|
||||
})
|
||||
|
||||
# 当前工具显示区域(右侧)
|
||||
if hasattr(self.maintenance_gui, 'current_tool_text') and self.maintenance_gui.current_tool_text:
|
||||
other_gui_areas.append({
|
||||
'name': '当前工具显示区域',
|
||||
'left': 0.2, 'right': 1.8,
|
||||
'bottom': 0.6, 'top': 1.0
|
||||
})
|
||||
|
||||
# 警告显示区域(中央)
|
||||
if hasattr(self.maintenance_gui, 'warning_text') and self.maintenance_gui.warning_text:
|
||||
other_gui_areas.append({
|
||||
'name': '警告显示区域',
|
||||
'left': -1.5, 'right': 1.5,
|
||||
'bottom': 0.0, 'top': 0.6
|
||||
})
|
||||
|
||||
# 检查其他GUI区域
|
||||
for area in other_gui_areas:
|
||||
if (area['left'] <= aspect_x <= area['right'] and
|
||||
area['bottom'] <= aspect_y <= area['top']):
|
||||
print(f"✅ 点击在GUI区域内: {area['name']}")
|
||||
return True
|
||||
|
||||
print("❌ 点击不在任何GUI区域内")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 检查GUI区域失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def handle_gui_button_click(self, click_x, click_y):
|
||||
"""直接处理GUI按钮点击"""
|
||||
try:
|
||||
if not self.maintenance_gui:
|
||||
return None
|
||||
|
||||
# 获取窗口尺寸用于坐标转换
|
||||
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
|
||||
|
||||
# 使用与maintenance_gui完全相同的坐标转换算法
|
||||
aspect_x = (click_x / widget_width) * 2.67 - 1.33
|
||||
aspect_y = 1.0 - (click_y / widget_height) * 2.0
|
||||
|
||||
print(f"🎯 直接处理按钮点击:Qt({click_x}, {click_y}) -> aspect2d({aspect_x:.3f}, {aspect_y:.3f})")
|
||||
|
||||
# 检查工具按钮点击
|
||||
if hasattr(self.maintenance_gui, 'available_tools') and self.maintenance_gui.available_tools:
|
||||
button_width = 0.4
|
||||
button_height = 0.25
|
||||
button_spacing = 0.45
|
||||
start_x = -0.8
|
||||
start_y = -0.75
|
||||
click_padding = 0.05 # 点击容差
|
||||
|
||||
for i, tool_data in enumerate(self.maintenance_gui.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 + click_padding)
|
||||
right = button_x + (button_width/2 + click_padding)
|
||||
bottom = button_y - (button_height/2 + click_padding)
|
||||
top = button_y + (button_height/2 + click_padding)
|
||||
|
||||
print(f"🔍 检查按钮{i} '{tool_name}': 范围x[{left:.3f}, {right:.3f}], y[{bottom:.3f}, {top:.3f}]")
|
||||
|
||||
# 检查点击是否在按钮范围内
|
||||
if (left <= aspect_x <= right and bottom <= aspect_y <= top):
|
||||
print(f"✅ 直接匹配按钮{i} '{tool_name}'!调用工具选择")
|
||||
# 直接调用维修GUI的工具选择方法
|
||||
self.maintenance_gui.on_tool_selected(tool_name)
|
||||
return tool_name
|
||||
|
||||
print("❌ 点击不在任何按钮范围内")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 直接处理按钮点击失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def qt_mouse_release_event(self, event):
|
||||
"""Qt鼠标释放事件处理(只拦截左键)"""
|
||||
"""Qt鼠标释放事件处理(维修系统优化版)"""
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 只拦截左键释放,其他按键使用原有处理方式
|
||||
if event.button() == Qt.LeftButton:
|
||||
print('🖱️ 左键释放被拦截处理')
|
||||
|
||||
# 维修系统模式:先让原有处理器处理
|
||||
if self.maintenance_gui:
|
||||
if hasattr(self, 'original_mouse_release_event'):
|
||||
self.original_mouse_release_event(event)
|
||||
|
||||
# 处理3D交互的鼠标释放
|
||||
self.on_mouse_up()
|
||||
# 接受左键释放事件
|
||||
event.accept()
|
||||
else:
|
||||
# 其他按键释放使用原有处理方式
|
||||
# print(f'🖱️ 其他按键释放({event.button()})使用原有处理方式') # 静默处理
|
||||
if hasattr(self, 'original_mouse_release_event'):
|
||||
self.original_mouse_release_event(event)
|
||||
else:
|
||||
@ -1314,6 +1543,11 @@ class AssemblyInteractionManager(DirectObject):
|
||||
|
||||
def complete_current_step(self):
|
||||
"""完成当前步骤"""
|
||||
# 记录操作完成(考核模式下)
|
||||
if self.mode == "exam" and self.current_step in self.step_scores:
|
||||
self.step_scores[self.current_step]['operation_attempts'] += 1
|
||||
print(f"📝 考核记录:步骤 {self.current_step + 1} 完成,操作次数: {self.step_scores[self.current_step]['operation_attempts']}")
|
||||
|
||||
# 在进入下一步之前,取消当前模型的碰撞,避免干扰
|
||||
step_data = self.config_data['steps'][self.current_step]
|
||||
target_model_name = step_data.get('target_model')
|
||||
@ -1338,6 +1572,11 @@ class AssemblyInteractionManager(DirectObject):
|
||||
self.is_active = False
|
||||
self.ignoreAll()
|
||||
|
||||
# 清理维修系统GUI
|
||||
if self.maintenance_gui:
|
||||
self.maintenance_gui.cleanup_gui()
|
||||
print("✅ 维修系统GUI已清理")
|
||||
|
||||
# 恢复原有的Qt鼠标事件处理
|
||||
print("🔄 恢复原有的Qt鼠标事件处理")
|
||||
if hasattr(self, 'original_mouse_press_event') and hasattr(self.world, 'qtWidget') and self.world.qtWidget:
|
||||
@ -1351,59 +1590,234 @@ class AssemblyInteractionManager(DirectObject):
|
||||
self.step_dialog.close()
|
||||
self.step_dialog = None
|
||||
|
||||
# 如果是考核模式,显示考核结果
|
||||
# 根据模式显示不同的完成结果
|
||||
if self.mode == "exam":
|
||||
# 考核模式:显示详细的考核结果
|
||||
self.show_exam_results()
|
||||
else:
|
||||
QMessageBox.information(None, "完成", "所有拆装步骤已完成!")
|
||||
# 训练模式:显示训练完成提示
|
||||
print("\n🎉 训练模式完成!")
|
||||
completion_msg = "🎉 训练完成!\n\n所有维修步骤已完成!\n现在可以尝试考核模式检验学习成果。"
|
||||
|
||||
if self.maintenance_gui:
|
||||
self.maintenance_gui.update_step_info(completion_msg)
|
||||
print("📋 训练完成信息已在GUI中显示")
|
||||
else:
|
||||
QMessageBox.information(None, "训练完成", completion_msg)
|
||||
|
||||
def show_exam_results(self):
|
||||
"""显示考核结果"""
|
||||
# 计算最终得分
|
||||
final_score = 0
|
||||
for step_record in self.step_scores.values():
|
||||
final_score += step_record['current_score']
|
||||
|
||||
# 计算得分率
|
||||
score_rate = (final_score / self.exam_max_score * 100) if self.exam_max_score > 0 else 0
|
||||
|
||||
# 生成详细报告
|
||||
report = f"📝 拆装考核结果报告\n"
|
||||
report += f"{'='*40}\n\n"
|
||||
report += f"总得分: {final_score:.0f} / {self.exam_max_score:.0f} 分\n"
|
||||
report += f"得分率: {score_rate:.1f}%\n\n"
|
||||
|
||||
# 评级
|
||||
if score_rate >= 90:
|
||||
grade = "优秀 ⭐⭐⭐"
|
||||
elif score_rate >= 80:
|
||||
grade = "良好 ⭐⭐"
|
||||
elif score_rate >= 60:
|
||||
grade = "及格 ⭐"
|
||||
else:
|
||||
grade = "不及格 ❌"
|
||||
|
||||
report += f"评级: {grade}\n\n"
|
||||
report += f"各步骤详情:\n"
|
||||
report += f"{'-'*30}\n"
|
||||
|
||||
# 显示每步详情
|
||||
for step_idx, step_record in self.step_scores.items():
|
||||
step_name = self.config_data['steps'][step_idx]['name']
|
||||
max_score = step_record['max_score']
|
||||
current_score = step_record['current_score']
|
||||
tool_error = "❌" if step_record['tool_error'] else "✅"
|
||||
attempts = step_record['operation_attempts']
|
||||
try:
|
||||
# 计算最终得分
|
||||
final_score = 0
|
||||
total_operations = 0
|
||||
total_errors = 0
|
||||
|
||||
report += f"{step_name}:\n"
|
||||
report += f" 得分: {current_score:.0f}/{max_score:.0f} 分\n"
|
||||
report += f" 工具使用: {tool_error}\n"
|
||||
report += f" 操作次数: {attempts}\n\n"
|
||||
|
||||
print(report)
|
||||
|
||||
# 显示结果对话框
|
||||
QMessageBox.information(None, "考核完成", report)
|
||||
for step_record in self.step_scores.values():
|
||||
final_score += step_record['current_score']
|
||||
total_operations += step_record['operation_attempts']
|
||||
if step_record['tool_error']:
|
||||
total_errors += 1
|
||||
|
||||
# 计算得分率和准确率
|
||||
score_rate = (final_score / self.exam_max_score * 100) if self.exam_max_score > 0 else 0
|
||||
accuracy_rate = ((self.total_steps - total_errors) / self.total_steps * 100) if self.total_steps > 0 else 100
|
||||
|
||||
# 生成考核报告
|
||||
print(f"\n{'='*50}")
|
||||
print(f"🎓 维修技能考核结果报告")
|
||||
print(f"{'='*50}")
|
||||
print(f"📊 总体成绩:")
|
||||
print(f" 总得分: {final_score:.0f} / {self.exam_max_score:.0f} 分")
|
||||
print(f" 得分率: {score_rate:.1f}%")
|
||||
print(f" 操作准确率: {accuracy_rate:.1f}%")
|
||||
print(f" 总操作次数: {total_operations}")
|
||||
print(f" 错误次数: {total_errors}")
|
||||
|
||||
# 评级系统
|
||||
if score_rate >= 90:
|
||||
grade = "优秀"
|
||||
grade_icon = "🏆"
|
||||
grade_desc = "表现卓越,技能熟练"
|
||||
elif score_rate >= 80:
|
||||
grade = "良好"
|
||||
grade_icon = "🥈"
|
||||
grade_desc = "表现良好,基本掌握"
|
||||
elif score_rate >= 60:
|
||||
grade = "及格"
|
||||
grade_icon = "✅"
|
||||
grade_desc = "达到基本要求"
|
||||
else:
|
||||
grade = "不及格"
|
||||
grade_icon = "❌"
|
||||
grade_desc = "需要加强练习"
|
||||
|
||||
print(f"\n🏅 评级: {grade_icon} {grade}")
|
||||
print(f" 评语: {grade_desc}")
|
||||
|
||||
# 详细步骤分析
|
||||
print(f"\n📋 详细步骤分析:")
|
||||
print(f"{'-'*40}")
|
||||
|
||||
perfect_steps = 0
|
||||
for step_idx, step_record in self.step_scores.items():
|
||||
step_data = self.config_data['steps'][step_idx]
|
||||
step_name = step_data.get('name', f'步骤{step_idx+1}')
|
||||
max_score = step_record['max_score']
|
||||
current_score = step_record['current_score']
|
||||
tool_error = step_record['tool_error']
|
||||
attempts = step_record['operation_attempts']
|
||||
|
||||
# 判断步骤完成质量
|
||||
if current_score >= max_score:
|
||||
step_status = "🟢 完美"
|
||||
perfect_steps += 1
|
||||
elif current_score >= max_score * 0.8:
|
||||
step_status = "🟡 良好"
|
||||
elif current_score > 0:
|
||||
step_status = "🟠 一般"
|
||||
else:
|
||||
step_status = "🔴 失败"
|
||||
|
||||
print(f"{step_name}:")
|
||||
print(f" 状态: {step_status}")
|
||||
print(f" 得分: {current_score:.0f}/{max_score:.0f} 分")
|
||||
print(f" 工具使用: {'❌ 错误' if tool_error else '✅ 正确'}")
|
||||
print(f" 操作次数: {attempts}")
|
||||
print()
|
||||
|
||||
# 生成改进建议
|
||||
suggestions = []
|
||||
if total_errors > 0:
|
||||
suggestions.append("• 注意选择正确的工具进行操作")
|
||||
if total_operations > self.total_steps * 1.5:
|
||||
suggestions.append("• 减少不必要的操作,提高操作效率")
|
||||
if perfect_steps < self.total_steps * 0.5:
|
||||
suggestions.append("• 多练习以提高操作的精确度")
|
||||
if score_rate < 80:
|
||||
suggestions.append("• 建议重新学习相关理论知识")
|
||||
|
||||
if suggestions:
|
||||
print("💡 改进建议:")
|
||||
for suggestion in suggestions:
|
||||
print(f" {suggestion}")
|
||||
print()
|
||||
|
||||
print(f"{'='*50}")
|
||||
|
||||
# 为GUI显示准备简化版本的报告
|
||||
gui_report = f"🎓 考核完成!\n\n"
|
||||
gui_report += f"📊 成绩总览:\n"
|
||||
gui_report += f"总得分: {final_score:.0f}/{self.exam_max_score:.0f} 分 ({score_rate:.1f}%)\n"
|
||||
gui_report += f"评级: {grade_icon} {grade}\n"
|
||||
gui_report += f"操作准确率: {accuracy_rate:.1f}%\n\n"
|
||||
gui_report += f"完美完成步骤: {perfect_steps}/{self.total_steps}\n"
|
||||
gui_report += f"总操作次数: {total_operations}\n"
|
||||
gui_report += f"错误次数: {total_errors}"
|
||||
|
||||
# 在维修GUI中显示详细考核结果
|
||||
if self.maintenance_gui:
|
||||
# 创建详细的考核结果数据
|
||||
exam_result_data = {
|
||||
'final_score': final_score,
|
||||
'max_score': self.exam_max_score,
|
||||
'score_rate': score_rate,
|
||||
'accuracy_rate': accuracy_rate,
|
||||
'grade': grade,
|
||||
'grade_icon': grade_icon,
|
||||
'grade_desc': grade_desc,
|
||||
'total_operations': total_operations,
|
||||
'total_errors': total_errors,
|
||||
'perfect_steps': perfect_steps,
|
||||
'total_steps': self.total_steps,
|
||||
'step_details': [],
|
||||
'suggestions': suggestions
|
||||
}
|
||||
|
||||
# 添加每步详情
|
||||
for step_idx, step_record in self.step_scores.items():
|
||||
step_data = self.config_data['steps'][step_idx]
|
||||
step_name = step_data.get('name', f'步骤{step_idx+1}')
|
||||
max_score = step_record['max_score']
|
||||
current_score = step_record['current_score']
|
||||
tool_error = step_record['tool_error']
|
||||
attempts = step_record['operation_attempts']
|
||||
|
||||
# 判断步骤完成质量
|
||||
if current_score >= max_score:
|
||||
step_status = "🟢 完美"
|
||||
elif current_score >= max_score * 0.8:
|
||||
step_status = "🟡 良好"
|
||||
elif current_score > 0:
|
||||
step_status = "🟠 一般"
|
||||
else:
|
||||
step_status = "🔴 失败"
|
||||
|
||||
exam_result_data['step_details'].append({
|
||||
'name': step_name,
|
||||
'status': step_status,
|
||||
'current_score': current_score,
|
||||
'max_score': max_score,
|
||||
'tool_error': tool_error,
|
||||
'attempts': attempts
|
||||
})
|
||||
|
||||
# 使用GUI显示考核结果
|
||||
self.maintenance_gui.show_exam_results(exam_result_data)
|
||||
print("📋 考核结果已在GUI中显示")
|
||||
else:
|
||||
# 如果没有GUI,使用简单的控制台输出
|
||||
print("⚠️ 维修GUI不可用,考核结果仅在控制台显示")
|
||||
|
||||
# 保存考核结果到文件(可选)
|
||||
self.save_exam_results(final_score, score_rate, grade)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 显示考核结果失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
QMessageBox.critical(None, "错误", f"显示考核结果失败: {str(e)}")
|
||||
|
||||
def save_exam_results(self, final_score, score_rate, grade):
|
||||
"""保存考核结果到文件"""
|
||||
try:
|
||||
import datetime
|
||||
import json
|
||||
|
||||
# 生成考核记录
|
||||
exam_record = {
|
||||
"timestamp": datetime.datetime.now().isoformat(),
|
||||
"mode": "exam",
|
||||
"final_score": final_score,
|
||||
"max_score": self.exam_max_score,
|
||||
"score_rate": score_rate,
|
||||
"grade": grade,
|
||||
"total_steps": self.total_steps,
|
||||
"step_details": {}
|
||||
}
|
||||
|
||||
# 添加每步详情
|
||||
for step_idx, step_record in self.step_scores.items():
|
||||
step_name = self.config_data['steps'][step_idx].get('name', f'步骤{step_idx+1}')
|
||||
exam_record["step_details"][step_name] = {
|
||||
"max_score": step_record['max_score'],
|
||||
"current_score": step_record['current_score'],
|
||||
"tool_error": step_record['tool_error'],
|
||||
"operation_attempts": step_record['operation_attempts']
|
||||
}
|
||||
|
||||
# 保存到文件
|
||||
filename = f"exam_result_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
||||
filepath = os.path.join(".", filename)
|
||||
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
json.dump(exam_record, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"📄 考核结果已保存到: {filepath}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 保存考核结果失败: {e}")
|
||||
# 不影响主流程,只是记录警告
|
||||
|
||||
def stop_interaction_mode(self):
|
||||
"""停止交互模式"""
|
||||
|
||||
1023
core/maintenance_gui.py
Normal file
1023
core/maintenance_gui.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -3516,18 +3516,24 @@ class MainWindow(QMainWindow):
|
||||
with open(subject_path, 'r', encoding='utf-8') as f:
|
||||
subject_config = json.load(f)
|
||||
|
||||
# 启动拆装交互系统
|
||||
if hasattr(self.world, 'assembly_interaction') and self.world.assembly_interaction:
|
||||
# 设置配置
|
||||
self.world.assembly_interaction.config_data = subject_config
|
||||
|
||||
# 启动交互模式
|
||||
self.world.assembly_interaction.start_interaction_mode(mode=mode)
|
||||
|
||||
# 初始化拆装交互系统(如果还没有)
|
||||
from core.assembly_interaction import AssemblyInteractionManager
|
||||
|
||||
if not hasattr(self.world, 'assembly_interaction') or not self.world.assembly_interaction:
|
||||
print("🔧 初始化拆装交互系统...")
|
||||
self.world.assembly_interaction = AssemblyInteractionManager(self.world)
|
||||
|
||||
# 设置配置并启动交互模式
|
||||
self.world.assembly_interaction.config_data = subject_config
|
||||
|
||||
# 启动交互模式
|
||||
success = self.world.assembly_interaction.start_interaction_mode(mode=mode)
|
||||
|
||||
if success:
|
||||
print(f"✅ 维修科目启动成功")
|
||||
else:
|
||||
print("❌ 错误:拆装交互系统未初始化")
|
||||
QMessageBox.warning(self, "错误", "拆装交互系统未初始化")
|
||||
print("❌ 维修科目启动失败")
|
||||
QMessageBox.warning(self, "错误", "维修科目启动失败")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 启动维修科目失败: {e}")
|
||||
|
||||
658
ui/maintenance_system.py
Normal file
658
ui/maintenance_system.py
Normal file
@ -0,0 +1,658 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
维修系统界面模块
|
||||
包含登录界面、科目列表、模式选择等功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QGroupBox,
|
||||
QLineEdit, QPushButton, QLabel, QListWidget, QListWidgetItem,
|
||||
QMessageBox, QRadioButton, QTextEdit, QSplitter
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QFont, QPixmap
|
||||
|
||||
|
||||
class MaintenanceLoginDialog(QDialog):
|
||||
"""维修系统登录对话框"""
|
||||
|
||||
login_success = pyqtSignal() # 登录成功信号
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUI()
|
||||
|
||||
def setupUI(self):
|
||||
"""设置用户界面"""
|
||||
self.setWindowTitle("维修系统 - 登录")
|
||||
self.setFixedSize(400, 300)
|
||||
self.setModal(True)
|
||||
|
||||
# 应用简化样式 - 确保可见性和可用性
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
}
|
||||
QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
min-height: 20px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #0078d4;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
min-height: 30px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #106ebe;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
QGroupBox {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #0078d4;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(15)
|
||||
layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
# 标题
|
||||
title_label = QLabel("🔧 维修系统")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("""
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #0078d4;
|
||||
margin: 10px 0px;
|
||||
""")
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# 登录表单
|
||||
login_group = QGroupBox("身份验证")
|
||||
login_layout = QFormLayout(login_group)
|
||||
login_layout.setSpacing(10)
|
||||
login_layout.setContentsMargins(15, 15, 15, 15)
|
||||
|
||||
# 用户名
|
||||
self.username_edit = QLineEdit()
|
||||
self.username_edit.setPlaceholderText("请输入用户名")
|
||||
self.username_edit.setText("") # 确保清空
|
||||
login_layout.addRow("用户名:", self.username_edit)
|
||||
|
||||
# 密码
|
||||
self.password_edit = QLineEdit()
|
||||
self.password_edit.setEchoMode(QLineEdit.Password)
|
||||
self.password_edit.setPlaceholderText("请输入密码")
|
||||
self.password_edit.setText("") # 确保清空
|
||||
login_layout.addRow("密码:", self.password_edit)
|
||||
|
||||
layout.addWidget(login_group)
|
||||
|
||||
# 提示信息
|
||||
hint_label = QLabel("💡 默认账号密码均为: admin")
|
||||
hint_label.setAlignment(Qt.AlignCenter)
|
||||
hint_label.setStyleSheet("""
|
||||
color: #666666;
|
||||
font-size: 12px;
|
||||
margin: 5px 0px;
|
||||
""")
|
||||
layout.addWidget(hint_label)
|
||||
|
||||
# 按钮
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setSpacing(10)
|
||||
|
||||
self.cancel_btn = QPushButton("取消")
|
||||
self.login_btn = QPushButton("登录")
|
||||
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.cancel_btn)
|
||||
button_layout.addWidget(self.login_btn)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
self.login_btn.clicked.connect(self.on_login)
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
self.password_edit.returnPressed.connect(self.on_login)
|
||||
|
||||
# 设置默认焦点和tab顺序
|
||||
self.username_edit.setFocus()
|
||||
self.setTabOrder(self.username_edit, self.password_edit)
|
||||
self.setTabOrder(self.password_edit, self.login_btn)
|
||||
self.setTabOrder(self.login_btn, self.cancel_btn)
|
||||
|
||||
print("🔧 登录界面创建完成")
|
||||
print(f"📝 用户名输入框: {self.username_edit}")
|
||||
print(f"📝 密码输入框: {self.password_edit}")
|
||||
print(f"📝 登录按钮: {self.login_btn}")
|
||||
print(f"📝 取消按钮: {self.cancel_btn}")
|
||||
|
||||
def on_login(self):
|
||||
"""处理登录"""
|
||||
username = self.username_edit.text().strip()
|
||||
password = self.password_edit.text().strip()
|
||||
|
||||
# 验证账号密码
|
||||
if username == "admin" and password == "admin":
|
||||
print("✅ 维修系统登录成功")
|
||||
self.login_success.emit()
|
||||
self.accept()
|
||||
else:
|
||||
QMessageBox.warning(self, "登录失败",
|
||||
"用户名或密码错误!\n请使用: admin / admin")
|
||||
self.password_edit.clear()
|
||||
self.password_edit.setFocus()
|
||||
|
||||
|
||||
class MaintenanceSubjectDialog(QDialog):
|
||||
"""维修系统科目选择对话框"""
|
||||
|
||||
subject_selected = pyqtSignal(str, str) # 科目路径, 模式
|
||||
|
||||
def __init__(self, project_path, parent=None):
|
||||
super().__init__(parent)
|
||||
self.project_path = project_path
|
||||
self.subjects_path = os.path.join(project_path, "Subjects") if project_path else None
|
||||
self.current_subject_path = None
|
||||
self.setupUI()
|
||||
self.load_subjects()
|
||||
|
||||
def setupUI(self):
|
||||
"""设置用户界面"""
|
||||
self.setWindowTitle("维修系统 - 科目选择")
|
||||
self.setFixedSize(800, 600)
|
||||
self.setModal(True)
|
||||
|
||||
# 应用样式
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #1e1e2e;
|
||||
color: #e0e0ff;
|
||||
}
|
||||
QLabel {
|
||||
color: #e0e0ff;
|
||||
}
|
||||
QListWidget {
|
||||
background-color: #2d2d44;
|
||||
color: #e0e0ff;
|
||||
border: 2px solid #3a3a4a;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
QListWidget::item {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin: 2px;
|
||||
}
|
||||
QListWidget::item:hover {
|
||||
background-color: #3a3a4a;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background-color: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
QTextEdit {
|
||||
background-color: #2d2d44;
|
||||
color: #e0e0ff;
|
||||
border: 2px solid #3a3a4a;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #8b5cf6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #7c3aed;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #6d28d9;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #4c4c6e;
|
||||
color: #8888aa;
|
||||
}
|
||||
QGroupBox {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #8b5cf6;
|
||||
border: 2px solid #3a3a4a;
|
||||
border-radius: 8px;
|
||||
margin: 10px 0px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
QRadioButton {
|
||||
color: #e0e0ff;
|
||||
font-size: 14px;
|
||||
spacing: 8px;
|
||||
}
|
||||
QRadioButton::indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
QRadioButton::indicator:unchecked {
|
||||
border: 2px solid #3a3a4a;
|
||||
border-radius: 8px;
|
||||
background-color: #2d2d44;
|
||||
}
|
||||
QRadioButton::indicator:checked {
|
||||
border: 2px solid #8b5cf6;
|
||||
border-radius: 8px;
|
||||
background-color: #8b5cf6;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# 标题
|
||||
title_label = QLabel("📚 选择维修科目")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("""
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #8b5cf6;
|
||||
margin: 15px 0px;
|
||||
""")
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# 主要内容区域
|
||||
main_splitter = QSplitter(Qt.Horizontal)
|
||||
|
||||
# 左侧:科目列表
|
||||
left_widget = QGroupBox("科目列表")
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
|
||||
self.subject_list = QListWidget()
|
||||
self.subject_list.itemClicked.connect(self.on_subject_selected)
|
||||
left_layout.addWidget(self.subject_list)
|
||||
|
||||
main_splitter.addWidget(left_widget)
|
||||
|
||||
# 右侧:科目详情和模式选择
|
||||
right_widget = QGroupBox("科目详情")
|
||||
right_layout = QVBoxLayout(right_widget)
|
||||
|
||||
# 科目描述
|
||||
self.subject_description = QTextEdit()
|
||||
self.subject_description.setReadOnly(True)
|
||||
self.subject_description.setMaximumHeight(150)
|
||||
self.subject_description.setPlainText("请选择一个科目查看详情...")
|
||||
right_layout.addWidget(self.subject_description)
|
||||
|
||||
# 模式选择
|
||||
mode_group = QGroupBox("选择模式")
|
||||
mode_layout = QVBoxLayout(mode_group)
|
||||
|
||||
self.training_radio = QRadioButton("🎓 训练模式")
|
||||
self.training_radio.setChecked(True)
|
||||
mode_layout.addWidget(self.training_radio)
|
||||
|
||||
training_desc = QLabel("• 显示详细的步骤描述和操作提示\n• 提供工具选择的正确性提示\n• 播放语音指导")
|
||||
training_desc.setStyleSheet("color: #94a3b8; font-size: 12px; margin-left: 20px;")
|
||||
mode_layout.addWidget(training_desc)
|
||||
|
||||
self.exam_radio = QRadioButton("📋 考核模式")
|
||||
mode_layout.addWidget(self.exam_radio)
|
||||
|
||||
exam_desc = QLabel("• 不显示步骤描述\n• 不提供工具选择提示\n• 无语音指导,独立完成操作")
|
||||
exam_desc.setStyleSheet("color: #94a3b8; font-size: 12px; margin-left: 20px;")
|
||||
mode_layout.addWidget(exam_desc)
|
||||
|
||||
right_layout.addWidget(mode_group)
|
||||
|
||||
main_splitter.addWidget(right_widget)
|
||||
|
||||
# 设置分割器比例
|
||||
main_splitter.setSizes([300, 500])
|
||||
layout.addWidget(main_splitter)
|
||||
|
||||
# 底部按钮
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
self.refresh_btn = QPushButton("🔄 刷新")
|
||||
self.cancel_btn = QPushButton("取消")
|
||||
self.start_btn = QPushButton("开始")
|
||||
self.start_btn.setEnabled(False)
|
||||
|
||||
button_layout.addWidget(self.refresh_btn)
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.cancel_btn)
|
||||
button_layout.addWidget(self.start_btn)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
self.refresh_btn.clicked.connect(self.load_subjects)
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
self.start_btn.clicked.connect(self.on_start)
|
||||
|
||||
def load_subjects(self):
|
||||
"""加载科目列表"""
|
||||
self.subject_list.clear()
|
||||
self.current_subject_path = None
|
||||
self.start_btn.setEnabled(False)
|
||||
self.subject_description.setPlainText("请选择一个科目查看详情...")
|
||||
|
||||
if not self.subjects_path or not os.path.exists(self.subjects_path):
|
||||
# 添加提示项
|
||||
item = QListWidgetItem("📁 未找到Subjects目录")
|
||||
item.setData(Qt.UserRole, None)
|
||||
self.subject_list.addItem(item)
|
||||
|
||||
if not self.project_path:
|
||||
self.subject_description.setPlainText(
|
||||
"❌ 错误:没有打开的项目\n\n"
|
||||
"请先通过 文件 -> 打开 菜单打开一个项目,\n"
|
||||
"然后再使用维修系统功能。"
|
||||
)
|
||||
else:
|
||||
self.subject_description.setPlainText(
|
||||
f"❌ 错误:找不到Subjects目录\n\n"
|
||||
f"项目路径: {self.project_path}\n"
|
||||
f"期望目录: {self.subjects_path}\n\n"
|
||||
"请在项目根目录下创建Subjects文件夹,\n"
|
||||
"并在其中放置科目的JSON配置文件。"
|
||||
)
|
||||
return
|
||||
|
||||
# 扫描JSON文件
|
||||
json_files = []
|
||||
try:
|
||||
for file in os.listdir(self.subjects_path):
|
||||
if file.endswith('.json'):
|
||||
json_path = os.path.join(self.subjects_path, file)
|
||||
json_files.append((file, json_path))
|
||||
except Exception as e:
|
||||
print(f"❌ 扫描Subjects目录失败: {e}")
|
||||
item = QListWidgetItem("❌ 读取目录失败")
|
||||
item.setData(Qt.UserRole, None)
|
||||
self.subject_list.addItem(item)
|
||||
return
|
||||
|
||||
if not json_files:
|
||||
item = QListWidgetItem("📄 目录中没有JSON文件")
|
||||
item.setData(Qt.UserRole, None)
|
||||
self.subject_list.addItem(item)
|
||||
self.subject_description.setPlainText(
|
||||
f"ℹ️ 提示:Subjects目录为空\n\n"
|
||||
f"目录路径: {self.subjects_path}\n\n"
|
||||
"请在此目录中放置维修科目的JSON配置文件。"
|
||||
)
|
||||
return
|
||||
|
||||
# 加载科目
|
||||
loaded_count = 0
|
||||
for filename, filepath in json_files:
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
subject_data = json.load(f)
|
||||
|
||||
# 获取科目名称
|
||||
subject_name = subject_data.get('name', filename.replace('.json', ''))
|
||||
|
||||
# 创建列表项
|
||||
item = QListWidgetItem(f"📋 {subject_name}")
|
||||
item.setData(Qt.UserRole, {
|
||||
'path': filepath,
|
||||
'data': subject_data,
|
||||
'filename': filename
|
||||
})
|
||||
self.subject_list.addItem(item)
|
||||
loaded_count += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 加载科目文件失败 {filename}: {e}")
|
||||
item = QListWidgetItem(f"❌ {filename} (加载失败)")
|
||||
item.setData(Qt.UserRole, None)
|
||||
self.subject_list.addItem(item)
|
||||
|
||||
print(f"✅ 成功加载 {loaded_count} 个维修科目")
|
||||
|
||||
def on_subject_selected(self, item):
|
||||
"""处理科目选择"""
|
||||
try:
|
||||
print(f"📋 科目选择事件触发,item: {item}")
|
||||
|
||||
if not item:
|
||||
print("❌ item为空")
|
||||
self.current_subject_path = None
|
||||
self.start_btn.setEnabled(False)
|
||||
self.subject_description.setPlainText("请选择一个有效的科目。")
|
||||
return
|
||||
|
||||
subject_info = item.data(Qt.UserRole)
|
||||
print(f"📋 科目信息: {subject_info}")
|
||||
|
||||
if not subject_info:
|
||||
print("❌ 科目信息为空")
|
||||
self.current_subject_path = None
|
||||
self.start_btn.setEnabled(False)
|
||||
self.subject_description.setPlainText("此科目无法加载,请检查文件格式。")
|
||||
return
|
||||
|
||||
self.current_subject_path = subject_info['path']
|
||||
self.start_btn.setEnabled(True)
|
||||
|
||||
print(f"✅ 选择科目: {subject_info.get('filename', 'unknown')}")
|
||||
|
||||
# 显示科目详情
|
||||
subject_data = subject_info['data']
|
||||
description = self.format_subject_description(subject_data, subject_info['filename'])
|
||||
self.subject_description.setPlainText(description)
|
||||
|
||||
print("✅ 科目详情显示完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 科目选择处理失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
self.current_subject_path = None
|
||||
self.start_btn.setEnabled(False)
|
||||
self.subject_description.setPlainText(f"处理科目信息时出错:\n{str(e)}")
|
||||
|
||||
# 显示错误对话框
|
||||
QMessageBox.critical(self, "错误", f"处理科目选择时出错:\n{str(e)}")
|
||||
|
||||
def format_subject_description(self, subject_data, filename):
|
||||
"""格式化科目描述"""
|
||||
try:
|
||||
print(f"🔧 格式化科目描述: {filename}")
|
||||
|
||||
if not subject_data:
|
||||
return "❌ 科目数据为空"
|
||||
|
||||
lines = []
|
||||
|
||||
# 基本信息
|
||||
name = subject_data.get('name', '未知科目')
|
||||
lines.append(f"📋 科目: {name}")
|
||||
lines.append(f"📁 文件: {filename}")
|
||||
lines.append("")
|
||||
|
||||
# 描述信息
|
||||
if 'description' in subject_data and subject_data['description']:
|
||||
lines.append(f"📝 描述: {subject_data['description']}")
|
||||
lines.append("")
|
||||
|
||||
# 步骤信息
|
||||
steps = subject_data.get('steps', [])
|
||||
if steps and isinstance(steps, list):
|
||||
lines.append(f"📊 步骤数量: {len(steps)}")
|
||||
|
||||
# 计算总分
|
||||
try:
|
||||
total_score = 0
|
||||
for step in steps:
|
||||
if isinstance(step, dict):
|
||||
score = step.get('score', 0)
|
||||
if isinstance(score, (int, float)):
|
||||
total_score += score
|
||||
|
||||
if total_score > 0:
|
||||
lines.append(f"💯 总分: {total_score} 分")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 计算总分时出错: {e}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("📋 步骤列表:")
|
||||
|
||||
for i, step in enumerate(steps, 1):
|
||||
try:
|
||||
if isinstance(step, dict):
|
||||
step_name = step.get('name', f'步骤{i}')
|
||||
step_score = step.get('score', 0)
|
||||
|
||||
if isinstance(step_score, (int, float)) and step_score > 0:
|
||||
lines.append(f" {i}. {step_name} ({step_score}分)")
|
||||
else:
|
||||
lines.append(f" {i}. {step_name}")
|
||||
else:
|
||||
lines.append(f" {i}. 步骤{i} (格式错误)")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 处理步骤{i}时出错: {e}")
|
||||
lines.append(f" {i}. 步骤{i} (处理错误)")
|
||||
else:
|
||||
lines.append("⚠️ 此科目没有配置步骤")
|
||||
|
||||
# 工具信息
|
||||
tools = subject_data.get('tools', [])
|
||||
if tools and isinstance(tools, list):
|
||||
try:
|
||||
valid_tools = [str(tool) for tool in tools if tool]
|
||||
if valid_tools:
|
||||
lines.append("")
|
||||
lines.append(f"🔧 可用工具: {', '.join(valid_tools)}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 处理工具列表时出错: {e}")
|
||||
lines.append("")
|
||||
lines.append("🔧 可用工具: (处理错误)")
|
||||
|
||||
result = '\n'.join(lines)
|
||||
print(f"✅ 科目描述格式化完成,长度: {len(result)}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 格式化科目描述失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
return f"❌ 格式化科目描述时出错:\n{str(e)}\n\n文件: {filename}"
|
||||
|
||||
def on_start(self):
|
||||
"""开始科目"""
|
||||
if not self.current_subject_path:
|
||||
QMessageBox.warning(self, "错误", "请先选择一个科目!")
|
||||
return
|
||||
|
||||
# 获取选择的模式
|
||||
mode = "training" if self.training_radio.isChecked() else "exam"
|
||||
|
||||
print(f"🚀 开始维修科目: {self.current_subject_path}, 模式: {mode}")
|
||||
|
||||
# 发射信号
|
||||
self.subject_selected.emit(self.current_subject_path, mode)
|
||||
self.accept()
|
||||
|
||||
|
||||
class MaintenanceSystemManager:
|
||||
"""维修系统管理器"""
|
||||
|
||||
def __init__(self, world):
|
||||
self.world = world
|
||||
self.login_dialog = None
|
||||
self.subject_dialog = None
|
||||
|
||||
def show_maintenance_system(self, parent_window):
|
||||
"""显示维修系统"""
|
||||
# 创建登录对话框
|
||||
self.login_dialog = MaintenanceLoginDialog(parent_window)
|
||||
self.login_dialog.login_success.connect(
|
||||
lambda: self.show_subject_selection(parent_window)
|
||||
)
|
||||
self.login_dialog.exec_()
|
||||
|
||||
def show_subject_selection(self, parent_window):
|
||||
"""显示科目选择界面"""
|
||||
# 获取当前项目路径
|
||||
project_path = None
|
||||
if hasattr(self.world, 'project_manager') and self.world.project_manager:
|
||||
project_path = self.world.project_manager.getCurrentProjectPath()
|
||||
|
||||
# 创建科目选择对话框
|
||||
self.subject_dialog = MaintenanceSubjectDialog(project_path, parent_window)
|
||||
self.subject_dialog.subject_selected.connect(self.start_maintenance_subject)
|
||||
self.subject_dialog.exec_()
|
||||
|
||||
def start_maintenance_subject(self, subject_path, mode):
|
||||
"""开始维修科目"""
|
||||
try:
|
||||
print(f"🎯 启动维修科目: {subject_path}")
|
||||
print(f"📝 模式: {mode}")
|
||||
|
||||
# 加载科目配置
|
||||
with open(subject_path, 'r', encoding='utf-8') as f:
|
||||
subject_config = json.load(f)
|
||||
|
||||
# 启动拆装交互系统
|
||||
if hasattr(self.world, 'assembly_interaction') and self.world.assembly_interaction:
|
||||
# 设置配置
|
||||
self.world.assembly_interaction.config_data = subject_config
|
||||
|
||||
# 启动交互模式
|
||||
self.world.assembly_interaction.start_interaction_mode(mode=mode)
|
||||
|
||||
print(f"✅ 维修科目启动成功")
|
||||
else:
|
||||
print("❌ 错误:拆装交互系统未初始化")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 启动维修科目失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
220
ui/simple_maintenance_login.py
Normal file
220
ui/simple_maintenance_login.py
Normal file
@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
简化的维修系统登录界面
|
||||
解决显示和输入问题
|
||||
"""
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QGroupBox,
|
||||
QLineEdit, QPushButton, QLabel, QMessageBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
|
||||
|
||||
class SimpleMaintenanceLoginDialog(QDialog):
|
||||
"""简化的维修系统登录对话框"""
|
||||
|
||||
login_success = pyqtSignal() # 登录成功信号
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUI()
|
||||
|
||||
def setupUI(self):
|
||||
"""设置用户界面"""
|
||||
self.setWindowTitle("维修系统 - 登录")
|
||||
self.resize(400, 250)
|
||||
self.setModal(True)
|
||||
|
||||
# 设置简洁的样式
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #ffffff;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
QGroupBox {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
border: 2px solid #bdc3c7;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
QLabel {
|
||||
color: #2c3e50;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #ffffff;
|
||||
border: 2px solid #bdc3c7;
|
||||
border-radius: 3px;
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
min-height: 16px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #3498db;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 20px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
min-height: 16px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #21618c;
|
||||
}
|
||||
QPushButton#cancel_btn {
|
||||
background-color: #95a5a6;
|
||||
}
|
||||
QPushButton#cancel_btn:hover {
|
||||
background-color: #7f8c8d;
|
||||
}
|
||||
""")
|
||||
|
||||
# 主布局
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setSpacing(20)
|
||||
main_layout.setContentsMargins(30, 30, 30, 30)
|
||||
|
||||
# 标题
|
||||
title_label = QLabel("🔧 维修系统")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("""
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
margin: 10px 0px;
|
||||
""")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# 登录表单组
|
||||
form_group = QGroupBox("身份验证")
|
||||
form_layout = QFormLayout(form_group)
|
||||
form_layout.setSpacing(15)
|
||||
form_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
# 用户名输入框
|
||||
self.username_edit = QLineEdit()
|
||||
self.username_edit.setPlaceholderText("请输入用户名")
|
||||
form_layout.addRow("用户名:", self.username_edit)
|
||||
|
||||
# 密码输入框
|
||||
self.password_edit = QLineEdit()
|
||||
self.password_edit.setEchoMode(QLineEdit.Password)
|
||||
self.password_edit.setPlaceholderText("请输入密码")
|
||||
form_layout.addRow("密码:", self.password_edit)
|
||||
|
||||
main_layout.addWidget(form_group)
|
||||
|
||||
# 提示信息
|
||||
hint_label = QLabel("💡 默认账号密码均为: admin")
|
||||
hint_label.setAlignment(Qt.AlignCenter)
|
||||
hint_label.setStyleSheet("""
|
||||
color: #7f8c8d;
|
||||
font-size: 12px;
|
||||
margin: 5px 0px;
|
||||
""")
|
||||
main_layout.addWidget(hint_label)
|
||||
|
||||
# 按钮布局
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setSpacing(10)
|
||||
|
||||
self.cancel_btn = QPushButton("取消")
|
||||
self.cancel_btn.setObjectName("cancel_btn")
|
||||
|
||||
self.login_btn = QPushButton("登录")
|
||||
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.cancel_btn)
|
||||
button_layout.addWidget(self.login_btn)
|
||||
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
self.login_btn.clicked.connect(self.handle_login)
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
self.password_edit.returnPressed.connect(self.handle_login)
|
||||
|
||||
# 设置焦点和Tab顺序
|
||||
self.username_edit.setFocus()
|
||||
self.setTabOrder(self.username_edit, self.password_edit)
|
||||
self.setTabOrder(self.password_edit, self.login_btn)
|
||||
self.setTabOrder(self.login_btn, self.cancel_btn)
|
||||
|
||||
print("✅ 简化登录界面创建完成")
|
||||
print(f"📝 窗口大小: {self.size().width()}x{self.size().height()}")
|
||||
|
||||
def handle_login(self):
|
||||
"""处理登录"""
|
||||
username = self.username_edit.text().strip()
|
||||
password = self.password_edit.text().strip()
|
||||
|
||||
print(f"🔍 登录尝试: 用户名='{username}', 密码='{password}'")
|
||||
|
||||
# 验证账号密码
|
||||
if username == "admin" and password == "admin":
|
||||
print("✅ 登录验证成功")
|
||||
self.login_success.emit()
|
||||
self.accept()
|
||||
else:
|
||||
print("❌ 登录验证失败")
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"登录失败",
|
||||
"用户名或密码错误!\n\n请使用:\n用户名: admin\n密码: admin"
|
||||
)
|
||||
self.password_edit.clear()
|
||||
self.password_edit.setFocus()
|
||||
|
||||
|
||||
def test_simple_login():
|
||||
"""测试简化登录界面"""
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
print("🧪 测试简化登录界面")
|
||||
print("=" * 30)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
dialog = SimpleMaintenanceLoginDialog()
|
||||
|
||||
print("💡 请测试以下功能:")
|
||||
print("1. 检查是否显示完整的登录界面")
|
||||
print("2. 在用户名框输入 'admin'")
|
||||
print("3. 在密码框输入 'admin'")
|
||||
print("4. 点击登录按钮")
|
||||
|
||||
def on_success():
|
||||
print("🎉 登录成功!")
|
||||
QMessageBox.information(None, "成功", "登录测试成功!")
|
||||
|
||||
dialog.login_success.connect(on_success)
|
||||
|
||||
result = dialog.exec_()
|
||||
print(f"📊 测试结果: {'成功' if result == QDialog.Accepted else '取消'}")
|
||||
|
||||
return result == QDialog.Accepted
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_simple_login()
|
||||
Loading…
Reference in New Issue
Block a user