1
0
forked from Rowland/EG
EG/ui/interface_manager.py
2025-08-14 11:38:21 +08:00

361 lines
14 KiB
Python

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 "<Unknown>"
# 检查是否为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)