1
0
forked from Rowland/EG

1.虚拟维修简单流程

This commit is contained in:
陈横 2025-09-22 10:04:20 +08:00
parent 7a621a3a51
commit e9039ab832
7 changed files with 3645 additions and 16 deletions

File diff suppressed because one or more lines are too long

1613
core/assembly_interaction.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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), # 模型碰撞 - 通用模型间碰撞检测
}
# 碰撞体形状类型

View 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, "提示", "应用配置功能待实现")

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ 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,
@ -709,6 +709,14 @@ 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.cesiumMenu = menubar.addMenu('Cesium')
self.loadCesiumTilesetAction = self.cesiumMenu.addAction('加载3Dtiles')
self.loadCesiumTilesetAction.triggered.connect(self.onLoadCesiumTileset)
@ -1851,7 +1859,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
@ -2864,6 +2872,34 @@ 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:
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()
except Exception as e:
QMessageBox.critical(self, "错误", f"启动拆装交互失败: {str(e)}")
import traceback
traceback.print_exc()
def setup_main_window(world,path = None):
"""设置主窗口的便利函数"""
app = QApplication.instance()

View File

@ -4756,12 +4756,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:
@ -4800,7 +4800,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
@ -5186,7 +5186,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方法
@ -5204,7 +5204,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)
@ -6812,7 +6812,7 @@ class PropertyPanelManager:
# print(f"找到匹配的几何节点: {geom_np.get_name()}")
return geom_np
#print("未找到匹配的几何节点")
print("未找到匹配的几何节点")
return None
def _findSpecificGeomNodeForMaterial(self, target_material):
@ -8097,7 +8097,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
@ -8131,7 +8131,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
@ -8165,7 +8165,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: