diff --git a/core/world.py b/core/world.py index 4860ad50..0538877f 100644 --- a/core/world.py +++ b/core/world.py @@ -288,6 +288,7 @@ class CoreWorld(Panda3DWorld): self.cam.setPos(0, -50, 20) self.cam.lookAt(0, 0, 0) self.camLens.setFov(80) + # self.cam.setTag("is_scene_element", "1") print("✓ 相机设置完成") def _setupLighting(self): @@ -321,6 +322,7 @@ class CoreWorld(Panda3DWorld): self.ground.setP(-90) self.ground.setZ(-0.1) self.ground.setColor(0.8, 0.8, 0.8, 1) + # self.ground.setTag("is_scene_element", "1") # 创建支持贴图的材质 mat = Material() diff --git a/scene/scene_manager.py b/scene/scene_manager.py index 4f5544dd..04837544 100644 --- a/scene/scene_manager.py +++ b/scene/scene_manager.py @@ -168,6 +168,7 @@ class SceneManager: # 添加文件标签用于保存/加载 model.setTag("file", model_name) model.setTag("is_model_root", "1") + model.setTag("is_scene_element", "1") # 记录应用的处理选项 if apply_unit_conversion: @@ -179,7 +180,29 @@ class SceneManager: self.models.append(model) # 更新场景树 - self.updateSceneTree() + # 获取树形控件并添加到Qt树中 + tree_widget = self._get_tree_widget() + if tree_widget: + # 找到根节点项 + root_item = None + for i in range(tree_widget.topLevelItemCount()): + item = tree_widget.topLevelItem(i) + if item.text(0) == "render" or item.data(0, Qt.UserRole) == self.world.render: + root_item = item + break + + if root_item: + qt_item = tree_widget.add_node_to_tree_widget(model, root_item, "IMPORTED_MODEL_NODE") + if qt_item: + tree_widget.setCurrentItem(qt_item) + # 更新选择和属性面板 + tree_widget.update_selection_and_properties(model, qt_item) + print("✅ Qt树节点添加成功") + else: + print("⚠️ Qt树节点添加失败,但Panda3D对象已创建") + else: + print("⚠️ 未找到根节点项,无法添加到Qt树") + # self.updateSceneTree() print(f"=== 模型导入成功: {model_name} ===\n") return model @@ -795,12 +818,25 @@ class SceneManager: """从BAM文件加载场景""" try: print(f"\n=== 开始加载场景: {filename} ===") - + tree_widget = self._get_tree_widget() # 清除当前场景 print("\n清除当前场景...") for model in self.models: - model.removeNode() - self.models.clear() + tree_widget.delete_item(model) + + # 清除灯光 + for light_node in self.Spotlight: + tree_widget.delete_item(light_node) + + for light_node in self.Pointlight: + tree_widget.delete_item(light_node) + + for terrain in self.world.terrain_manager.terrains: + tree_widget.delete_item(terrain) + + # 清除tilesets + for tileset_info in self.tilesets: + tree_widget.delete_item(tileset_info['node']) # 清理可能存在的辅助节点(坐标轴、选择框等) self._cleanupAuxiliaryNodes() @@ -810,6 +846,7 @@ class SceneManager: if not scene: return False + # tree_widget.create_model_items(scene) # 遍历场景中的所有模型节点 def processNode(nodePath, depth=0): indent = " " * depth @@ -842,7 +879,6 @@ class SceneManager: if isinstance(nodePath.node(), ModelRoot): print(f"{indent}找到模型根节点!") - # 清除现有材质状态 nodePath.clearMaterial() nodePath.clearColor() @@ -928,6 +964,7 @@ class SceneManager: # 更新场景树 self.updateSceneTree() + # self._get_tree_widget().create_model_items(scene) print("=== 场景加载完成 ===\n") return True diff --git a/ui/main_window.py b/ui/main_window.py index 1fbc5078..9f935eaf 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -6,7 +6,7 @@ - 停靠窗口设置 - 事件连接和信号处理 """ - +import os import sys from PyQt5.QtGui import QKeySequence, QIcon @@ -69,6 +69,20 @@ class MainWindow(QMainWindow): int(screen.height() / 2 - self.height() / 2), ) + @staticmethod + def get_icon_path(icon_name): + """获取图标文件的完整路径""" + # 假设 icons 文件夹在项目根目录下 + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + icon_path = os.path.join(project_root, "icons", icon_name) + + # 检查文件是否存在,如果不存在则返回默认值或None + if not os.path.exists(icon_path): + print(f"警告: 图标文件不存在: {icon_path}") + return "" # 返回空字符串,QIcon会处理空路径 + + return icon_path + def setupEmbeddedToolbar(self): """创建Unity风格的内嵌工具栏""" # 创建工具栏容器 @@ -109,8 +123,11 @@ class MainWindow(QMainWindow): # 选择工具 self.selectTool = QToolButton() - self.selectTool.setIcon(QIcon("icons/select_tool.png")) # 使用图标资源 - self.selectTool.setText('选择') + icon_path = self.get_icon_path("select_tool.png") + if icon_path and os.path.exists(icon_path): + self.selectTool.setIcon(QIcon(icon_path)) + else: + self.selectTool.setText('选择') # 如果没有图标则显示文字 self.selectTool.setIconSize(QSize(16, 16)) self.selectTool.setCheckable(True) self.selectTool.setToolTip("选择工具 (Q)") @@ -120,8 +137,11 @@ class MainWindow(QMainWindow): # 移动工具 self.moveTool = QToolButton() - self.moveTool.setIcon(QIcon("icons/move_tool.png")) - self.moveTool.setText('移动') + icon_path = self.get_icon_path("move_tool.png") + if icon_path and os.path.exists(icon_path): + self.moveTool.setIcon(QIcon(icon_path)) + else: + self.moveTool.setText('移动') self.moveTool.setIconSize(QSize(16, 16)) self.moveTool.setCheckable(True) self.moveTool.setToolTip("移动工具 (W)") @@ -131,8 +151,11 @@ class MainWindow(QMainWindow): # 旋转工具 self.rotateTool = QToolButton() - self.rotateTool.setIcon(QIcon("icons/rotate_tool.png")) - self.rotateTool.setText('旋转') + icon_path = self.get_icon_path("rotate_tool.png") + if icon_path and os.path.exists(icon_path): + self.rotateTool.setIcon(QIcon(icon_path)) + else: + self.rotateTool.setText('旋转') self.rotateTool.setIconSize(QSize(16, 16)) self.rotateTool.setCheckable(True) self.rotateTool.setToolTip("旋转工具 (E)") @@ -142,8 +165,11 @@ class MainWindow(QMainWindow): # 缩放工具 self.scaleTool = QToolButton() - self.scaleTool.setIcon(QIcon("icons/scale_tool.png")) - self.scaleTool.setText('缩放') + icon_path = self.get_icon_path("scale_tool.png") + if icon_path and os.path.exists(icon_path): + self.scaleTool.setIcon(QIcon(icon_path)) + else: + self.scaleTool.setText('缩放') self.scaleTool.setIconSize(QSize(16, 16)) self.scaleTool.setCheckable(True) self.scaleTool.setToolTip("缩放工具 (R)") diff --git a/ui/widgets.py b/ui/widgets.py index bd5ba590..92ada437 100644 --- a/ui/widgets.py +++ b/ui/widgets.py @@ -18,7 +18,7 @@ from PyQt5.QtCore import Qt, QUrl, QMimeData from PyQt5.QtGui import QDrag, QPainter, QPixmap, QPen, QBrush from PyQt5.sip import wrapinstance from direct.showbase.ShowBaseGlobal import aspect2d -from panda3d.core import ModelRoot +from panda3d.core import ModelRoot, NodePath from QPanda3D.QPanda3DWidget import QPanda3DWidget from scene import util @@ -1474,12 +1474,9 @@ class CustomTreeWidget(QTreeWidget): def showContextMenu(self, position): """显示右键菜单 - 复用主菜单的创建动作""" - if not self.selectedItems(): - print("没有选中的项目,不显示右键菜单") - return - - item = self.selectedItems()[0] - print(f"为项目 '{item.text(0)}' 显示右键菜单") + if self.selectedItems(): + item = self.selectedItems()[0] + print(f"为项目 '{item.text(0)}' 显示右键菜单") # 创建右键菜单 menu = QMenu(self) @@ -2187,6 +2184,62 @@ class CustomTreeWidget(QTreeWidget): except Exception as e: print(e) + def _findSceneRoot(self): + """查找场景根节点""" + for i in range(self.topLevelItemCount()): + top_item = self.topLevelItem(i) + if top_item.data(0, Qt.UserRole + 1) == "SCENE_ROOT": + return top_item + return None + + def create_model_items(self, model): + if not model: + print("传入的参数model为空") + return + # 创建根节点项 + # root_item = QTreeWidgetItem(self) + # root_item.setText(0, model.getName() or "Unnamed Node") + # root_item.setText(1, model.node().getTypeName()) + # root_item.setIcon(0, self.item_icons.get('model', self.item_icons['default'])) + + # 存储NodePath引用以便后续操作 + # root_item.setData(0, Qt.UserRole, model) + root_item = self._findSceneRoot() + + # 递归添加子节点 + self._add_children_recursive(root_item, model) + + return root_item + + def _add_children_recursive(self, parent_item, node_path: NodePath): + """递归添加子节点到树项""" + print(f'开始递归添加子节点') + # 获取所有子节点 + children = node_path.getChildren() + + for i in range(children.getNumPaths()): + child_node: NodePath = children.getPath(i) + + # 过滤条件 + if not child_node.hasTag("is_scene_element"): + print(f"不存在------------------------{child_node.hasTag('is_scene_element')}") + continue + + print(f"存在------------------------{child_node.getName()}") + # 创建子项 + child_item = QTreeWidgetItem(parent_item) + child_item.setText(0, child_node.getName() or f"Child_{i}") + + # 存储NodePath引用 + child_item.setData(0, Qt.UserRole, child_node) + + # 添加额外信息(可选) + # self._add_node_info(child_item, child_node) + + # 递归处理子节点的子节点 + if child_node.getNumChildren() > 0: + self._add_children_recursive(child_item, child_node) + # ==================== 辅助方法 ==================== def _findSceneRoot(self): """查找场景根节点"""