"""
GUI元素管理模块

负责2D/3D GUI元素的管理：
- GUI元素的创建（按钮、标签、输入框等）
- GUI编辑模式
- GUI属性编辑
- GUI元素的复制和删除
"""

from direct.gui.DirectGui import *
from panda3d.core import *
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QFormLayout, QLineEdit,
                             QDoubleSpinBox, QPushButton, QDialogButtonBox,
                             QColorDialog, QLabel, QWidget, QGroupBox, QHBoxLayout, QGridLayout, QSpinBox)
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
# 尝试导入 QtWebEngineWidgets，如果失败则设置为 None
try:
    from PyQt5.QtWebEngineWidgets import QWebEngineView
    WEB_ENGINE_AVAILABLE = True
except ImportError:
    QWebEngineView = None
    WEB_ENGINE_AVAILABLE = False
    print("⚠️ QtWebEngineWidgets 不可用，Cesium 集成功能将被禁用")

    # def createGUI3DImage(self, pos=(0, 0, 0), image_path=None, size=1.0):
    #     from panda3d.core import CardMaker, Material, LColor,TransparencyAttrib
    #
    #     # 参数类型检查和转换
    #     if isinstance(size, (list, tuple)):
    #         if len(size) >= 2:
    #             x_size, y_size = float(size[0]), float(size[1])
    #         else:
    #             x_size = y_size = float(size[0]) if size else 1.0
    #     else:
    #         x_size = y_size = float(size)
    #
    #     # 创建卡片
    #     cm = CardMaker('gui_3d_image')
    #     cm.setFrame(-x_size/2, x_size/2, -y_size/2, y_size/2)
    #
    #     # 创建3D图像节点
    #     image_node = self.world.render.attachNewNode(cm.generate())
    #     image_node.setPos(*pos)
    #
    #     # 为3D图像创建独立的材质
    #     material = Material(f"image-material-{len(self.gui_elements)}")
    #     material.setBaseColor(LColor(1, 1, 1, 1))
    #     material.setDiffuse(LColor(1, 1, 1, 1))
    #     material.setAmbient(LColor(0.5, 0.5, 0.5, 1))
    #     material.setSpecular(LColor(0.1, 0.1, 0.1, 1.0))
    #     material.setShininess(10.0)
    #     material.setEmission(LColor(0, 0, 0, 1))  # 无自发光
    #     image_node.setMaterial(material, 1)
    #
    #     image_node.setTransparency(TransparencyAttrib.MAlpha)
    #
    #     # 如果提供了图像路径，则加载纹理
    #     if image_path:
    #         self.update3DImageTexture(image_node, image_path)
    #
    #     # 应用PBR效果（如果可用）
    #     try:
    #         if hasattr(self, 'render_pipeline') and self.render_pipeline:
    #             self.render_pipeline.set_effect(
    #                 image_node,
    #                 "effects/default.yaml",
    #                 {
    #                     "normal_mapping": True,
    #                     "render_gbuffer": True,
    #                     "alpha_testing": False,
    #                     "parallax_mapping": False,
    #                     "render_shadow": False,
    #                     "render_envmap": True,
    #                     "disable_children_effects": True
    #                 },
    #                 50
    #             )
    #             print("✓ GUI 3D图像PBR效果已应用")
    #     except Exception as e:
    #         print(f"⚠️ GUI 3D图像PBR效果应用失败: {e}")
    #
    #     # 为GUI元素添加标识（效仿3D文本方法）
    #     image_node.setTag("gui_type", "3d_image")
    #     image_node.setTag("gui_id", f"3d_image_{len(self.gui_elements)}")
    #     image_node.setTag("is_scene_element", "1")
    #     image_node.setTag("tree_item_type", "GUI_3DIMAGE")
    #     if image_path:
    #         image_node.setTag("gui_image_path", image_path)
    #     image_node.setTag("is_gui_element", "1")
    #
    #     self.gui_elements.append(image_node)
    #
    #     # 更新场景树
    #     if hasattr(self.world, 'updateSceneTree'):
    #         self.world.updateSceneTree()
    #
    #     print(f"✓ 3D图像创建完成: {image_path or '无纹理'} (世界位置: {pos})")
    #     return image_node

