diff --git a/PROJECT_MODULE_INDEX.md b/PROJECT_MODULE_INDEX.md new file mode 100644 index 00000000..b658fce4 --- /dev/null +++ b/PROJECT_MODULE_INDEX.md @@ -0,0 +1,352 @@ +# PROJECT_MODULE_INDEX + +> 本文档只基于本地工作区扫描生成,不依赖 Git 远程信息。 + +- 生成时间: 2026-02-28 16:41:46 +- 目标: 为后续优化/重构提供 功能模块 -> 文件 的快速定位索引 + +## 0. 关联分析文档 + +- 优化分析(本轮): `PROJECT_OPTIMIZATION_ANALYSIS.md` +- Qt 清理迁移清单: `QT_TO_IMGUI_MIGRATION_CHECKLIST.md` +- 历史模块分析: `IMGUI_MODULE_ANALYSIS.md` + +## 1. 一级目录规模(Python) + +| 目录 | 文件数 | 代码行数 | +|---|---:|---:| +| core | 40 | 19892 | +| gui | 1 | 7 | +| project | 2 | 759 | +| root | 2 | 812 | +| scene | 10 | 3792 | +| scripts | 18 | 1378 | +| ssbo_component | 3 | 1907 | +| templates | 1 | 1282 | +| tools | 1 | 73 | +| TransformGizmo | 5 | 3705 | +| ui | 63 | 17403 | + +## 2. 功能模块 -> 关键文件映射 + +### 应用启动与组装 + +- 模块职责: 程序入口、系统初始化、模块装配 +- 对应文件: + - Start_Run.py + - main.py + - templates/main_template.py + +### 世界与运行时核心 + +- 模块职责: 世界生命周期、输入事件、选择、命令、资源、碰撞、地形等 +- 对应文件: + - core/world.py + - core/event_handler.py + - core/selection.py + - core/Command_System.py + - core/tool_manager.py + - core/script_system.py + - core/resource_manager.py + - core/collision_manager.py + - core/terrain_manager.py + - core/model_drag_drop.py + - core/InfoPanelManager.py + - core/CustomMouseController.py + - core/imgui_style_manager.py + - core/imgui_webview.py + - core/selection_outline.py + - core/render_pipeline_utils.py + +### VR 系统 + +- 模块职责: VR 管理、渲染阶段、交互、追踪、性能、可视化、配置 +- 对应文件: + - core/vr_manager.py + - core/vr/config/vr_config.py + - core/vr/config/joystick_config.py + - core/vr/config/shadow_stage.py + - core/vr/rendering/stages.py + - core/vr/interaction/actions.py + - core/vr/interaction/grab.py + - core/vr/interaction/joystick.py + - core/vr/interaction/teleport.py + - core/vr/tracking/controllers.py + - core/vr/visualization/controllers.py + - core/vr/visualization/effects.py + - core/vr/performance/monitoring.py + - core/vr/performance/optimization.py + - core/vr/testing/test_mode.py + +### 场景管理 + +- 模块职责: 模型导入、灯光管理、序列化、IO、切片转换 +- 对应文件: + - scene/scene_manager.py + - scene/scene_manager_impl.py + - scene/scene_manager_model_mixin.py + - scene/scene_manager_light_mixin.py + - scene/scene_manager_serialization_mixin.py + - scene/scene_manager_io_mixin.py + - scene/scene_manager_convert_tiles_mixin.py + - scene/util.py + - scene/tree_roles.py + +### 项目管理 + +- 模块职责: 项目创建、打开、保存、资源清单 +- 对应文件: + - project/project_manager.py + +### 编辑器面板 (ImGui) + +- 模块职责: 顶部/左侧/中心/右侧属性面板、弹窗、运行时动作、创建对象、动画工具 +- 对应文件: + - ui/panels/editor_panels.py + - ui/panels/editor_panels_top.py + - ui/panels/editor_panels_left.py + - ui/panels/editor_panels_center.py + - ui/panels/editor_panels_right.py + - ui/panels/editor_panels_right_transform.py + - ui/panels/editor_panels_right_material.py + - ui/panels/editor_panels_right_collision.py + - ui/panels/panel_delegates.py + - ui/panels/property_helpers.py + - ui/panels/runtime_actions.py + - ui/panels/create_actions.py + - ui/panels/app_actions.py + - ui/panels/dialog_panels.py + - ui/panels/script_panels.py + - ui/panels/interaction_panels.py + - ui/panels/object_factory.py + - ui/panels/animation_tools.py + +### LUI 编辑器与组件 + +- 模块职责: LUI 管理、属性编辑、交互编辑、组件定义 +- 对应文件: + - ui/lui_manager.py + - ui/lui_function.py + - ui/LUI/lui_manager_editor.py + - ui/LUI/lui_manager_interaction.py + - ui/LUI/lui_function_properties.py + - ui/LUI/lui_function_components.py + - ui/LUI/lui_shared.py + - ui/widgets.py + - ui/icon_manager.py + +### LUI 内建控件与皮肤 + +- 模块职责: LUI 基础控件实现与皮肤资源脚本 +- 对应文件: + - ui/Builtin/Elements.py + - ui/Builtin/LUI*.py + - ui/Builtin/RectTransform.py + - ui/Skins/Metro/LUIMetroSkin.py + - ui/Skins/Metro/copy_frames.py + +### Transform Gizmo + +- 模块职责: 移动/旋转/缩放 gizmo 与事件 +- 对应文件: + - TransformGizmo/transform_gizmo.py + - TransformGizmo/move_gizmo.py + - TransformGizmo/rotate_gizmo.py + - TransformGizmo/scale_gizmo.py + - TransformGizmo/events.py + +### SSBO 选取与编辑 + +- 模块职责: 基于 SSBO 的对象选取与编辑器 +- 对应文件: + - ssbo_component/ssbo_editor.py + - ssbo_component/ssbo_controller.py + - ssbo_component/demo_component.py + +### 脚本样例与测试 + +- 模块职责: 内置脚本、旋转/移动/缩放测试脚本 +- 对应文件: + - scripts/*.py + +### 开发工具 + +- 模块职责: 辅助分析脚本 +- 对应文件: + - tools/open_source_rate.py + +## 3. 配置与运行关键文件 + +- config/vr_settings.json +- core/vr/config/vr_settings.json +- vr_actions/actions.json +- vr_actions/bindings_index.json +- vr_actions/bindings_oculus.json +- vr_actions/bindings_vive.json +- imgui.ini +- requirements/requirements.txt +- requirements/clean-requirements.txt +- requirements/conda-requirements.txt +- requirements/environment.yml + +## 4. 第三方/外部子树说明 + +- RenderPipelineFile/: 第三方渲染管线源码与工具,Python 文件 246 个,约 31629 行。 +- 优化建议: 优先在本项目业务目录改动(core/, ui/, scene/, project/, ssbo_component/, TransformGizmo/),避免直接修改第三方目录。 + +## 5. 完整 Python 文件索引(含行数) + +| 文件 | 行数 | +|---|---:| +| core/__init__.py | 23 | +| core/collision_manager.py | 1050 | +| core/Command_System.py | 576 | +| core/CustomMouseController.py | 185 | +| core/event_handler.py | 576 | +| core/imgui_style_manager.py | 428 | +| core/imgui_webview.py | 189 | +| core/InfoPanelManager.py | 1460 | +| core/model_drag_drop.py | 485 | +| core/render_pipeline_utils.py | 17 | +| core/resource_manager.py | 471 | +| core/script_system.py | 817 | +| core/selection.py | 2461 | +| core/selection_outline.py | 263 | +| core/terrain_manager.py | 571 | +| core/tool_manager.py | 133 | +| core/vr/__init__.py | 31 | +| core/vr/config/__init__.py | 8 | +| core/vr/config/joystick_config.py | 226 | +| core/vr/config/shadow_stage.py | 145 | +| core/vr/config/vr_config.py | 216 | +| core/vr/interaction/__init__.py | 9 | +| core/vr/interaction/actions.py | 484 | +| core/vr/interaction/grab.py | 329 | +| core/vr/interaction/joystick.py | 561 | +| core/vr/interaction/teleport.py | 331 | +| core/vr/performance/__init__.py | 9 | +| core/vr/performance/monitoring.py | 972 | +| core/vr/performance/optimization.py | 287 | +| core/vr/rendering/__init__.py | 10 | +| core/vr/rendering/stages.py | 712 | +| core/vr/testing/__init__.py | 6 | +| core/vr/testing/test_mode.py | 609 | +| core/vr/tracking/__init__.py | 9 | +| core/vr/tracking/controllers.py | 391 | +| core/vr/visualization/__init__.py | 8 | +| core/vr/visualization/controllers.py | 631 | +| core/vr/visualization/effects.py | 174 | +| core/vr_manager.py | 2970 | +| core/world.py | 1059 | +| gui/__init__.py | 7 | +| main.py | 770 | +| project/__init__.py | 10 | +| project/project_manager.py | 749 | +| scene/__init__.py | 10 | +| scene/scene_manager.py | 17 | +| scene/scene_manager_convert_tiles_mixin.py | 473 | +| scene/scene_manager_impl.py | 15 | +| scene/scene_manager_io_mixin.py | 962 | +| scene/scene_manager_light_mixin.py | 403 | +| scene/scene_manager_model_mixin.py | 919 | +| scene/scene_manager_serialization_mixin.py | 749 | +| scene/tree_roles.py | 3 | +| scene/util.py | 241 | +| scripts/a.py | 25 | +| scripts/BouncerScript.py | 99 | +| scripts/ColorChangerScript.py | 157 | +| scripts/ComboAnimatorScript.py | 36 | +| scripts/example_script.py | 44 | +| scripts/FollowerScript.py | 66 | +| scripts/MoverScript.py | 88 | +| scripts/R_P.py | 110 | +| scripts/R_R.py | 110 | +| scripts/Rotate_H_Script.py | 181 | +| scripts/Rotate_P_Script.py | 181 | +| scripts/Rotate_R_Script.py | 44 | +| scripts/RotatorScript.py | 36 | +| scripts/ScalerScript.py | 88 | +| scripts/test_quick_script.py | 25 | +| scripts/TestMover.py | 38 | +| scripts/TestRotator.py | 25 | +| scripts/TestScaler.py | 25 | +| ssbo_component/demo_component.py | 70 | +| ssbo_component/ssbo_controller.py | 1055 | +| ssbo_component/ssbo_editor.py | 782 | +| Start_Run.py | 42 | +| templates/main_template.py | 1282 | +| tools/open_source_rate.py | 73 | +| TransformGizmo/events.py | 14 | +| TransformGizmo/move_gizmo.py | 950 | +| TransformGizmo/rotate_gizmo.py | 1412 | +| TransformGizmo/scale_gizmo.py | 860 | +| TransformGizmo/transform_gizmo.py | 469 | +| ui/Builtin/__init__.py | 0 | +| ui/Builtin/Elements.py | 242 | +| ui/Builtin/LUIBlockText.py | 74 | +| ui/Builtin/LUIButton.py | 169 | +| ui/Builtin/LUICanvas.py | 97 | +| ui/Builtin/LUICheckbox.py | 65 | +| ui/Builtin/LUIFormattedLabel.py | 38 | +| ui/Builtin/LUIFrame.py | 54 | +| ui/Builtin/LUIHorizontalLayout.py | 13 | +| ui/Builtin/LUIInitialState.py | 32 | +| ui/Builtin/LUIInputField.py | 180 | +| ui/Builtin/LUIInputHandler.py | 5 | +| ui/Builtin/LUILabel.py | 63 | +| ui/Builtin/LUILayouts.py | 80 | +| ui/Builtin/LUIObject.py | 12 | +| ui/Builtin/LUIProgressbar.py | 54 | +| ui/Builtin/LUIRadiobox.py | 73 | +| ui/Builtin/LUIRadioboxGroup.py | 31 | +| ui/Builtin/LUIRegion.py | 5 | +| ui/Builtin/LUIRoot.py | 5 | +| ui/Builtin/LUIScrollableRegion.py | 119 | +| ui/Builtin/LUISelectbox.py | 158 | +| ui/Builtin/LUISkin.py | 34 | +| ui/Builtin/LUISlider.py | 185 | +| ui/Builtin/LUISprite.py | 12 | +| ui/Builtin/LUISpriteButton.py | 23 | +| ui/Builtin/LUITabbedFrame.py | 77 | +| ui/Builtin/LUIVerticalLayout.py | 13 | +| ui/Builtin/RectTransform.py | 73 | +| ui/icon_manager.py | 123 | +| ui/LUI/__init__.py | 1 | +| ui/LUI/lui_function_components.py | 1149 | +| ui/LUI/lui_function_properties.py | 1601 | +| ui/LUI/lui_manager_editor.py | 1593 | +| ui/LUI/lui_manager_interaction.py | 1351 | +| ui/LUI/lui_shared.py | 62 | +| ui/lui_function.py | 31 | +| ui/lui_manager.py | 199 | +| ui/panels/__init__.py | 0 | +| ui/panels/animation_tools.py | 1390 | +| ui/panels/app_actions.py | 1097 | +| ui/panels/create_actions.py | 147 | +| ui/panels/dialog_panels.py | 992 | +| ui/panels/editor_panels.py | 20 | +| ui/panels/editor_panels_center.py | 187 | +| ui/panels/editor_panels_left.py | 543 | +| ui/panels/editor_panels_right.py | 742 | +| ui/panels/editor_panels_right_collision.py | 210 | +| ui/panels/editor_panels_right_material.py | 222 | +| ui/panels/editor_panels_right_transform.py | 89 | +| ui/panels/editor_panels_top.py | 319 | +| ui/panels/interaction_panels.py | 128 | +| ui/panels/object_factory.py | 390 | +| ui/panels/panel_delegates.py | 512 | +| ui/panels/property_helpers.py | 1583 | +| ui/panels/runtime_actions.py | 356 | +| ui/panels/script_panels.py | 287 | +| ui/Skins/__init__.py | 0 | +| ui/Skins/Default/__init__.py | 0 | +| ui/Skins/Metro/__init__.py | 0 | +| ui/Skins/Metro/copy_frames.py | 31 | +| ui/Skins/Metro/LUIMetroSkin.py | 24 | +| ui/widgets.py | 38 | + +## 6. 使用方式(优化流程建议) + +1. 先在 功能模块 -> 关键文件映射 中定位模块。 +2. 再到 完整 Python 文件索引 按文件名快速跳转。 +3. 优先处理高行数核心文件(如 core/selection.py, core/vr_manager.py, ui/panels/property_helpers.py)。 diff --git a/PROJECT_OPTIMIZATION_ANALYSIS.md b/PROJECT_OPTIMIZATION_ANALYSIS.md new file mode 100644 index 00000000..e1695852 --- /dev/null +++ b/PROJECT_OPTIMIZATION_ANALYSIS.md @@ -0,0 +1,187 @@ +# PROJECT_OPTIMIZATION_ANALYSIS + +> 基于本地代码静态扫描生成(不含远程仓库信息,不含 `RenderPipelineFile/` 第三方目录)。 + +- 分析时间: 2026-02-28 +- 扫描范围: `main.py`, `Start_Run.py`, `core/`, `scene/`, `project/`, `ui/`, `ssbo_component/`, `TransformGizmo/`, `scripts/`, `tools/`, `templates/` + +## 0. 执行进展(非 VR) + +- 已完成 `EditorContext` 适配层:`core/editor_context.py` +- 已接入文件: + - `core/event_handler.py` + - `core/selection.py` + - `core/InfoPanelManager.py` + - `ui/panels/runtime_actions.py` + - `core/terrain_manager.py` + - `scene/scene_manager_convert_tiles_mixin.py` + - `scene/scene_manager_serialization_mixin.py` + - `scene/scene_manager_model_mixin.py` +- 效果(本地静态检索): + - 直接访问 `world.interface_manager` / `interface_manager.treeWidget` 已基本移除(仅剩注释或非本轮范围点位)。 + +## 1. 总体画像 + +- Python 文件: `146` +- 代码总行数: `58,371` +- `except Exception` / `except:` 总计: `950` +- 裸 `except:` 总计: `63` +- 旧上下文关键词引用总量: + - `interface_manager`: `35` + - `treeWidget`: `10` + - `gui_manager`: `77` + +结论: + +- Qt 依赖已清理后,当前主要技术债集中在三类: + - 过大函数(可维护性差) + - 异常处理过宽(问题可观测性差) + - 旧 GUI 上下文命名耦合(边界不清晰) + +## 2. 热点文件(按规模/风险) + +### 2.1 超大文件 Top + +1. `core/vr_manager.py` (`3553` 行) +2. `core/selection.py` (`2942` 行) +3. `core/InfoPanelManager.py` (`1726` 行) +4. `ui/LUI/lui_manager_editor.py` (`1724` 行) +5. `ui/panels/property_helpers.py` (`1711` 行) +6. `ui/LUI/lui_function_properties.py` (`1707` 行) +7. `TransformGizmo/rotate_gizmo.py` (`1587` 行) +8. `ui/panels/animation_tools.py` (`1579` 行) + +### 2.2 长函数 Top(优先拆分) + +1. `ui/LUI/lui_function_properties.py::_draw_component_properties` (`1441` 行) +2. `scene/scene_manager_io_mixin.py::loadScene` (`556` 行) +3. `ui/panels/animation_tools.py::_getActor` (`510` 行) +4. `main.py::__init__` (`375` 行) +5. `ui/LUI/lui_manager_interaction.py::_update_drag` (`348` 行) +6. `ui/panels/editor_panels_left.py::_draw_resource_manager` (`310` 行) +7. `scene/scene_manager_io_mixin.py::processNode` (`281` 行) +8. `core/selection.py::updateGizmoDrag` (`278` 行) + +### 2.3 异常处理密度高(可观测性风险) + +1. `ui/panels/animation_tools.py` (`except* = 85`) +2. `core/vr_manager.py` (`71`) +3. `core/selection.py` (`57`) +4. `ui/panels/property_helpers.py` (`54`) +5. `ui/panels/app_actions.py` (`46`) +6. `scene/scene_manager_model_mixin.py` (`36`) +7. `scene/scene_manager_serialization_mixin.py` (`27`) +8. `scene/scene_manager_io_mixin.py` (`20`) +9. `project/project_manager.py` (`20`) +10. `ui/panels/runtime_actions.py` (`20`) + +裸 `except:` 集中区: + +- `core/selection.py` (`7`) +- `scene/scene_manager_model_mixin.py` (`7`) +- `ui/panels/editor_panels_right_material.py` (`6`) +- `ui/panels/editor_panels_left.py` (`5`) + +### 2.4 旧上下文耦合集中区 + +1. `ui/panels/runtime_actions.py` (`gui_manager=29`) +2. `core/event_handler.py` (`interface_manager=11`, `gui_manager=11`) +3. `ui/panels/editor_panels_right.py` (`gui_manager=18`) +4. `scene/scene_manager_serialization_mixin.py` (`interface_manager=6`, `treeWidget=2`, `gui_manager=5`) +5. `core/selection.py` (`interface_manager=4`, `treeWidget=1`) +6. `core/InfoPanelManager.py` (`interface_manager=4`, `treeWidget=1`) +7. `core/terrain_manager.py` (`interface_manager=3`, `treeWidget=2`) + +## 3. 优化优先级(建议执行顺序) + +## P0: 上下文收敛(先做) + +目标: 统一 GUI/场景树访问边界,减少跨模块 `hasattr(..., 'interface_manager')` 与 `treeWidget` 语义残留。 + +建议动作: + +1. 引入 `EditorContext`(或 `UIContext`)统一提供: + - `get_tree_adapter()` + - `get_gui_service()` + - `get_selection_service()` +2. 在以下文件先改为调用上下文接口: + - `core/event_handler.py` + - `core/selection.py` + - `core/InfoPanelManager.py` + - `core/terrain_manager.py` + - `scene/scene_manager_serialization_mixin.py` + - `scene/scene_manager_convert_tiles_mixin.py` + - `ui/panels/runtime_actions.py` + +预期收益: + +- 降低命名残留与多处 `hasattr` 防御代码。 +- 后续模块拆分时边界更稳定。 + +## P1: 大函数拆分(第二阶段) + +目标: 将核心长函数拆分成“流程编排 + 子步骤函数”,减少单函数认知负担。 + +建议拆分顺序: + +1. `scene/scene_manager_io_mixin.py::loadScene` +2. `main.py::__init__` +3. `ui/panels/animation_tools.py::_getActor` +4. `core/selection.py::updateGizmoDrag` +5. `ui/LUI/lui_function_properties.py::_draw_component_properties`(可按“变换/布局/视觉/交互/脚本”分区) + +预期收益: + +- 回归问题定位更快。 +- 面板和场景加载逻辑更易测试。 + +## P2: 异常处理治理(并行推进) + +目标: 将“吞异常”改为“有边界的降级 + 可追踪日志”。 + +建议规则: + +1. 禁止新增裸 `except:`。 +2. 高风险路径必须记录上下文: + - 节点名/资源路径/操作类型/当前工具状态 +3. 对可恢复错误使用 `warning`,不可恢复错误返回显式失败值。 + +优先文件: + +- `ui/panels/animation_tools.py` +- `core/vr_manager.py` +- `core/selection.py` +- `ui/panels/property_helpers.py` +- `scene/scene_manager_io_mixin.py` + +## 4. 下一步可直接执行的任务包 + +### Task A(推荐先做,1-2 天) + +- 建立 `core/editor_context.py`(或同级命名) +- 给 `event_handler/selection/InfoPanelManager/runtime_actions` 接入上下文 +- 保持外部 API 不变,仅替换内部访问路径 + +### Task B(1 天) + +- 重构 `scene_manager_io_mixin.loadScene`: + - `preflight` + - `clear_old_scene` + - `load_bam` + - `rebuild_scene_tree` + - `post_load_sync` + +### Task C(1 天) + +- 统一异常日志工具(轻量封装) +- 首批替换 `animation_tools.py` 与 `property_helpers.py` + +## 5. 与现有文档关系 + +- 模块总索引: `PROJECT_MODULE_INDEX.md` +- Qt 迁移状态: `QT_TO_IMGUI_MIGRATION_CHECKLIST.md` +- 历史分析: `IMGUI_MODULE_ANALYSIS.md` + +--- + +如果按此路线继续,建议下一轮直接从 **Task A** 开始,我可以先落地上下文适配层并改 4 个高耦合文件。 diff --git a/core/editor_context.py b/core/editor_context.py new file mode 100644 index 00000000..58466884 --- /dev/null +++ b/core/editor_context.py @@ -0,0 +1,50 @@ +"""Editor context adapter for legacy bridge access.""" + + +class EditorContext: + """Provide a stable access layer for editor-facing services.""" + + def __init__(self, world): + self.world = world + + def get_interface_manager(self): + return getattr(self.world, "interface_manager", None) + + def get_tree_widget(self): + interface_manager = self.get_interface_manager() + if not interface_manager: + return None + return getattr(interface_manager, "treeWidget", None) + + def get_gui_manager(self): + return getattr(self.world, "gui_manager", None) + + def ensure_gui_elements(self): + gui_manager = self.get_gui_manager() + if gui_manager is not None: + gui_elements = getattr(gui_manager, "gui_elements", None) + if gui_elements is None: + gui_elements = [] + setattr(gui_manager, "gui_elements", gui_elements) + return gui_elements + + gui_elements = getattr(self.world, "gui_elements", None) + if gui_elements is None: + gui_elements = [] + setattr(self.world, "gui_elements", gui_elements) + return gui_elements + + def append_gui_element(self, element): + gui_elements = self.ensure_gui_elements() + gui_elements.append(element) + return True + + +def get_editor_context(owner): + """Get or create editor context from a world/owner object.""" + world = getattr(owner, "world", owner) + context = getattr(world, "_editor_context", None) + if context is None or getattr(context, "world", None) is not world: + context = EditorContext(world) + setattr(world, "_editor_context", context) + return context diff --git a/core/event_handler.py b/core/event_handler.py index 8902c525..360891b9 100644 --- a/core/event_handler.py +++ b/core/event_handler.py @@ -1,6 +1,7 @@ from panda3d.core import (Point3, Point2, CollisionTraverser, CollisionHandlerQueue, CollisionNode, CollisionRay, GeomNode, LineSegs, RenderState, DepthTestAttrib, ColorAttrib) +from core.editor_context import get_editor_context class EventHandler: @@ -9,22 +10,29 @@ class EventHandler: def __init__(self, world): """初始化事件处理器""" self.world = world + self._editor_context = get_editor_context(world) # 射线显示相关 self.showRay = False # 是否显示射线(默认关闭) self.rayNode = None # 当前显示的射线节点 self.rayLifetime = 2.0 # 射线显示时间(秒) + def _get_editor_context(self): + if not getattr(self, "_editor_context", None): + self._editor_context = get_editor_context(self.world) + return self._editor_context + def _get_interface_manager(self): """获取兼容层中的场景树管理器(若存在)。""" - return getattr(self.world, "interface_manager", None) + return self._get_editor_context().get_interface_manager() def _get_tree_widget(self): """安全获取场景树控件。""" - interface_manager = self._get_interface_manager() - if not interface_manager: - return None - return getattr(interface_manager, "treeWidget", None) + return self._get_editor_context().get_tree_widget() + + def _get_gui_manager(self): + """安全获取 GUI 管理器。""" + return self._get_editor_context().get_gui_manager() def _sync_tree_selection(self, selected_model): """将当前选中模型同步到场景树。""" @@ -276,7 +284,7 @@ class EventHandler: # 处理GUI编辑模式 if self.world.guiEditMode: - gui_manager = getattr(self.world, "gui_manager", None) + gui_manager = self._get_gui_manager() if not gui_manager: print("GUI编辑模式已开启,但 gui_manager 不可用") pickerNP.removeNode() @@ -322,7 +330,7 @@ class EventHandler: default_height = 0.0 world_pos = Point3(mx * 10, 0, my * 10) # 简单的屏幕到世界坐标转换 world_pos.setZ(default_height) - gui_manager = getattr(self.world, "gui_manager", None) + gui_manager = self._get_gui_manager() if gui_manager: gui_manager.createGUIAtPosition(world_pos, self.world.currentGUITool) diff --git a/core/selection.py b/core/selection.py index 5ebe0e84..280f1ac5 100644 --- a/core/selection.py +++ b/core/selection.py @@ -15,6 +15,7 @@ from panda3d.core import (Vec3, Point3, Point2, LineSegs, ColorAttrib, RenderSta from direct.task.TaskManagerGlobal import taskMgr import math from core.selection_outline import SelectionOutlineManager +from core.editor_context import get_editor_context class SelectionSystem: """选择和变换系统类""" @@ -26,6 +27,7 @@ class SelectionSystem: world: 核心世界对象引用 """ self.world = world + self._editor_context = get_editor_context(world) # 选择相关状态 self.selectedNode = None @@ -93,10 +95,7 @@ class SelectionSystem: def _get_tree_widget(self): """统一获取场景树控件。""" - interface_manager = getattr(self.world, "interface_manager", None) - if not interface_manager: - return None - return getattr(interface_manager, "treeWidget", None) + return self._editor_context.get_tree_widget() def _clear_tree_selection(self): """清空场景树选中项。""" diff --git a/core/terrain_manager.py b/core/terrain_manager.py index a0dedff3..1f5003e6 100644 --- a/core/terrain_manager.py +++ b/core/terrain_manager.py @@ -3,29 +3,25 @@ import time import urllib from panda3d.core import GeoMipTerrain, PNMImage, Texture, Vec3, NodePath -from panda3d.core import Filename, Material, ColorAttrib, AmbientLight, DirectionalLight -import os - -from scene import util +from panda3d.core import Filename, Material, ColorAttrib, AmbientLight, DirectionalLight +import os + +from scene import util +from core.editor_context import get_editor_context class TerrainManager: """地形管理类""" - def __init__(self, world): - self.world = world - self.terrains = [] + def __init__(self, world): + self.world = world + self._editor_context = get_editor_context(world) + self.terrains = [] # core/terrain_manager.py - def _get_tree_widget(self): - """安全获取树形控件""" - try: - if (hasattr(self.world, 'interface_manager') and - hasattr(self.world.interface_manager, 'treeWidget')): - return self.world.interface_manager.treeWidget - except AttributeError: - pass - return None + def _get_tree_widget(self): + """安全获取树形控件""" + return self._editor_context.get_tree_widget() def createTerrainFromHeightMap(self, heightmap_path, scale=(1, 1, 1)): """从高度图创建地形""" diff --git a/scene/scene_manager_convert_tiles_mixin.py b/scene/scene_manager_convert_tiles_mixin.py index 0b94414e..8d64c78e 100644 --- a/scene/scene_manager_convert_tiles_mixin.py +++ b/scene/scene_manager_convert_tiles_mixin.py @@ -18,17 +18,12 @@ from panda3d.egg import EggData, EggVertexPool from direct.actor.Actor import Actor from RenderPipelineFile.rpplugins.smaa.jitters import halton_seq from scene import util +from core.editor_context import get_editor_context class SceneManagerConvertTilesMixin: def _get_tree_widget(self): """安全获取树形控件""" - try: - if (hasattr(self.world, 'interface_manager') and - hasattr(self.world.interface_manager, 'treeWidget')): - return self.world.interface_manager.treeWidget - except AttributeError: - pass - return None + return get_editor_context(self.world).get_tree_widget() def _shouldConvertToGLB(self, filepath): """判断是否应该转换为GLB格式""" diff --git a/scene/scene_manager_model_mixin.py b/scene/scene_manager_model_mixin.py index 1afc5d61..f2ec2c73 100644 --- a/scene/scene_manager_model_mixin.py +++ b/scene/scene_manager_model_mixin.py @@ -19,6 +19,7 @@ from direct.actor.Actor import Actor from RenderPipelineFile.rpplugins.smaa.jitters import halton_seq from scene import util from scene.tree_roles import TREE_USER_ROLE +from core.editor_context import get_editor_context class SceneManagerModelMixin: def importModel(self, filepath, apply_unit_conversion=False, normalize_scales=True, auto_convert_to_glb=True): @@ -848,10 +849,10 @@ class SceneManagerModelMixin: def updateSceneTree(self): """更新场景树显示 - 代理到interface_manager""" - if hasattr(self.world, 'interface_manager'): - return self.world.interface_manager.updateSceneTree() - else: - print("界面管理器未初始化,无法更新场景树") + interface_manager = get_editor_context(self.world).get_interface_manager() + if interface_manager and hasattr(interface_manager, "updateSceneTree"): + return interface_manager.updateSceneTree() + print("界面管理器未初始化,无法更新场景树") def _get_script_file_path(self, script_class, script_name): """ diff --git a/scene/scene_manager_serialization_mixin.py b/scene/scene_manager_serialization_mixin.py index f7143a56..6fbc69cf 100644 --- a/scene/scene_manager_serialization_mixin.py +++ b/scene/scene_manager_serialization_mixin.py @@ -18,8 +18,18 @@ from panda3d.egg import EggData, EggVertexPool from direct.actor.Actor import Actor from RenderPipelineFile.rpplugins.smaa.jitters import halton_seq from scene import util +from core.editor_context import get_editor_context class SceneManagerSerializationMixin: + def _get_editor_context(self): + return get_editor_context(self.world) + + def _get_tree_widget(self): + return self._get_editor_context().get_tree_widget() + + def _get_gui_manager(self): + return self._get_editor_context().get_gui_manager() + def serializeNode(self, node): """序列化节点为字典数据""" try: @@ -411,14 +421,13 @@ class SceneManagerSerializationMixin: if new_node: # 尝试更新场景树以显示新节点 try: - if hasattr(self.world, 'interface_manager') and self.world.interface_manager: + tree_widget = self._get_tree_widget() + if tree_widget: # 查找父节点在场景树中的对应项 parent_item = self._findTreeItemForNode(parent_node) if parent_item: # 添加新节点到场景树 - tree_widget = self.world.interface_manager.treeWidget - if tree_widget: - tree_widget.add_node_to_tree_widget(new_node, parent_item, node_type or "NODE") + tree_widget.add_node_to_tree_widget(new_node, parent_item, node_type or "NODE") except Exception as e: print(f"添加节点到场景树时出错: {e}") @@ -433,15 +442,14 @@ class SceneManagerSerializationMixin: def _findTreeItemForNode(self, node): """根据节点查找对应的场景树项""" try: - if hasattr(self.world, 'interface_manager') and self.world.interface_manager: - tree_widget = self.world.interface_manager.treeWidget - if tree_widget: - # 遍历场景树查找匹配的节点项 - for i in range(tree_widget.topLevelItemCount()): - item = tree_widget.topLevelItem(i) - result = self._findTreeItemForNodeRecursive(item, node) - if result: - return result + tree_widget = self._get_tree_widget() + if tree_widget: + # 遍历场景树查找匹配的节点项 + for i in range(tree_widget.topLevelItemCount()): + item = tree_widget.topLevelItem(i) + result = self._findTreeItemForNodeRecursive(item, node) + if result: + return result return None except Exception as e: print(f"查找场景树项时出错: {e}") @@ -508,6 +516,7 @@ class SceneManagerSerializationMixin: # 根据GUI类型调用相应的创建方法 new_gui_element = None + gui_manager = self._get_gui_manager() if gui_type == "button" and hasattr(self.world, 'createGUIButton'): pos = node_data.get('pos', (0, 0, 0)) @@ -550,17 +559,18 @@ class SceneManagerSerializationMixin: else: scale = (1, 1) print(f"正在创建3D图片: 位置={pos}, 路径={image_path}, 大小={scale}") - new_gui_element = self.world.gui_manager.createGUI3DImage(pos, image_path, scale) - elif gui_type == "video_screen" and hasattr(self.world.gui_manager, 'createVideoScreen'): + if gui_manager and hasattr(gui_manager, 'createGUI3DImage'): + new_gui_element = gui_manager.createGUI3DImage(pos, image_path, scale) + elif gui_type == "video_screen" and gui_manager and hasattr(gui_manager, 'createVideoScreen'): pos = node_data.get('pos', (0, 0, 0)) video_path = node_data.get('tags').get('video_path', '') scale = node_data.get('scale', (1, 1,1)) - new_gui_element = self.world.gui_manager.createVideoScreen(pos,scale,video_path) - elif gui_type == "2d_video_screen" and hasattr(self.world.gui_manager, 'createGUI2DVideoScreen'): + new_gui_element = gui_manager.createVideoScreen(pos,scale,video_path) + elif gui_type == "2d_video_screen" and gui_manager and hasattr(gui_manager, 'createGUI2DVideoScreen'): pos = node_data.get('pos', (0, 0, 0)) video_path = node_data.get('tags').get('video_path', '') scale = node_data.get('scale', (1, 1, 1)) - new_gui_element = self.world.gui_manager.createGUI2DVideoScreen(pos,scale,video_path) + new_gui_element = gui_manager.createGUI2DVideoScreen(pos,scale,video_path) if new_gui_element: # 设置名称和变换 diff --git a/ui/panels/runtime_actions.py b/ui/panels/runtime_actions.py index 9a2d8ffa..0459a0df 100644 --- a/ui/panels/runtime_actions.py +++ b/ui/panels/runtime_actions.py @@ -1,5 +1,6 @@ import os from pathlib import Path +from core.editor_context import get_editor_context class RuntimeActions: @@ -7,28 +8,34 @@ class RuntimeActions: def __init__(self, app): self.app = app + object.__setattr__(self, "_editor_context", get_editor_context(app)) def __getattr__(self, name): return getattr(self.app, name) def __setattr__(self, name, value): - if name == "app" or name in self.__dict__ or hasattr(type(self), name): + if name == "app" or name.startswith("_") or name in self.__dict__ or hasattr(type(self), name): object.__setattr__(self, name, value) else: setattr(self.app, name, value) + def _get_editor_context(self): + context = getattr(self, "_editor_context", None) + if context is None: + context = get_editor_context(self.app) + object.__setattr__(self, "_editor_context", context) + return context + def _get_gui_manager(self): """统一获取 GUI 管理器,避免散落的 hasattr 判空。""" - return getattr(self.app, "gui_manager", None) + return self._get_editor_context().get_gui_manager() def _append_gui_element(self, element_wrapper): """统一维护 gui_elements 列表。""" gui_manager = self._get_gui_manager() if not gui_manager: return False - if not hasattr(gui_manager, "gui_elements") or gui_manager.gui_elements is None: - gui_manager.gui_elements = [] - gui_manager.gui_elements.append(element_wrapper) + self._get_editor_context().append_gui_element(element_wrapper) return True def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=0.1):