1.虚拟维修基本流程

This commit is contained in:
陈横 2025-09-26 09:24:07 +08:00
parent d32993b903
commit 64df3107fe
5 changed files with 2395 additions and 74 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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