class GUIManager:
    """GUI元素管理系统类"""
    
    def __init__(self, world):
        """初始化GUI管理系统
        
        Args:
            world: 核心世界对象引用
        """
        self.world = world
        
        # GUI元素列表
        self.gui_elements = []

        #光源列表
        self.light_elements = []
        
        # GUI编辑模式状态
        self.guiEditMode = False
        self.guiEditPanel = None
        self.guiPreviewWindow = None
        self.currentGUITool = None
        
        print("✓ GUI管理系统初始化完成")

    def _get_chinese_font(self):
        """获取中文字体，用于3D文本显示"""
        try:
            from panda3d.core import TextNode
            from pathlib import Path
            
            # 尝试不同的中文字体路径
            font_paths = [
                "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",  # Linux 文泉驿
                "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",  # Linux Noto
                "/System/Library/Fonts/PingFang.ttc",  # macOS
                "C:/Windows/Fonts/msyh.ttc",  # Windows 微软雅黑
                "C:/Windows/Fonts/simhei.ttf",  # Windows 黑体
            ]
            
            for font_path in font_paths:
                if Path(font_path).exists():
                    font = TextNode.getFont(font_path)
                    if font:
                        print(f"✓ 为3D文本加载中文字体成功: {font_path}")
                        return font
            
            # 如果都失败了，返回默认字体
            print("⚠️ 无法加载中文字体，使用默认字体")
            return None
            
        except Exception as e:
            print(f"⚠️ 加载中文字体失败: {e}")
            return None

    # ==================== GUI元素创建方法 ====================

    def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=(0.1,0.1,0.1)):
        """创建2D GUI按钮 - 支持多选创建和GUI父子关系，优化版本"""
        try:
            from direct.gui.DirectGui import DirectButton
            from PyQt5.QtCore import Qt

            print(f"🔘 开始创建GUI按钮，位置: {pos}, 文本: {text}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 使用CustomTreeWidget的方法获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_gui_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_buttons = []

            # 为每个有效的父节点创建GUI按钮
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    button_name = f"GUIButton_{len(self.gui_elements)}"

                    # 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
                    if tree_widget.is_gui_element(parent_node):
                        # 父节点是GUI元素 - 作为子GUI挂载
                        gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
                        parent_gui_node = parent_node  # 直接挂载到GUI元素
                        parent_scale=parent_node.getScale()
                        relative_scale = (
                            size/parent_scale[0] if parent_scale[0]!=0 else size,
                            size/parent_scale[1] if parent_scale[1]!=0 else size,
                            size/parent_scale[2] if parent_scale[2]!=0 else size
                        )
                    else:
                        # 父节点是普通3D节点 - 使用屏幕坐标
                        gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
                        parent_gui_node = None  # 使用默认的aspect2d
                        relative_scale = size
                        print(f"📎 挂载到3D父节点: {parent_item.text(0)}")

                    button = DirectButton(
                        text=text,
                        pos=gui_pos,
                        scale=relative_scale,
                        command=self.onGUIButtonClick,
                        extraArgs=[f"button_{len(self.gui_elements)}"],
                        frameColor=(0.2, 0.6, 0.8, 1),
                        text_font=self.world.getChineseFont() if self.world.getChineseFont() else None,
                        rolloverSound=None,
                        clickSound=None,
                        parent=parent_gui_node
                    )

                    if not hasattr(button,'_tags'):
                        button._tags = {}

                    # button._tags["gui_type"] = "button"
                    # button._tags["gui_id"] = f"button_{len(self.gui_elements)}"
                    # button._tags["gui_text"] = text
                    # button._tags["is_gui_element"] = "1"
                    # button._tags["is_scene_element"] = "1"
                    # button._tags["saved_gui_type"] = "button"
                    # button._tags["gui_element_type"] = "button"
                    # button._tags["created_by_user"] = "1"
                    # button._tags["name"] = button_name
                    # button.setName(button_name)

                    # 设置节点标签
                    button.setTag("gui_type", "button")
                    button.setTag("gui_id", f"button_{len(self.gui_elements)}")
                    button.setTag("gui_text", text)
                    button.setTag("is_gui_element", "1")
                    button.setTag("is_scene_element", "1")  # 确保这个标签被设置
                    button.setTag("tree_item_type", "GUI_BUTTON")
                    button.setTag("saved_gui_type", "button")  # 添加这个标签以确保兼容性
                    button.setTag("gui_element_type", "button")
                    button.setTag("created_by_user", "1")
                    button.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
                    button.setTag("name", button_name)
                    button.setName(button_name)

                    # 如果有GUI父节点，建立引用关系
                    if parent_gui_node:
                        parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
                        button.setTag("gui_parent_id", parent_id)

                    # 添加到GUI元素列表
                    self.gui_elements.append(button)
                    button.reparentTo(self.world.aspect2d)

                    print(f"✅ 为 {parent_item.text(0)} 创建GUI按钮成功: {button_name}")

                    # 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(button, parent_item, "GUI_BUTTON")
                    if qt_item:
                        created_buttons.append((button, qt_item))
                    else:
                        created_buttons.append((button, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建GUI按钮失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_buttons:
                print("❌ 没有成功创建任何GUI按钮")
                return None

            # 选中最后创建的按钮并更新场景树
            # if created_buttons:
            #     last_button, last_qt_item = created_buttons[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_button, last_qt_item)

            print(f"🎉 总共创建了 {len(created_buttons)} 个GUI按钮")

            # 返回值处理
            if len(created_buttons) == 1:
                return created_buttons[0][0]
            else:
                return [button for button, _ in created_buttons]

        except Exception as e:
            print(f"❌ 创建GUI按钮过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createGUILabel(self, pos=(0, 0, 0), text="标签", size=0.08):
        """创建2D GUI标签 - 支持多选创建和GUI父子关系，优化版本"""
        try:
            from direct.gui.DirectGui import DirectLabel
            from PyQt5.QtCore import Qt

            print(f"🏷️ 开始创建GUI标签，位置: {pos}, 文本: {text}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 使用CustomTreeWidget的方法获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_gui_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_labels = []

            # 为每个有效的父节点创建GUI标签
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    label_name = f"GUILabel_{len(self.gui_elements)}"

                    # 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
                    if tree_widget.is_gui_element(parent_node):
                        # 父节点是GUI元素 - 作为子GUI挂载
                        gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
                        parent_gui_node = parent_node

                        parent_scale = parent_node.getScale()
                        relative_scale = (
                            size/parent_scale[0] if parent_scale[0]!= 0 else size,
                            size/parent_scale[1] if parent_scale[1]!= 0 else size,
                            size/parent_scale[2] if parent_scale[2]!= 0 else size
                        )
                    else:
                        # 父节点是普通3D节点 - 使用屏幕坐标
                        gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
                        parent_gui_node = None
                        relative_scale = size
                    label = DirectLabel(
                        text=text,
                        pos=gui_pos,
                        scale=relative_scale,
                        frameColor=(0, 0, 0, 0),  # 透明背景
                        text_fg=(1, 1, 1, 1),
                        text_font=self.world.getChineseFont() if self.world.getChineseFont() else None,
                        parent=parent_gui_node  # 设置GUI父节点
                    )

                    # 设置节点标签
                    label.setTag("gui_type", "label")
                    label.setTag("gui_id", f"label_{len(self.gui_elements)}")
                    label.setTag("gui_text", text)
                    label.setTag("is_gui_element", "1")
                    label.setTag("tree_item_type", "GUI_LABEL")
                    label.setTag("is_scene_element", "1")
                    label.setTag("created_by_user", "1")
                    label.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
                    label.setTag("name",label_name)
                    label.setName(label_name)

                    # 如果有GUI父节点，建立引用关系
                    if parent_gui_node:
                        parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
                        label.setTag("gui_parent_id", parent_id)

                    # 添加到GUI元素列表
                    self.gui_elements.append(label)

                    print(f"✅ 为 {parent_item.text(0)} 创建GUI标签成功: {label_name}")

                    # 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(label, parent_item, "GUI_LABEL")
                    if qt_item:
                        created_labels.append((label, qt_item))
                    else:
                        created_labels.append((label, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建GUI标签失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_labels:
                print("❌ 没有成功创建任何GUI标签")
                return None

            # 选中最后创建的标签并更新场景树
            # if created_labels:
            #     last_label, last_qt_item = created_labels[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_label, last_qt_item)

            print(f"🎉 总共创建了 {len(created_labels)} 个GUI标签")

            # 返回值处理
            if len(created_labels) == 1:
                return created_labels[0][0]
            else:
                return [label for label, _ in created_labels]

        except Exception as e:
            print(f"❌ 创建GUI标签过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createGUIEntry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08):
        """创建2D GUI文本输入框 - 支持多选创建和GUI父子关系，优化版本"""
        try:
            from direct.gui.DirectGui import DirectEntry
            from PyQt5.QtCore import Qt

            print(f"📝 开始创建GUI输入框，位置: {pos}, 占位符: {placeholder}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 使用CustomTreeWidget的方法获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_gui_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_entries = []

            # 为每个有效的父节点创建GUI输入框
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    entry_name = f"GUIEntry_{len(self.gui_elements)}"

                    # 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
                    if tree_widget.is_gui_element(parent_node):
                        # 父节点是GUI元素 - 作为子GUI挂载
                        gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
                        parent_gui_node = parent_node
                        print(f"📎 挂载到GUI父节点: {parent_node.getName()}")
                    else:
                        # 父节点是普通3D节点 - 使用屏幕坐标
                        gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
                        parent_gui_node = None
                        print(f"📎 挂载到3D父节点: {parent_item.text(0)}")

                    font = None
                    if hasattr(self.world,'getChineseFont'):
                        font = self.world.getChineseFont()

                    entry = DirectEntry(
                        text="",
                        pos=(pos[0],pos[1],pos[2]),
                        scale=size,
                        command=self.onGUIEntrySubmit,
                        extraArgs=[f"entry_{len(self.gui_elements)}"],
                        initialText=placeholder,
                        numLines=1,
                        width=12,
                        focus=0,
                        frameColor = (0,0,0,0),
                        text_fg=(1,1,1,1),
                        text_align=TextNode.ACenter,
                        text_wordwrap=None,
                        rolloverSound=None,
                        clickSound=None,
                        parent=parent_gui_node,  # 设置GUI父节点
                        text_font = font,
                        frameSize=(-0.1,0.1,-0.05,0.05)
                    )

                    # 设置节点标签
                    entry.setTag("gui_type", "entry")
                    entry.setTag("gui_id", f"entry_{len(self.gui_elements)}")
                    entry.setTag("gui_placeholder", placeholder)
                    entry.setTag("is_gui_element", "1")
                    entry.setTag("tree_item_type", "GUI_ENTRY")
                    entry.setTag("is_scene_element", "1")
                    entry.setTag("created_by_user", "1")
                    entry.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
                    entry.setTag("name",entry_name)
                    entry.setName(entry_name)

                    # 如果有GUI父节点，建立引用关系
                    if parent_gui_node:
                        parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
                        entry.setTag("gui_parent_id", parent_id)

                    # 添加到GUI元素列表
                    self.gui_elements.append(entry)

                    print(f"✅ 为 {parent_item.text(0)} 创建GUI输入框成功: {entry_name}")

                    # 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(entry, parent_item, "GUI_ENTRY")
                    if qt_item:
                        created_entries.append((entry, qt_item))
                    else:
                        created_entries.append((entry, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建GUI输入框失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_entries:
                print("❌ 没有成功创建任何GUI输入框")
                return None

            # 选中最后创建的输入框并更新场景树
            # if created_entries:
            #     last_entry, last_qt_item = created_entries[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_entry, last_qt_item)

            print(f"🎉 总共创建了 {len(created_entries)} 个GUI输入框")

            # 返回值处理
            if len(created_entries) == 1:
                return created_entries[0][0]
            else:
                return [entry for entry, _ in created_entries]

        except Exception as e:
            print(f"❌ 创建GUI输入框过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=(1,1,1)):
        """创建2D GUI图片"""
        try:
            from direct.gui.DirectGui import DirectButton
            from PyQt5.QtCore import Qt

            print(f"🔘 开始创建GUI按钮，位置: {pos}, 图片路径: {image_path}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 使用CustomTreeWidget的方法获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_gui_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_2dimage = []

            # 为每个有效的父节点创建GUI按钮
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    image_name = f"GUIImage_{len(self.gui_elements)}"

                    # 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
                    if tree_widget.is_gui_element(parent_node):
                        # 父节点是GUI元素 - 作为子GUI挂载
                        gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
                        parent_gui_node = parent_node  # 直接挂载到GUI元素
                        print(f"📎 挂载到GUI父节点: {parent_node.getName()}")
                    else:
                        # 父节点是普通3D节点 - 使用屏幕坐标
                        gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
                        parent_gui_node = None  # 使用默认的aspect2d
                        print(f"📎 挂载到3D父节点: {parent_item.text(0)}")

                    if isinstance(size, (list, tuple)) and len(size) >= 2:
                        # 分别处理宽度和高度的缩放
                        width_scale = size[0] * 0.25
                        height_scale = size[2] * 0.25
                    else:
                        # 如果只提供了一个缩放值，则使用相同值
                        width_scale = size * 0.1 if isinstance(size, (int, float)) else 0.2
                        height_scale = width_scale

                    cm = CardMaker("gui-2d-image")
                    cm.setFrame(-width_scale, width_scale, -height_scale, height_scale)

                    # image_node = self.world.aspect2d.attachNewNode(cm.generate())
                    if parent_gui_node:
                        image_node = parent_gui_node.attachNewNode(cm.generate())
                    else:
                        image_node = self.world.aspect2d.attachNewNode(cm.generate())
                    image_node.setPos(gui_pos)
                    image_node.setBin('fixed', 0)
                    image_node.setDepthWrite(False)
                    image_node.setDepthTest(False)
                    image_node.setColor(1, 1, 1, 1)

                    # 设置透明度支持
                    image_node.setTransparency(TransparencyAttrib.MAlpha)

                    # 如果提供了图像路径，则加载纹理
                    if image_path:
                        try:
                            image_node.setTag("image_path", image_path)
                            texture = self.world.loader.loadTexture(image_path)
                            if texture:
                                image_node.setTexture(texture, 1)
                                texture.setWrapU(Texture.WM_clamp)
                                texture.setWrapV(Texture.WM_clamp)
                                texture.setMinfilter(Texture.FT_linear)
                                texture.setMagfilter(Texture.FT_linear)
                                image_node.setColor(1, 1, 1, 1)
                            else:
                                print(f"⚠️ 无法加载2D图片纹理: {image_path}")
                        except Exception as e:
                            print(f"❌ 加载2D图片纹理失败: {e}")

                    # 设置节点标签
                    image_node.setTag("gui_type", "2d_image")
                    image_node.setTag("gui_id", f"2d_image_{len(self.gui_elements)}")
                    image_node.setTag("gui_text", f"2D图片_{len(self.gui_elements)}")
                    image_node.setTag("is_gui_element", "1")
                    image_node.setTag("is_scene_element", "1")
                    image_node.setTag("tree_item_type", "GUI_IMAGE")
                    image_node.setTag("created_by_user", "1")
                    image_node.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
                    image_node.setTag("name",image_name)
                    image_node.setName(image_name)

                    # 如果有GUI父节点，建立引用关系
                    if parent_gui_node:
                        parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
                        image_node.setTag("gui_parent_id", parent_id)

                    # 添加到GUI元素列表
                    self.gui_elements.append(image_node)

                    print(f"✅ 为 {parent_item.text(0)} 创建GUI按钮成功: {image_name}")

                    # 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_IMAGE")
                    if qt_item:
                        created_2dimage.append((image_node, qt_item))
                    else:
                        created_2dimage.append((image_node, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建GUI按钮失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_2dimage:
                print("❌ 没有成功创建任何GUI按钮")
                return None

            # 选中最后创建的按钮并更新场景树
            # if created_2dimage:
            #     last_button, last_qt_item = created_2dimage[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_button, last_qt_item)

            print(f"🎉 总共创建了 {len(created_2dimage)} 个GUI按钮")

            # 返回值处理
            if len(created_2dimage) == 1:
                return created_2dimage[0][0]
            else:
                return [button for button, _ in created_2dimage]

        except Exception as e:
            print(f"❌ 创建GUI按钮过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def constrain2DPosition(self,gui_element,new_x=None,new_z=None):
        """限制2dGUI元素位置在屏幕范围内"""
        try:
            from panda3d.core import Vec3

            bounds = gui_element.getTightBounds()
            element_width=0
            element_height=0

            if bounds:
                min_point,max_point = bounds
                element_width = (max_point.getX() - min_point.getX())/2
                element_height = (max_point.getZ()-min_point.getZ())/2

            #获取当前缩放
            scale = gui_element.getScale()
            if hasattr(scale,'getX'):
                scale_x = scale.getX()
                scale_z = scale.getZ() if hasattr(scale,'getZ') else scale_x
            else:
                scale_x = scale_z = scale if isinstance(scale,(int,float)) else 1.0

            actual_width = element_width * scale_x
            actual_height = element_height * scale_z

            screen_width = 1.9
            screen_height = 0.9

            min_x = -screen_width + actual_width
            max_x = screen_width - actual_width
            min_z = -screen_height + actual_height
            max_z = screen_height - actual_height

            #获取当前位置
            current_pos = gui_element.getPos()
            x = new_x if new_x is not None else current_pos.getX()
            z = new_z if new_z is not None else current_pos.getZ()

            #应用边界限制
            x = max(min_x,min(max_x,x))
            z = max(min_z,min(max_z,z))

            return Vec3(x,current_pos.getY(),z)
        except Exception as e:
            print(f"约束2D位置时出错: {e}")
            # 出错时返回原始值
            current_pos = gui_element.getPos()
            x = new_x if new_x is not None else current_pos.getX()
            z = new_z if new_z is not None else current_pos.getZ()
            return Vec3(x, current_pos.getY(), z)

    def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=0.5):
        """创建3D空间文本 - 支持多选创建，优化版本"""
        try:
            from panda3d.core import TextNode
            from PyQt5.QtCore import Qt

            print(f"📄 开始创建3D文本，位置: {pos}, 文本: {text}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_texts = []

            # 为每个有效的父节点创建3D文本
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    text_name = f"GUI3DText_{len(self.gui_elements)}"

                    textNode = TextNode(f'3d-text-{len(self.gui_elements)}')
                    textNode.setText(text)
                    textNode.setAlign(TextNode.ACenter)
                    
                    # 设置中文字体
                    chinese_font = self._get_chinese_font()
                    if chinese_font:
                        textNode.setFont(chinese_font)

                    # 挂载到选中的父节点
                    textNodePath = parent_node.attachNewNode(textNode)
                    textNodePath.setPos(*pos)
                    textNodePath.setScale(size)
                    textNodePath.setColor(1, 1, 0, 1)
                    #textNodePath.setBillboardAxis()  # 让文本总是面向相机
                    textNodePath.setName(text_name)

                    # 设置节点标签
                    textNodePath.setTag("gui_type", "3d_text")
                    textNodePath.setTag("gui_id", f"3d_text_{len(self.gui_elements)}")
                    textNodePath.setTag("gui_text", text)
                    textNodePath.setTag("is_gui_element", "1")
                    textNodePath.setTag("is_scene_element", "1")
                    textNodePath.setTag("tree_item_type", "GUI_3DTEXT")
                    textNodePath.setTag("created_by_user", "1")
                    textNodePath.setTag("name", text_name)

                    # 添加到GUI元素列表
                    self.gui_elements.append(textNodePath)

                    print(f"✅ 为 {parent_item.text(0)} 创建3D文本成功: {text_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(textNodePath, parent_item, "GUI_3DTEXT")
                    if qt_item:
                        created_texts.append((textNodePath, qt_item))
                    else:
                        created_texts.append((textNodePath, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建3D文本失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_texts:
                print("❌ 没有成功创建任何3D文本")
                return None

            # 选中最后创建的文本并更新场景树
            # if created_texts:
            #     last_text, last_qt_item = created_texts[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_text, last_qt_item)

            print(f"🎉 总共创建了 {len(created_texts)} 个3D文本")

            # 返回值处理
            if len(created_texts) == 1:
                return created_texts[0][0]
            else:
                return [text_np for text_np, _ in created_texts]

        except Exception as e:
            print(f"❌ 创建3D文本过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createGUI3DImage(self, pos=(0, 0, 0), image_path=None, size=(1.0,1.0,1.0)):
        """创建3D空间图片"""
        try:

            from panda3d.core import TextNode
            from PyQt5.QtCore import Qt

            print(f"📄 开始创建3D文本，位置: {pos}, 3D图片位置: {image_path}, 尺寸: {size}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_3dimage = []

            # 为每个有效的父节点创建3D文本
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    image_name = f"GUI3DImage_{len(self.gui_elements)}"

                    # 参数类型检查和转换
                    if isinstance(size, (list, tuple)):
                        if len(size) >= 2:
                            x_size, y_size = float(size[0]), float(size[2])
                        else:
                            x_size = y_size = float(size[0]) if size else 1.0
                    else:
                        x_size = y_size = float(size)

                    # 创建卡片
                    cm = CardMaker('gui_3d_image')
                    cm.setFrame(-x_size, x_size, -y_size, y_size)

                    # 创建3D图像节点
                    # image_node = self.world.render.attachNewNode(cm.generate())
                    image_node = parent_node.attachNewNode(cm.generate())
                    image_node.setPos(*pos)

                    # 为3D图像创建独立的材质
                    material = Material(f"image-material-{len(self.gui_elements)}")
                    material.setBaseColor(LColor(1, 1, 1, 1))
                    material.setDiffuse(LColor(1, 1, 1, 1))
                    material.setAmbient(LColor(0.5, 0.5, 0.5, 1))
                    material.setSpecular(LColor(0.1, 0.1, 0.1, 1.0))
                    material.setShininess(10.0)
                    material.setEmission(LColor(0, 0, 0, 1))  # 无自发光
                    image_node.setMaterial(material, 1)

                    image_node.setTransparency(TransparencyAttrib.MAlpha)

                    # 如果提供了图像路径，则加载纹理
                    if image_path:
                        self.update3DImageTexture(image_node, image_path)

                    # 应用PBR效果（如果可用）
                    try:
                        if hasattr(self, 'render_pipeline') and self.render_pipeline:
                            self.render_pipeline.set_effect(
                                image_node,
                                "effects/default.yaml",
                                {
                                    "normal_mapping": True,
                                    "render_gbuffer": True,
                                    "alpha_testing": False,
                                    "parallax_mapping": False,
                                    "render_shadow": False,
                                    "render_envmap": True,
                                    "disable_children_effects": True
                                },
                                50
                            )
                            print("✓ GUI 3D图像PBR效果已应用")
                    except Exception as e:
                        print(f"⚠️ GUI 3D图像PBR效果应用失败: {e}")
                    image_node.setName(image_name)

                    # 设置节点标签
                    image_node.setTag("gui_type", "3d_image")
                    image_node.setTag("gui_id", f"3d_image_{len(self.gui_elements)}")
                    if image_path:
                        image_node.setTag("image_path", image_path)
                    image_node.setTag("is_gui_element", "1")
                    image_node.setTag("is_scene_element", "1")
                    image_node.setTag("tree_item_type", "GUI_3DIMAGE")
                    image_node.setTag("created_by_user", "1")
                    image_node.setTag("name",image_name)

                    # 添加到GUI元素列表
                    self.gui_elements.append(image_node)

                    print(f"✅ 为 {parent_item.text(0)} 创建3D文本成功: {image_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_3DIMAGE")
                    if qt_item:
                        created_3dimage.append((image_node, qt_item))
                    else:
                        created_3dimage.append((image_node, None))
                        print("⚠️ Qt树节点添加失败，但GUI对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建3D文本失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_3dimage:
                print("❌ 没有成功创建任何3D文本")
                return None

            # 选中最后创建的文本并更新场景树
            # if created_3dimage:
            #     last_image, last_qt_item = created_3dimage[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         tree_widget.update_selection_and_properties(last_image, last_qt_item)

            print(f"🎉 总共创建了 {len(created_3dimage)} 个3D文本")

            # 返回值处理
            if len(created_3dimage) == 1:
                return created_3dimage[0][0]
            else:
                return [text_np for text_np, _ in created_3dimage]

        except Exception as e:
            print(f"❌ 创建3D文本过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createVideoScreen(self, pos=(0, 0, 0), size=1, video_path=None):
        """创建3D视频播放屏幕 - 添加占位符纹理支持"""
        try:
            from panda3d.core import CardMaker, TransparencyAttrib, Texture, TextureStage
            import os

            # 确保 pos 是有效的三维坐标元组
            if not isinstance(pos, (tuple, list)) or len(pos) != 3:
                print(f"⚠️ 位置参数无效，使用默认值 (0, 0, 0)，原始值: {pos}")
                pos = (0, 0, 0)
            else:
                # 确保所有坐标都是数值类型
                try:
                    pos = (float(pos[0]), float(pos[1]), float(pos[2]))
                except (ValueError, TypeError):
                    print(f"⚠️ 位置参数包含非数值，使用默认值 (0, 0, 0)，原始值: {pos}")
                    pos = (0, 0, 0)

            # 确保 size 是有效数值
            try:
                size = float(size)
            except (ValueError, TypeError):
                print(f"⚠️ 尺寸参数无效，使用默认值 0.2，原始值: {size}")
                size = 0.2*5

            print(f"📺 开始创建视频屏幕，位置: {pos}, 尺寸: {size}, 视频路径: {video_path}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_videoscreens = []

            # 为每个有效的父节点创建视频屏幕
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    screen_name = f"VideoScreen_{len(self.gui_elements)}"

                    # 使用CardMaker创建视频屏幕框架
                    cm = CardMaker('video-screen')
                    cm.setFrame(-size, size, -size, size)

                    # 创建挂载节点 - 挂载到选中的父节点
                    video_screen = parent_node.attachNewNode(cm.generate())
                    video_screen.setPos(*pos)
                    video_screen.setName(screen_name)
                    video_screen.setBin('fixed', 10)

                    # 设置透明度支持
                    video_screen.setTransparency(TransparencyAttrib.MAlpha)

                    # 设置初始颜色为白色，确保纹理能正确显示
                    video_screen.setColor(1, 1, 1, 1)

                    # 确保视频屏幕有正确的材质
                    self._ensureVideoScreenMaterial(video_screen)

                    # 设置节点标签
                    video_screen.setTag("gui_type", "video_screen")
                    video_screen.setTag("gui_id", f"video_screen_{len(self.gui_elements)}")
                    video_screen.setTag("gui_text", f"视频屏幕_{len(self.gui_elements)}")
                    video_screen.setTag("is_gui_element", "1")
                    video_screen.setTag("is_scene_element", "1")
                    video_screen.setTag("tree_item_type", "GUI_VIDEO_SCREEN")
                    video_screen.setTag("created_by_user", "1")
                    video_screen.setTag("name",screen_name)

                    # 设置视频路径标签
                    if video_path and os.path.exists(video_path):
                        video_screen.setTag("video_path", video_path)
                    else:
                        video_screen.setTag("video_path", "")

                    # 关键修改：预先创建一个占位符纹理，为后续视频播放做准备
                    placeholder_texture = Texture(f"placeholder_video_texture_3d_{len(self.gui_elements)}")
                    placeholder_texture.setup2dTexture(1, 1, Texture.TUnsignedByte, Texture.FRgb)
                    placeholder_data = b'\x19\x19\x4c'  # 深蓝色占位符颜色 (25, 25, 76)
                    placeholder_texture.setRamImage(placeholder_data)

                    # 创建纹理阶段并应用占位符纹理到视频屏幕
                    texture_stage = TextureStage("video_placeholder")
                    texture_stage.setSort(0)
                    texture_stage.setMode(TextureStage.MModulate)
                    video_screen.setTexture(texture_stage, placeholder_texture)

                    # 保存占位符纹理引用
                    video_screen.setPythonTag("placeholder_texture", placeholder_texture)

                    print(f"🔧 为3D视频屏幕创建了占位符纹理环境: {screen_name}")

                    # 如果提供了视频路径，则加载视频纹理
                    movie_texture = None

                    if video_path:
                        if video_path.startswith(("http://","https://")):
                            try:
                                if hasattr(self.world,'property_panel'):
                                    success = self.world.property_panel._loadVideoFromURLWithOpenCV_3D(video_screen,video_path)
                                    if success:
                                        print(f"✅ 视频流URL加载成功: {video_path}")
                                    else:
                                        print(f"⚠️ 视频流URL加载失败: {video_path}")
                                else:
                                    print("⚠️ property_manager不可用，无法加载视频流")
                            except Exception as e:
                                print(f"❌ 加载视频流URL失败: {e}")
                                import traceback
                                traceback.print_exc()
                        elif os.path.exists(video_path):
                            # 对于本地视频文件，使用原有方法
                            try:
                                print(f"🔍 尝试加载2D视频纹理: {video_path}")
                                # 加载视频纹理
                                movie_texture = self._loadMovieTexture(video_path)
                                if movie_texture:
                                    # 应用纹理到视频屏幕（替换占位符）
                                    video_screen["frameTexture"] = movie_texture
                                    print(f"✅ 2D视频纹理加载成功: {video_path}")

                                    # 保存视频纹理引用以便后续控制
                                    video_screen.setPythonTag("movie_texture", movie_texture)

                                    # 尝试自动播放视频（如果支持）
                                    try:
                                        if hasattr(movie_texture, 'play'):
                                            movie_texture.play()
                                            print("▶️ 2D视频已开始播放")
                                    except Exception as play_error:
                                        print(f"⚠️ 2D视频自动播放失败: {play_error}")
                                else:
                                    print(f"⚠️ 无法加载2D视频纹理: {video_path}")
                            except Exception as e:
                                print(f"❌ 加载2D视频纹理失败: {e}")
                                import traceback
                                traceback.print_exc()
                        else:
                            print(f"⚠️ 2D视频文件不存在: {video_path}")

                    # if video_path and os.path.exists(video_path):
                    #     try:
                    #         print(f"🔍 尝试加载视频纹理: {video_path}")
                    #         # 加载视频纹理
                    #         movie_texture = self._loadMovieTexture(video_path)
                    #         if movie_texture:
                    #             # 创建纹理阶段
                    #             texture_stage = TextureStage("video")
                    #             texture_stage.setSort(0)
                    #             texture_stage.setMode(TextureStage.MModulate)
                    #             video_screen.setTexture(texture_stage, movie_texture)
                    #
                    #             print(f"✅ 视频纹理加载成功: {video_path}")
                    #
                    #             # 保存视频纹理引用以便后续控制
                    #             video_screen.setPythonTag("movie_texture", movie_texture)
                    #
                    #             # 尝试自动播放视频（如果支持）
                    #             try:
                    #                 if hasattr(movie_texture, 'play'):
                    #                     movie_texture.play()
                    #                     print("▶️ 视频已开始播放")
                    #             except Exception as play_error:
                    #                 print(f"⚠️ 视频自动播放失败: {play_error}")
                    #         else:
                    #             print(f"⚠️ 无法加载视频纹理: {video_path}")
                    #             # 使用默认颜色作为占位符
                    #             video_screen.setColor(0.1, 0.1, 0.3, 0.8)
                    #     except Exception as e:
                    #         print(f"❌ 加载视频纹理失败: {e}")
                    #         import traceback
                    #         traceback.print_exc()
                    #         # 使用默认颜色作为占位符
                    #         video_screen.setColor(0.1, 0.1, 0.3, 0.8)
                    # else:
                    #     # 没有视频文件时显示默认颜色
                    #     video_screen.setColor(0.1, 0.1, 0.3, 0.8)
                    #     if video_path:
                    #         print(f"⚠️ 视频文件不存在: {video_path}")
                    #     else:
                    #         print("ℹ️ 未提供视频文件，显示默认占位符")
                    #
                    # # 保存视频纹理引用以便后续控制
                    # if movie_texture:
                    #     video_screen.setPythonTag("movie_texture", movie_texture)

                    # 添加到GUI元素列表
                    self.gui_elements.append(video_screen)

                    print(f"✅ 为 {parent_item.text(0)} 创建视频屏幕成功: {screen_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(video_screen, parent_item, "GUI_VIDEO_SCREEN")
                    if qt_item:
                        created_videoscreens.append((video_screen, qt_item))
                    else:
                        created_videoscreens.append((video_screen, None))
                        print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建视频屏幕失败: {str(e)}")
                    import traceback
                    traceback.print_exc()
                    continue

            # 处理创建结果
            if not created_videoscreens:
                print("❌ 没有成功创建任何视频屏幕")
                return None

            # 选中最后创建的视频屏幕
            # if created_videoscreens:
            #     last_screen_np, last_qt_item = created_videoscreens[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         # 更新选择和属性面板
            #         tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_videoscreens)} 个视频屏幕")

            # 返回值处理
            if len(created_videoscreens) == 1:
                return created_videoscreens[0][0]  # 单个屏幕返回NodePath
            else:
                return [screen_np for screen_np, _ in created_videoscreens]  # 多个屏幕返回列表

        except Exception as e:
            print(f"❌ 创建视频屏幕过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def _ensureVideoScreenMaterial(self, video_screen):
        """确保视频屏幕有正确的材质设置"""
        try:
            from panda3d.core import Material, LColor

            # 如果还没有材质，则创建一个
            if not video_screen.hasMaterial():
                material = Material(f"video-material-{video_screen.getName()}")
                material.setBaseColor(LColor(1, 1, 1, 1))
                material.setDiffuse(LColor(1, 1, 1, 1))
                material.setAmbient(LColor(1, 1, 1, 1))  # 确保环境光为白色
                material.setEmission(LColor(0, 0, 0, 1))
                material.setSpecular(LColor(0, 0, 0, 1))
                material.setShininess(0)
                video_screen.setMaterial(material, 1)
                print(f"✅ 为视频屏幕创建了新材质: {video_screen.getName()}")
            else:
                # 更新现有材质确保正确设置
                material = video_screen.getMaterial()
                material.setBaseColor(LColor(1, 1, 1, 1))
                material.setAmbient(LColor(1, 1, 1, 1))  # 确保环境光为白色
                video_screen.setMaterial(material, 1)
                print(f"✅ 更新了视频屏幕材质: {video_screen.getName()}")

        except Exception as e:
            print(f"⚠️ 设置视频屏幕材质时出错: {e}")

    def _debugVideoScreenTextures(self, video_screen):
        """调试视频屏幕的纹理状态"""
        try:
            print(f"调试视频屏幕 {video_screen.getName()}:")

            # 检查PythonTag
            movie_texture = video_screen.getPythonTag("movie_texture")
            if movie_texture:
                print(f"  - PythonTag movie_texture: {type(movie_texture)}")
                if hasattr(movie_texture, 'is_playable'):
                    print(f"  - is_playable: {movie_texture.is_playable()}")
            else:
                print("  - PythonTag movie_texture: None")

            # 检查所有纹理阶段
            texture_stages = video_screen.findAllTextureStages()
            print(f"  - 纹理阶段数: {texture_stages.getNumStages()}")
            for i in range(texture_stages.getNumStages()):
                stage = texture_stages.getStage(i)
                texture = video_screen.getTexture(stage)
                print(f"    - 阶段 {i}: {stage.getName()}, 纹理: {texture.getName() if texture else 'None'}")

        except Exception as e:
            print(f"调试视频屏幕纹理时出错: {e}")

    def playVideo(self, video_screen):
        """播放视频 - 改进版本，支持从暂停处继续播放"""
        try:
            # 获取视频纹理
            movie_texture = self._getMovieTextureFromScreen(video_screen)

            if movie_texture:
                # 检查是否有播放方法
                if hasattr(movie_texture, 'play'):
                    try:
                        self.loadVideoFile(video_screen, video_screen.getTag("video_path"))
                        movie_texture.play()
                        print(f"▶️ 继续播放视频: {video_screen.getName()}")
                        return True
                    except Exception as play_error:
                        print(f"⚠️ 播放视频时出错: {play_error}")
                        return False
                else:
                    print(f"⚠️ 纹理对象没有播放方法: {video_screen.getName()}")
                    return False
            else:
                self.loadVideoFile(video_screen,video_screen.getTag("video_path"))
        except Exception as e:
            print(f"❌ 播放视频失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def _getMovieTextureFromScreen(self, video_screen):
        """从视频屏幕获取视频纹理"""
        try:
            # 方法1: 从PythonTag获取
            movie_texture = video_screen.getPythonTag("movie_texture")
            if movie_texture:
                return movie_texture

            # 方法2: 从纹理阶段获取
            from panda3d.core import TextureStage
            texture_stage = video_screen.findTextureStage("video")
            if texture_stage:
                movie_texture = video_screen.getTexture(texture_stage)
                if movie_texture:
                    return movie_texture

            # 方法3: 获取第一个纹理
            if video_screen.hasTexture():
                movie_texture = video_screen.getTexture()
                if movie_texture:
                    return movie_texture

            return None
        except Exception as e:
            print(f"获取视频纹理时出错: {e}")
            return None

    def pauseVideo(self, video_screen):
        """暂停视频"""
        try:
            movie_texture = self._getMovieTextureFromScreen(video_screen)

            if movie_texture:
                # 检查是否有暂停方法
                if hasattr(movie_texture, 'stop'):  # MovieTexture使用stop来暂停
                    try:
                        movie_texture.stop()
                        print(f"⏸️ 视频已暂停: {video_screen.getName()}")
                        return True
                    except Exception as stop_error:
                        print(f"⚠️ 暂停视频时出错: {stop_error}")
                        return False
                elif hasattr(movie_texture, 'set play rate'):  # 某些版本支持设置播放速率
                    try:
                        movie_texture.setPlayRate(0.0)
                        print(f"⏸️ 视频已暂停(播放速率设为0): {video_screen.getName()}")
                        return True
                    except Exception as rate_error:
                        print(f"⚠️ 设置播放速率时出错: {rate_error}")
                        return False
                else:
                    print(f"⚠️ 纹理对象没有暂停方法: {video_screen.getName()}")
                    return False
            else:
                print(f"❌ 视频屏幕没有关联的视频纹理: {video_screen.getName()}")
                return False
        except Exception as e:
            print(f"❌ 暂停视频失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def stopVideo(self, video_screen):
        """停止视频（回到开头）"""
        try:
            movie_texture = self._getMovieTextureFromScreen(video_screen)

            if movie_texture:
                # 停止并重置到开头
                if hasattr(movie_texture, 'stop'):
                    try:
                        movie_texture.stop()
                        # 如果有重置方法，调用它
                        if hasattr(movie_texture, 'setTime'):
                            movie_texture.setTime(0.0)
                        print(f"⏹️ 视频已停止: {video_screen.getName()}")
                        return True
                    except Exception as stop_error:
                        print(f"⚠️ 停止视频时出错: {stop_error}")
                        return False
                else:
                    print(f"⚠️ 纹理对象没有停止方法: {video_screen.getName()}")
                    return False
            else:
                print(f"❌ 视频屏幕没有关联的视频纹理: {video_screen.getName()}")
                return False
        except Exception as e:
            print(f"❌ 停止视频失败: {e}")
            import traceback
            traceback.print_exc()
            return False
    def setVideoTime(self, video_screen, time_seconds):
        """设置视频播放时间"""
        try:
            movie_texture = video_screen.getPythonTag("movie_texture")

            # 备用获取方法
            if not movie_texture:
                from panda3d.core import TextureStage
                texture_stage = video_screen.findTextureStage("video")
                if texture_stage:
                    movie_texture = video_screen.getTexture(texture_stage)

            if not movie_texture and video_screen.hasTexture():
                movie_texture = video_screen.getTexture()

            if movie_texture and hasattr(movie_texture, 'set_time'):
                movie_texture.set_time(time_seconds)
                print(f"🕒 设置视频时间 {time_seconds}s: {video_screen.getName()}")
                return True
            else:
                print(f"❌ 视频屏幕没有关联的视频纹理: {video_screen.getName()}")
                return False
        except Exception as e:
            print(f"❌ 设置视频时间失败: {e}")
            return False

    def loadVideoFile(self, video_screen, video_path):
        """为视频屏幕加载新的视频文件"""
        try:
            import os

            if not os.path.exists(video_path):
                print(f"❌ 视频文件不存在: {video_path}")
                return False

            # Convert Windows path to Panda3D compatible path format
            from panda3d.core import Filename
            panda_path = Filename.fromOsSpecific(video_path)
            converted_path = str(panda_path)

            # 加载新的视频纹理
            movie_texture = self._loadMovieTexture(video_path)  # Pass original path for existence check
            if movie_texture:
                # 清除现有的纹理
                video_screen.clearTexture()

                # 设置视频纹理属性
                movie_texture.setWrapU(Texture.WM_clamp)
                movie_texture.setWrapV(Texture.WM_clamp)
                movie_texture.setMinfilter(Texture.FT_linear)
                movie_texture.setMagfilter(Texture.FT_linear)

                # 如果视频纹理支持循环播放，设置循环
                if hasattr(movie_texture, 'set_loop'):
                    movie_texture.set_loop(True)

                # 如果视频纹理支持播放速率控制，设置正常速率
                if hasattr(movie_texture, 'set_play_rate'):
                    movie_texture.set_play_rate(1.0)

                # 重要：为视频纹理创建专用的纹理阶段
                texture_stage = TextureStage("video")
                texture_stage.setSort(0)  # 使用第一个纹理槽
                texture_stage.setMode(TextureStage.MModulate)

                # 应用纹理到视频屏幕
                video_screen.setTexture(texture_stage, movie_texture)

                # 保存新的视频纹理引用到PythonTag
                video_screen.setPythonTag("movie_texture", movie_texture)
                video_screen.setTag("video_path", converted_path)  # Store converted path

                # 确保视频屏幕有正确的材质
                self._ensureVideoScreenMaterial(video_screen)

                print(f"✅ 成功加载新视频: {converted_path}")
                return True
            else:
                print(f"❌ 无法加载视频文件: {converted_path}")
                return False

        except Exception as e:
            print(f"❌ 加载视频文件失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def _loadMovieTexture(self, video_path):
        """加载视频纹理的兼容方法"""
        try:
            from panda3d.core import Texture, MovieTexture, Filename
            import os

            # 检查文件是否存在
            if not os.path.exists(video_path):
                print(f"❌ 视频文件不存在: {video_path}")
                return None

            # Convert Windows path to Panda3D compatible path format
            panda_path = Filename.fromOsSpecific(video_path)
            converted_path = str(panda_path)

            print(f"🔍 尝试加载视频文件: {converted_path}")

            # 方法1: 尝试使用 MovieTexture（专门用于视频）
            try:
                movie_texture = MovieTexture(converted_path)
                if movie_texture.read(converted_path):
                    print("✅ 使用 MovieTexture 成功加载视频")
                    self._configureVideoTexture(movie_texture)
                    return movie_texture
                else:
                    print("⚠️ MovieTexture.read() 返回失败")
            except Exception as e:
                print(f"⚠️ MovieTexture 方法失败: {e}")

            # 方法2: 尝试使用 loader.loadTexture
            try:
                movie_texture = self.world.loader.loadTexture(converted_path)
                if movie_texture and hasattr(movie_texture, 'is_playable') and movie_texture.is_playable():
                    print("✅ 使用 loader.loadTexture 成功加载视频纹理")
                    self._configureVideoTexture(movie_texture)
                    return movie_texture
                else:
                    print("⚠️ loader.loadTexture 加载的不是可播放的视频纹理")
            except Exception as e:
                print(f"⚠️ loader.loadTexture 方法失败: {e}")

            # 方法3: 尝试使用 Texture.read（作为最后备选）
            try:
                texture = Texture()
                if texture.read(converted_path):
                    print("✅ 使用 Texture.read 成功加载（可能作为静态纹理）")
                    self._configureVideoTexture(texture)
                    return texture
            except Exception as e:
                print(f"⚠️ Texture.read 方法失败: {e}")

            print("❌ 所有视频纹理加载方法都失败")
            return None

        except Exception as e:
            print(f"❌ 加载视频纹理时发生未知错误: {e}")
            import traceback
            traceback.print_exc()
            return None

    def _configureVideoTexture(self, texture):
        """配置视频纹理属性"""
        try:
            from panda3d.core import Texture

            # 设置纹理属性
            texture.setWrapU(Texture.WM_clamp)
            texture.setWrapV(Texture.WM_clamp)
            texture.setMinfilter(Texture.FT_linear)
            texture.setMagfilter(Texture.FT_linear)

            # 如果是可播放的视频纹理，设置播放属性
            if hasattr(texture, 'set_loop') and hasattr(texture, 'set_play_rate'):
                texture.set_loop(True)
                texture.set_play_rate(1.0)

            print(f"✅ 视频纹理配置完成: {texture.getName()}")

        except Exception as e:
            print(f"⚠️ 配置视频纹理时出错: {e}")

    def createGUI2DVideoScreen(self, pos=(0, 0), size=0.2, video_path=None):
        """创建2D视频播放屏幕 - 使用2D坐标"""
        try:
            from direct.gui.DirectGui import DirectFrame
            from panda3d.core import TransparencyAttrib, Texture, TextureStage
            from PyQt5.QtCore import Qt
            import os

            # 确保 pos 是有效的二维坐标元组
            if pos is None or pos is False or not isinstance(pos, (tuple, list)) or len(pos) != 2:
                print(f"⚠️ 位置参数无效，使用默认值 (0, 0)，原始值: {pos}")
                pos = (0, 0)
            else:
                # 确保所有坐标都是数值类型
                try:
                    pos = (float(pos[0]), float(pos[1]))
                except (ValueError, TypeError, IndexError) as e:
                    print(f"⚠️ 位置参数包含非数值或索引错误，使用默认值 (0, 0)，原始值: {pos}, 错误: {e}")
                    pos = (0, 0)

            # 确保 size 是有效数值
            try:
                size = float(size)
            except (ValueError, TypeError) as e:
                print(f"⚠️ 尺寸参数无效，使用默认值 0.2，原始值: {size}, 错误: {e}")
                size = 0.2

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_gui_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_videoscreens = []

            # 为每个有效的父节点创建2D视频屏幕
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    screen_name = f"GUI2DVideoScreen_{len(self.gui_elements)}"

                    # 使用DirectFrame创建2D视频屏幕
                    video_screen = DirectFrame(
                        frameSize=(-size, size, -size, size),
                        frameColor=(1, 1, 1, 1),  # 默认背景色
                        pos=(pos[0] * 0.1, 0, pos[1] * 0.1),  # 转换为屏幕坐标
                        parent=parent_node if tree_widget.is_gui_element(parent_node) else self.world.aspect2d,
                        suppressMouse=True,
                    )

                    video_screen.setName(screen_name)

                    # 设置透明度支持
                    video_screen.setTransparency(TransparencyAttrib.MAlpha)

                    #设置2D视频屏幕特有的标签
                    video_screen.setTag("gui_type", "2d_video_screen")
                    video_screen.setTag("gui_id", f"2d_video_screen_{len(self.gui_elements)}")
                    video_screen.setTag("gui_text", f"2D视频屏幕_{len(self.gui_elements)}")
                    video_screen.setTag("is_gui_element", "1")
                    video_screen.setTag("is_scene_element", "1")
                    video_screen.setTag("tree_item_type", "GUI_2D_VIDEO_SCREEN")
                    video_screen.setTag("created_by_user", "1")
                    video_screen.setTag("name",screen_name)
                    video_screen.setTag("video_path",video_path or "")

                    # 关键修改：预先创建一个占位符纹理，为后续视频播放做准备
                    placeholder_texture = Texture(f"placeholder_video_texture_{len(self.gui_elements)}")
                    placeholder_texture.setup2dTexture(1, 1, Texture.TUnsignedByte, Texture.FRgb)
                    placeholder_data = b'\x19\x19\x4c'  # 深蓝色占位符颜色 (25, 25, 76)
                    placeholder_texture.setRamImage(placeholder_data)

                    # 应用占位符纹理到视频屏幕
                    video_screen["frameTexture"] = placeholder_texture

                    # 保存占位符纹理引用
                    video_screen.setPythonTag("placeholder_texture", placeholder_texture)

                    print(f"🔧 为2D视频屏幕创建了占位符纹理环境: {screen_name}")

                    # 如果提供了视频路径，则加载视频纹理
                    movie_texture = None

                    if video_path:
                        if video_path.startswith(("http://","https://")):
                            try:
                                if hasattr(self.world,'property_panel'):
                                    success = self.world.property_panel._loadVideoFromURLWithOpenCV(video_screen,video_path)
                                    if success:
                                        print(f"✅ 视频流URL加载成功: {video_path}")
                                    else:
                                        print(f"⚠️ 视频流URL加载失败: {video_path}")
                                else:
                                    print("⚠️ property_manager不可用，无法加载视频流")
                            except Exception as e:
                                print(f"❌ 加载视频流URL失败: {e}")
                                import traceback
                                traceback.print_exc()
                        elif os.path.exists(video_path):
                            try:
                                print(f"🔍 尝试加载2D视频纹理: {video_path}")
                                # 加载视频纹理
                                movie_texture = self._loadMovieTexture(video_path)
                                if movie_texture:
                                    # 应用纹理到视频屏幕（替换占位符）
                                    video_screen["frameTexture"] = movie_texture
                                    print(f"✅ 2D视频纹理加载成功: {video_path}")

                                    # 保存视频纹理引用以便后续控制
                                    video_screen.setPythonTag("movie_texture", movie_texture)

                                    # 尝试自动播放视频（如果支持）
                                    try:
                                        if hasattr(movie_texture, 'play'):
                                            movie_texture.play()
                                            print("▶️ 2D视频已开始播放")
                                    except Exception as play_error:
                                        print(f"⚠️ 2D视频自动播放失败: {play_error}")
                                else:
                                    print(f"⚠️ 无法加载2D视频纹理: {video_path}")
                            except Exception as e:
                                print(f"❌ 加载2D视频纹理失败: {e}")
                                import traceback
                                traceback.print_exc()
                        else:
                            print(f"⚠️ 2D视频文件不存在: {video_path}")

                    # if video_path and os.path.exists(video_path):
                    #     try:
                    #         print(f"🔍 尝试加载2D视频纹理: {video_path}")
                    #         # 加载视频纹理
                    #         movie_texture = self._loadMovieTexture(video_path)
                    #         if movie_texture:
                    #             # 应用纹理到视频屏幕（替换占位符）
                    #             video_screen["frameTexture"] = movie_texture
                    #             print(f"✅ 2D视频纹理加载成功: {video_path}")
                    #
                    #             # 保存视频纹理引用以便后续控制
                    #             video_screen.setPythonTag("movie_texture", movie_texture)
                    #
                    #             # 尝试自动播放视频（如果支持）
                    #             try:
                    #                 if hasattr(movie_texture, 'play'):
                    #                     movie_texture.play()
                    #                     print("▶️ 2D视频已开始播放")
                    #             except Exception as play_error:
                    #                 print(f"⚠️ 2D视频自动播放失败: {play_error}")
                    #         else:
                    #             print(f"⚠️ 无法加载2D视频纹理: {video_path}")
                    #     except Exception as e:
                    #         print(f"❌ 加载2D视频纹理失败: {e}")
                    #         import traceback
                    #         traceback.print_exc()
                    # else:
                    #     if not video_path:
                    #         print(f"⚠️ 2D视频文件不存在: {video_path}")

                    # 添加到GUI元素列表
                    self.gui_elements.append(video_screen)

                    print(f"✅ 为 {parent_item.text(0)} 创建2D视频屏幕成功: {screen_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(video_screen, parent_item, "GUI_2D_VIDEO_SCREEN")
                    if qt_item:
                        created_videoscreens.append((video_screen, qt_item))
                    else:
                        created_videoscreens.append((video_screen, None))
                        print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建2D视频屏幕失败: {str(e)}")
                    import traceback
                    traceback.print_exc()
                    continue

            # 处理创建结果
            if not created_videoscreens:
                print("❌ 没有成功创建任何2D视频屏幕")
                return None

            # 选中最后创建的视频屏幕
            # if created_videoscreens:
            #     last_screen_np, last_qt_item = created_videoscreens[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         # 更新选择和属性面板
            #         tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_videoscreens)} 个2D视频屏幕")

            # 返回值处理
            if len(created_videoscreens) == 1:
                return created_videoscreens[0][0]  # 单个屏幕返回NodePath
            else:
                return [screen_np for screen_np, _ in created_videoscreens]  # 多个屏幕返回列表

        except Exception as e:
            print(f"❌ 创建2D视频屏幕过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def load2DVideoFile(self, video_screen, video_path):
        """为2D视频屏幕加载新的视频文件"""
        try:
            import os

            video_screen.setTag("video_path",video_path)
            path = video_screen.getTag("video_path")
            print({video_screen.getTag("gui_type")})
            print(f"🔧 更新2D视频屏幕标签 - video_path: {path}")

            if not video_path or not os.path.exists(video_path):
                print(f"❌ 2D视频文件不存在: {video_path}")
                return False

            # 加载新的视频纹理
            movie_texture = self._loadMovieTexture(video_path)
            if movie_texture:
                # 应用纹理到2D视频屏幕
                video_screen["frameTexture"] = movie_texture

                # 保存视频纹理引用
                video_screen.setPythonTag("movie_texture", movie_texture)

                print(f"✅ 成功加载新2D视频: {video_path}")
                return True
            else:
                print(f"❌ 无法加载2D视频文件: {video_path}")
                return False

        except Exception as e:
            print(f"❌ 加载2D视频文件失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def play2DVideo(self, video_screen):
        """播放2D视频"""
        try:
            if video_screen.hasPythonTag("video_capture"):
                print("视频已在播放中")
                return True

            # 获取视频路径并重新开始播放
            video_path = video_screen.getTag("video_path")
            if video_path:
                return self.world.property_manager._loadVideoFromURL(video_screen, video_path)
            else:
                print("❌ 没有找到视频源")
                return False

        except Exception as e:
            print(f"❌ 播放视频失败: {e}")
            return False

    def pause2DVideo(self, video_screen):
        """暂停2D视频"""
        try:
            # 在OpenCV中没有直接的暂停功能，我们通过停止线程来模拟暂停
            if video_screen.hasPythonTag("video_capture"):
                cap = video_screen.getPythonTag("video_capture")
                if cap:
                    cap.release()
                video_screen.clearPythonTag("video_capture")
                print("⏸️ 视频已暂停")
                return True
            return False
        except Exception as e:
            print(f"❌ 暂停视频失败: {e}")
            return False

    def stop2DVideo(self, video_screen):
        """停止2D视频"""
        try:
            # 停止视频捕获
            if video_screen.hasPythonTag("video_capture"):
                cap = video_screen.getPythonTag("video_capture")
                if cap:
                    cap.release()
                video_screen.clearPythonTag("video_capture")

            # 清理纹理
            if video_screen.hasPythonTag("video_texture"):
                video_screen.clearPythonTag("video_texture")

            # 清理视频路径标签
            video_screen.clearTag("video_path")

            print("⏹️ 视频已停止")
            return True
        except Exception as e:
            print(f"❌ 停止视频失败: {e}")
            return False

    def createSphericalVideo(self, pos=(0, 0, 0), radius=5.0, video_path=None):
        """创建球形视频（360度视频）- 支持无初始视频文件"""
        try:
            from panda3d.core import GeomVertexFormat, GeomVertexData, GeomVertexWriter
            from panda3d.core import Geom, GeomTriangles, GeomNode
            from panda3d.core import TextureStage, Texture
            import math
            import os

            # 确保 pos 是有效的三维坐标元组
            if not isinstance(pos, (tuple, list)) or len(pos) != 3:
                print(f"⚠️ 位置参数无效，使用默认值 (0, 0, 0)，原始值: {pos}")
                pos = (0, 0, 0)
            else:
                # 确保所有坐标都是数值类型
                try:
                    pos = (float(pos[0]), float(pos[1]), float(pos[2]))
                except (ValueError, TypeError):
                    print(f"⚠️ 位置参数包含非数值，使用默认值 (0, 0, 0)，原始值: {pos}")
                    pos = (0, 0, 0)

            # 确保 radius 是有效数值
            try:
                radius = float(radius)
            except (ValueError, TypeError):
                print(f"⚠️ 半径参数无效，使用默认值 5.0，原始值: {radius}")
                radius = 5.0

            print(f"🌍 开始创建球形视频，位置: {pos}, 半径: {radius}, 视频路径: {video_path}")

            # 不再强制检查视频文件是否存在，允许创建空的球形视频
            if video_path and not os.path.exists(video_path):
                print(f"⚠️ 视频文件不存在，将创建空的球形视频: {video_path}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_spherical_videos = []

            # 为每个有效的父节点创建球形视频
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    sphere_name = f"SphericalVideo_{len(self.gui_elements)}"

                    # 创建球形几何体
                    sphere_geom = self._createSphereGeometry(radius, segments=32)

                    # 创建几何节点
                    sphere_node = GeomNode('sphere_video')
                    sphere_node.addGeom(sphere_geom)

                    # 创建节点路径并挂载
                    sphere_np = parent_node.attachNewNode(sphere_node)
                    sphere_np.setPos(*pos)  # 现在 pos 已经是有效的元组
                    sphere_np.setName(sphere_name)

                    # 翻转法线，使视频在球体内部显示
                    sphere_np.setTwoSided(True)
                    sphere_np.setBin('background', 0)  # 确保在背景层
                    sphere_np.setDepthWrite(False)  # 不写入深度缓冲

                    # 设置初始颜色为占位符颜色
                    sphere_np.setColor(0.1, 0.1, 0.3, 0.8)  # 深蓝色占位符

                    # 如果提供了视频路径且文件存在，则加载视频纹理
                    movie_texture = None
                    if video_path and os.path.exists(video_path):
                        try:
                            # 加载视频纹理
                            movie_texture = self._loadMovieTexture(video_path)
                            if movie_texture:
                                # 设置视频纹理属性
                                movie_texture.setWrapU(Texture.WM_clamp)
                                movie_texture.setWrapV(Texture.WM_clamp)
                                movie_texture.setMinfilter(Texture.FT_linear)
                                movie_texture.setMagfilter(Texture.FT_linear)

                                # 为视频纹理创建专用的纹理阶段
                                texture_stage = TextureStage("spherical_video")
                                texture_stage.setMode(TextureStage.MModulate)

                                # 应用纹理到球体
                                sphere_np.setTexture(texture_stage, movie_texture)

                                # 设置为白色以正确显示视频
                                sphere_np.setColor(1, 1, 1, 1)

                                print(f"✅ 视频纹理已应用到球形视频: {video_path}")

                                # 尝试自动播放
                                try:
                                    if hasattr(movie_texture, 'play'):
                                        movie_texture.play()
                                        print("▶️ 球形视频已开始播放")
                                except Exception as play_error:
                                    print(f"⚠️ 球形视频自动播放失败: {play_error}")
                            else:
                                print(f"⚠️ 无法加载视频纹理: {video_path}")
                                # 保持占位符颜色
                        except Exception as e:
                            print(f"❌ 加载视频纹理失败: {e}")
                            import traceback
                            traceback.print_exc()
                            # 保持占位符颜色
                    else:
                        if video_path:
                            print(f"⚠️ 视频文件不存在: {video_path}")
                        else:
                            print("ℹ️ 未提供视频文件，创建空的球形视频")

                    # 设置标签以便识别和管理
                    sphere_np.setTag("gui_type", "spherical_video")
                    sphere_np.setTag("is_gui_element", "1")
                    sphere_np.setTag("tree_item_type", "GUI_SPHERICAL_VIDEO")
                    sphere_np.setTag("video_path", video_path or "")
                    sphere_np.setTag("original_radius", str(radius))

                    # 保存视频纹理引用
                    if movie_texture:
                        sphere_np.setPythonTag("movie_texture", movie_texture)

                    # 添加到GUI元素列表
                    self.gui_elements.append(sphere_np)

                    print(f"✅ 为 {parent_item.text(0)} 创建球形视频成功: {sphere_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(sphere_np, parent_item, "GUI_SPHERICAL_VIDEO")
                    if qt_item:
                        created_spherical_videos.append((sphere_np, qt_item))
                    else:
                        created_spherical_videos.append((sphere_np, None))
                        print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建球形视频失败: {str(e)}")
                    import traceback
                    traceback.print_exc()
                    continue

            # 处理创建结果
            if not created_spherical_videos:
                print("❌ 没有成功创建任何球形视频")
                return None

            # 选中最后创建的球形视频
            # if created_spherical_videos:
            #     last_sphere_np, last_qt_item = created_spherical_videos[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         # 更新选择和属性面板
            #         tree_widget.update_selection_and_properties(last_sphere_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_spherical_videos)} 个球形视频")

            # 返回值处理
            if len(created_spherical_videos) == 1:
                return created_spherical_videos[0][0]  # 单个球形视频返回NodePath
            else:
                return [sphere_np for sphere_np, _ in created_spherical_videos]  # 多个球形视频返回列表

        except Exception as e:
            print(f"❌ 创建球形视频过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def _createSphereGeometry(self, radius=5.0, segments=32):
        """创建球形几何体"""
        try:
            from panda3d.core import GeomVertexFormat, GeomVertexData, GeomVertexWriter
            from panda3d.core import Geom, GeomTriangles
            import math

            # 创建顶点数据
            format = GeomVertexFormat.getV3n3t2()
            vdata = GeomVertexData('sphere', format, Geom.UHStatic)

            vertex = GeomVertexWriter(vdata, 'vertex')
            normal = GeomVertexWriter(vdata, 'normal')
            texcoord = GeomVertexWriter(vdata, 'texcoord')

            # 生成球体顶点
            vertices = []
            for i in range(segments + 1):
                phi = math.pi * i / segments  # 从0到π
                for j in range(segments * 2 + 1):
                    theta = 2 * math.pi * j / (segments * 2)  # 从0到2π

                    x = radius * math.sin(phi) * math.cos(theta)
                    y = radius * math.cos(phi)
                    z = radius * math.sin(phi) * math.sin(theta)

                    # 纹理坐标
                    u = j / (segments * 2)
                    v = i / segments

                    # 修复：将坐标作为一个元组添加到列表中
                    vertices.append((x, y, z, u, v))

            # 添加顶点数据
            for vert in vertices:
                vertex.addData3f(vert[0], vert[1], vert[2])
                # 法线（标准化顶点位置）
                length = math.sqrt(vert[0] ** 2 + vert[1] ** 2 + vert[2] ** 2)
                if length > 0:
                    normal.addData3f(vert[0] / length, vert[1] / length, vert[2] / length)
                else:
                    normal.addData3f(0, 1, 0)
                texcoord.addData2f(vert[3], vert[4])

            # 创建几何体
            geom = Geom(vdata)
            prim = GeomTriangles(Geom.UHStatic)

            # 生成三角形面
            for i in range(segments):
                for j in range(segments * 2):
                    # 计算顶点索引
                    v1 = i * (segments * 2 + 1) + j
                    v2 = (i + 1) * (segments * 2 + 1) + j
                    v3 = (i + 1) * (segments * 2 + 1) + (j + 1)
                    v4 = i * (segments * 2 + 1) + (j + 1)

                    # 添加两个三角形
                    prim.addVertices(v1, v2, v3)
                    prim.addVertices(v1, v3, v4)

            prim.closePrimitive()
            geom.addPrimitive(prim)

            return geom

        except Exception as e:
            print(f"❌ 创建球形几何体失败: {e}")
            import traceback
            traceback.print_exc()
            raise

    def playSphericalVideo(self,spherical_video_node):
        try:
            if not spherical_video_node:
                return False
            texture = spherical_video_node.getTexture()
            if texture and hasattr(texture,'play'):
                texture.play()
                return True
            else:
                return False
        except Exception as e:
            return False

    def pauseSphericalVideo(self,spherical_video_node):
        try:
            if not spherical_video_node:
                return False
            texture = spherical_video_node.getTexture()
            if texture and hasattr(texture,'stop'):
                texture.stop()
                return True
            else:
                return False
        except Exception as e:
            return False

    def setSphericalVideoTime(self,spherical_video_node,time_seconds):
        try:
            if not spherical_video_node:
                return False
            texture = spherical_video_node.getTexture()
            if texture and hasattr(texture,'set_time'):
                texture.setTime(time_seconds)
                return True
            else:
                return False
        except Exception as e:
            return False

    def createGUIVirtualScreen(self, pos=(0, 0, 0), size=(2, 1), text="虚拟屏幕"):
        """创建3D虚拟屏幕 - 支持多选创建，优化版本"""
        try:
            from panda3d.core import CardMaker, TransparencyAttrib, TextNode
            from PyQt5.QtCore import Qt

            print(f"🖥️ 开始创建虚拟屏幕，位置: {pos}, 尺寸: {size}, 文本: {text}")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_screens = []

            # 为每个有效的父节点创建虚拟屏幕
            for parent_item, parent_node in target_parents:
                try:
                    # 生成唯一名称
                    screen_name = f"VirtualScreen_{len(self.gui_elements)}"

                    # 创建虚拟屏幕几何体
                    cm = CardMaker(f"virtual-screen-{len(self.gui_elements)}")
                    cm.setFrame(-size[0] / 2, size[0] / 2, -size[1] / 2, size[1] / 2)

                    # 创建挂载节点 - 挂载到选中的父节点
                    virtual_screen = parent_node.attachNewNode(cm.generate())
                    virtual_screen.setPos(*pos)
                    virtual_screen.setName(screen_name)
                    virtual_screen.setColor(0.2, 0.2, 0.2, 0.8)
                    virtual_screen.setTransparency(TransparencyAttrib.MAlpha)

                    # 在虚拟屏幕上添加文本
                    screen_text_node = self._create_screen_text(virtual_screen, text, len(self.gui_elements))

                    # 设置节点标签
                    virtual_screen.setTag("gui_type", "virtual_screen")
                    virtual_screen.setTag("gui_id", f"virtual_screen_{len(self.gui_elements)}")
                    virtual_screen.setTag("gui_text", text)
                    virtual_screen.setTag("is_gui_element", "1")
                    virtual_screen.setTag("is_scene_element", "1")
                    virtual_screen.setTag("tree_item_type", "GUI_VirtualScreen")
                    virtual_screen.setTag("created_by_user", "1")

                    # 添加到GUI元素列表
                    self.gui_elements.append(virtual_screen)

                    print(f"✅ 为 {parent_item.text(0)} 创建虚拟屏幕成功: {screen_name}")

                    # 在Qt树形控件中添加对应节点
                    qt_item = tree_widget.add_node_to_tree_widget(virtual_screen, parent_item, "GUI_VirtualScreen")
                    if qt_item:
                        created_screens.append((virtual_screen, qt_item))
                    else:
                        created_screens.append((virtual_screen, None))
                        print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

                except Exception as e:
                    print(f"❌ 为 {parent_item.text(0)} 创建虚拟屏幕失败: {str(e)}")
                    continue

            # 处理创建结果
            if not created_screens:
                print("❌ 没有成功创建任何虚拟屏幕")
                return None

            # 选中最后创建的虚拟屏幕
            # if created_screens:
            #     last_screen_np, last_qt_item = created_screens[-1]
            #     if last_qt_item:
            #         tree_widget.setCurrentItem(last_qt_item)
            #         # 更新选择和属性面板
            #         tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_screens)} 个虚拟屏幕")

            # 返回值处理
            if len(created_screens) == 1:
                return created_screens[0][0]  # 单个屏幕返回NodePath
            else:
                return [screen_np for screen_np, _ in created_screens]  # 多个屏幕返回列表

        except Exception as e:
            print(f"❌ 创建虚拟屏幕过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def _create_screen_text(self, virtual_screen, text, screen_index):
        """为虚拟屏幕创建文本节点"""
        try:
            from panda3d.core import TextNode

            screen_text = TextNode(f'screen-text-{screen_index}')
            screen_text.setText(text)
            screen_text.setAlign(TextNode.ACenter)

            # 设置中文字体
            if hasattr(self.world, 'getChineseFont') and self.world.getChineseFont():
                screen_text.setFont(self.world.getChineseFont())

            # 创建文本节点路径并设置属性
            screen_text_np = virtual_screen.attachNewNode(screen_text)
            screen_text_np.setPos(0, 0.01, 0)  # 稍微向前偏移避免Z-fighting
            screen_text_np.setScale(0.3)
            screen_text_np.setColor(0, 1, 0, 1)  # 绿色文本

            print(f"✅ 虚拟屏幕文本创建成功: {text}")
            return screen_text_np

        except Exception as e:
            print(f"❌ 创建虚拟屏幕文本失败: {str(e)}")
            return None

    def _get_tree_widget(self):
        """安全获取树形控件"""
        try:
            if (hasattr(self.world, 'interface_manager') and
                    hasattr(self.world.interface_manager, 'treeWidget')):
                return self.world.interface_manager.treeWidget
        except AttributeError:
            pass
        return None

    # 暂无滑块功能
    def createGUISlider(self, pos=(0, 0, 0), text="滑块", scale=0.3):
        pass
        """创建2D GUI滑块"""
        from direct.gui.DirectGui import DirectSlider

        gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)

        slider = DirectSlider(
            pos=gui_pos,
            scale=scale,
            range=(0, 100),
            value=50,
            frameColor=(0.6, 0.6, 0.6, 1),
            thumbColor=(0.2, 0.8, 0.2, 1)
        )

        slider.setTag("gui_type", "slider")
        slider.setTag("gui_id", f"slider_{len(self.gui_elements)}")
        slider.setTag("gui_text", text)
        slider.setTag("is_gui_element", "1")

        self.gui_elements.append(slider)
        # 安全地调用updateSceneTree
        if hasattr(self.world, 'updateSceneTree'):
            pass  # CH
            self.world.updateSceneTree()

        print(f"✓ 创建GUI滑块: {text} (逻辑位置: {pos}, 屏幕位置: {gui_pos})")
        return slider

    # ==================== GUI元素管理方法 ====================
    
    def deleteGUIElement(self, gui_element):
        """删除GUI元素"""
        try:
            if gui_element in self.gui_elements:
                # # 移除GUI元素
                # if hasattr(gui_element, 'removeNode'):
                #     gui_element.removeNode()
                # elif hasattr(gui_element, 'destroy'):
                #     gui_element.destroy()
                #
                # # 从列表中移除
                # self.gui_elements.remove(gui_element)
                
                # 更新场景树
                # 安全地调用updateSceneTree
                tree_widget = self._get_tree_widget()
                if tree_widget:
                    tree_widget.delete_items(tree_widget.selectedItems())
                # if hasattr(self.world, 'updateSceneTree'):
                #     self.world.updateSceneTree()
                
                print(f"删除GUI元素: {gui_element}")
                return True
        except Exception as e:
            print(f"删除GUI元素失败: {str(e)}")
        return False

    # 在 gui_manager.py 中确保 editGUIElement 方法正确处理文本颜色
    def editGUIElement(self, gui_element, property_name, value):
        """编辑GUI元素属性"""
        try:
            from panda3d.core import TextNode

            gui_type = gui_element.getTag("gui_type") if hasattr(gui_element, 'getTag') else "unknown"
            print(f"开始编辑GUI元素: 类型={gui_type}, 属性={property_name}, 值={value}")

            if property_name == "text":
                original_frame_size = None
                if hasattr(gui_element,'getFrameSize'):
                    try:
                        original_frame_size = gui_element.getFrameSize()
                    except:
                        pass
                if gui_type in ["button", "label"]:
                    gui_element['text'] = value
                    print(f"成功更新2D GUI文本: {value}")
                    # if gui_type == "button":
                    #     self._resizeButtonToText(gui_element,value,original_frame_size)
                elif gui_type == "entry":
                    gui_element.set(value)
                    print(f"成功更新输入框文本: {value}")
                elif gui_type == "3d_text":
                    # 对于3D文本，直接修改自身的TextNode
                    if isinstance(gui_element.node(), TextNode):
                        gui_element.node().setText(value)
                        print(f"成功更新3D文本: {value}")
                    else:
                        print(f"警告: {gui_type}节点类型为{type(gui_element.node())}，不是TextNode类型")

                elif gui_type == "virtual_screen":
                    # 对于虚拟屏幕，需要找到TextNode子节点
                    print(f"虚拟屏幕有 {gui_element.getNumChildren()} 个子节点")
                    text_found = False
                    for i, child in enumerate(gui_element.getChildren()):
                        print(f"子节点 {i}: {child.getName()}, 类型: {type(child.node())}")
                        if isinstance(child.node(), TextNode):
                            child.node().setText(value)
                            text_found = True
                            print(f"成功更新虚拟屏幕文本: {value}")
                            break

                    if not text_found:
                        print(f"警告: 在{gui_type}中未找到TextNode子节点")

                gui_element.setTag("gui_text", value)

            elif property_name == "color":  # 添加颜色处理
                if isinstance(value, (list, tuple)) and len(value) >= 3:
                    # 对于2D GUI元素（button和label），使用frameColor属性
                    if gui_type in ["button", "label"]:
                        # 设置背景颜色
                        gui_element['frameColor'] = (value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0)
                        print(f"成功更新2D GUI背景颜色: {gui_type} -> {value}")

                    # 对于3D元素，使用材质颜色
                    elif gui_type in ["3d_text", "3d_image", "virtual_screen"]:
                        # 更新材质颜色
                        if not gui_element.hasMaterial():
                            material = Material(f"text-material-{gui_element.getName()}")
                            material.setBaseColor(Vec4(value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0))
                            material.setDiffuse(Vec4(value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0))
                            gui_element.setMaterial(material, 1)
                        else:
                            material = gui_element.getMaterial()
                            material.setBaseColor(Vec4(value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0))
                            material.setDiffuse(Vec4(value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0))
                            gui_element.setMaterial(material, 1)

                        # 更新 TextNode 的文本颜色
                        if isinstance(gui_element.node(), TextNode):
                            gui_element.node().setTextColor(
                                Vec4(value[0], value[1], value[2], value[3] if len(value) > 3 else 1.0))
                        print(f"成功更新3D GUI颜色: {gui_type} -> {value}")

                    else:
                        print(f"警告: 未知的GUI类型 {gui_type}，无法设置颜色")

            elif property_name == "position":
                if isinstance(value, (list, tuple)) and len(value) >= 3:
                    gui_element.setPos(*value[:3])

            elif property_name == "scale":
                if isinstance(value, (int, float)):
                    gui_element.setScale(value)
                elif isinstance(value, (list, tuple)) and len(value) >= 3:
                    gui_element.setScale(*value[:3])

            print(f"编辑GUI元素 {gui_type}: {property_name} = {value}")
            return True

        except Exception as e:
            print(f"编辑GUI元素失败: {str(e)}")
            import traceback
            traceback.print_exc()
        return False

    def _resizeButtonToText(self, button, text, original_frame_size=None):
        """
        根据文本内容调整按钮大小

        Args:
            button: DirectButton 对象
            text: 新的文本内容
            original_frame_size: 原始frameSize，用于计算合适的padding
        """
        try:
            # 获取按钮当前的文本缩放
            text_scale = 0.03  # 默认文本缩放
            padding = 0.2  # 默认边距

            # 尝试从按钮获取实际的文本缩放值
            if hasattr(button, 'getTextScale'):
                text_scale = button.getTextScale()
            elif hasattr(button, 'textScale'):
                text_scale = button.textScale

            # 根据原始frameSize计算合适的padding
            if original_frame_size and len(original_frame_size) >= 4:
                # 基于原始按钮宽度计算合适的padding
                padding = max((original_frame_size[1] - original_frame_size[0]) * 0.15, 0.1)

            # 更精确的文本尺寸估算
            # 考虑中文字符和英文字符的不同宽度
            chinese_chars = len([c for c in text if ord(c) > 127])
            english_chars = len(text) - chinese_chars

            # 中文字符通常比英文字符宽
            char_width = text_scale * 0.6
            text_width = (chinese_chars * char_width * 1.5) + (english_chars * char_width)
            text_height = text_scale * 1.2  # 文本高度

            # 计算新的frameSize，确保有足够的边距
            half_width = max(text_width * 0.5 + padding, 0.3)  # 最小宽度0.3
            half_height = max(text_height * 0.5 + padding * 0.5, 0.15)  # 最小高度0.15

            # 正确设置frameSize - 使用字典方式设置
            new_frame = (-half_width, half_width, -half_height, half_height)
            button['frameSize'] = new_frame  # 使用字典方式设置

            print(f"按钮大小已调整: {new_frame} (文本: {text})")

        except Exception as e:
            print(f"调整按钮大小失败: {e}")
            # 如果自动调整失败，保持原有大小或设置一个合理的默认大小
            try:
                if original_frame_size:
                    # 保持原有大小
                    button['frameSize'] = original_frame_size
                else:
                    # 设置一个合理的默认大小
                    button['frameSize'] = (-0.5, 0.5, -0.15, 0.15)
            except:
                pass

    def duplicateGUIElement(self, gui_element):
        """复制GUI元素"""
        try:
            gui_type = gui_element.getTag("gui_type")
            gui_text = gui_element.getTag("gui_text")
            
            # 获取当前位置并偏移
            pos = gui_element.getPos()
            new_pos = (pos.getX() + 0.2, pos.getY(), pos.getZ() + 0.2)
            
            # 根据类型创建新的GUI元素
            if gui_type == "button":
                self.createGUIButton(new_pos, gui_text + "_副本")
            elif gui_type == "label":
                self.createGUILabel(new_pos, gui_text + "_副本")
            elif gui_type == "entry":
                self.createGUIEntry(new_pos, gui_text + "_副本")
            elif gui_type == "3d_text":
                self.createGUI3DText(new_pos, gui_text + "_副本")
            elif gui_type == "3d_image":
                image_path = gui_element.getTag("image_path")
                self.createGUI3DImage(new_pos,image_path,size=(2,2))
            elif gui_type == "2d_image":
                image_path = gui_element.getTag("image_path")
                self.createGUI2DImage(new_pos, image_path, size=0.2)
            elif gui_type == "virtual_screen":
                self.createGUIVirtualScreen(new_pos, text=gui_text + "_副本")
                
            print(f"复制GUI元素: {gui_type} - {gui_text}")
            
        except Exception as e:
            print(f"复制GUI元素失败: {str(e)}")
    
    def editGUIElementDialog(self, gui_element):
        """显示GUI元素编辑对话框"""
        dialog = QDialog()
        dialog.setWindowTitle("编辑GUI元素")
        dialog.setMinimumWidth(300)
        
        layout = QVBoxLayout(dialog)
        form = QFormLayout()
        
        gui_type = gui_element.getTag("gui_type")
        gui_text = gui_element.getTag("gui_text")
        
        # 文本编辑
        if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]:
            textEdit = QLineEdit(gui_text or "")
            form.addRow("文本:", textEdit)
        
        # 位置编辑
        if hasattr(gui_element, 'getPos'):
            pos = gui_element.getPos()
            xEdit = QDoubleSpinBox()
            xEdit.setRange(-1000, 1000)
            xEdit.setValue(pos.getX())
            form.addRow("位置 X:", xEdit)
            
            yEdit = QDoubleSpinBox()
            yEdit.setRange(-1000, 1000)
            yEdit.setValue(pos.getY())
            form.addRow("位置 Y:", yEdit)
            
            zEdit = QDoubleSpinBox()
            zEdit.setRange(-1000, 1000)
            zEdit.setValue(pos.getZ())
            form.addRow("位置 Z:", zEdit)
        
        # 缩放编辑
        if hasattr(gui_element, 'getScale'):
            scale = gui_element.getScale()
            scaleEdit = QDoubleSpinBox()
            scaleEdit.setRange(0.01, 100)
            scaleEdit.setSingleStep(0.1)
            scaleEdit.setValue(scale.getX())
            form.addRow("缩放:", scaleEdit)
        
        layout.addLayout(form)
        
        # 按钮
        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttonBox.accepted.connect(dialog.accept)
        buttonBox.rejected.connect(dialog.reject)
        layout.addWidget(buttonBox)
        
        # 执行对话框
        if dialog.exec_() == QDialog.Accepted:
            try:
                # 应用更改
                if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]:
                    self.editGUIElement(gui_element, "text", textEdit.text())
                
                if hasattr(gui_element, 'getPos'):
                    self.editGUIElement(gui_element, "position", [xEdit.value(), yEdit.value(), zEdit.value()])
                
                if hasattr(gui_element, 'getScale'):
                    self.editGUIElement(gui_element, "scale", scaleEdit.value())
                
                # 更新属性面板
                if self.world.treeWidget:
                    currentItem = self.world.treeWidget.currentItem()
                    if currentItem:
                        self.world.updatePropertyPanel(currentItem)
                        
                print("GUI元素编辑完成")
                
            except Exception as e:
                print(f"应用GUI编辑失败: {str(e)}")
    
    # ==================== GUI事件处理方法 ====================
    
    def onGUIButtonClick(self, button_id):
        """GUI按钮点击事件处理"""
        print(f"GUI按钮被点击: {button_id}")
    
    def onGUIEntrySubmit(self, text, entry_id):
        """GUI输入框提交事件处理"""
        print(f"GUI输入框提交: {entry_id} = {text}")
    
    # ==================== GUI编辑模式方法 ====================
    
    def toggleGUIEditMode(self):
        """切换GUI编辑模式"""
        self.guiEditMode = not self.guiEditMode
        
        if self.guiEditMode:
            self.enterGUIEditMode()
        else:
            self.exitGUIEditMode()
    
    def enterGUIEditMode(self):
        """进入GUI编辑模式"""
        print("\n=== 进入GUI编辑模式 ===")
        
        # 打开GUI预览窗口
        self.openGUIPreviewWindow()
        
        # 创建GUI编辑面板
        self.createGUIEditPanel()
        
        # 改变当前工具为GUI选择工具
        self.world.currentTool = "GUI编辑"
        
        print("GUI编辑模式已激活")
        print("- 使用右侧工具栏创建GUI元素")
        print("- 在独立预览窗口中查看效果")
        print("- 左键点击现有GUI元素选择和编辑")
    
    def exitGUIEditMode(self):
        """退出GUI编辑模式"""
        print("\n=== 退出GUI编辑模式 ===")
        
        # 关闭GUI预览窗口
        self.closeGUIPreviewWindow()
        
        # 移除GUI编辑面板
        if self.guiEditPanel:
            self.guiEditPanel.destroy()
            self.guiEditPanel = None
        
        # 恢复普通工具
        self.world.currentTool = "选择"
        
        print("GUI编辑模式已关闭")
    
    def createGUIEditPanel(self):
        """创建GUI编辑面板"""
        from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel
        
        # 创建主面板
        self.guiEditPanel = DirectFrame(
            pos=(0.85, 0, 0),
            frameSize=(-0.15, 0.15, -0.9, 0.9),
            frameColor=(0.1, 0.1, 0.1, 0.8),
            text="GUI编辑器",
            text_pos=(0, 0.85),
            text_scale=0.05,
            text_fg=(1, 1, 1, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        
        # 创建工具按钮
        y_pos = 0.7
        spacing = 0.12
        
        # 2D GUI工具
        label_2d = DirectLabel(
            parent=self.guiEditPanel,
            text="2D GUI",
            pos=(0, 0, y_pos),
            scale=0.04,
            text_fg=(1, 1, 0, 1),
            frameColor=(0, 0, 0, 0),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= 0.08
        
        # 按钮工具
        btn_button = DirectButton(
            parent=self.guiEditPanel,
            text="按钮",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["button"],
            frameColor=(0.2, 0.6, 0.8, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing
        
        # 标签工具
        btn_label = DirectButton(
            parent=self.guiEditPanel,
            text="标签",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["label"],
            frameColor=(0.6, 0.8, 0.2, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing
        
        # 输入框工具
        btn_entry = DirectButton(
            parent=self.guiEditPanel,
            text="输入框",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["entry"],
            frameColor=(0.8, 0.6, 0.2, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing
        
        # 3D GUI工具
        label_3d = DirectLabel(
            parent=self.guiEditPanel,
            text="3D GUI",
            pos=(0, 0, y_pos),
            scale=0.04,
            text_fg=(1, 1, 0, 1),
            frameColor=(0, 0, 0, 0),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= 0.08
        
        # 3D文本工具
        btn_3dtext = DirectButton(
            parent=self.guiEditPanel,
            text="3D文本",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["3d_text"],
            frameColor=(0.8, 0.2, 0.6, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing

        #3D图片工具
        btn_image = DirectButton(
            parent = self.guiEditPanel,
            text="3D图片",
            pos=(0,0,y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["3d_image"],
            frameColor=(0.2,0.8,0.8,1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing

        # 2D图片工具
        btn_2d_image = DirectButton(
            parent=self.guiEditPanel,
            text="2D图片",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["2d_image"],
            frameColor=(0.8, 0.6, 0.2, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing

        # 虚拟屏幕工具
        btn_screen = DirectButton(
            parent=self.guiEditPanel,
            text="虚拟屏幕",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.setGUICreateTool,
            extraArgs=["virtual_screen"],
            frameColor=(0.6, 0.2, 0.8, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing

        #Cesium 集成工具 （仅在Webengine 可用时显示）
        if WEB_ENGINE_AVAILABLE:
            label_cesium = DirectLabel(
                parent=self.guiEditPanel,
                text="Cesium 集成",
                pos=(0, 0, y_pos),
                scale=0.04,
                text_fg=(1, 1, 0, 1),
                frameColor=(0, 0, 0, 0),
                text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
            )
            y_pos -= 0.08

            # 切换 Cesium 视图按钮
            btn_toggle_cesium = DirectButton(
                parent=self.guiEditPanel,
                text="切换地图视图",
                pos=(0, 0, y_pos),
                scale=0.04,
                command=self.toggleCesiumView,
                frameColor=(0.2, 0.8, 0.6, 1),
                text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
            )
            y_pos -= spacing

            # 刷新 Cesium 视图按钮
            btn_refresh_cesium = DirectButton(
                parent=self.guiEditPanel,
                text="刷新地图",
                pos=(0, 0, y_pos),
                scale=0.04,
                command=self.refreshCesiumView,
                frameColor=(0.6, 0.8, 0.2, 1),
                text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
            )
            y_pos -= spacing

        # 分隔线
        y_pos -= 0.1
        
        # 操作按钮
        label_ops = DirectLabel(
            parent=self.guiEditPanel,
            text="操作",
            pos=(0, 0, y_pos),
            scale=0.04,
            text_fg=(1, 1, 0, 1),
            frameColor=(0, 0, 0, 0),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= 0.08
        
        # 删除工具
        btn_delete = DirectButton(
            parent=self.guiEditPanel,
            text="删除",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.deleteSelectedGUI,
            frameColor=(0.8, 0.2, 0.2, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing
        
        # 复制工具
        btn_copy = DirectButton(
            parent=self.guiEditPanel,
            text="复制",
            pos=(0, 0, y_pos),
            scale=0.04,
            command=self.copySelectedGUI,
            frameColor=(0.2, 0.8, 0.2, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        y_pos -= spacing
        
        # 退出GUI编辑模式
        btn_exit = DirectButton(
            parent=self.guiEditPanel,
            text="退出",
            pos=(0, 0, -0.8),
            scale=0.04,
            command=self.toggleGUIEditMode,
            frameColor=(0.5, 0.5, 0.5, 1),
            text_font=self.world.getChineseFont() if self.world.getChineseFont() else None
        )
        
        # 存储当前的GUI创建工具
        self.currentGUITool = None
    
    def openGUIPreviewWindow(self):
        """打开独立的GUI预览窗口"""
        try:
            from gui_preview_window import GUIPreviewWindow
            
            self.guiPreviewWindow = GUIPreviewWindow()
            self.guiPreviewWindow.set_main_world(self.world)
            
            print("✓ GUI预览窗口已打开")
            print("这个独立窗口会实时显示您创建的GUI元素")
            
        except ImportError:
            print("错误: 无法导入GUI预览窗口模块")
        except Exception as e:
            print(f"打开GUI预览窗口失败: {str(e)}")
    
    def closeGUIPreviewWindow(self):
        """关闭GUI预览窗口"""
        if self.guiPreviewWindow:
            self.guiPreviewWindow.destroy()
            self.guiPreviewWindow = None
            print("GUI预览窗口已关闭")
    
    # ==================== GUI工具和选择方法 ====================
    
    def setGUICreateTool(self, tool_type):
        """设置GUI创建工具"""
        self.currentGUITool = tool_type
        print(f"选择GUI创建工具: {tool_type}")
    
    def deleteSelectedGUI(self):
        """删除选中的GUI元素"""
        if self.world.selection.selectedNode and hasattr(self.world.selection.selectedNode, 'getTag'):
            gui_type = self.world.selection.selectedNode.getTag("gui_type")
            if gui_type:
                success = self.deleteGUIElement(self.world.selection.selectedNode)
                if success:
                    self.world.selection.updateSelection(None)
                    print("GUI元素已删除")
                else:
                    print("删除GUI元素失败")
            else:
                print("选中的不是GUI元素")
        else:
            print("没有选中的GUI元素")
    
    def copySelectedGUI(self):
        """复制选中的GUI元素"""
        if self.world.selection.selectedNode and hasattr(self.world.selection.selectedNode, 'getTag'):
            gui_type = self.world.selection.selectedNode.getTag("gui_type")
            if gui_type:
                self.duplicateGUIElement(self.world.selection.selectedNode)
                print("GUI元素已复制")
            else:
                print("选中的不是GUI元素")
        else:
            print("没有选中的GUI元素")
    
    def handleGUIEditClick(self, hitPos):
        """处理GUI编辑模式下的点击"""
        if not self.guiEditMode:
            return False
            
        if self.currentGUITool:
            # 创建新的GUI元素
            self.createGUIAtPosition(hitPos, self.currentGUITool)
            return True
        
        return False
    
    def createGUIAtPosition(self, world_pos, gui_type):
        """在指定位置创建GUI元素"""
        print(f"在位置 {world_pos} 创建 {gui_type}")
        
        # 根据GUI类型选择合适的坐标转换
        if gui_type in ["button", "label", "entry"]:
            # 2D GUI - 将世界坐标转换为屏幕逻辑坐标
            screen_x = world_pos.getX() * 2  # 缩放因子
            screen_z = world_pos.getZ() * 2
            pos = (screen_x, 0, screen_z)
        else:
            # 3D GUI - 直接使用世界坐标
            pos = (world_pos.getX(), world_pos.getY(), world_pos.getZ())
        
        # 创建不同类型的GUI元素
        if gui_type == "button":
            element = self.createGUIButton(pos, f"按钮_{len(self.gui_elements)}")
        elif gui_type == "label":
            element = self.createGUILabel(pos, f"标签_{len(self.gui_elements)}")
        elif gui_type == "entry":
            element = self.createGUIEntry(pos, f"输入框_{len(self.gui_elements)}")
        elif gui_type == "3d_text":
            element = self.createGUI3DText(pos, f"3D文本_{len(self.gui_elements)}")
        elif gui_type == "3d_image":
            element = self.createGUI3DImage(pos)
        elif gui_type == "2d_image":
            element = self.createGUI2DImage(pos)
        elif gui_type == "virtual_screen":
            element = self.createGUIVirtualScreen(pos, text=f"屏幕_{len(self.gui_elements)}")
        else:
            print(f"未知的GUI类型: {gui_type}")
            return
        
        # 自动选中新创建的元素
        self.world.selection.updateSelection(element)
        self.selectGUIInTree(element)
        print(f"创建并选中了新的{gui_type}元素")
    
    def findClickedGUI(self, hitNode):
        """查找被点击的GUI元素"""
        # 检查点击的节点是否是GUI元素
        current = hitNode
        while current != self.world.render:
            if hasattr(current, 'getTag') and current.getTag("gui_type"):
                return current
            current = current.getParent()
        return None

    def selectGUIInTree(self, gui_element):
        """在树形控件中选中GUI元素"""
        if not self.world.treeWidget or not gui_element:
            return

        def findGUIItem(item):
            """递归查找GUI元素对应的树形项"""
            if item.data(0, Qt.UserRole) == gui_element:
                return item

            for i in range(item.childCount()):
                child = item.child(i)
                result = findGUIItem(child)
                if result:
                    return result
            return None

        # 从根开始查找
        root = self.world.treeWidget.invisibleRootItem()
        for i in range(root.childCount()):
            sceneItem = root.child(i)
            if sceneItem.text(0) == "场景":
                for j in range(sceneItem.childCount()):
                    childItem = sceneItem.child(j)
                    if childItem.text(0) == "GUI元素":
                        foundItem = findGUIItem(childItem)
                        if foundItem:
                            self.world.treeWidget.setCurrentItem(foundItem)
                            self.world.updatePropertyPanel(foundItem)
                            return

    def updateGUISelection(self, gui_element):
        """更新GUI元素选择状态"""
        self.world.selection.updateSelection(gui_element)
        if gui_element and hasattr(gui_element, 'getTag'):
            gui_type = gui_element.getTag("gui_type")
            gui_text = gui_element.getTag("gui_text")
            print(f"选中GUI元素: {gui_type} - {gui_text}")
            
            # 在树形控件中选中
            self.selectGUIInTree(gui_element)
    
    # ==================== GUI属性面板方法 ====================
    
    def updateGUIPropertyPanel(self, gui_element, layout):
        """更新GUI元素属性面板"""
        gui_type = gui_element.getTag("gui_type")
        gui_text = gui_element.getTag("gui_text")
        
        # GUI类型显示
        typeLabel = QLabel("GUI类型:")
        typeValue = QLabel(gui_type)
        typeValue.setStyleSheet("color: #00AAFF; font-weight: bold;")
        layout.addRow(typeLabel, typeValue)
        
        # 文本属性（如果适用）
        if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]:
            textLabel = QLabel("文本:")
            textEdit = QLineEdit(gui_text or "")
            
            # 创建一个更新函数来处理文本变化
            def updateText(text):
                success = self.editGUIElement(gui_element, "text", text)
                if success:
                    # 更新场景树显示的名称
                    # 安全地调用updateSceneTree
                    if hasattr(self.world, 'updateSceneTree'):
                        self.world.updateSceneTree()
                    
            textEdit.textChanged.connect(updateText)
            layout.addRow(textLabel, textEdit)
        
        # 位置属性
        if hasattr(gui_element, 'getPos'):
            # 根据GUI类型设置组名
            if gui_type in ["button", "label", "entry", "2d_image"]:
                transform_group = QGroupBox("变换 Rect Transform")
            else:
                transform_group = QGroupBox("变换 Transform")

            transform_layout = QGridLayout()

            pos = gui_element.getPos()
            
            # 根据GUI类型决定位置编辑方式
            if gui_type in ["button", "label", "entry","2d_image"]:
                # 2D GUI组件使用屏幕坐标
                logical_x = pos.getX() / 0.1  # 反向转换为逻辑坐标
                logical_z = pos.getZ() / 0.1

                transform_layout.addWidget(QLabel("屏幕位置"), 0, 0)

                x_label = QLabel("X")
                z_label = QLabel("z")
                x_label.setAlignment(Qt.Aligncenter)
                z_label.setAlignment(Qt.AlignCenter)

                transform_layout.addWidget(x_label, 0, 1)
                transform_layout.addWidget(z_label, 0, 2)

                xPos = QDoubleSpinBox()
                xPos.setRange(-1000, 1000)
                xPos.setValue(logical_x)
                xPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "x", v))
                transform_layout.addWidget(xPos, 1, 1)

                zPos = QDoubleSpinBox()
                zPos.setRange(-1000, 1000)
                zPos.setValue(logical_z)
                zPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "z", v))
                transform_layout.addWidget(zPos, 1, 2)

                # 显示实际屏幕坐标（只读）
                transform_layout.addWidget(QLabel("实际坐标"), 2, 0)

                actualXLabel = QLabel(f"{pos.getX():.3f}")
                actualXLabel.setStyleSheet("color: gray; font-size: 10px;")
                actualZLabel = QLabel(f"{pos.getZ():.3f}")
                actualZLabel.setStyleSheet("color: gray; font-size: 10px;")

                transform_layout.addWidget(actualXLabel, 3, 1)
                transform_layout.addWidget(actualZLabel, 3, 2)
                # 添加宽度和高度控件（对于2D图像）
                if gui_type == "2d_image":

                    # 添加排序控制组
                    sort_group = QGroupBox("渲染顺序")
                    sort_layout = QGridLayout()

                    # 获取当前的sort值，如果没有设置则默认为0
                    current_sort = int(gui_element.getTag("sort") or "0")

                    sort_layout.addWidget(QLabel("层级:"), 0, 0)

                    sort_spin = QSpinBox()
                    sort_spin.setRange(-1000, 1000)  # 设置合理的范围
                    sort_spin.setValue(current_sort)

                    # 创建更新排序的函数
                    def updateSort(value):
                        try:
                            # 设置标签
                            gui_element.setTag("sort", str(value))
                            # 应用sort到节点 - 使用fixed bin和指定的值
                            gui_element.setBin("fixed", value)
                            print(f"✓ 更新2D图像渲染顺序: {value}")
                        except Exception as e:
                            print(f"✗ 更新2D图像渲染顺序失败: {e}")

                    sort_spin.valueChanged.connect(updateSort)
                    sort_layout.addWidget(sort_spin, 0, 1)

                    # 添加说明标签
                    sort_help = QLabel("(数值越大越靠前)")
                    sort_help.setStyleSheet("font-size: 10px; color: gray;")
                    sort_layout.addWidget(sort_help, 1, 0, 1, 2)

                    sort_group.setLayout(sort_layout)
                    layout.addWidget(sort_group)

                    scale = gui_element.getScale()
                    width = scale.getX() if hasattr(scale, 'getX') else scale[0] if isinstance(scale,
                                                                                               (tuple, list)) else scale
                    height = scale.getZ() if hasattr(scale, 'getZ') else scale[1] if isinstance(scale,
                                                                                                (tuple, list)) and len(
                        scale) > 1 else scale

                    # 宽度控件
                    transform_layout.addWidget(QLabel("宽度"), 4, 0)
                    widthSpinBox = QDoubleSpinBox()
                    widthSpinBox.setRange(0.01, 100)
                    widthSpinBox.setSingleStep(0.1)
                    widthSpinBox.setValue(width)
                    widthSpinBox.valueChanged.connect(
                        lambda v: self.world.gui_manager.editGUIScale(gui_element, "x", v))
                    transform_layout.addWidget(widthSpinBox, 4, 1)

                    # 高度控件
                    transform_layout.addWidget(QLabel("高度"), 4, 2)
                    heightSpinBox = QDoubleSpinBox()
                    heightSpinBox.setRange(0.01, 100)
                    heightSpinBox.setSingleStep(0.1)
                    heightSpinBox.setValue(height)
                    heightSpinBox.valueChanged.connect(
                        lambda v: self.world.gui_manager.editGUIScale(gui_element, "z", v))
                    transform_layout.addWidget(heightSpinBox, 4, 3)

            else:
                # 3D GUI组件使用世界坐标
                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)

                # 位置数值输入框
                xPos = QDoubleSpinBox()
                xPos.setRange(-100, 100)
                xPos.setValue(pos.getX())
                xPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "x", v))
                transform_layout.addWidget(xPos, 1, 1)

                yPos = QDoubleSpinBox()
                yPos.setRange(-100, 100)
                yPos.setValue(pos.getY())
                yPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "y", v))
                transform_layout.addWidget(yPos, 1, 2)

                zPos = QDoubleSpinBox()
                zPos.setRange(-100, 100)
                zPos.setValue(pos.getZ())
                zPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "z", v))
                transform_layout.addWidget(zPos, 1, 3)

                # 缩放属性
                scale = gui_element.getScale()
                transform_layout.addWidget(QLabel("缩放"), 2, 0)

                # X, Y, Z 缩放标签居中
                sx_label = QLabel("X")
                sy_label = QLabel("Y")
                sz_label = QLabel("Z")
                sx_label.setAlignment(Qt.AlignCenter)
                sy_label.setAlignment(Qt.AlignCenter)
                sz_label.setAlignment(Qt.AlignCenter)

                transform_layout.addWidget(sx_label, 2, 1)
                transform_layout.addWidget(sy_label, 2, 2)
                transform_layout.addWidget(sz_label, 2, 3)

                # 缩放数值输入框
                scale_x = QDoubleSpinBox()
                scale_x.setRange(0.01, 100)
                scale_x.setSingleStep(0.1)
                scale_x.setValue(
                    scale.getX() if hasattr(scale, 'getX') else scale[0] if isinstance(scale, (tuple, list)) else scale)
                scale_x.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "x", v))
                transform_layout.addWidget(scale_x, 3, 1)

                scale_y = QDoubleSpinBox()
                scale_y.setRange(0.01, 100)
                scale_y.setSingleStep(0.1)
                scale_y.setValue(
                    scale.getY() if hasattr(scale, 'getY') else scale[1] if isinstance(scale, (tuple, list)) and len(
                        scale) > 1 else scale)
                scale_y.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "y", v))
                transform_layout.addWidget(scale_y, 3, 2)

                scale_z = QDoubleSpinBox()
                scale_z.setRange(0.01, 100)
                scale_z.setSingleStep(0.1)
                scale_z.setValue(
                    scale.getZ() if hasattr(scale, 'getZ') else scale[2] if isinstance(scale, (tuple, list)) and len(
                        scale) > 2 else scale)
                scale_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "z", v))
                transform_layout.addWidget(scale_z, 3, 3)
            transform_group.setLayout(transform_layout)
            self._propertyLayout.addWidget(transform_group)

        # 缩放属性
        if hasattr(gui_element, 'getScale'):
            scale = gui_element.getScale()

            scaleSpinBox = QDoubleSpinBox()
            scaleSpinBox.setRange(0.01, 100)
            scaleSpinBox.setSingleStep(0.1)
            scaleSpinBox.setValue(scale.getX())
            scaleSpinBox.valueChanged.connect(lambda v: self.editGUIElement(gui_element, "scale", v))
            layout.addRow("缩放:", scaleSpinBox)
        
        # 颜色属性（针对2D GUI）
        if gui_type in ["button", "label"]:
            colorButton = QPushButton("选择颜色")
            colorButton.clicked.connect(lambda: self.selectGUIColor(gui_element))
            layout.addRow("背景颜色:", colorButton)
        
        # 3D特有属性
        if gui_type in ["3d_text", "virtual_screen"]:
            # 旋转属性
            if hasattr(gui_element, 'getHpr'):
                hpr = gui_element.getHpr()
                
                hRot = QDoubleSpinBox()
                hRot.setRange(-180, 180)
                hRot.setValue(hpr.getX())
                hRot.valueChanged.connect(lambda v: gui_element.setH(v))
                layout.addRow("旋转 H:", hRot)
                
                pRot = QDoubleSpinBox()
                pRot.setRange(-180, 180)
                pRot.setValue(hpr.getY())
                pRot.valueChanged.connect(lambda v: gui_element.setP(v))
                layout.addRow("旋转 P:", pRot)
                
                rRot = QDoubleSpinBox()
                rRot.setRange(-180, 180)
                rRot.setValue(hpr.getZ())
                rRot.valueChanged.connect(lambda v: gui_element.setR(v))
                layout.addRow("旋转 R:", rRot)
    
    def selectGUIColor(self, gui_element):
        """选择GUI元素颜色"""
        color = QColorDialog.getColor(QColor(128, 128, 128), None, "选择颜色")
        if color.isValid():
            r, g, b = color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0
            self.editGUIElement(gui_element, "color", [r, g, b, 1.0])

    def update3DImageTexture(self, model_nodepath, image_path):
        from panda3d.core import Texture, TextureStage

        try:
            # 加载新纹理
            new_texture = self.world.loader.loadTexture(image_path)
            if new_texture:
                # 确保纹理过滤质量
                new_texture.setMagfilter(Texture.FT_linear)
                new_texture.setMinfilter(Texture.FT_linear_mipmap_linear)

                model_nodepath.clearTexture()

                # 为3D图像创建独立的纹理阶段
                image_stage = TextureStage("3d_image_texture")
                image_stage.setSort(0)  # 使用第一个纹理槽
                image_stage.setMode(TextureStage.MModulate)  # 使用调制模式

                # 应用纹理到模型，使用独立的纹理阶段
                model_nodepath.setTexture(image_stage, new_texture)

                # 更新标签
                model_nodepath.setTag("gui_image_path", image_path)

                # 确保材质设置正确
                if not model_nodepath.hasMaterial():
                    from panda3d.core import Material, LColor
                    mat = Material()
                    mat.setName(f"image-material-{id(model_nodepath)}")
                    mat.setBaseColor(LColor(1, 1, 1, 1))
                    mat.setDiffuse(LColor(1, 1, 1, 1))
                    mat.setAmbient(LColor(0.5, 0.5, 0.5, 1))
                    mat.setSpecular(LColor(0.1, 0.1, 0.1, 1.0))
                    mat.setShininess(10.0)
                    model_nodepath.setMaterial(mat, 1)

                # 保护子节点的纹理（特别是3D文本）
                self._preserveChildNodeTextures(model_nodepath)

                print(f"✅ 3D图像纹理已更新为: {image_path}")
                return True
            else:
                print(f"❌ 无法加载纹理: {image_path}")
                return False
        except Exception as e:
            print(f"❌ 更新纹理时出错: {e}")
            return False

    def _preserveChildNodeTextures(self, parent_node):
        """保护子节点的纹理不被父节点纹理影响"""
        try:
            # 遍历所有直接子节点
            for i in range(parent_node.getNumChildren()):
                child = parent_node.getChild(i)

                # 检查子节点是否为3D文本或其他需要特殊处理的节点
                if self._is3DTextElement(child):
                    # 为3D文本创建独立的纹理阶段
                    self._restore3DTextTexture(child)
                elif self._isOtherSpecialElement(child):
                    # 为其他特殊元素恢复纹理
                    self._restoreSpecialElementTexture(child)

        except Exception as e:
            print(f"保护子节点纹理时出错: {e}")

    def _is3DTextElement(self, node):
        """检查节点是否为3D文本元素"""
        try:
            return (hasattr(node, 'getTag') and
                    node.getTag("gui_type") == "3d_text")
        except:
            return False

    def _isOtherSpecialElement(self, node):
        """检查节点是否为其他需要特殊处理的元素"""
        try:
            return (hasattr(node, 'getTag') and
                    node.getTag("gui_type") in ["3d_image", "button", "label"])
        except:
            return False

    def _restore3DTextTexture(self, text_node):
        """恢复3D文本的纹理设置"""
        try:
            from panda3d.core import TextureStage

            # 如果3D文本已经有字体纹理，确保它使用正确的纹理阶段
            if hasattr(text_node, 'getTexture') and text_node.getTexture():
                # 创建专门用于文本的纹理阶段
                text_stage = TextureStage("text_texture")
                text_stage.setSort(10)  # 使用较高的纹理槽索引
                text_stage.setMode(TextureStage.MModulate)

                # 重新应用文本纹理
                current_texture = text_node.getTexture()
                text_node.setTexture(text_stage, current_texture)

            print(f"已恢复3D文本纹理: {text_node.getName()}")

        except Exception as e:
            print(f"恢复3D文本纹理失败: {e}")

    def update2DImageTexture(self, gui_element, image_path):
        """更新2D图片纹理"""
        try:
            # 加载新纹理
            new_texture = self.world.loader.loadTexture(image_path)
            if new_texture:
                new_texture.setMagfilter(Texture.FT_linear)
                new_texture.setMinfilter(Texture.FT_linear_mipmap_linear)

                new_texture.setFormat(Texture.F_rgba)
                new_texture.setWrapU(Texture.WM_clamp)
                new_texture.setWrapV(Texture.WM_clamp)

                # 应用纹理到模型
                gui_element.setTexture(new_texture, 1)

                # 更新标签
                gui_element.setTag("texture_path", image_path)
                gui_element.setTag("image_path", image_path)

                print(f"✅ 2D图像纹理已更新为: {image_path}")
                return True
            else:
                print(f"❌ 无法加载2D图片纹理: {image_path}")
                return False
        except Exception as e:
            print(f"❌ 更新2D图片纹理时出错: {e}")
            return False

    # 在gui_manager.py或其他相关文件中添加以下方法

    def editGUI2DPosition(self, gui_element, axis, value):
        """编辑2D GUI元素位置并应用边界约束"""
        try:
            gui_type = gui_element.getTag("gui_type")

            if gui_type in ["button", "label", "entry", "2d_image","2d_video_screen"]:
                # 2D元素使用屏幕坐标，需要转换
                current_pos = gui_element.getPos()

                if axis == "x":
                    # 转换逻辑坐标到屏幕坐标
                    screen_x = value * 0.1
                    new_pos = (screen_x,current_pos[1],current_pos[2])
                elif axis == "z":
                    screen_z = value * 0.1
                    new_pos = (current_pos[0],current_pos[1],screen_z)
                else:
                    return False
                gui_element.setPos(*new_pos)
                return True
            else:
                print(f"✗ 不支持的GUI类型进行2D位置编辑: {gui_type}")
                return False
        except Exception as e:
            print(f"✗ 更新2D GUI元素位置失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def editGUI3DPosition(self, gui_element, axis, value):
        """编辑3D GUI元素位置"""
        try:
            gui_type = gui_element.getTag("gui_type")

            if gui_type in ["3d_text", "3d_image", "video_screen","info_panel","info_panel_3d"]:
                current_pos = gui_element.getPos()

                if axis == "x":
                    new_pos = (value, current_pos.getY(), current_pos.getZ())
                elif axis == "y":
                    new_pos = (current_pos.getX(), value, current_pos.getZ())
                elif axis == "z":
                    new_pos = (current_pos.getX(), current_pos.getY(), value)
                else:
                    return False

                gui_element.setPos(*new_pos)
                #print(f"✓ 更新3D GUI元素位置: {axis}={value}")
                return True
            else:
                print(f"✗ 不支持的GUI类型进行3D位置编辑: {gui_type}")
                return False
        except Exception as e:
            print(f"✗ 更新3D GUI元素位置失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def editGUIScale(self, gui_element, axis, value):
        """编辑GUI元素缩放"""
        try:
            gui_type = gui_element.getTag("gui_type")
            current_scale = gui_element.getScale()

            # 确保缩放值不为0
            if value == 0:
                value = 0.01

            if gui_type in ["3d_text", "3d_image","video_screen","virtual_screen","info_panel","info_panel_3d"]:
                # 3D元素处理
                if axis == "x":
                    new_scale = (value, current_scale.getY(), current_scale.getZ())
                elif axis == "y":
                    new_scale = (current_scale.getX(), value, current_scale.getZ())
                elif axis == "z":
                    new_scale = (current_scale.getX(), current_scale.getY(), value)
                else:
                    return False

                gui_element.setScale(*new_scale)
            elif gui_type == "2d_image":
                # 2D图像特殊处理 - 分别控制宽度和高度
                if axis == "x":
                    # X轴控制宽度
                    gui_element.setScale(value, current_scale.getZ() if hasattr(current_scale, 'getZ')
                    else current_scale[1] if isinstance(current_scale, (list, tuple))
                    else current_scale)
                elif axis == "z":
                    # Z轴控制高度
                    gui_element.setScale(current_scale.getX() if hasattr(current_scale, 'getX')
                                         else current_scale[0] if isinstance(current_scale, (list, tuple))
                    else current_scale, value)
                else:
                    # 其他情况使用统一缩放
                    gui_element.setScale(value)
                gui_element.setTransparency(TransparencyAttrib.MAlpha)
            else:
                # 其他2D元素处理
                if axis in ["x", "z"]:  # 对于2D图像，x和z分别代表宽度和高度
                    # 保持原有缩放比例，仅调整指定轴
                    if axis == "x":
                        gui_element.setScale(value,
                                             current_scale.getZ() if hasattr(current_scale, 'getZ')
                                             else current_scale[1] if isinstance(current_scale, (list, tuple))
                                             else current_scale)
                    elif axis == "z":
                        gui_element.setScale(
                            current_scale.getX() if hasattr(current_scale, 'getX')
                            else current_scale[0] if isinstance(current_scale, (list, tuple))
                            else current_scale, value)
                else:
                    # 对于其他2D元素，使用统一缩放
                    gui_element.setScale(value)

            return True
        except Exception as e:
            print(f"✗ 更新GUI元素缩放失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def createCesiumView(self, main_window=None):
        """创建 Cesium 视图窗口（离线版本）"""
        if not WEB_ENGINE_AVAILABLE:
            print("❌ 无法创建Cesium视图: Web引擎不可用")
            return None

        try:
            from PyQt5.QtWebEngineWidgets import QWebEngineView
            from PyQt5.QtWidgets import QDockWidget
            from PyQt5.QtCore import QUrl
            import os

            # 尝试获取主窗口引用
            if main_window is None:
                print("🔍 尝试获取主窗口引用...")

                # 检查各种可能的主窗口引用
                if hasattr(self.world, 'interface_manager'):
                    print(f"  - interface_manager 存在: {self.world.interface_manager}")
                    if hasattr(self.world.interface_manager, 'main_window'):
                        main_window = self.world.interface_manager.main_window
                        print(f"  - interface_manager.main_window: {main_window}")

                if main_window is None and hasattr(self.world, 'main_window'):
                    main_window = self.world.main_window
                    print(f"  - world.main_window: {main_window}")

                # 如果仍然没有主窗口，尝试从树形控件获取
                if main_window is None and self.world.treeWidget:
                    try:
                        main_window = self.world.treeWidget.window()
                        print(f"  - 从 treeWidget 获取窗口: {main_window}")
                    except:
                        pass

                if main_window is None:
                    print("✗ 无法获取主窗口引用")
                    return None
            else:
                print(f"✅ 使用传入的主窗口引用: {main_window}")

            # 检查主窗口是否有效
            if not hasattr(main_window, 'addDockWidget'):
                print(f"✗ 主窗口引用无效，缺少 addDockWidget 方法")
                return None

            # 检查是否已经存在 Cesium 视图
            for element in self.gui_elements:
                if hasattr(element, 'objectName') and element.objectName() == "CesiumView":
                    print("⚠ Cesium 视图已经存在")
                    # 将其前置显示
                    element.show()
                    element.raise_()
                    return element

            # 创建停靠窗口
            print(f"🔧 创建 Cesium 停靠窗口，父窗口: {main_window}")
            cesium_dock = QDockWidget("Cesium 地图视图（离线）", main_window)
            cesium_dock.setObjectName("CesiumView")

            # 创建 Web 视图
            self.cesium_view = QWebEngineView()

            # 使用本地 HTML 文件（离线模式）
            local_html_path = os.path.abspath("./cesium_offline.html")
            if os.path.exists(local_html_path):
                print(f"🌐 加载离线 Cesium: file://{local_html_path}")
                self.cesium_view.load(QUrl(f"file://{local_html_path}"))
            else:
                print("⚠️ 离线文件不存在，使用在线版本")
                self.cesium_view.load(QUrl("http://localhost:8080/Apps/HelloWorld.html"))

            # 设置内容
            cesium_dock.setWidget(self.cesium_view)

            # 添加到主窗口
            print("📍 将 Cesium 视图添加到主窗口")
            main_window.addDockWidget(Qt.RightDockWidgetArea, cesium_dock)

            # 添加到GUI元素列表以便管理
            self.gui_elements.append(cesium_dock)

            print("✓ Cesium 离线视图已创建并集成到项目中")
            return cesium_dock

        except Exception as e:
            print(f"✗ 创建 Cesium 视图失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def toggleCesiumView(self):
        """切换 Cesium 视图显示状态"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用，无法切换 Cesium 视图")
            return None

        try:
            # 查找现有的 Cesium 视图
            cesium_dock = None
            cesium_index = -1
            for i, element in enumerate(self.gui_elements):
                if hasattr(element, 'objectName') and element.objectName() == "CesiumView":
                    cesium_dock = element
                    cesium_index = i
                    break

            # 如果存在则移除，否则创建
            if cesium_dock:
                # 获取主窗口引用以正确移除停靠窗口
                main_window = None
                if (hasattr(self.world, 'interface_manager') and
                        hasattr(self.world.interface_manager, 'main_window') and
                        self.world.interface_manager.main_window):
                    main_window = self.world.interface_manager.main_window
                elif hasattr(self.world, 'main_window') and self.world.main_window:
                    main_window = self.world.main_window

                if main_window and hasattr(main_window, 'removeDockWidget'):
                    main_window.removeDockWidget(cesium_dock)

                # 从列表中移除
                if cesium_index >= 0:
                    self.gui_elements.pop(cesium_index)

                print("✓ Cesium 视图已隐藏")
                return None
            else:
                return self.createCesiumView()

        except Exception as e:
            print(f"✗ 切换 Cesium 视图失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def refreshCesiumView(self):
        """刷新 Cesium 视图"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用，无法刷新 Cesium 视图")
            return False

        try:
            for element in self.gui_elements:
                if hasattr(element, 'objectName') and element.objectName() == "CesiumView":
                    web_view = element.widget()
                    if isinstance(web_view, QWebEngineView):
                        web_view.reload()
                        print("✓ Cesium 视图已刷新")
                        return True
            print("⚠ 未找到 Cesium 视图")
            return False
        except Exception as e:
            print(f"✗ 刷新 Cesium 视图失败: {str(e)}")
            return False

    def updateCesiumURL(self, url):
        """更新 Cesium 视图的 URL"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用，无法更新 Cesium URL")
            return False

        try:
            for element in self.gui_elements:
                if hasattr(element, 'objectName') and element.objectName() == "CesiumView":
                    web_view = element.widget()
                    if isinstance(web_view, QWebEngineView):
                        from PyQt5.QtCore import QUrl
                        web_view.load(QUrl(url))
                        print(f"✓ Cesium URL 已更新为: {url}")
                        return True
            print("⚠ 未找到 Cesium 视图")
            return False
        except Exception as e:
            print(f"✗ 更新 Cesium URL 失败: {str(e)}")
            return False

    # 在 GUIManager 类中添加以下方法

    def addModelToCesium(self, model_id, model_url, longitude, latitude, height=0, scale=1.0):
        """向 Cesium 添加模型"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用，无法操作 Cesium")
            return False

        try:
            # 查找 Cesium 视图
            cesium_view = None
            for element in self.gui_elements:
                if (hasattr(element, 'objectName') and
                        element.objectName() == "CesiumView" and
                        hasattr(element, 'widget')):
                    cesium_view = element.widget()
                    break

            if not cesium_view:
                print("✗ 未找到 Cesium 视图")
                return False

            # 转义特殊字符以防止 JavaScript 语法错误
            escaped_model_id = str(model_id).replace("'", "\\'")
            escaped_model_url = str(model_url).replace("'", "\\'").replace("\\", "/")

            # 构造 JavaScript 调用
            js_code = f"""
            (function() {{
                if (window.CesiumAPI && typeof window.CesiumAPI.addModel === 'function') {{
                    try {{
                        var result = window.CesiumAPI.addModel(
                            '{escaped_model_id}',
                            '{escaped_model_url}',
                            {{
                                longitude: {longitude},
                                latitude: {latitude},
                                height: {height}
                            }},
                            {scale}
                        );
                        console.log('Cesium 添加模型结果:', result);
                        return result || {{success: true, message: 'Model added'}};
                    }} catch (error) {{
                        console.error('JavaScript 错误:', error);
                        return {{success: false, message: 'JavaScript error: ' + error.message}};
                    }}
                }} else {{
                    console.error('CesiumAPI.addModel 不可用');
                    return {{success: false, message: 'CesiumAPI.addModel not available'}};
                }}
            }})();
            """

            # 定义回调函数处理结果
            def handle_result(result):
                try:
                    if isinstance(result, dict):
                        if result.get('success', False):
                            print(f"✓ 成功在 Cesium 中添加模型: {model_id}")
                        else:
                            print(f"✗ 在 Cesium 中添加模型失败: {result.get('message', 'Unknown error')}")
                    else:
                        print(f"✓ 已发送添加模型请求: {model_id}")
                except Exception as callback_error:
                    print(f"✗ 处理回调结果时出错: {callback_error}")

            # 执行 JavaScript 并获取结果
            cesium_view.page().runJavaScript(js_code, handle_result)
            return True

        except Exception as e:
            print(f"✗ 添加模型到 Cesium 失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    # 添加新的方法来集成 Panda3D 场景中的 Cesium Tiles
    def addCesiumTilesetToScene(self, tileset_name, tileset_url, position=(0, 0, 0)):
        """在 Panda3D 场景中添加 Cesium 3D Tiles"""
        try:
            # 使用场景管理器加载 tileset
            tileset_node = self.world.scene_manager.load_cesium_tileset(tileset_url, position)

            if tileset_node:
                # 添加到 GUI 元素列表以便管理
                self.gui_elements.append({
                    'type': 'cesium_tileset',
                    'name': tileset_name,
                    'node': tileset_node,
                    'url': tileset_url
                })

                print(f"✓ 在场景中添加 Cesium tileset: {tileset_name}")
                return tileset_node
            else:
                print(f"✗ 在场景中添加 Cesium tileset 失败: {tileset_name}")
                return None

        except Exception as e:
            print(f"✗ 在场景中添加 Cesium tileset 出错: {e}")
            return None

    def removeModelFromCesium(self, model_id):
        """从 Cesium 移除模型"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用")
            return False

        try:
            # 查找 Cesium 视图
            cesium_view = None
            for element in self.gui_elements:
                if (hasattr(element, 'objectName') and
                        element.objectName() == "CesiumView" and
                        hasattr(element, 'widget')):
                    cesium_view = element.widget()
                    break

            if not cesium_view:
                print("✗ 未找到 Cesium 视图")
                return False

            # 构造 JavaScript 调用
            js_code = f"""
            if (window.CesiumAPI && typeof window.CesiumAPI.removeModel === 'function') {{
                var result = window.CesiumAPI.removeModel('{model_id}');
                result;
            }} else {{
                {{success: false, message: 'CesiumAPI.removeModel not available'}};
            }}
            """

            # 定义回调函数处理结果
            def handle_result(result):
                if result and isinstance(result, dict):
                    if result.get('success', False):
                        print(f"✓ 成功从 Cesium 中移除模型: {model_id}")
                    else:
                        print(f"✗ 从 Cesium 中移除模型失败: {result.get('message', 'Unknown error')}")
                else:
                    print(f"✓ 已发送移除模型请求: {model_id} (无法获取详细结果)")

            # 执行 JavaScript 并获取结果
            cesium_view.page().runJavaScript(js_code, handle_result)
            return True

        except Exception as e:
            print(f"✗ 从 Cesium 移除模型失败: {e}")
            return False

    def updateCesiumModelPosition(self, model_id, longitude, latitude, height=0):
        """更新 Cesium 中模型的位置"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用")
            return False

        try:
            # 查找 Cesium 视图
            cesium_view = None
            for element in self.gui_elements:
                if (hasattr(element, 'objectName') and
                        element.objectName() == "CesiumView" and
                        hasattr(element, 'widget')):
                    cesium_view = element.widget()
                    break

            if not cesium_view:
                print("✗ 未找到 Cesium 视图")
                return False

            # 使用更安全的 JavaScript 字符串构造方式
            escaped_model_id = model_id.replace("'", "\\'")

            # 构造 JavaScript 调用
            js_code = f"""
            (function() {{
                if (window.CesiumAPI && typeof window.CesiumAPI.updateModelPosition === 'function') {{
                    try {{
                        var result = window.CesiumAPI.updateModelPosition(
                            '{escaped_model_id}',
                            {{
                                longitude: {longitude},
                                latitude: {latitude},
                                height: {height}
                            }}
                        );
                        return result || {{success: true, message: 'Position updated'}};
                    }} catch (error) {{
                        return {{success: false, message: 'JavaScript error: ' + error.message}};
                    }}
                }} else {{
                    return {{success: false, message: 'CesiumAPI.updateModelPosition not available'}};
                }}
            }})();
            """

            # 定义回调函数处理结果
            def handle_result(result):
                try:
                    if isinstance(result, dict):
                        if result.get('success', False):
                            print(f"✓ 成功更新 Cesium 中模型位置: {model_id}")
                        else:
                            print(f"✗ 更新 Cesium 中模型位置失败: {result.get('message', 'Unknown error')}")
                    else:
                        print(f"✓ 已发送更新模型位置请求: {model_id}")
                except Exception as callback_error:
                    print(f"✗ 处理回调结果时出错: {callback_error}")

            # 执行 JavaScript 并获取结果
            cesium_view.page().runJavaScript(js_code, handle_result)
            return True

        except Exception as e:
            print(f"✗ 更新 Cesium 中模型位置失败: {e}")
            return False

    def getAllCesiumModels(self):
        """获取 Cesium 中所有模型的列表"""
        if not WEB_ENGINE_AVAILABLE:
            print("✗ QtWebEngineWidgets 不可用")
            return None

        try:
            # 查找 Cesium 视图
            cesium_view = None
            for element in self.gui_elements:
                if (hasattr(element, 'objectName') and
                        element.objectName() == "CesiumView" and
                        hasattr(element, 'widget')):
                    cesium_view = element.widget()
                    break

            if not cesium_view:
                print("✗ 未找到 Cesium 视图")
                return None

            # 构造 JavaScript 调用
            js_code = """
            if (window.CesiumAPI && typeof window.CesiumAPI.getAllModels === 'function') {
                var result = window.CesiumAPI.getAllModels();
                JSON.stringify(result);
            } else {
                JSON.stringify({success: false, message: 'CesiumAPI.getAllModels not available'});
            }
            """

            # 定义回调函数处理结果
            def handle_result(result):
                try:
                    if isinstance(result, str):
                        import json
                        result = json.loads(result)

                    if result and result.get('success', False):
                        models = result.get('models', [])
                        print(f"✓ Cesium 中的模型列表: {models}")
                        return models
                    else:
                        print(f"✗ 获取 Cesium 模型列表失败: {result.get('message', 'Unknown error')}")
                        return []
                except Exception as e:
                    print(f"✗ 解析 Cesium 模型列表结果失败: {e}")
                    return []

            # 执行 JavaScript 并获取结果
            cesium_view.page().runJavaScript(js_code)
            return None  # 异步操作，实际结果通过回调处理

        except Exception as e:
            print(f"✗ 获取 Cesium 模型列表失败: {e}")
            return None

    # 添加一个便捷方法来加载本地模型文件
    def addLocalModelToCesium(self, model_id, local_model_path, longitude, latitude, height=0, scale=1.0):
        """向 Cesium 添加本地模型文件"""
        try:
            # 将本地路径转换为相对路径或 URL
            import os
            if os.path.exists(local_model_path):
                # 如果 Cesium 服务器可以访问该路径，可以直接使用
                # 否则需要将模型文件放在 Cesium 的静态资源目录中
                model_url = local_model_path.replace('\\', '/')  # 确保使用正斜杠
                return self.addModelToCesium(model_id, model_url, longitude, latitude, height, scale)
            else:
                print(f"✗ 模型文件不存在: {local_model_path}")
                return False
        except Exception as e:
            print(f"✗ 添加本地模型失败: {e}")
            return False
