脚本保存和移除
This commit is contained in:
parent
7a621a3a51
commit
fc2550ce03
@ -73,7 +73,6 @@ native_module = None
|
||||
|
||||
# If the module was built, use it, otherwise use the python wrappers
|
||||
if NATIVE_CXX_LOADED:
|
||||
print(f'12121212121212121212121212')
|
||||
try:
|
||||
from panda3d import _rplight as _native_module # pylint: disable=wrong-import-position
|
||||
RPObject.global_debug("CORE", "Using panda3d-supplied core module")
|
||||
|
||||
@ -253,7 +253,7 @@ class ScriptLoader:
|
||||
|
||||
for component in components_to_remove:
|
||||
self.script_manager.remove_script_from_object(component.game_object, script_name)
|
||||
|
||||
|
||||
# 从sys.modules中移除
|
||||
module = self.loaded_modules[script_name]
|
||||
if module.__name__ in sys.modules:
|
||||
@ -696,18 +696,62 @@ class {class_name}(ScriptBase):
|
||||
return False
|
||||
|
||||
script_components = self.object_scripts[game_object]
|
||||
removed = False
|
||||
|
||||
for component in script_components[:]: # 复制列表以避免修改时出错
|
||||
if component.script_instance.__class__.__name__ == script_name:
|
||||
# 从引擎移除
|
||||
self.engine.remove_script_component(component)
|
||||
# 从对象脚本列表移除
|
||||
script_components.remove(component)
|
||||
removed = True
|
||||
|
||||
print(f"✓ 从对象 {game_object.getName()} 移除脚本: {script_name}")
|
||||
return True
|
||||
|
||||
|
||||
if not script_components:
|
||||
del self.object_scripts[game_object]
|
||||
|
||||
# 更新节点上保存的脚本信息标签
|
||||
if removed:
|
||||
self._update_node_script_tags_after_removal(game_object, script_name)
|
||||
|
||||
return False
|
||||
|
||||
return removed
|
||||
|
||||
def _update_node_script_tags_after_removal(self, game_object, removed_script_name):
|
||||
"""在移除脚本后更新节点标签"""
|
||||
try:
|
||||
# 获取对象上剩余的脚本
|
||||
remaining_scripts = self.get_scripts_on_object(game_object)
|
||||
|
||||
if not remaining_scripts:
|
||||
# 如果没有其他脚本,清除所有脚本标签
|
||||
if game_object.hasTag("has_scripts"):
|
||||
game_object.clearTag("has_scripts")
|
||||
if game_object.hasTag("scripts_info"):
|
||||
game_object.clearTag("scripts_info")
|
||||
print(f"✓ 清除节点 {game_object.getName()} 的所有脚本标签")
|
||||
else:
|
||||
# 如果还有其他脚本,更新脚本信息标签
|
||||
script_info_list = []
|
||||
for script_component in remaining_scripts:
|
||||
script_name = script_component.script_name
|
||||
script_class = script_component.script_instance.__class__
|
||||
script_file = self.loader.find_script_file(script_name) or ""
|
||||
|
||||
script_info_list.append({
|
||||
"name": script_name,
|
||||
"file": script_file
|
||||
})
|
||||
|
||||
import json
|
||||
game_object.setTag("has_scripts", "true")
|
||||
game_object.setTag("scripts_info", json.dumps(script_info_list, ensure_ascii=False))
|
||||
print(f"✓ 更新节点 {game_object.getName()} 的脚本标签信息,剩余 {len(script_info_list)} 个脚本")
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新节点标签失败: {e}")
|
||||
|
||||
def get_scripts_on_object(self, game_object) -> List[ScriptComponent]:
|
||||
"""获取对象上的所有脚本"""
|
||||
return self.object_scripts.get(game_object, [])
|
||||
|
||||
@ -1926,15 +1926,6 @@ class SelectionSystem:
|
||||
node_name = nodePath.getName()
|
||||
print(f"新选择的节点: {node_name}")
|
||||
|
||||
# 检查是否为双击
|
||||
is_double_click = self.checkDoubleClick(nodePath)
|
||||
if is_double_click:
|
||||
print(f"检测到双击 {node_name},执行聚焦")
|
||||
# 双击时直接执行聚焦,不执行选择逻辑
|
||||
self.focusCameraOnSelectedNodeAdvanced()
|
||||
print("=== 选择状态更新完成 ===\n")
|
||||
return # 直接返回,不执行下面的选择逻辑
|
||||
|
||||
self.selectedNode = nodePath
|
||||
# 添加兼容性属性
|
||||
self.selectedObject = nodePath
|
||||
@ -1985,6 +1976,45 @@ class SelectionSystem:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _updateSelectionVisuals(self, nodePath):
|
||||
"""更新选择的视觉效果(选择框和坐标轴)"""
|
||||
try:
|
||||
if nodePath and not nodePath.isEmpty():
|
||||
node_name = nodePath.getName()
|
||||
print(f"开始为节点 {node_name} 创建选择框和坐标轴...")
|
||||
|
||||
# 创建选择框
|
||||
print("创建选择框...")
|
||||
self.createSelectionBox(nodePath)
|
||||
if self.selectionBox:
|
||||
box_name = "Unknown"
|
||||
if self.selectionBox and not self.selectionBox.isEmpty():
|
||||
box_name = self.selectionBox.getName()
|
||||
print(f"✓ 选择框创建成功: {box_name}")
|
||||
else:
|
||||
print("× 选择框创建失败")
|
||||
|
||||
# 创建坐标轴
|
||||
print("创建坐标轴...")
|
||||
self.createGizmo(nodePath)
|
||||
if self.gizmo:
|
||||
gizmo_name = "Unknown"
|
||||
if self.gizmo and not self.gizmo.isEmpty():
|
||||
gizmo_name = self.gizmo.getName()
|
||||
print(f"✓ 坐标轴创建成功: {gizmo_name}")
|
||||
else:
|
||||
print("× 坐标轴创建失败")
|
||||
|
||||
print(f"✓ 选中了节点: {node_name}")
|
||||
else:
|
||||
print("清除选择...")
|
||||
self.clearSelectionBox()
|
||||
self.clearGizmo()
|
||||
print("✓ 取消选择")
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新选择视觉效果失败: {e}")
|
||||
|
||||
def getSelectedNode(self):
|
||||
"""获取当前选中的节点"""
|
||||
return self.selectedNode
|
||||
@ -2529,6 +2559,7 @@ class SelectionSystem:
|
||||
|
||||
# 检查是否为双击(同一节点且在时间阈值内)
|
||||
is_double_click = (self._last_clicked_node == target_node and
|
||||
target_node is not None and
|
||||
current_time - self._last_click_time < self._double_click_threshold)
|
||||
|
||||
if is_double_click:
|
||||
@ -2539,8 +2570,12 @@ class SelectionSystem:
|
||||
# 无论是点击模型还是坐标轴,都执行聚焦
|
||||
if target_node and not target_node.isEmpty():
|
||||
print(f"双击聚焦到节点: {target_node.getName()}")
|
||||
# 执行聚焦
|
||||
self.focusCameraOnSelectedNodeAdvanced()
|
||||
if self.selectedNode != target_node:
|
||||
self.updateSelection(target_node)
|
||||
else:
|
||||
self.focusCameraOnSelectedNodeAdvanced()
|
||||
else:
|
||||
print("双击事件:没有有效的目标节点")
|
||||
|
||||
# 重置状态以避免三击等误触发
|
||||
self._last_click_time = 0
|
||||
@ -2598,21 +2633,20 @@ class SelectionSystem:
|
||||
import time
|
||||
current_time = time.time()
|
||||
|
||||
# 检查节点和时间
|
||||
time_diff = current_time - self._last_click_time
|
||||
is_same_node = (self._last_clicked_node == nodePath)
|
||||
# 必须是同一节点且在时间阈值内
|
||||
is_double_click = (self._last_clicked_node == nodePath and
|
||||
nodePath is not None and
|
||||
current_time - self._last_click_time < self._double_click_threshold)
|
||||
|
||||
# 如果是同一节点且在时间阈值内,认为是双击
|
||||
if is_same_node and time_diff < self._double_click_threshold:
|
||||
# 只有在双击时才重置状态
|
||||
if is_double_click:
|
||||
# 重置状态
|
||||
self._last_click_time = 0
|
||||
self._last_clicked_node = None
|
||||
return True
|
||||
else:
|
||||
# 只有在非双击情况下才更新状态
|
||||
if not is_same_node:
|
||||
self._last_click_time = current_time
|
||||
self._last_clicked_node = nodePath
|
||||
# 更新状态
|
||||
self._last_click_time = current_time
|
||||
self._last_clicked_node = nodePath
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
|
||||
2
main.py
2
main.py
@ -235,7 +235,7 @@ class MyWorld(CoreWorld):
|
||||
"""创建2D GUI文本输入框"""
|
||||
return self.gui_manager.createGUIEntry(pos, placeholder, size)
|
||||
|
||||
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=0.5):
|
||||
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=1):
|
||||
"""创建3D空间文本"""
|
||||
return self.gui_manager.createGUI3DText(pos, text, size)
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QTreeWidgetItem
|
||||
from panda3d.core import (
|
||||
ModelPool, ModelRoot, Filename, NodePath, GeomNode, Material, Vec4, Vec3,
|
||||
MaterialAttrib, ColorAttrib, Point3, CollisionNode, CollisionSphere,
|
||||
@ -22,6 +23,7 @@ from pathlib import Path
|
||||
from panda3d.egg import EggData, EggVertexPool
|
||||
from direct.actor.Actor import Actor
|
||||
from QPanda3D.Panda3DWorld import get_render_pipeline
|
||||
from RenderPipelineFile.rpplugins.smaa.jitters import halton_seq
|
||||
from scene import util
|
||||
|
||||
class CesiumIntegration:
|
||||
@ -141,24 +143,20 @@ class SceneManager:
|
||||
print("加载模型失败")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
# 设置模型名称
|
||||
model_name = os.path.basename(filepath)
|
||||
# 确保名称有效
|
||||
if not model_name:
|
||||
model_name = "imported_model"
|
||||
model.setName(model_name)
|
||||
|
||||
# 使用安全方法将模型添加到场景
|
||||
#self._safeReparentTo(model, self.world.render)
|
||||
# 将模型添加到场景
|
||||
model.reparentTo(self.world.render)
|
||||
|
||||
# 设置模型名称
|
||||
model_name = os.path.basename(filepath)
|
||||
model.setName(model_name)
|
||||
|
||||
# 将模型添加到场景
|
||||
model.reparentTo(self.world.render)
|
||||
|
||||
# 保存原始路径和转换后的路径
|
||||
model.setTag("model_path", filepath)
|
||||
model.setTag("original_path", original_filepath)
|
||||
@ -166,18 +164,31 @@ class SceneManager:
|
||||
model.setTag("converted_from", os.path.splitext(original_filepath)[1])
|
||||
model.setTag("converted_to_glb", "true")
|
||||
|
||||
# 可选的单位转换(主要针对FBX)
|
||||
if apply_unit_conversion and filepath.lower().endswith('.fbx'):
|
||||
#print("应用FBX单位转换(厘米到米)...")
|
||||
self._applyUnitConversion(model, 0.01)
|
||||
# 特殊处理FBX模型
|
||||
if filepath.lower().endswith('.fbx'):
|
||||
print("检测到FBX模型,应用特殊处理...")
|
||||
|
||||
# 智能缩放标准化(处理FBX子节点的大缩放值)
|
||||
if normalize_scales and filepath.lower().endswith('.fbx'):
|
||||
#print("标准化FBX模型缩放层级...")
|
||||
self._normalizeModelScales(model)
|
||||
# 将模型缩放设置为原来的1/100
|
||||
model.setScale(0.01)
|
||||
print("设置模型缩放为 0.01 (原始大小的1/100)")
|
||||
|
||||
# 设置模型旋转为 (0, 90, 0)
|
||||
model.setHpr(0, 90, 0)
|
||||
print("设置模型旋转为 (0, 90, 0)")
|
||||
|
||||
# # 可选的单位转换(主要针对FBX
|
||||
# if apply_unit_conversion and filepath.lower().endswith('.fbx'):
|
||||
# #print("应用FBX单位转换(厘米到米)...")
|
||||
# self._applyUnitConversion(model, 0.01)
|
||||
#
|
||||
# # 智能缩放标准化(处理FBX子节点的大缩放值)
|
||||
# if normalize_scales and filepath.lower().endswith('.fbx'):
|
||||
# #print("标准化FBX模型缩放层级...")
|
||||
# self._normalizeModelScales(model)
|
||||
|
||||
# 调整模型位置到地面
|
||||
self._adjustModelToGround(model)
|
||||
model.setPos(0,0,0)
|
||||
#self._adjustModelToGround(model)
|
||||
|
||||
# 创建并设置基础材质
|
||||
print("\n=== 开始设置材质 ===")
|
||||
@ -887,9 +898,16 @@ class SceneManager:
|
||||
"position": list(gui_node.getPos()),
|
||||
"rotation": list(gui_node.getHpr()),
|
||||
"scale": list(gui_node.getScale()),
|
||||
"tags": {}
|
||||
"tags": {},
|
||||
"parent_name":None
|
||||
}
|
||||
|
||||
parent = gui_node.getParent()
|
||||
if parent and not parent.isEmpty():
|
||||
parent_name = parent.getName()
|
||||
if parent_name not in ["render","aspect2d","render2d"]:
|
||||
gui_info["parent_name"] = parent_name
|
||||
|
||||
# 收集所有标签(仅对NodePath类型的对象)
|
||||
if hasattr(gui_node, 'getTagNames'):
|
||||
for tag in gui_node.getTagNames():
|
||||
@ -1205,6 +1223,30 @@ class SceneManager:
|
||||
import json
|
||||
node.setTag("info_panel_data", json.dumps(panel_data, ensure_ascii=False))
|
||||
|
||||
if hasattr(self.world,'script_manager') and self.world.script_manager:
|
||||
script_manager = self.world.script_manager
|
||||
scripts = script_manager.get_scripts_on_object(node)
|
||||
if scripts:
|
||||
node.setTag("has_scripts", "true")
|
||||
script_info_list = []
|
||||
for script_component in scripts:
|
||||
script_name = script_component.script_name
|
||||
print(f"保存脚本信息: {script_name}")
|
||||
|
||||
# 获取脚本类的文件路径
|
||||
script_class = script_component.script_instance.__class__
|
||||
script_file = self._get_script_file_path(script_class, script_name)
|
||||
|
||||
script_info_list.append({
|
||||
"name": script_name,
|
||||
"file": script_file
|
||||
})
|
||||
|
||||
# 将脚本信息保存为JSON字符串
|
||||
import json
|
||||
node.setTag("scripts_info", json.dumps(script_info_list, ensure_ascii=False))
|
||||
print(f"为节点 {node.getName()} 保存了 {len(script_info_list)} 个脚本")
|
||||
|
||||
try:
|
||||
print("--- 打印当前场景图 (render) ---")
|
||||
self.world.render.ls()
|
||||
@ -1459,6 +1501,51 @@ class SceneManager:
|
||||
nodePath.setScale(scale)
|
||||
print(f"{indent}恢复缩放: {scale}")
|
||||
|
||||
if nodePath.hasTag("has_scripts") and nodePath.getTag("has_scripts") == "true":
|
||||
if hasattr(self.world,'script_manager') and self.world.script_manager:
|
||||
try:
|
||||
import json
|
||||
scripts_info = json.loads(nodePath.getTag("scripts_info"))
|
||||
print(f"节点 {nodePath.getName()} 需要重新挂载 {len(scripts_info)} 个脚本")
|
||||
|
||||
script_manager = self.world.script_manager
|
||||
for script_info in scripts_info:
|
||||
script_name = script_info["name"]
|
||||
script_file = script_info.get("file","")
|
||||
|
||||
print(f"尝试重新挂载脚本{script_name}from {script_file}")
|
||||
|
||||
if script_name not in script_manager.loader.script_classes:
|
||||
if script_file and os.path.exists(script_file):
|
||||
print(f"从文件加载脚本:{script_file}")
|
||||
loaded_class = script_manager.load_script_from_file(script_file)
|
||||
if loaded_class is None:
|
||||
print(f"从文件加载脚本失败{script_file}")
|
||||
script_path = self._find_scrip_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载{script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
script_path = self._find_script_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载: {script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
print(f"找不到脚本文件: {script_name}")
|
||||
if script_name in script_manager.loader.script_classes:
|
||||
script_component = script_manager.add_script_to_object(nodePath,script_name)
|
||||
if script_component:
|
||||
print(f"成功为 {nodePath.getName()} 添加脚本: {script_name}")
|
||||
else:
|
||||
print(f"为 {nodePath.getName()} 添加脚本失败: {script_name}")
|
||||
else:
|
||||
print(f"脚本 {script_name} 不可用,跳过挂载")
|
||||
except Exception as e:
|
||||
print(f"重新挂载脚本失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 恢复材质属性
|
||||
def parseColor(color_str):
|
||||
"""解析颜色字符串为Vec4"""
|
||||
@ -1554,11 +1641,11 @@ class SceneManager:
|
||||
|
||||
self._fixModelStructure(nodePath)
|
||||
|
||||
if self.world.property_panel._hasCollision(nodePath):
|
||||
print(f"{indent}模型{nodePath.getName()}已有碰撞体,跳过碰撞体设置")
|
||||
else:
|
||||
print(f"{indent}为模型{nodePath.getName()}设置碰撞检测")
|
||||
self.setupCollision(nodePath)
|
||||
# if self.world.property_panel._hasCollision(nodePath):
|
||||
# print(f"{indent}模型{nodePath.getName()}已有碰撞体,跳过碰撞体设置")
|
||||
# else:
|
||||
# print(f"{indent}为模型{nodePath.getName()}设置碰撞检测")
|
||||
# self.setupCollision(nodePath)
|
||||
self.models.append(nodePath)
|
||||
|
||||
# 递归处理子节点
|
||||
@ -1599,7 +1686,7 @@ class SceneManager:
|
||||
|
||||
# 更新场景树
|
||||
#self.updateSceneTree()
|
||||
# self._get_tree_widget().create_model_items(scene)
|
||||
#self._get_tree_widget().create_model_items(scene)
|
||||
|
||||
print(f"加载完成,GUI元素数量: {len(self.world.gui_elements)}")
|
||||
if len(self.world.gui_elements) > 0:
|
||||
@ -1653,6 +1740,18 @@ class SceneManager:
|
||||
print(f"开始重建 {len(gui_data)} 个GUI元素...")
|
||||
|
||||
processed_names = set()
|
||||
created_elements = {}
|
||||
#存储原始的缩放和位置信息,用于后续计算
|
||||
element_original_data = {}
|
||||
|
||||
# 第一遍:收集所有元素信息
|
||||
for i, gui_info in enumerate(gui_data):
|
||||
name = gui_info.get("name", f"gui_element_{i}")
|
||||
element_original_data[name] = {
|
||||
"scale": gui_info.get("scale", [1, 1, 1]),
|
||||
"position": gui_info.get("position", [0, 0, 0]),
|
||||
"parent_name": gui_info.get("parent_name")
|
||||
}
|
||||
|
||||
pos = (0, 0, 0)
|
||||
for i, gui_info in enumerate(gui_data):
|
||||
@ -1668,6 +1767,7 @@ class SceneManager:
|
||||
bg_image_path = gui_info.get("bg_image_path", "") # 背景图片路径
|
||||
panel_id = gui_info.get("panel_id", name) # 信息面板ID
|
||||
panel_data = gui_info.get("panel_data", None) # 面板数据
|
||||
parent_name = gui_info.get("parent_name")
|
||||
|
||||
# 检查是否已经处理过同名元素
|
||||
if name in processed_names:
|
||||
@ -1684,63 +1784,83 @@ class SceneManager:
|
||||
print(f" 背景图片路径: {bg_image_path}")
|
||||
print(f"视频路径:{video_path}")
|
||||
|
||||
absolute_position = list(position)
|
||||
absolute_scale = list(scale)
|
||||
|
||||
if parent_name and parent_name in element_original_data:
|
||||
parent_data = element_original_data[parent_name]
|
||||
parent_scale = parent_data["scale"]
|
||||
|
||||
if gui_type in ["3d_text", "3d_image", "button", "label", "entry", "2d_image",
|
||||
"2d_video_screen"]:
|
||||
# 位置需要乘以父级缩放来得到绝对位置
|
||||
for j in range(min(len(absolute_position), len(parent_scale))):
|
||||
absolute_position[j] *= parent_scale[j] if len(parent_scale) > j else parent_scale[0]
|
||||
|
||||
# 缩放需要乘以父级缩放来得到绝对缩放
|
||||
for j in range(min(len(absolute_scale), len(parent_scale))):
|
||||
absolute_scale[j] *= parent_scale[j] if len(parent_scale) > j else parent_scale[0]
|
||||
|
||||
print(f" 绝对位置: {absolute_position}")
|
||||
print(f" 绝对缩放: {absolute_scale}")
|
||||
|
||||
# 根据类型创建相应的GUI元素
|
||||
new_element = None
|
||||
|
||||
if gui_type == "button" and hasattr(gui_manager, 'createGUIButton'):
|
||||
new_element = gui_manager.createGUIButton(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
text=text,
|
||||
size=scale[0] if scale and len(scale) > 0 else 1.0
|
||||
size=absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 1.0
|
||||
)
|
||||
elif gui_type == "label" and hasattr(gui_manager, 'createGUILabel'):
|
||||
scale_value = scale[0] if scale and len(scale) > 0 else 1.0
|
||||
scale_value = absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 1.0
|
||||
new_element = gui_manager.createGUILabel(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
text=text,
|
||||
size=scale_value
|
||||
)
|
||||
elif gui_type == "entry" and hasattr(gui_manager, 'createGUIEntry'):
|
||||
new_element = gui_manager.createGUIEntry(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
placeholder=text,
|
||||
size=scale[0] if scale and len(scale) > 0 else 1.0
|
||||
size=absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 1.0
|
||||
)
|
||||
elif gui_type == "2d_image" and hasattr(gui_manager, 'createGUI2DImage'):
|
||||
scale_value = scale[0] if scale and len(scale) > 0 else 0.2
|
||||
scale_value = absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 0.2
|
||||
new_element = gui_manager.createGUI2DImage(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
image_path=image_path,
|
||||
size=scale_value*0.2
|
||||
)
|
||||
elif gui_type == "3d_text" and hasattr(gui_manager,'createGUI3DText'):
|
||||
size = scale[0] if scale and len(scale) > 0 else 0.5
|
||||
size = absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 0.5
|
||||
new_element = gui_manager.createGUI3DText(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
text=text,
|
||||
size=size
|
||||
)
|
||||
elif gui_type == "3d_image" and hasattr(gui_manager, 'createGUI3DImage'):
|
||||
# 处理3D图像
|
||||
# 根据缩放值的数量处理尺寸
|
||||
if len(scale) >= 3:
|
||||
size = (scale[0] * 2, scale[1] * 2)
|
||||
elif len(scale) >= 2:
|
||||
size = (scale[0] * 2, scale[1] * 2)
|
||||
if len(absolute_scale) >= 3:
|
||||
size = (absolute_scale[0] * 2, absolute_scale[1] * 2)
|
||||
elif len(absolute_scale) >= 2:
|
||||
size = (absolute_scale[0] * 2, absolute_scale[1] * 2)
|
||||
elif len(scale) >= 1:
|
||||
size = (scale[0] * 2, scale[0] * 2)
|
||||
size = (absolute_scale[0] * 2, absolute_scale[0] * 2)
|
||||
else:
|
||||
size = (1.0, 1.0)
|
||||
|
||||
new_element = gui_manager.createGUI3DImage(
|
||||
pos=tuple(position),
|
||||
pos=tuple(absolute_position),
|
||||
image_path=image_path,
|
||||
size=size
|
||||
)
|
||||
elif gui_type == "video_screen" and hasattr(gui_manager,'createVideoScreen'):
|
||||
new_element = gui_manager.createVideoScreen(
|
||||
pos=tuple(position),
|
||||
size=scale,
|
||||
pos=tuple(absolute_position),
|
||||
size=absolute_scale,
|
||||
video_path=video_path
|
||||
)
|
||||
if video_path and new_element and hasattr(gui_manager, 'loadVideoFile'):
|
||||
@ -1754,8 +1874,8 @@ class SceneManager:
|
||||
|
||||
elif gui_type == "2d_video_screen" and hasattr(gui_manager,'createGUI2DVideoScreen'):
|
||||
new_element = gui_manager.createGUI2DVideoScreen(
|
||||
pos=tuple(position),
|
||||
size=scale,
|
||||
pos=tuple(absolute_position),
|
||||
size=absolute_scale,
|
||||
video_path=video_path
|
||||
)
|
||||
elif gui_type in ["info_panel", "info_panel_3d"]:
|
||||
@ -1836,9 +1956,146 @@ class SceneManager:
|
||||
elif hasattr(new_element, '_tags'):
|
||||
new_element._tags.update(tags)
|
||||
|
||||
created_elements[name] = new_element
|
||||
|
||||
# 重新挂载脚本(如果有的话)
|
||||
# 在 _recreateGUIElementsFromData 方法中找到重新挂载脚本的部分,替换为以下代码:
|
||||
|
||||
# 重新挂载脚本(如果有的话)
|
||||
# if "scripts" in gui_info and hasattr(self.world,
|
||||
# 'script_manager') and self.world.script_manager:
|
||||
# script_manager = self.world.script_manager
|
||||
# for script_info in gui_info["scripts"]:
|
||||
# script_name = script_info["name"]
|
||||
# script_file = script_info.get("file", "")
|
||||
#
|
||||
# print(f"尝试重新挂载脚本: {script_name} from {script_file}")
|
||||
#
|
||||
# # 检查脚本是否已加载
|
||||
# if script_name not in script_manager.loader.script_classes:
|
||||
# # 如果脚本未加载,尝试从保存的文件路径加载
|
||||
# if script_file and os.path.exists(script_file):
|
||||
# print(f"从文件加载脚本: {script_file}")
|
||||
# loaded_class = script_manager.load_script_from_file(script_file)
|
||||
# if loaded_class is None:
|
||||
# print(f"从文件加载脚本失败: {script_file}")
|
||||
# # 如果从文件加载失败,尝试在脚本目录中查找
|
||||
# script_path = self._find_script_in_directory(script_name)
|
||||
# if script_path:
|
||||
# print(f"从目录找到脚本并加载: {script_path}")
|
||||
# script_manager.load_script_from_file(script_path)
|
||||
# else:
|
||||
# # 如果没有文件路径或文件不存在,尝试在脚本目录中查找
|
||||
# script_path = self._find_script_in_directory(script_name)
|
||||
# if script_path:
|
||||
# print(f"从目录找到脚本并加载: {script_path}")
|
||||
# script_manager.load_script_from_file(script_path)
|
||||
# else:
|
||||
# print(f"找不到脚本文件: {script_name}")
|
||||
#
|
||||
# # 为元素添加脚本
|
||||
# script_component = script_manager.add_script_to_object(new_element, script_name)
|
||||
# if script_component:
|
||||
# print(f"成功为 {name} 添加脚本: {script_name}")
|
||||
# else:
|
||||
# print(f"为 {name} 添加脚本失败: {script_name}")
|
||||
|
||||
print(f"GUI元素重建成功: {name}")
|
||||
else:
|
||||
print(f"无法重建GUI元素: {name} (类型: {gui_type})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"重建GUI元素失败 {name}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
||||
# 第二遍:设置父子级关系并更新Qt树
|
||||
print("开始设置父子级关系...")
|
||||
try:
|
||||
# 创建父子级关系映射
|
||||
parent_child_map = {}
|
||||
for gui_info in gui_data:
|
||||
name = gui_info.get("name")
|
||||
parent_name = gui_info.get("parent_name")
|
||||
|
||||
if name and parent_name and parent_name in created_elements:
|
||||
parent_child_map[name] = parent_name
|
||||
print(f"父子级关系映射: {parent_name} -> {name}")
|
||||
|
||||
# 按正确的顺序设置父子级关系并更新Qt树
|
||||
tree_widget = self._get_tree_widget()
|
||||
if tree_widget:
|
||||
# 先将所有元素添加到Qt树中
|
||||
qt_tree_items = {}
|
||||
for name, element in created_elements.items():
|
||||
# 尝试在Qt树中找到对应的项,如果找不到则创建
|
||||
qt_item = self._findOrCreateQtTreeItem(tree_widget, element, name)
|
||||
if qt_item:
|
||||
qt_tree_items[name] = qt_item
|
||||
|
||||
# 然后设置父子级关系
|
||||
for child_name, parent_name in parent_child_map.items():
|
||||
try:
|
||||
if child_name in created_elements and parent_name in created_elements:
|
||||
child_element = created_elements[child_name]
|
||||
parent_element = created_elements[parent_name]
|
||||
|
||||
# 设置父子级关系
|
||||
if hasattr(child_element, 'reparentTo'):
|
||||
child_element.reparentTo(parent_element)
|
||||
print(f"成功设置父子级关系: {parent_name} -> {child_name}")
|
||||
|
||||
# 更新Qt树显示
|
||||
if child_name in qt_tree_items and parent_name in qt_tree_items:
|
||||
child_item = qt_tree_items[child_name]
|
||||
parent_item = qt_tree_items[parent_name]
|
||||
|
||||
# 从当前位置移除子项
|
||||
if child_item.parent():
|
||||
child_item.parent().removeChild(child_item)
|
||||
else:
|
||||
# 如果是顶级项,从树中移除
|
||||
index = tree_widget.indexOfTopLevelItem(child_item)
|
||||
if index >= 0:
|
||||
tree_widget.takeTopLevelItem(index)
|
||||
|
||||
# 将子项添加到新的父项下
|
||||
parent_item.addChild(child_item)
|
||||
print(f"Qt树更新: {child_name} 移动到 {parent_name} 下")
|
||||
else:
|
||||
print(f"元素 {child_name} 不支持 reparentTo 操作")
|
||||
else:
|
||||
print(f"元素未找到: 父级={parent_name}, 子级={child_name}")
|
||||
except Exception as e:
|
||||
print(f"设置父子级关系失败 {parent_name} -> {child_name}: {e}")
|
||||
continue
|
||||
else:
|
||||
# 如果没有tree_widget,只设置父子级关系
|
||||
for child_name, parent_name in parent_child_map.items():
|
||||
try:
|
||||
if child_name in created_elements and parent_name in created_elements:
|
||||
child_element = created_elements[child_name]
|
||||
parent_element = created_elements[parent_name]
|
||||
|
||||
# 设置父子级关系
|
||||
if hasattr(child_element, 'reparentTo'):
|
||||
child_element.reparentTo(parent_element)
|
||||
print(f"成功设置父子级关系: {parent_name} -> {child_name}")
|
||||
except Exception as e:
|
||||
print(f"设置父子级关系失败 {parent_name} -> {child_name}: {e}")
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
print(f"设置父子级关系时出错: {e}")
|
||||
# 第三遍:重新挂载脚本
|
||||
print("开始重新挂载脚本...")
|
||||
for gui_info in gui_data:
|
||||
try:
|
||||
name = gui_info.get("name")
|
||||
if name in created_elements and "scripts" in gui_info:
|
||||
new_element = created_elements[name]
|
||||
|
||||
# 重新挂载脚本(如果有的话)
|
||||
if "scripts" in gui_info and hasattr(self.world,
|
||||
'script_manager') and self.world.script_manager:
|
||||
@ -1877,15 +2134,8 @@ class SceneManager:
|
||||
print(f"成功为 {name} 添加脚本: {script_name}")
|
||||
else:
|
||||
print(f"为 {name} 添加脚本失败: {script_name}")
|
||||
|
||||
print(f"GUI元素重建成功: {name}")
|
||||
else:
|
||||
print(f"无法重建GUI元素: {name} (类型: {gui_type})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"重建GUI元素失败 {name}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(f"重新挂载脚本失败 {gui_info.get('name', 'unknown')}: {e}")
|
||||
continue
|
||||
|
||||
print("GUI元素重建完成")
|
||||
@ -1895,6 +2145,69 @@ class SceneManager:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _findOrCreateQtTreeItem(self, tree_widget, target_element, element_name):
|
||||
"""在Qt树中查找或创建指定元素对应的项"""
|
||||
try:
|
||||
# 首先尝试查找现有的项
|
||||
existing_item = self._findQtTreeItem(tree_widget, target_element)
|
||||
if existing_item:
|
||||
return existing_item
|
||||
|
||||
# 如果找不到,创建新的项
|
||||
# 找到场景根节点
|
||||
scene_root = None
|
||||
for i in range(tree_widget.topLevelItemCount()):
|
||||
top_item = tree_widget.topLevelItem(i)
|
||||
if top_item.data(0, Qt.UserRole + 1) == "SCENE_ROOT":
|
||||
scene_root = top_item
|
||||
break
|
||||
|
||||
if not scene_root:
|
||||
print("无法找到场景根节点")
|
||||
return None
|
||||
|
||||
# 创建新的Qt树项
|
||||
new_item = QTreeWidgetItem(scene_root, [element_name])
|
||||
new_item.setData(0, Qt.UserRole, target_element)
|
||||
new_item.setData(0, Qt.UserRole + 1, "SCENE_NODE") # 或根据元素类型设置适当的类型
|
||||
|
||||
print(f"为元素 {element_name} 创建了新的Qt树项")
|
||||
return new_item
|
||||
|
||||
except Exception as e:
|
||||
print(f"查找或创建Qt树项失败: {e}")
|
||||
return None
|
||||
|
||||
def _findQtTreeItem(self, tree_widget, target_element):
|
||||
"""在Qt树中查找指定元素对应的项"""
|
||||
try:
|
||||
def search_recursive(parent_item):
|
||||
# 检查当前项
|
||||
if parent_item:
|
||||
item_element = parent_item.data(0, Qt.UserRole)
|
||||
if item_element == target_element:
|
||||
return parent_item
|
||||
|
||||
# 递归检查子项
|
||||
for i in range(parent_item.childCount()):
|
||||
child_item = parent_item.child(i)
|
||||
result = search_recursive(child_item)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
# 从根节点开始搜索
|
||||
root = tree_widget.invisibleRootItem()
|
||||
for i in range(root.childCount()):
|
||||
top_item = root.child(i)
|
||||
result = search_recursive(top_item)
|
||||
if result:
|
||||
return result
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"查找Qt树项失败: {e}")
|
||||
return None
|
||||
def _find_script_in_directory(self, script_name):
|
||||
"""在脚本目录中查找脚本文件"""
|
||||
try:
|
||||
|
||||
@ -768,6 +768,7 @@ class PropertyPanelManager:
|
||||
if spinbox and not spinbox.isHidden(): # 检查控件是否仍然存在且可见
|
||||
# 检查对象是否仍然有效
|
||||
spinbox.blockSignals(True)
|
||||
spinbox.setKeyboardTracking(False) # 确保禁用键盘跟踪
|
||||
spinbox.setValue(value)
|
||||
spinbox.blockSignals(False)
|
||||
except RuntimeError as e:
|
||||
@ -790,42 +791,79 @@ class PropertyPanelManager:
|
||||
# 位置控件
|
||||
transform_layout.addWidget(QLabel("相对位置"), 0, 0)
|
||||
|
||||
# 创建并设置 X, Y, Z 标签居中
|
||||
x_label = QLabel("X")
|
||||
y_label = QLabel("Y")
|
||||
z_label = QLabel("Z")
|
||||
x_label.setAlignment(Qt.AlignCenter)
|
||||
y_label.setAlignment(Qt.AlignCenter)
|
||||
z_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
transform_layout.addWidget(x_label, 0, 1)
|
||||
transform_layout.addWidget(y_label, 0, 2)
|
||||
transform_layout.addWidget(z_label, 0, 3)
|
||||
|
||||
# 位置数值输入框
|
||||
self.pos_x = self._createSafeSpinBox(-1000, 1000)
|
||||
self.pos_y = self._createSafeSpinBox(-1000, 1000)
|
||||
self.pos_z = self._createSafeSpinBox(-1000, 1000)
|
||||
|
||||
# X坐标
|
||||
transform_layout.addWidget(QLabel("X:"), 1, 0)
|
||||
self.pos_x = QLineEdit()
|
||||
self.pos_x.setText(str(round(nodePath.getX(), 6)))
|
||||
self.pos_x.editingFinished.connect(lambda: self._onPositionEditFinished(nodePath, 'x'))
|
||||
transform_layout.addWidget(self.pos_x, 1, 1)
|
||||
transform_layout.addWidget(self.pos_y, 1, 2)
|
||||
transform_layout.addWidget(self.pos_z, 1, 3)
|
||||
|
||||
# 世界位置 (只读)
|
||||
transform_layout.addWidget(QLabel("世界位置"), 2, 0)
|
||||
self.world_pos_x = self._createSafeSpinBox(-10000, 10000, True) # 只读
|
||||
self.world_pos_y = self._createSafeSpinBox(-10000, 10000, True)
|
||||
self.world_pos_z = self._createSafeSpinBox(-10000, 10000, True)
|
||||
# Y坐标
|
||||
transform_layout.addWidget(QLabel("Y:"), 1, 2)
|
||||
self.pos_y = QLineEdit()
|
||||
self.pos_y.setText(str(round(nodePath.getY(), 6)))
|
||||
self.pos_y.editingFinished.connect(lambda: self._onPositionEditFinished(nodePath, 'y'))
|
||||
transform_layout.addWidget(self.pos_y, 1, 3)
|
||||
|
||||
transform_layout.addWidget(self.world_pos_x, 2, 1)
|
||||
transform_layout.addWidget(self.world_pos_y, 2, 2)
|
||||
transform_layout.addWidget(self.world_pos_z, 2, 3)
|
||||
# Z坐标
|
||||
transform_layout.addWidget(QLabel("Z:"), 1, 4)
|
||||
self.pos_z = QLineEdit()
|
||||
self.pos_z.setText(str(round(nodePath.getZ(), 6)))
|
||||
self.pos_z.editingFinished.connect(lambda: self._onPositionEditFinished(nodePath, 'z'))
|
||||
transform_layout.addWidget(self.pos_z, 1, 5)
|
||||
|
||||
return transform_layout
|
||||
|
||||
except Exception as e:
|
||||
print(f"创建变换控件失败: {e}")
|
||||
print(f"创建变换控件时出错: {e}")
|
||||
return None
|
||||
|
||||
def _onPositionEditFinished(self, nodePath, axis):
|
||||
"""位置编辑完成时的处理"""
|
||||
try:
|
||||
# 检查控件是否仍然有效
|
||||
if not hasattr(self, f'pos_{axis}') or getattr(self, f'pos_{axis}') is None:
|
||||
return
|
||||
|
||||
line_edit = getattr(self, f'pos_{axis}')
|
||||
if line_edit is None or line_edit.isHidden():
|
||||
return
|
||||
|
||||
# 获取文本并转换为数值
|
||||
text = line_edit.text()
|
||||
try:
|
||||
new_value = float(text)
|
||||
except ValueError:
|
||||
print(f"无效的数值输入: {text}")
|
||||
# 恢复原来的值
|
||||
if axis == 'x':
|
||||
line_edit.setText(str(round(nodePath.getX(), 6)))
|
||||
elif axis == 'y':
|
||||
line_edit.setText(str(round(nodePath.getY(), 6)))
|
||||
elif axis == 'z':
|
||||
line_edit.setText(str(round(nodePath.getZ(), 6)))
|
||||
return
|
||||
|
||||
# 根据轴设置位置
|
||||
if axis == 'x':
|
||||
nodePath.setX(new_value)
|
||||
elif axis == 'y':
|
||||
nodePath.setY(new_value)
|
||||
elif axis == 'z':
|
||||
nodePath.setZ(new_value)
|
||||
|
||||
print(f"位置已更新: {nodePath.getName()} {axis.upper()} = {new_value}")
|
||||
|
||||
# 如果是坐标轴节点,需要更新坐标轴位置
|
||||
if hasattr(self.world, 'selection_manager'):
|
||||
selection_manager = self.world.selection_manager
|
||||
if (hasattr(selection_manager, 'gizmoTarget') and
|
||||
selection_manager.gizmoTarget == nodePath):
|
||||
# 更新坐标轴位置
|
||||
selection_manager._updateGizmoPositionAndOrientation()
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新位置时出错: {e}")
|
||||
def _createSafeSpinBox(self, min_val, max_val, read_only=False):
|
||||
"""创建安全的数值框"""
|
||||
try:
|
||||
@ -8805,9 +8843,9 @@ except Exception as e:
|
||||
current_row += 1
|
||||
|
||||
# X, Y, Z 位置调整
|
||||
self.collision_pos_x = self._createCollisionSpinBox(-100, 100, 2)
|
||||
self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2)
|
||||
self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2)
|
||||
self.collision_pos_x = self._createCollisionSpinBox(-1000000, 1000000, 2)
|
||||
self.collision_pos_y = self._createCollisionSpinBox(-1000000, 1000000, 2)
|
||||
self.collision_pos_z = self._createCollisionSpinBox(-1000000, 1000000, 2)
|
||||
|
||||
# 只在没有现有碰撞时设置默认值,否则由_loadCurrentCollisionParameters加载实际值
|
||||
if not self._hasCollision(model):
|
||||
@ -8868,7 +8906,7 @@ except Exception as e:
|
||||
radius_label = QLabel("半径:")
|
||||
layout.addWidget(radius_label, current_row, 0)
|
||||
|
||||
self.collision_radius = self._createCollisionSpinBox(0.1, 100, 2)
|
||||
self.collision_radius = self._createCollisionSpinBox(0.01, 1000000, 2)
|
||||
|
||||
# 只在没有现有碰撞时设置默认值,否则由_loadCurrentCollisionParameters加载实际值
|
||||
if not self._hasCollision(model):
|
||||
@ -8900,9 +8938,9 @@ except Exception as e:
|
||||
current_row += 1
|
||||
|
||||
# 宽度、长度、高度
|
||||
self.collision_width = self._createCollisionSpinBox(0.1, 100, 2)
|
||||
self.collision_length = self._createCollisionSpinBox(0.1, 100, 2)
|
||||
self.collision_height = self._createCollisionSpinBox(0.1, 100, 2)
|
||||
self.collision_width = self._createCollisionSpinBox(0.01, 1000000, 2)
|
||||
self.collision_length = self._createCollisionSpinBox(0.1, 1000000, 2)
|
||||
self.collision_height = self._createCollisionSpinBox(0.1, 1000000, 2)
|
||||
|
||||
# 只在没有现有碰撞时设置默认值,否则由_loadCurrentCollisionParameters加载实际值
|
||||
if not self._hasCollision(model):
|
||||
@ -8950,7 +8988,7 @@ except Exception as e:
|
||||
radius_label = QLabel("半径:")
|
||||
layout.addWidget(radius_label, current_row, 0)
|
||||
|
||||
self.collision_capsule_radius = self._createCollisionSpinBox(0.1, 100, 2)
|
||||
self.collision_capsule_radius = self._createCollisionSpinBox(0.01, 1000000, 2)
|
||||
|
||||
# 只在没有现有碰撞时设置默认值,否则由_loadCurrentCollisionParameters加载实际值
|
||||
if not self._hasCollision(model):
|
||||
|
||||
@ -1873,7 +1873,7 @@ class CustomTreeWidget(QTreeWidget):
|
||||
elif is_dragged_3d_gui:
|
||||
if is_target_3d_scene:
|
||||
print(f"✅ 3D GUI元素 {dragged_item.text(0)} 可以拖拽到3D场景节点 {target_item.text(0)}")
|
||||
return True
|
||||
return False
|
||||
elif is_target_2d_gui:
|
||||
print(f"❌ 3D GUI元素 {dragged_item.text(0)} 不能拖拽到2D GUI元素 {target_item.text(0)} 下")
|
||||
print(" 💡 提示: 3D GUI元素不能与2D GUI元素建立父子关系")
|
||||
@ -1890,7 +1890,7 @@ class CustomTreeWidget(QTreeWidget):
|
||||
elif is_dragged_3d_scene:
|
||||
if is_target_3d_scene:
|
||||
print(f"✅ 3D场景元素 {dragged_item.text(0)} 可以拖拽到3D场景节点 {target_item.text(0)}")
|
||||
return True
|
||||
return False
|
||||
elif is_target_2d_gui:
|
||||
print(f"❌ 3D场景元素 {dragged_item.text(0)} 不能拖拽到2D GUI元素 {target_item.text(0)} 下")
|
||||
print(" 💡 提示: 3D场景元素不能与2D GUI元素建立父子关系")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user