Merge remote-tracking branch 'refs/remotes/origin/main_ch_eg' into addRender
# Conflicts: # scene/scene_manager.py # ui/main_window.py
This commit is contained in:
commit
0d898b498a
File diff suppressed because one or more lines are too long
2285
core/assembly_interaction.py
Normal file
2285
core/assembly_interaction.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,10 @@ class CollisionManager:
|
||||
# === 基础碰撞掩码定义 ===
|
||||
# 每个掩码使用不同的位(bit)来标识,可以进行位运算组合
|
||||
|
||||
'TERRAIN': BitMask32.bit(0), # 地形/地面 - 通常用于地面碰撞检测
|
||||
'UI_ELEMENT': BitMask32.bit(1), # UI元素 - 界面组件的碰撞检测
|
||||
'CAMERA': BitMask32.bit(2), # 摄像机 - 相机的碰撞检测
|
||||
'MODEL_COLLISION': BitMask32.bit(3), # 模型碰撞 - 通用模型间碰撞检测
|
||||
# 'TERRAIN': BitMask32.bit(0), # 地形/地面 - 通常用于地面碰撞检测
|
||||
# 'UI_ELEMENT': BitMask32.bit(1), # UI元素 - 界面组件的碰撞检测
|
||||
# 'CAMERA': BitMask32.bit(2), # 摄像机 - 相机的碰撞检测
|
||||
'MODEL_COLLISION': BitMask32.bit(6), # 模型碰撞 - 通用模型间碰撞检测
|
||||
}
|
||||
|
||||
# 碰撞体形状类型
|
||||
|
||||
1090
core/maintenance_gui.py
Normal file
1090
core/maintenance_gui.py
Normal file
File diff suppressed because it is too large
Load Diff
944
ui/assembly_disassembly_config.py
Normal file
944
ui/assembly_disassembly_config.py
Normal file
@ -0,0 +1,944 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
拆装交互配置界面
|
||||
实现模型的分步拆装交互功能配置
|
||||
"""
|
||||
|
||||
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QTabWidget,
|
||||
QWidget, QLabel, QListWidget, QListWidgetItem,
|
||||
QPushButton, QComboBox, QSpinBox, QLineEdit,
|
||||
QTextEdit, QGroupBox, QGridLayout, QCheckBox,
|
||||
QSplitter, QTreeWidget, QTreeWidgetItem,
|
||||
QMessageBox, QFileDialog, QScrollArea,
|
||||
QFrame, QDoubleSpinBox, QSlider, QProgressBar)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QFont
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class AssemblyDisassemblyConfigDialog(QDialog):
|
||||
"""拆装配置主对话框"""
|
||||
|
||||
def __init__(self, parent=None, world=None):
|
||||
super().__init__(parent)
|
||||
self.world = world
|
||||
self.config_data = {
|
||||
'models': [], # 参与拆装的模型列表
|
||||
'steps': [], # 拆装步骤配置
|
||||
'tools': [], # 使用的工具列表
|
||||
'settings': {} # 全局设置
|
||||
}
|
||||
|
||||
self.setupUI()
|
||||
self.loadSceneModels()
|
||||
|
||||
def setupUI(self):
|
||||
"""设置界面"""
|
||||
self.setWindowTitle("拆装交互配置")
|
||||
self.setModal(False) # 设置为非模态对话框
|
||||
self.resize(1200, 800)
|
||||
|
||||
# 主布局
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# 创建分割器
|
||||
splitter = QSplitter(Qt.Horizontal)
|
||||
main_layout.addWidget(splitter)
|
||||
|
||||
# 左侧面板 - 模型和工具选择
|
||||
left_panel = self.createLeftPanel()
|
||||
splitter.addWidget(left_panel)
|
||||
|
||||
# 右侧面板 - 步骤配置
|
||||
right_panel = self.createRightPanel()
|
||||
splitter.addWidget(right_panel)
|
||||
|
||||
# 设置分割器比例
|
||||
splitter.setSizes([400, 800])
|
||||
|
||||
# 底部按钮
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
self.save_button = QPushButton("保存配置")
|
||||
self.load_button = QPushButton("加载配置")
|
||||
self.preview_button = QPushButton("预览效果")
|
||||
self.apply_button = QPushButton("应用配置")
|
||||
|
||||
button_layout.addWidget(self.save_button)
|
||||
button_layout.addWidget(self.load_button)
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.preview_button)
|
||||
button_layout.addWidget(self.apply_button)
|
||||
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
self.connectSignals()
|
||||
|
||||
def createLeftPanel(self):
|
||||
"""创建左侧面板"""
|
||||
left_widget = QWidget()
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
|
||||
# 场景模型选择区域
|
||||
models_group = QGroupBox("场景模型")
|
||||
models_layout = QVBoxLayout(models_group)
|
||||
|
||||
# 模型搜索框
|
||||
search_layout = QHBoxLayout()
|
||||
search_label = QLabel("搜索:")
|
||||
self.model_search_edit = QLineEdit()
|
||||
self.model_search_edit.setPlaceholderText("输入模型名称搜索...")
|
||||
search_layout.addWidget(search_label)
|
||||
search_layout.addWidget(self.model_search_edit)
|
||||
models_layout.addLayout(search_layout)
|
||||
|
||||
# 场景模型列表
|
||||
self.scene_models_list = QTreeWidget()
|
||||
self.scene_models_list.setHeaderLabel("场景中的模型")
|
||||
self.scene_models_list.setSelectionMode(QTreeWidget.ExtendedSelection)
|
||||
models_layout.addWidget(self.scene_models_list)
|
||||
|
||||
# 选中模型操作按钮
|
||||
model_buttons_layout = QHBoxLayout()
|
||||
self.add_to_config_button = QPushButton("添加到配置")
|
||||
self.remove_from_config_button = QPushButton("从配置移除")
|
||||
model_buttons_layout.addWidget(self.add_to_config_button)
|
||||
model_buttons_layout.addWidget(self.remove_from_config_button)
|
||||
models_layout.addLayout(model_buttons_layout)
|
||||
|
||||
left_layout.addWidget(models_group)
|
||||
|
||||
# 已配置模型列表
|
||||
config_models_group = QGroupBox("已配置模型")
|
||||
config_models_layout = QVBoxLayout(config_models_group)
|
||||
|
||||
self.config_models_list = QListWidget()
|
||||
config_models_layout.addWidget(self.config_models_list)
|
||||
|
||||
left_layout.addWidget(config_models_group)
|
||||
|
||||
# 工具配置区域
|
||||
tools_group = QGroupBox("工具配置")
|
||||
tools_layout = QVBoxLayout(tools_group)
|
||||
|
||||
# 工具添加
|
||||
tool_add_layout = QHBoxLayout()
|
||||
self.tool_name_edit = QLineEdit()
|
||||
self.tool_name_edit.setPlaceholderText("工具名称")
|
||||
self.add_tool_button = QPushButton("添加工具")
|
||||
tool_add_layout.addWidget(self.tool_name_edit)
|
||||
tool_add_layout.addWidget(self.add_tool_button)
|
||||
tools_layout.addLayout(tool_add_layout)
|
||||
|
||||
# 工具列表
|
||||
self.tools_list = QListWidget()
|
||||
tools_layout.addWidget(self.tools_list)
|
||||
|
||||
# 工具操作按钮
|
||||
tool_buttons_layout = QHBoxLayout()
|
||||
self.edit_tool_button = QPushButton("编辑")
|
||||
self.remove_tool_button = QPushButton("删除")
|
||||
tool_buttons_layout.addWidget(self.edit_tool_button)
|
||||
tool_buttons_layout.addWidget(self.remove_tool_button)
|
||||
tools_layout.addLayout(tool_buttons_layout)
|
||||
|
||||
left_layout.addWidget(tools_group)
|
||||
|
||||
return left_widget
|
||||
|
||||
def createRightPanel(self):
|
||||
"""创建右侧面板"""
|
||||
right_widget = QWidget()
|
||||
right_layout = QVBoxLayout(right_widget)
|
||||
|
||||
# 步骤配置标签页
|
||||
self.tab_widget = QTabWidget()
|
||||
|
||||
# 步骤列表标签页
|
||||
self.steps_tab = self.createStepsTab()
|
||||
self.tab_widget.addTab(self.steps_tab, "步骤配置")
|
||||
|
||||
# 全局设置标签页
|
||||
self.settings_tab = self.createSettingsTab()
|
||||
self.tab_widget.addTab(self.settings_tab, "全局设置")
|
||||
|
||||
right_layout.addWidget(self.tab_widget)
|
||||
|
||||
return right_widget
|
||||
|
||||
def createStepsTab(self):
|
||||
"""创建步骤配置标签页"""
|
||||
steps_widget = QWidget()
|
||||
steps_layout = QHBoxLayout(steps_widget)
|
||||
|
||||
# 左侧步骤列表
|
||||
steps_list_layout = QVBoxLayout()
|
||||
|
||||
# 步骤列表标题和操作按钮
|
||||
steps_header_layout = QHBoxLayout()
|
||||
steps_label = QLabel("拆装步骤")
|
||||
steps_label.setFont(QFont("", 12, QFont.Bold))
|
||||
self.add_step_button = QPushButton("添加步骤")
|
||||
self.remove_step_button = QPushButton("删除步骤")
|
||||
steps_header_layout.addWidget(steps_label)
|
||||
steps_header_layout.addStretch()
|
||||
steps_header_layout.addWidget(self.add_step_button)
|
||||
steps_header_layout.addWidget(self.remove_step_button)
|
||||
steps_list_layout.addLayout(steps_header_layout)
|
||||
|
||||
# 步骤列表
|
||||
self.steps_list = QListWidget()
|
||||
steps_list_layout.addWidget(self.steps_list)
|
||||
|
||||
# 步骤顺序调整按钮
|
||||
step_order_layout = QHBoxLayout()
|
||||
self.move_step_up_button = QPushButton("上移")
|
||||
self.move_step_down_button = QPushButton("下移")
|
||||
step_order_layout.addWidget(self.move_step_up_button)
|
||||
step_order_layout.addWidget(self.move_step_down_button)
|
||||
steps_list_layout.addLayout(step_order_layout)
|
||||
|
||||
# 右侧步骤详细配置
|
||||
self.step_config_widget = self.createStepConfigWidget()
|
||||
|
||||
# 添加到布局
|
||||
steps_layout.addLayout(steps_list_layout, 1)
|
||||
steps_layout.addWidget(self.step_config_widget, 2)
|
||||
|
||||
return steps_widget
|
||||
|
||||
def createStepConfigWidget(self):
|
||||
"""创建步骤详细配置部件"""
|
||||
config_widget = QWidget()
|
||||
config_layout = QVBoxLayout(config_widget)
|
||||
|
||||
# 滚动区域
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidgetResizable(True)
|
||||
scroll_content = QWidget()
|
||||
scroll_layout = QVBoxLayout(scroll_content)
|
||||
|
||||
# 步骤基本信息
|
||||
basic_info_group = QGroupBox("基本信息")
|
||||
basic_info_layout = QGridLayout(basic_info_group)
|
||||
|
||||
# 步骤名称
|
||||
basic_info_layout.addWidget(QLabel("步骤名称:"), 0, 0)
|
||||
self.step_name_edit = QLineEdit()
|
||||
basic_info_layout.addWidget(self.step_name_edit, 0, 1)
|
||||
|
||||
# 步骤描述
|
||||
basic_info_layout.addWidget(QLabel("步骤描述:"), 1, 0)
|
||||
self.step_description_edit = QTextEdit()
|
||||
self.step_description_edit.setMaximumHeight(80)
|
||||
basic_info_layout.addWidget(self.step_description_edit, 1, 1)
|
||||
|
||||
# 步骤类型
|
||||
basic_info_layout.addWidget(QLabel("步骤类型:"), 2, 0)
|
||||
self.step_type_combo = QComboBox()
|
||||
self.step_type_combo.addItems(["拆卸", "安装"])
|
||||
basic_info_layout.addWidget(self.step_type_combo, 2, 1)
|
||||
|
||||
scroll_layout.addWidget(basic_info_group)
|
||||
|
||||
# 目标模型配置
|
||||
target_model_group = QGroupBox("目标模型")
|
||||
target_model_layout = QGridLayout(target_model_group)
|
||||
|
||||
# 目标模型选择
|
||||
target_model_layout.addWidget(QLabel("目标模型:"), 0, 0)
|
||||
self.target_model_combo = QComboBox()
|
||||
target_model_layout.addWidget(self.target_model_combo, 0, 1)
|
||||
|
||||
# 交互方式
|
||||
target_model_layout.addWidget(QLabel("交互方式:"), 1, 0)
|
||||
self.interaction_type_combo = QComboBox()
|
||||
self.interaction_type_combo.addItems(["鼠标拖拽", "点击触发"])
|
||||
target_model_layout.addWidget(self.interaction_type_combo, 1, 1)
|
||||
|
||||
scroll_layout.addWidget(target_model_group)
|
||||
|
||||
# 拖拽交互配置
|
||||
self.drag_config_group = QGroupBox("拖拽交互配置")
|
||||
drag_config_layout = QGridLayout(self.drag_config_group)
|
||||
|
||||
# 目标位置
|
||||
drag_config_layout.addWidget(QLabel("目标位置 X:"), 0, 0)
|
||||
self.target_pos_x_spin = QDoubleSpinBox()
|
||||
self.target_pos_x_spin.setRange(-1000, 1000)
|
||||
self.target_pos_x_spin.setSingleStep(0.1)
|
||||
drag_config_layout.addWidget(self.target_pos_x_spin, 0, 1)
|
||||
|
||||
drag_config_layout.addWidget(QLabel("目标位置 Y:"), 1, 0)
|
||||
self.target_pos_y_spin = QDoubleSpinBox()
|
||||
self.target_pos_y_spin.setRange(-1000, 1000)
|
||||
self.target_pos_y_spin.setSingleStep(0.1)
|
||||
drag_config_layout.addWidget(self.target_pos_y_spin, 1, 1)
|
||||
|
||||
drag_config_layout.addWidget(QLabel("目标位置 Z:"), 2, 0)
|
||||
self.target_pos_z_spin = QDoubleSpinBox()
|
||||
self.target_pos_z_spin.setRange(-1000, 1000)
|
||||
self.target_pos_z_spin.setSingleStep(0.1)
|
||||
drag_config_layout.addWidget(self.target_pos_z_spin, 2, 1)
|
||||
|
||||
# 拖拽约束
|
||||
self.constrain_x_check = QCheckBox("约束X轴")
|
||||
self.constrain_y_check = QCheckBox("约束Y轴")
|
||||
self.constrain_z_check = QCheckBox("约束Z轴")
|
||||
|
||||
constraint_layout = QHBoxLayout()
|
||||
constraint_layout.addWidget(self.constrain_x_check)
|
||||
constraint_layout.addWidget(self.constrain_y_check)
|
||||
constraint_layout.addWidget(self.constrain_z_check)
|
||||
|
||||
drag_config_layout.addWidget(QLabel("拖拽约束:"), 3, 0)
|
||||
drag_config_layout.addLayout(constraint_layout, 3, 1)
|
||||
|
||||
scroll_layout.addWidget(self.drag_config_group)
|
||||
|
||||
# 点击触发配置
|
||||
self.click_config_group = QGroupBox("点击触发配置")
|
||||
click_config_layout = QGridLayout(self.click_config_group)
|
||||
|
||||
# 位移距离
|
||||
click_config_layout.addWidget(QLabel("位移距离 X:"), 0, 0)
|
||||
self.move_distance_x_spin = QDoubleSpinBox()
|
||||
self.move_distance_x_spin.setRange(-100, 100)
|
||||
self.move_distance_x_spin.setSingleStep(0.1)
|
||||
click_config_layout.addWidget(self.move_distance_x_spin, 0, 1)
|
||||
|
||||
click_config_layout.addWidget(QLabel("位移距离 Y:"), 1, 0)
|
||||
self.move_distance_y_spin = QDoubleSpinBox()
|
||||
self.move_distance_y_spin.setRange(-100, 100)
|
||||
self.move_distance_y_spin.setSingleStep(0.1)
|
||||
click_config_layout.addWidget(self.move_distance_y_spin, 1, 1)
|
||||
|
||||
click_config_layout.addWidget(QLabel("位移距离 Z:"), 2, 0)
|
||||
self.move_distance_z_spin = QDoubleSpinBox()
|
||||
self.move_distance_z_spin.setRange(-100, 100)
|
||||
self.move_distance_z_spin.setSingleStep(0.1)
|
||||
click_config_layout.addWidget(self.move_distance_z_spin, 2, 1)
|
||||
|
||||
# 位移时间
|
||||
click_config_layout.addWidget(QLabel("位移时间(秒):"), 3, 0)
|
||||
self.move_duration_spin = QDoubleSpinBox()
|
||||
self.move_duration_spin.setRange(0.1, 10.0)
|
||||
self.move_duration_spin.setSingleStep(0.1)
|
||||
self.move_duration_spin.setValue(1.0)
|
||||
click_config_layout.addWidget(self.move_duration_spin, 3, 1)
|
||||
|
||||
# 完成后隐藏
|
||||
self.hide_after_move_check = QCheckBox("位移完成后隐藏模型")
|
||||
click_config_layout.addWidget(self.hide_after_move_check, 4, 0, 1, 2)
|
||||
|
||||
scroll_layout.addWidget(self.click_config_group)
|
||||
|
||||
# 提示音频配置
|
||||
audio_config_group = QGroupBox("步骤提示")
|
||||
audio_config_layout = QGridLayout(audio_config_group)
|
||||
|
||||
# 提示音频文件
|
||||
audio_config_layout.addWidget(QLabel("提示音频:"), 0, 0)
|
||||
audio_file_layout = QHBoxLayout()
|
||||
self.audio_file_edit = QLineEdit()
|
||||
self.browse_audio_button = QPushButton("浏览...")
|
||||
audio_file_layout.addWidget(self.audio_file_edit)
|
||||
audio_file_layout.addWidget(self.browse_audio_button)
|
||||
audio_config_layout.addLayout(audio_file_layout, 0, 1)
|
||||
|
||||
# 提示文字
|
||||
audio_config_layout.addWidget(QLabel("提示文字:"), 1, 0)
|
||||
self.hint_text_edit = QTextEdit()
|
||||
self.hint_text_edit.setMaximumHeight(60)
|
||||
audio_config_layout.addWidget(self.hint_text_edit, 1, 1)
|
||||
|
||||
scroll_layout.addWidget(audio_config_group)
|
||||
|
||||
# 使用工具配置
|
||||
tool_config_group = QGroupBox("使用工具")
|
||||
tool_config_layout = QGridLayout(tool_config_group)
|
||||
|
||||
tool_config_layout.addWidget(QLabel("所需工具:"), 0, 0)
|
||||
self.required_tool_combo = QComboBox()
|
||||
self.required_tool_combo.addItem("无")
|
||||
tool_config_layout.addWidget(self.required_tool_combo, 0, 1)
|
||||
|
||||
scroll_layout.addWidget(tool_config_group)
|
||||
|
||||
# 设置滚动区域
|
||||
scroll_area.setWidget(scroll_content)
|
||||
config_layout.addWidget(scroll_area)
|
||||
|
||||
# 默认隐藏点击配置
|
||||
self.click_config_group.setVisible(False)
|
||||
|
||||
return config_widget
|
||||
|
||||
def createSettingsTab(self):
|
||||
"""创建全局设置标签页"""
|
||||
settings_widget = QWidget()
|
||||
settings_layout = QVBoxLayout(settings_widget)
|
||||
|
||||
# 全局设置组
|
||||
global_settings_group = QGroupBox("全局设置")
|
||||
global_settings_layout = QGridLayout(global_settings_group)
|
||||
|
||||
# 自动播放提示音
|
||||
self.auto_play_audio_check = QCheckBox("自动播放步骤提示音")
|
||||
self.auto_play_audio_check.setChecked(True)
|
||||
global_settings_layout.addWidget(self.auto_play_audio_check, 0, 0, 1, 2)
|
||||
|
||||
# 显示步骤提示文字
|
||||
self.show_hint_text_check = QCheckBox("显示步骤提示文字")
|
||||
self.show_hint_text_check.setChecked(True)
|
||||
global_settings_layout.addWidget(self.show_hint_text_check, 1, 0, 1, 2)
|
||||
|
||||
# 步骤完成确认
|
||||
self.require_confirmation_check = QCheckBox("步骤完成需要确认")
|
||||
global_settings_layout.addWidget(self.require_confirmation_check, 2, 0, 1, 2)
|
||||
|
||||
# 拖拽灵敏度
|
||||
global_settings_layout.addWidget(QLabel("拖拽灵敏度:"), 3, 0)
|
||||
self.drag_sensitivity_slider = QSlider(Qt.Horizontal)
|
||||
self.drag_sensitivity_slider.setRange(1, 10)
|
||||
self.drag_sensitivity_slider.setValue(5)
|
||||
global_settings_layout.addWidget(self.drag_sensitivity_slider, 3, 1)
|
||||
|
||||
# 动画速度
|
||||
global_settings_layout.addWidget(QLabel("动画速度:"), 4, 0)
|
||||
self.animation_speed_slider = QSlider(Qt.Horizontal)
|
||||
self.animation_speed_slider.setRange(1, 10)
|
||||
self.animation_speed_slider.setValue(5)
|
||||
global_settings_layout.addWidget(self.animation_speed_slider, 4, 1)
|
||||
|
||||
settings_layout.addWidget(global_settings_group)
|
||||
|
||||
# 预设配置组
|
||||
presets_group = QGroupBox("预设配置")
|
||||
presets_layout = QVBoxLayout(presets_group)
|
||||
|
||||
# 预设列表
|
||||
self.presets_list = QListWidget()
|
||||
presets_layout.addWidget(self.presets_list)
|
||||
|
||||
# 预设操作按钮
|
||||
preset_buttons_layout = QHBoxLayout()
|
||||
self.save_preset_button = QPushButton("保存为预设")
|
||||
self.load_preset_button = QPushButton("加载预设")
|
||||
self.delete_preset_button = QPushButton("删除预设")
|
||||
preset_buttons_layout.addWidget(self.save_preset_button)
|
||||
preset_buttons_layout.addWidget(self.load_preset_button)
|
||||
preset_buttons_layout.addWidget(self.delete_preset_button)
|
||||
presets_layout.addLayout(preset_buttons_layout)
|
||||
|
||||
settings_layout.addWidget(presets_group)
|
||||
|
||||
# 添加弹性空间
|
||||
settings_layout.addStretch()
|
||||
|
||||
return settings_widget
|
||||
|
||||
def connectSignals(self):
|
||||
"""连接信号槽"""
|
||||
# 模型搜索
|
||||
self.model_search_edit.textChanged.connect(self.filterSceneModels)
|
||||
|
||||
# 模型操作
|
||||
self.add_to_config_button.clicked.connect(self.addModelsToConfig)
|
||||
self.remove_from_config_button.clicked.connect(self.removeModelsFromConfig)
|
||||
|
||||
# 工具操作
|
||||
self.add_tool_button.clicked.connect(self.addTool)
|
||||
self.edit_tool_button.clicked.connect(self.editTool)
|
||||
self.remove_tool_button.clicked.connect(self.removeTool)
|
||||
|
||||
# 步骤操作
|
||||
self.add_step_button.clicked.connect(self.addStep)
|
||||
self.remove_step_button.clicked.connect(self.removeStep)
|
||||
self.move_step_up_button.clicked.connect(self.moveStepUp)
|
||||
self.move_step_down_button.clicked.connect(self.moveStepDown)
|
||||
self.steps_list.itemSelectionChanged.connect(self.onStepSelectionChanged)
|
||||
|
||||
# 步骤配置变化
|
||||
self.interaction_type_combo.currentTextChanged.connect(self.onInteractionTypeChanged)
|
||||
self.browse_audio_button.clicked.connect(self.browseAudioFile)
|
||||
|
||||
# 步骤配置实时更新
|
||||
self.step_name_edit.textChanged.connect(self.updateCurrentStep)
|
||||
self.step_description_edit.textChanged.connect(self.updateCurrentStep)
|
||||
self.step_type_combo.currentTextChanged.connect(self.updateCurrentStep)
|
||||
self.target_model_combo.currentTextChanged.connect(self.updateCurrentStep)
|
||||
|
||||
# 按钮操作
|
||||
self.save_button.clicked.connect(self.saveConfiguration)
|
||||
self.load_button.clicked.connect(self.loadConfiguration)
|
||||
self.preview_button.clicked.connect(self.previewConfiguration)
|
||||
self.apply_button.clicked.connect(self.applyConfiguration)
|
||||
|
||||
def loadSceneModels(self):
|
||||
"""加载场景中的模型"""
|
||||
if not self.world:
|
||||
return
|
||||
|
||||
# 清空现有列表
|
||||
self.scene_models_list.clear()
|
||||
|
||||
# 遍历场景中的所有节点
|
||||
self._addNodeToTree(self.world.render, self.scene_models_list)
|
||||
|
||||
# 展开根节点
|
||||
self.scene_models_list.expandAll()
|
||||
|
||||
def _addNodeToTree(self, node, parent_item):
|
||||
"""递归添加节点到树形控件"""
|
||||
if node.getName() in ['render', 'camera', 'aspect2d', 'pixel2d']:
|
||||
# 跳过系统节点
|
||||
for child in node.getChildren():
|
||||
self._addNodeToTree(child, parent_item)
|
||||
return
|
||||
|
||||
# 创建树形项
|
||||
if isinstance(parent_item, QTreeWidget):
|
||||
item = QTreeWidgetItem(parent_item)
|
||||
else:
|
||||
item = QTreeWidgetItem(parent_item)
|
||||
|
||||
item.setText(0, node.getName())
|
||||
item.setData(0, Qt.UserRole, node)
|
||||
|
||||
# 添加子节点
|
||||
for child in node.getChildren():
|
||||
self._addNodeToTree(child, item)
|
||||
|
||||
def filterSceneModels(self, text):
|
||||
"""过滤场景模型列表"""
|
||||
# 简单的文本过滤实现
|
||||
def filterItems(item):
|
||||
item_text = item.text(0).lower()
|
||||
text_lower = text.lower()
|
||||
|
||||
# 检查当前项是否匹配
|
||||
matches = text_lower in item_text
|
||||
|
||||
# 检查子项
|
||||
child_matches = False
|
||||
for i in range(item.childCount()):
|
||||
child_item = item.child(i)
|
||||
if filterItems(child_item):
|
||||
child_matches = True
|
||||
|
||||
# 显示/隐藏项
|
||||
item.setHidden(not (matches or child_matches))
|
||||
|
||||
return matches or child_matches
|
||||
|
||||
# 从根项开始过滤
|
||||
for i in range(self.scene_models_list.topLevelItemCount()):
|
||||
filterItems(self.scene_models_list.topLevelItem(i))
|
||||
|
||||
def addModelsToConfig(self):
|
||||
"""添加选中的模型到配置"""
|
||||
selected_items = self.scene_models_list.selectedItems()
|
||||
|
||||
for item in selected_items:
|
||||
node = item.data(0, Qt.UserRole)
|
||||
if node:
|
||||
model_name = node.getName()
|
||||
|
||||
# 检查是否已经存在
|
||||
existing_items = self.config_models_list.findItems(model_name, Qt.MatchExactly)
|
||||
if not existing_items:
|
||||
# 添加到配置模型列表
|
||||
list_item = QListWidgetItem(model_name)
|
||||
list_item.setData(Qt.UserRole, node)
|
||||
self.config_models_list.addItem(list_item)
|
||||
|
||||
# 添加到配置数据
|
||||
self.config_data['models'].append({
|
||||
'name': model_name,
|
||||
'node': node,
|
||||
'original_pos': node.getPos(),
|
||||
'original_hpr': node.getHpr()
|
||||
})
|
||||
|
||||
# 更新目标模型下拉框
|
||||
self.target_model_combo.addItem(model_name)
|
||||
|
||||
def removeModelsFromConfig(self):
|
||||
"""从配置中移除选中的模型"""
|
||||
selected_items = self.config_models_list.selectedItems()
|
||||
|
||||
for item in selected_items:
|
||||
model_name = item.text()
|
||||
|
||||
# 从列表中移除
|
||||
row = self.config_models_list.row(item)
|
||||
self.config_models_list.takeItem(row)
|
||||
|
||||
# 从配置数据中移除
|
||||
self.config_data['models'] = [
|
||||
model for model in self.config_data['models']
|
||||
if model['name'] != model_name
|
||||
]
|
||||
|
||||
# 从目标模型下拉框中移除
|
||||
index = self.target_model_combo.findText(model_name)
|
||||
if index >= 0:
|
||||
self.target_model_combo.removeItem(index)
|
||||
|
||||
def addTool(self):
|
||||
"""添加工具"""
|
||||
tool_name = self.tool_name_edit.text().strip()
|
||||
if not tool_name:
|
||||
QMessageBox.warning(self, "警告", "请输入工具名称")
|
||||
return
|
||||
|
||||
# 检查是否已存在
|
||||
existing_items = self.tools_list.findItems(tool_name, Qt.MatchExactly)
|
||||
if existing_items:
|
||||
QMessageBox.warning(self, "警告", "工具已存在")
|
||||
return
|
||||
|
||||
# 添加到工具列表
|
||||
self.tools_list.addItem(tool_name)
|
||||
self.required_tool_combo.addItem(tool_name)
|
||||
|
||||
# 添加到配置数据
|
||||
self.config_data['tools'].append({
|
||||
'name': tool_name,
|
||||
'description': '',
|
||||
'icon': ''
|
||||
})
|
||||
|
||||
# 清空输入框
|
||||
self.tool_name_edit.clear()
|
||||
|
||||
def editTool(self):
|
||||
"""编辑工具"""
|
||||
current_item = self.tools_list.currentItem()
|
||||
if not current_item:
|
||||
QMessageBox.warning(self, "警告", "请选择要编辑的工具")
|
||||
return
|
||||
|
||||
# 这里可以添加工具编辑对话框
|
||||
QMessageBox.information(self, "提示", "工具编辑功能待实现")
|
||||
|
||||
def removeTool(self):
|
||||
"""删除工具"""
|
||||
current_item = self.tools_list.currentItem()
|
||||
if not current_item:
|
||||
QMessageBox.warning(self, "警告", "请选择要删除的工具")
|
||||
return
|
||||
|
||||
tool_name = current_item.text()
|
||||
|
||||
# 从列表中移除
|
||||
row = self.tools_list.row(current_item)
|
||||
self.tools_list.takeItem(row)
|
||||
|
||||
# 从下拉框中移除
|
||||
index = self.required_tool_combo.findText(tool_name)
|
||||
if index >= 0:
|
||||
self.required_tool_combo.removeItem(index)
|
||||
|
||||
# 从配置数据中移除
|
||||
self.config_data['tools'] = [
|
||||
tool for tool in self.config_data['tools']
|
||||
if tool['name'] != tool_name
|
||||
]
|
||||
|
||||
def addStep(self):
|
||||
"""添加步骤"""
|
||||
step_count = len(self.config_data['steps']) + 1
|
||||
step_name = f"步骤 {step_count}"
|
||||
|
||||
# 创建新步骤数据
|
||||
step_data = {
|
||||
'name': step_name,
|
||||
'description': '',
|
||||
'type': '拆卸',
|
||||
'target_model': '',
|
||||
'interaction_type': '鼠标拖拽',
|
||||
'target_position': [0, 0, 0],
|
||||
'constraints': {'x': False, 'y': False, 'z': False},
|
||||
'move_distance': [0, 0, 0],
|
||||
'move_duration': 1.0,
|
||||
'hide_after_move': False,
|
||||
'audio_file': '',
|
||||
'hint_text': '',
|
||||
'required_tool': '无'
|
||||
}
|
||||
|
||||
# 添加到配置数据
|
||||
self.config_data['steps'].append(step_data)
|
||||
|
||||
# 添加到步骤列表
|
||||
list_item = QListWidgetItem(step_name)
|
||||
list_item.setData(Qt.UserRole, step_data)
|
||||
self.steps_list.addItem(list_item)
|
||||
|
||||
# 选中新添加的步骤
|
||||
self.steps_list.setCurrentItem(list_item)
|
||||
|
||||
def removeStep(self):
|
||||
"""删除步骤"""
|
||||
current_item = self.steps_list.currentItem()
|
||||
if not current_item:
|
||||
QMessageBox.warning(self, "警告", "请选择要删除的步骤")
|
||||
return
|
||||
|
||||
# 从列表中移除
|
||||
row = self.steps_list.row(current_item)
|
||||
self.steps_list.takeItem(row)
|
||||
|
||||
# 从配置数据中移除
|
||||
self.config_data['steps'].pop(row)
|
||||
|
||||
# 重新编号步骤
|
||||
self.renumberSteps()
|
||||
|
||||
def moveStepUp(self):
|
||||
"""上移步骤"""
|
||||
current_row = self.steps_list.currentRow()
|
||||
if current_row <= 0:
|
||||
return
|
||||
|
||||
# 交换数据
|
||||
self.config_data['steps'][current_row], self.config_data['steps'][current_row - 1] = \
|
||||
self.config_data['steps'][current_row - 1], self.config_data['steps'][current_row]
|
||||
|
||||
# 交换列表项
|
||||
current_item = self.steps_list.takeItem(current_row)
|
||||
self.steps_list.insertItem(current_row - 1, current_item)
|
||||
self.steps_list.setCurrentRow(current_row - 1)
|
||||
|
||||
# 重新编号
|
||||
self.renumberSteps()
|
||||
|
||||
def moveStepDown(self):
|
||||
"""下移步骤"""
|
||||
current_row = self.steps_list.currentRow()
|
||||
if current_row >= self.steps_list.count() - 1:
|
||||
return
|
||||
|
||||
# 交换数据
|
||||
self.config_data['steps'][current_row], self.config_data['steps'][current_row + 1] = \
|
||||
self.config_data['steps'][current_row + 1], self.config_data['steps'][current_row]
|
||||
|
||||
# 交换列表项
|
||||
current_item = self.steps_list.takeItem(current_row)
|
||||
self.steps_list.insertItem(current_row + 1, current_item)
|
||||
self.steps_list.setCurrentRow(current_row + 1)
|
||||
|
||||
# 重新编号
|
||||
self.renumberSteps()
|
||||
|
||||
def renumberSteps(self):
|
||||
"""重新编号步骤"""
|
||||
for i in range(self.steps_list.count()):
|
||||
item = self.steps_list.item(i)
|
||||
step_data = item.data(Qt.UserRole)
|
||||
new_name = f"步骤 {i + 1}"
|
||||
step_data['name'] = new_name
|
||||
item.setText(new_name)
|
||||
|
||||
def onStepSelectionChanged(self):
|
||||
"""步骤选择改变"""
|
||||
current_item = self.steps_list.currentItem()
|
||||
if not current_item:
|
||||
return
|
||||
|
||||
step_data = current_item.data(Qt.UserRole)
|
||||
if not step_data:
|
||||
return
|
||||
|
||||
# 更新步骤配置界面
|
||||
self.loadStepConfiguration(step_data)
|
||||
|
||||
def loadStepConfiguration(self, step_data):
|
||||
"""加载步骤配置到界面"""
|
||||
# 基本信息
|
||||
self.step_name_edit.setText(step_data.get('name', ''))
|
||||
self.step_description_edit.setPlainText(step_data.get('description', ''))
|
||||
self.step_type_combo.setCurrentText(step_data.get('type', '拆卸'))
|
||||
|
||||
# 目标模型
|
||||
target_model = step_data.get('target_model', '')
|
||||
index = self.target_model_combo.findText(target_model)
|
||||
if index >= 0:
|
||||
self.target_model_combo.setCurrentIndex(index)
|
||||
|
||||
# 交互方式
|
||||
interaction_type = step_data.get('interaction_type', '鼠标拖拽')
|
||||
self.interaction_type_combo.setCurrentText(interaction_type)
|
||||
self.onInteractionTypeChanged(interaction_type)
|
||||
|
||||
# 拖拽配置
|
||||
target_pos = step_data.get('target_position', [0, 0, 0])
|
||||
self.target_pos_x_spin.setValue(target_pos[0])
|
||||
self.target_pos_y_spin.setValue(target_pos[1])
|
||||
self.target_pos_z_spin.setValue(target_pos[2])
|
||||
|
||||
constraints = step_data.get('constraints', {'x': False, 'y': False, 'z': False})
|
||||
self.constrain_x_check.setChecked(constraints.get('x', False))
|
||||
self.constrain_y_check.setChecked(constraints.get('y', False))
|
||||
self.constrain_z_check.setChecked(constraints.get('z', False))
|
||||
|
||||
# 点击触发配置
|
||||
move_distance = step_data.get('move_distance', [0, 0, 0])
|
||||
self.move_distance_x_spin.setValue(move_distance[0])
|
||||
self.move_distance_y_spin.setValue(move_distance[1])
|
||||
self.move_distance_z_spin.setValue(move_distance[2])
|
||||
|
||||
self.move_duration_spin.setValue(step_data.get('move_duration', 1.0))
|
||||
self.hide_after_move_check.setChecked(step_data.get('hide_after_move', False))
|
||||
|
||||
# 提示配置
|
||||
self.audio_file_edit.setText(step_data.get('audio_file', ''))
|
||||
self.hint_text_edit.setPlainText(step_data.get('hint_text', ''))
|
||||
|
||||
# 工具配置
|
||||
required_tool = step_data.get('required_tool', '无')
|
||||
index = self.required_tool_combo.findText(required_tool)
|
||||
if index >= 0:
|
||||
self.required_tool_combo.setCurrentIndex(index)
|
||||
|
||||
def onInteractionTypeChanged(self, interaction_type):
|
||||
"""交互方式改变"""
|
||||
if interaction_type == "鼠标拖拽":
|
||||
self.drag_config_group.setVisible(True)
|
||||
self.click_config_group.setVisible(False)
|
||||
else: # 点击触发
|
||||
self.drag_config_group.setVisible(False)
|
||||
self.click_config_group.setVisible(True)
|
||||
|
||||
def updateCurrentStep(self):
|
||||
"""更新当前步骤数据"""
|
||||
current_item = self.steps_list.currentItem()
|
||||
if not current_item:
|
||||
return
|
||||
|
||||
step_data = current_item.data(Qt.UserRole)
|
||||
if not step_data:
|
||||
return
|
||||
|
||||
# 更新步骤数据
|
||||
step_data['name'] = self.step_name_edit.text()
|
||||
step_data['description'] = self.step_description_edit.toPlainText()
|
||||
step_data['type'] = self.step_type_combo.currentText()
|
||||
step_data['target_model'] = self.target_model_combo.currentText()
|
||||
step_data['interaction_type'] = self.interaction_type_combo.currentText()
|
||||
|
||||
# 更新列表项显示
|
||||
current_item.setText(step_data['name'])
|
||||
|
||||
def browseAudioFile(self):
|
||||
"""浏览音频文件"""
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "选择音频文件", "",
|
||||
"音频文件 (*.wav *.mp3 *.ogg *.flac);;所有文件 (*)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
self.audio_file_edit.setText(file_path)
|
||||
|
||||
def saveConfiguration(self):
|
||||
"""保存配置"""
|
||||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
self, "保存拆装配置", "",
|
||||
"JSON文件 (*.json);;所有文件 (*)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
try:
|
||||
# 准备保存的数据(去除不能序列化的对象)
|
||||
save_data = {
|
||||
'models': [
|
||||
{
|
||||
'name': model['name'],
|
||||
'original_pos': list(model['original_pos']),
|
||||
'original_hpr': list(model['original_hpr'])
|
||||
} for model in self.config_data['models']
|
||||
],
|
||||
'steps': self.config_data['steps'],
|
||||
'tools': self.config_data['tools'],
|
||||
'settings': {
|
||||
'auto_play_audio': self.auto_play_audio_check.isChecked(),
|
||||
'show_hint_text': self.show_hint_text_check.isChecked(),
|
||||
'require_confirmation': self.require_confirmation_check.isChecked(),
|
||||
'drag_sensitivity': self.drag_sensitivity_slider.value(),
|
||||
'animation_speed': self.animation_speed_slider.value()
|
||||
}
|
||||
}
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(save_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
QMessageBox.information(self, "成功", "配置保存成功")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"保存配置失败: {str(e)}")
|
||||
|
||||
def loadConfiguration(self):
|
||||
"""加载配置"""
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "加载拆装配置", "",
|
||||
"JSON文件 (*.json);;所有文件 (*)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 加载配置数据
|
||||
self.loadConfigurationData(data)
|
||||
QMessageBox.information(self, "成功", "配置加载成功")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"加载配置失败: {str(e)}")
|
||||
|
||||
def loadConfigurationData(self, data):
|
||||
"""加载配置数据到界面"""
|
||||
# 清空现有配置
|
||||
self.config_models_list.clear()
|
||||
self.target_model_combo.clear()
|
||||
self.steps_list.clear()
|
||||
|
||||
# 加载模型配置
|
||||
# 注意:这里需要重新匹配场景中的模型节点
|
||||
|
||||
# 加载步骤配置
|
||||
self.config_data['steps'] = data.get('steps', [])
|
||||
for step_data in self.config_data['steps']:
|
||||
list_item = QListWidgetItem(step_data['name'])
|
||||
list_item.setData(Qt.UserRole, step_data)
|
||||
self.steps_list.addItem(list_item)
|
||||
|
||||
# 加载工具配置
|
||||
self.tools_list.clear()
|
||||
self.required_tool_combo.clear()
|
||||
self.required_tool_combo.addItem("无")
|
||||
|
||||
self.config_data['tools'] = data.get('tools', [])
|
||||
for tool in self.config_data['tools']:
|
||||
self.tools_list.addItem(tool['name'])
|
||||
self.required_tool_combo.addItem(tool['name'])
|
||||
|
||||
# 加载全局设置
|
||||
settings = data.get('settings', {})
|
||||
self.auto_play_audio_check.setChecked(settings.get('auto_play_audio', True))
|
||||
self.show_hint_text_check.setChecked(settings.get('show_hint_text', True))
|
||||
self.require_confirmation_check.setChecked(settings.get('require_confirmation', False))
|
||||
self.drag_sensitivity_slider.setValue(settings.get('drag_sensitivity', 5))
|
||||
self.animation_speed_slider.setValue(settings.get('animation_speed', 5))
|
||||
|
||||
def previewConfiguration(self):
|
||||
"""预览配置效果"""
|
||||
QMessageBox.information(self, "提示", "预览功能待实现")
|
||||
|
||||
def applyConfiguration(self):
|
||||
"""应用配置"""
|
||||
QMessageBox.information(self, "提示", "应用配置功能待实现")
|
||||
1069
ui/assembly_disassembly_config_simple.py
Normal file
1069
ui/assembly_disassembly_config_simple.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,13 +10,13 @@ import os
|
||||
import sys
|
||||
|
||||
from PyQt5.QtGui import QKeySequence, QIcon, QPalette, QColor
|
||||
from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
# from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction,
|
||||
QDockWidget, QTreeWidget, QListWidget, QWidget, QVBoxLayout, QTreeWidgetItem,
|
||||
QLabel, QLineEdit, QFormLayout, QDoubleSpinBox, QScrollArea,
|
||||
QFileSystemModel, QButtonGroup, QToolButton, QPushButton, QHBoxLayout,
|
||||
QComboBox, QGroupBox, QInputDialog, QFileDialog, QMessageBox, QDesktopWidget, QDialog,
|
||||
QSpinBox, QFrame)
|
||||
QSpinBox, QFrame, QRadioButton, QTextEdit)
|
||||
from PyQt5.QtCore import Qt, QDir, QTimer, QSize, QPoint, QUrl, QRect
|
||||
from direct.showbase.ShowBaseGlobal import aspect2d
|
||||
from panda3d.core import OrthographicLens
|
||||
@ -713,6 +713,17 @@ class MainWindow(QMainWindow):
|
||||
self.scriptMenu.addSeparator()
|
||||
self.openScriptsManagerAction = self.scriptMenu.addAction('脚本管理器')
|
||||
|
||||
# 交互菜单
|
||||
self.interactionMenu = menubar.addMenu('交互')
|
||||
self.assemblyDisassemblyConfigAction = self.interactionMenu.addAction('拆装配置')
|
||||
self.assemblyDisassemblyConfigAction.triggered.connect(self.onOpenAssemblyDisassemblyConfig)
|
||||
self.interactionMenu.addSeparator()
|
||||
self.startAssemblyInteractionAction = self.interactionMenu.addAction('开始拆装交互')
|
||||
self.startAssemblyInteractionAction.triggered.connect(self.onStartAssemblyInteraction)
|
||||
self.interactionMenu.addSeparator()
|
||||
self.maintenanceSystemAction = self.interactionMenu.addAction('🔧 维修系统')
|
||||
self.maintenanceSystemAction.triggered.connect(self.onOpenMaintenanceSystem)
|
||||
|
||||
self.cesiumMenu = menubar.addMenu('Cesium')
|
||||
self.loadCesiumTilesetAction = self.cesiumMenu.addAction('加载3Dtiles')
|
||||
self.loadCesiumTilesetAction.triggered.connect(self.onLoadCesiumTileset)
|
||||
@ -2540,7 +2551,7 @@ class MainWindow(QMainWindow):
|
||||
if not WEB_ENGINE_AVAILABLE:
|
||||
return None
|
||||
try:
|
||||
from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
# from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt5.QtWidgets import QDockWidget
|
||||
from PyQt5.QtCore import QUrl
|
||||
import os
|
||||
@ -3239,6 +3250,16 @@ class MainWindow(QMainWindow):
|
||||
try:
|
||||
print("🔄 正在关闭应用程序...")
|
||||
|
||||
# 关闭拆装交互相关的弹窗
|
||||
if hasattr(self.world, 'assembly_interaction') and self.world.assembly_interaction:
|
||||
print("🧹 关闭拆装交互弹窗...")
|
||||
if hasattr(self.world.assembly_interaction, 'step_dialog') and self.world.assembly_interaction.step_dialog:
|
||||
self.world.assembly_interaction.step_dialog.close()
|
||||
self.world.assembly_interaction.step_dialog = None
|
||||
# 停止交互模式
|
||||
if self.world.assembly_interaction.is_active:
|
||||
self.world.assembly_interaction.stop_interaction_mode()
|
||||
|
||||
# 清理工具管理器中的进程
|
||||
if hasattr(self.world, 'tool_manager') and self.world.tool_manager:
|
||||
print("🧹 清理工具管理器进程...")
|
||||
@ -3444,6 +3465,224 @@ class MainWindow(QMainWindow):
|
||||
else:
|
||||
QMessageBox.warning(self, "错误", "高度图地形创建失败!")
|
||||
|
||||
def onOpenAssemblyDisassemblyConfig(self):
|
||||
"""打开拆装配置界面"""
|
||||
try:
|
||||
from ui.assembly_disassembly_config_simple import AssemblyDisassemblyConfigDialog
|
||||
config_dialog = AssemblyDisassemblyConfigDialog(self, self.world)
|
||||
config_dialog.show()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"打开拆装配置界面失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def onStartAssemblyInteraction(self):
|
||||
"""开始拆装交互"""
|
||||
try:
|
||||
# 显示模式选择对话框
|
||||
mode_dialog = AssemblyModeSelectionDialog(self)
|
||||
if mode_dialog.exec_() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
selected_mode = mode_dialog.get_selected_mode()
|
||||
print(f"🎯 用户选择的拆装模式: {selected_mode}")
|
||||
|
||||
from core.assembly_interaction import AssemblyInteractionManager
|
||||
|
||||
# 检查是否已有交互管理器实例
|
||||
if not hasattr(self.world, 'assembly_interaction'):
|
||||
self.world.assembly_interaction = AssemblyInteractionManager(self.world)
|
||||
|
||||
# 启动交互模式,传递模式参数
|
||||
self.world.assembly_interaction.start_interaction_mode(mode=selected_mode)
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"启动拆装交互失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def onOpenMaintenanceSystem(self):
|
||||
"""打开维修系统"""
|
||||
try:
|
||||
# 导入简化的登录界面
|
||||
from ui.simple_maintenance_login import SimpleMaintenanceLoginDialog
|
||||
from ui.maintenance_system import MaintenanceSubjectDialog, MaintenanceSystemManager
|
||||
|
||||
print("🔧 启动维修系统...")
|
||||
|
||||
# 显示登录界面
|
||||
login_dialog = SimpleMaintenanceLoginDialog(self)
|
||||
|
||||
if login_dialog.exec_() == QDialog.Accepted:
|
||||
print("✅ 登录成功,显示科目选择界面")
|
||||
|
||||
# 获取当前项目路径
|
||||
project_path = None
|
||||
if hasattr(self.world, 'project_manager') and self.world.project_manager:
|
||||
project_path = self.world.project_manager.getCurrentProjectPath()
|
||||
|
||||
# 显示科目选择界面
|
||||
subject_dialog = MaintenanceSubjectDialog(project_path, self)
|
||||
|
||||
def on_subject_selected(subject_path, mode):
|
||||
"""处理科目选择"""
|
||||
try:
|
||||
print(f"🎯 启动维修科目: {subject_path}")
|
||||
print(f"📝 模式: {mode}")
|
||||
|
||||
# 加载科目配置
|
||||
import json
|
||||
with open(subject_path, 'r', encoding='utf-8') as f:
|
||||
subject_config = json.load(f)
|
||||
|
||||
# 初始化拆装交互系统(如果还没有)
|
||||
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, "错误", "维修科目启动失败")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 启动维修科目失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"启动维修科目失败:\n{str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
subject_dialog.subject_selected.connect(on_subject_selected)
|
||||
subject_dialog.exec_()
|
||||
|
||||
else:
|
||||
print("ℹ️ 用户取消了登录")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 打开维修系统失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"打开维修系统失败:\n{str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
class AssemblyModeSelectionDialog(QDialog):
|
||||
"""拆装模式选择对话框"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.selected_mode = "training" # 默认选择训练模式
|
||||
self.setupUI()
|
||||
|
||||
def setupUI(self):
|
||||
self.setWindowTitle("选择拆装模式")
|
||||
self.setFixedSize(400, 300)
|
||||
self.setModal(True)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# 标题
|
||||
title_label = QLabel("请选择拆装交互模式")
|
||||
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #2E86C1; margin: 10px;")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# 模式选择组
|
||||
mode_group = QGroupBox("模式选择")
|
||||
mode_layout = QVBoxLayout(mode_group)
|
||||
|
||||
# 训练模式
|
||||
self.training_radio = QRadioButton("训练模式")
|
||||
self.training_radio.setChecked(True) # 默认选中
|
||||
self.training_radio.setStyleSheet("font-size: 14px; margin: 5px;")
|
||||
mode_layout.addWidget(self.training_radio)
|
||||
|
||||
training_desc = QTextEdit()
|
||||
training_desc.setMaximumHeight(60)
|
||||
training_desc.setReadOnly(True)
|
||||
training_desc.setPlainText("• 显示详细的步骤描述和操作提示\n• 提供工具选择的正确性提示\n• 播放语音指导")
|
||||
training_desc.setStyleSheet("background-color: #f0f8ff; border: 1px solid #ccc; margin-left: 20px;")
|
||||
mode_layout.addWidget(training_desc)
|
||||
|
||||
# 考核模式
|
||||
self.exam_radio = QRadioButton("考核模式")
|
||||
self.exam_radio.setStyleSheet("font-size: 14px; margin: 5px;")
|
||||
mode_layout.addWidget(self.exam_radio)
|
||||
|
||||
exam_desc = QTextEdit()
|
||||
exam_desc.setMaximumHeight(60)
|
||||
exam_desc.setReadOnly(True)
|
||||
exam_desc.setPlainText("• 不显示步骤描述\n• 工具选择错误时不提示,直接扣分\n• 不播放语音指导")
|
||||
exam_desc.setStyleSheet("background-color: #fff5f5; border: 1px solid #ccc; margin-left: 20px;")
|
||||
mode_layout.addWidget(exam_desc)
|
||||
|
||||
layout.addWidget(mode_group)
|
||||
|
||||
# 按钮
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
self.ok_button = QPushButton("开始")
|
||||
self.ok_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #27AE60;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 8px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #2ECC71;
|
||||
}
|
||||
""")
|
||||
self.ok_button.clicked.connect(self.accept)
|
||||
|
||||
self.cancel_button = QPushButton("取消")
|
||||
self.cancel_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #95A5A6;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
padding: 8px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #BDC3C7;
|
||||
}
|
||||
""")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.ok_button)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 连接单选按钮信号
|
||||
self.training_radio.toggled.connect(self.on_mode_changed)
|
||||
self.exam_radio.toggled.connect(self.on_mode_changed)
|
||||
|
||||
def on_mode_changed(self):
|
||||
"""模式改变时的处理"""
|
||||
if self.training_radio.isChecked():
|
||||
self.selected_mode = "training"
|
||||
elif self.exam_radio.isChecked():
|
||||
self.selected_mode = "exam"
|
||||
print(f"🔄 模式选择改变: {self.selected_mode}")
|
||||
|
||||
def get_selected_mode(self):
|
||||
"""获取选中的模式"""
|
||||
return self.selected_mode
|
||||
|
||||
|
||||
# ==================== VR事件处理 ====================
|
||||
|
||||
def onEnterVR(self):
|
||||
|
||||
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()
|
||||
@ -4806,12 +4806,12 @@ class PropertyPanelManager:
|
||||
if geom_node:
|
||||
geom_node_name = geom_node.getName()
|
||||
unique_name = f"{geom_node_name}({model_name})"
|
||||
#print(f"材质 {i}: 使用几何节点名称 '{geom_node_name}'")
|
||||
print(f"材质 {i}: 使用几何节点名称 '{geom_node_name}'")
|
||||
else:
|
||||
material_name = material.get_name() if hasattr(material,
|
||||
'get_name') and material.get_name() else f"材质{i + 1}"
|
||||
unique_name = f"{material_name}({model_name})"
|
||||
#print(f"材质 {i}: 未找到几何节点,使用材质名称 '{material_name}'")
|
||||
print(f"材质 {i}: 未找到几何节点,使用材质名称 '{material_name}'")
|
||||
|
||||
# 处理重复名称
|
||||
if unique_name in name_counter:
|
||||
@ -4850,7 +4850,7 @@ class PropertyPanelManager:
|
||||
# 基础颜色编辑
|
||||
base_color = self._getOrCreateMaterialBaseColor(material)
|
||||
if base_color is not None:
|
||||
#print(f"材质基础颜色: {base_color}")
|
||||
print(f"材质基础颜色: {base_color}")
|
||||
|
||||
# 基础颜色标题
|
||||
color_row = 2 if material_status != "标准PBR材质" else 1
|
||||
@ -5236,7 +5236,7 @@ class PropertyPanelManager:
|
||||
try:
|
||||
# 方法1: 尝试获取base_color属性
|
||||
if hasattr(material, 'base_color') and material.base_color is not None:
|
||||
#print(f"✓ 找到base_color属性: {material.base_color}")
|
||||
print(f"✓ 找到base_color属性: {material.base_color}")
|
||||
return material.base_color
|
||||
|
||||
# 方法2: 尝试调用get_base_color方法
|
||||
@ -5254,7 +5254,7 @@ class PropertyPanelManager:
|
||||
try:
|
||||
diffuse_color = material.getDiffuse()
|
||||
if diffuse_color is not None:
|
||||
#print(f"✓ 从diffuse颜色获取: {diffuse_color}")
|
||||
print(f"✓ 从diffuse颜色获取: {diffuse_color}")
|
||||
# 同时设置为base_color
|
||||
if hasattr(material, 'set_base_color'):
|
||||
material.set_base_color(diffuse_color)
|
||||
@ -6862,7 +6862,7 @@ class PropertyPanelManager:
|
||||
# print(f"找到匹配的几何节点: {geom_np.get_name()}")
|
||||
return geom_np
|
||||
|
||||
#print("未找到匹配的几何节点")
|
||||
print("未找到匹配的几何节点")
|
||||
return None
|
||||
|
||||
def _findSpecificGeomNodeForMaterial(self, target_material):
|
||||
@ -8147,7 +8147,7 @@ class PropertyPanelManager:
|
||||
format_info = self._getModelFormat(origin_model)
|
||||
processed = []
|
||||
|
||||
#print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||
print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||
|
||||
for name in anim_names:
|
||||
display_name = name
|
||||
@ -8181,7 +8181,7 @@ class PropertyPanelManager:
|
||||
display_name = name
|
||||
|
||||
processed.append((display_name, original_name))
|
||||
#print(f"[动画分析] {original_name} → {display_name}")
|
||||
print(f"[动画分析] {original_name} → {display_name}")
|
||||
|
||||
return processed
|
||||
|
||||
@ -8215,7 +8215,7 @@ class PropertyPanelManager:
|
||||
if frames > 1:
|
||||
valid_anims += 1
|
||||
total_frames += frames
|
||||
#print(f"[动画分析] '{anim_name}': {frames} 帧")
|
||||
print(f"[动画分析] '{anim_name}': {frames} 帧")
|
||||
else:
|
||||
print(f"[动画分析] '{anim_name}': 无有效帧数 ({frames})")
|
||||
except Exception as e:
|
||||
|
||||
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