from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QMenu from PyQt5.QtCore import Qt from panda3d.core import GeomNode, ModelRoot class InterfaceManager: """界面管理器 - 处理树形控件和UI交互""" def __init__(self, world): """初始化界面管理器""" self.world = world self.treeWidget = None self._expanded_paths = set() def setTreeWidget(self, treeWidget): """设置树形控件引用并更新场景树""" self.treeWidget = treeWidget # 添加右键菜单 self.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.treeWidget.customContextMenuRequested.connect(self.showTreeContextMenu) # 更新场景树 self.world.scene_manager.updateSceneTree() def onTreeItemClicked(self, item, column): """处理树形控件项目点击事件""" if not item: return # 获取节点对象 nodePath = item.data(0, Qt.UserRole) if nodePath: # 更新选择状态 self.world.selected_np = nodePath self.world.selection.updateSelection(nodePath) # 更新属性面板 self.world.property_panel.updatePropertyPanel(item) print(f"树形控件点击: {item.text(0)}") else: # 如果没有节点对象,清除选择 self.world.selection.updateSelection(None) #self.world.property_panel.clearPropertyPanel() def showTreeContextMenu(self, position): """显示树形控件的右键菜单""" item = self.treeWidget.itemAt(position) if not item: return # 获取节点对象 nodePath = item.data(0, Qt.UserRole) if not nodePath: return # 创建菜单 menu = QMenu() # 检查是否是GUI元素 if hasattr(nodePath, 'getTag') and nodePath.getTag("gui_type"): # GUI元素菜单 editAction = menu.addAction("编辑") editAction.triggered.connect(lambda: self.world.gui_manager.editGUIElementDialog(nodePath)) deleteAction = menu.addAction("删除GUI元素") deleteAction.triggered.connect(lambda: self.world.gui_manager.deleteGUIElement(nodePath)) duplicateAction = menu.addAction("复制") duplicateAction.triggered.connect(lambda: self.world.gui_manager.duplicateGUIElement(nodePath)) else: # 为模型节点或其子节点添加删除选项 parentItem = item.parent() if parentItem: if self.isModelOrChild(item): deleteAction = menu.addAction("删除") deleteAction.triggered.connect(lambda: self.deleteNode(nodePath, item)) # 显示菜单 menu.exec_(self.treeWidget.viewport().mapToGlobal(position)) def isModelOrChild(self, item): """检查是否是模型节点或其子节点""" while item and item.parent(): if item.parent().text(0) == "模型": return True item = item.parent() return False def deleteNode(self, nodePath, item): """删除节点""" try: # 从场景中移除 self.world.property_panel.removeActorForModel(nodePath) nodePath.removeNode() # 如果是模型根节点,从模型列表中移除 if item.parent().text(0) == "模型": if nodePath in self.world.models: self.world.models.remove(nodePath) # 从树形控件中移除 parentItem = item.parent() if parentItem: parentItem.removeChild(item) print(f"成功删除节点: {nodePath.getName()}") # 清空属性面板和选择框 self.world.property_panel.clearPropertyPanel() self.world.selection.updateSelection(None) except Exception as e: print(f"删除节点失败: {str(e)}") def updateSceneTree(self): """更新场景树显示 - 实际实现""" if not self.treeWidget: return self._expanded_paths.clear() self._collect_expanded() print("\n=== 更新场景树 ===") self.treeWidget.clear() # 创建场景根节点 sceneRoot = QTreeWidgetItem(self.treeWidget, ['场景']) # 添加相机节点 cameraItem = QTreeWidgetItem(sceneRoot, ['相机']) cameraItem.setData(0, Qt.UserRole, self.world.cam) print("添加相机节点") # 添加模型节点组 modelsItem = QTreeWidgetItem(sceneRoot, ['模型']) print(f"模型列表中的模型数量: {len(self.world.models)}") # 添加GUI元素节点组 guiItem = QTreeWidgetItem(sceneRoot, ['GUI元素']) lightItem = QTreeWidgetItem(sceneRoot,['灯光']) # 用于跟踪已添加的节点,避免重复 added_nodes = set() # 检查是否有图标配置 if not hasattr(self, 'item_icons'): self.item_icons = { 'model': None, 'geometry': None, 'empty': None, 'default': None } def _add_children_recursive(parent_item, node_path): """递归添加子节点到树项 - 使用用户提供的有效方法""" # 防止重复添加 node_id = id(node_path) if node_id in added_nodes: print(f"⚠️ 跳过重复节点: {node_path.getName()}") return added_nodes.add(node_id) # 获取所有子节点 children = node_path.getChildren() for i in range(children.getNumPaths()): child_node = children.getPath(i) # 跳过黑名单节点 child_name = child_node.getName() if child_name in {'', '**', 'temp', 'collision'} or child_name.startswith('__'): continue # 创建子项 child_item = QTreeWidgetItem(parent_item) display_name = child_name or f"Child_{i}" child_item.setText(0, display_name) # 检查是否是几何节点并设置图标 if child_node.find("**/+GeomNode").isEmpty(): # 不是几何节点 if hasattr(self, 'item_icons') and self.item_icons.get('model'): child_item.setIcon(0, self.item_icons.get('model', self.item_icons['default'])) display_name += " 📦" # 模型节点标记 else: # 是几何节点 if hasattr(self, 'item_icons') and self.item_icons.get('empty'): child_item.setIcon(0, self.item_icons.get('empty', self.item_icons['default'])) display_name += " 🔷" # 几何节点标记 # 更新显示名称 child_item.setText(0, display_name) # 存储NodePath引用 child_item.setData(0, Qt.UserRole, child_node) # 添加节点信息到提示 node_info = [] if hasattr(child_node, 'node') and child_node.node(): node_info.append(f"类型: {type(child_node.node()).__name__}") if child_node.getNumChildren() > 0: node_info.append(f"子节点: {child_node.getNumChildren()}") if hasattr(child_node, 'getTags') and child_node.getTags(): tags = [f"{k}:{v}" for k, v in child_node.getTags().items()] node_info.append(f"标签: {', '.join(tags)}") if node_info: child_item.setToolTip(0, "\n".join(node_info)) print(f"+ 添加节点: {display_name}") # 递归处理子节点的子节点 if child_node.getNumChildren() > 0: _add_children_recursive(child_item, child_node) def addNodeToTree(node, parentItem, force=True): """添加单个节点到树 - 包装函数""" node_id = id(node) if node_id in added_nodes: print(f"⚠️ 跳过重复根节点: {node.getName()}") return added_nodes.add(node_id) # 创建根节点项 display_name = node.getName() or "" # 检查是否为GLB模型 is_glb_model = False if hasattr(node, 'getTags'): tags = node.getTags() if hasattr(node, 'getTags') else {} is_glb_model = any('glb' in str(v).lower() or 'gltf' in str(v).lower() for v in tags.values()) if not is_glb_model: node_name = node.getName().lower() if node.getName() else "" is_glb_model = '.glb' in node_name or '.gltf' in node_name if is_glb_model: display_name += " 🎯 GLB" print(f"🎯 GLB模型检测到: {display_name}") else: display_name += " 📦" print(f"📦 常规模型: {display_name}") nodeItem = QTreeWidgetItem(parentItem, [display_name]) nodeItem.setData(0, Qt.UserRole, node) # 添加根节点信息 node_info = [] if hasattr(node, 'node') and node.node(): node_info.append(f"类型: {type(node.node()).__name__}") if node.getNumChildren() > 0: node_info.append(f"子节点: {node.getNumChildren()}") if node_info: nodeItem.setToolTip(0, "\n".join(node_info)) # 使用改进的递归方法处理子节点 _add_children_recursive(nodeItem, node) # 递归添加节点及其子节点 # def addNodeToTree(node, parentItem): # print(f"\n处理节点: {node.getName()}") # # 创建节点项 # nodeItem = QTreeWidgetItem(parentItem, [node.getName()]) # nodeItem.setData(0, Qt.UserRole, node) # print(f"添加节点: {node.getName()}") # # # 递归处理所有子节点 # for child in node.getChildren(): # # 检查是否是有效的模型节点 # if (isinstance(child.node(), GeomNode) or # child.hasTag("file") or # child.getName() == "RootNode" or # isinstance(child.node(), ModelRoot)): # print(f"处理子节点: {child.getName()}") # addNodeToTree(child, nodeItem) # else: # print(f"跳过节点: {child.getName()}") # 清空去重集合 added_nodes.clear() # 添加所有模型及其子节点 print(f"\n=== 开始处理 {len(self.world.models)} 个模型 ===") for i, model in enumerate(self.world.models): model_name = model.getName() or f"Model_{i}" print(f"\n--- 处理模型 {i+1}: {model_name} ---") addNodeToTree(model, modelsItem, force=True) print("=== 模型处理完成 ===") # 添加所有GUI元素 for gui_element in self.world.gui_elements: gui_type = gui_element.getTag("gui_type") if hasattr(gui_element, 'getTag') else "unknown" gui_text = gui_element.getTag("gui_text") if hasattr(gui_element, 'getTag') else "GUI元素" display_name = f"{gui_type}: {gui_text}" guiElementItem = QTreeWidgetItem(guiItem, [display_name]) guiElementItem.setData(0, Qt.UserRole, gui_element) print(f"添加GUI元素: {display_name}") # 添加聚光灯 if hasattr(self.world, 'Spotlight'): for light_element in self.world.Spotlight: addNodeToTree(light_element, lightItem, force=True) # 添加点光源 if hasattr(self.world, 'Pointlight'): for light_element in self.world.Pointlight: addNodeToTree(light_element, lightItem, force=True) # 添加地板节点 if hasattr(self.world, 'ground'): groundItem = QTreeWidgetItem(sceneRoot, ['地板']) groundItem.setData(0, Qt.UserRole, self.world.ground) print("添加地板节点") # 展开所有节点 #self.treeWidget.expandAll() self._restore_expanded() print("=== 场景树更新完成 ===\n") def findTreeItem(self, node, parentItem): """在树形控件中查找指定的节点项""" for i in range(parentItem.childCount()): item = parentItem.child(i) itemNode = item.data(0, Qt.UserRole) if itemNode == node: return item # 递归查找子项 if item.childCount() > 0: found = self.findTreeItem(node, item) if found: return found return None def _collect_expanded(self,item=None,prefix=""): if item is None: root = self.treeWidget.invisibleRootItem() for i in range(root.childCount()): self._collect_expanded(root.child(i),prefix) return path = f"{prefix}/{item.text(0)}" if item.isExpanded(): self._expanded_paths.add(path) for i in range(item.childCount()): self._collect_expanded(item.child(i),path) def _restore_expanded(self,item=None,prefix=""): if item is None: root = self.treeWidget.invisibleRootItem() for i in range(root.childCount()): self._restore_expanded(root.child(i),prefix) return path = f"{prefix}/{item.text(0)}" if path in self._expanded_paths: item.setExpanded(True) for i in range(item.childCount()): self._restore_expanded(item.child(i),path)