302 lines
11 KiB
Python
302 lines
11 KiB
Python
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, modelsItem,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(guiItem, [f"{gui_type}: {gui_text}"])
|
|
item.setData(0, Qt.UserRole, gui)
|
|
|
|
for light in self.world.Spotlight + self.world.Pointlight:
|
|
addNodeToTree(light, lightItem, 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)
|