from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QMenu from PyQt5.QtCore import Qt from PyQt5.sip import delete 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: self.world.selection.updateSelection(None) return self.world.property_panel.updatePropertyPanel(item) # 获取节点对象 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)) else: 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) if hasattr(nodePath,'getPythonTag'): light_object = nodePath.getPythonTag('rp_light_object') if light_object and hasattr(self.world,'render_pipeline'): self.world.render_pipeline.remove_light(light_object) if hasattr(self.world,'selection'): if self.world.selection.selectedNode == nodePath: self.world.selection.clearSelectionBox() self.world.selection.clearGizmo() self.world.selection.selectedNode = None self.world.selection.selectedObject = None if nodePath in self.world.Spotlight: self.world.Spotlight.remove(nodePath) if nodePath in self.world.Pointlight: self.world.Pointlight.remove(nodePath) nodePath.removeNode() if hasattr(self.world,'selection'): self.world.selection.checkAndClearIfTargetDeleted() # 如果是模型根节点,从模型列表中移除 #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,['灯光']) BLACK_LIST = {'','**','temp','collision'} from panda3d.core import CollisionNode def should_skip(node): name = node.getName() return name in BLACK_LIST or name.startswith('__') or isinstance(node.node(),CollisionNode) or isinstance(node.node(),ModelRoot) or name=="" def addNodeToTree(node,parentItem,force = False): if not force and should_skip(node): return nodeItem = QTreeWidgetItem(parentItem,[node.getName()]) nodeItem.setData(0,Qt.UserRole,node) for child in node.getChildren(): addNodeToTree(child,nodeItem,force = False) # 递归添加节点及其子节点 # 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()}") for model in self.world.models: addNodeToTree(model, sceneRoot,force=True) # 添加所有GUI元素 for gui in self.world.gui_elements: gui_type = gui.getTag("gui_type") or "unknown" gui_text = gui.getTag("gui_text") or "GUI元素" item = QTreeWidgetItem(sceneRoot, [f"{gui_type}: {gui_text}"]) item.setData(0, Qt.UserRole, gui) for light in self.world.Spotlight + self.world.Pointlight: addNodeToTree(light, sceneRoot, force=True) # 添加地板节点 if hasattr(self.world, 'ground') and self.world.ground: groundItem = QTreeWidgetItem(sceneRoot, ['地板']) groundItem.setData(0, Qt.UserRole, self.world.ground) # 展开所有节点 #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) def syncVisibilityDownward(self): from collections import deque if not self.world.models: return q = deque() for root in self.world.models: q.append(root) while q: node = q.popleft() visible = node.getPythonTag("visible") if visible is None: visible = True if not visible: stack = [node] while stack: cur = stack.pop() cur.hide() for child in cur.getChildren(): stack.append(child) continue node.show() for child in node.getChildren(): q.append(child)