refactor: 收敛非VR编辑上下文并更新优化索引

This commit is contained in:
ayuan9957 2026-02-28 17:06:15 +08:00
parent 381a478abf
commit c2e528c55f
10 changed files with 666 additions and 61 deletions

352
PROJECT_MODULE_INDEX.md Normal file
View File

@ -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

View File

@ -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 B1 天)
- 重构 `scene_manager_io_mixin.loadScene`
- `preflight`
- `clear_old_scene`
- `load_bam`
- `rebuild_scene_tree`
- `post_load_sync`
### Task C1 天)
- 统一异常日志工具(轻量封装)
- 首批替换 `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 个高耦合文件。

50
core/editor_context.py Normal file
View File

@ -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

View File

@ -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)

View File

@ -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):
"""清空场景树选中项。"""

View File

@ -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)):
"""从高度图创建地形"""

View File

@ -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格式"""

View File

@ -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):
"""

View File

@ -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:
# 设置名称和变换

View File

@ -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):