diff --git a/ui/panels/animation_tools.py b/ui/panels/animation_tools.py index 6429748d..70dc174d 100644 --- a/ui/panels/animation_tools.py +++ b/ui/panels/animation_tools.py @@ -1003,10 +1003,24 @@ class AnimationTools: try: if node and (not node.isEmpty()): # If the user explicitly selected the animated skeleton node, - # respect that directly. More aggressive driver remapping made - # playback regress to "cannot play at all". - if self._node_has_animation_nodes(node): + # respect that directly only when it also carries visible geom. + # Skeleton-only nodes like Armature still need a deeper renderable + # driver node, otherwise playback can report success but show no motion. + if self._node_has_animation_nodes(node) and self._node_has_geom(node): return node + driver = self._find_animation_driver_node(node) + if driver and (not driver.isEmpty()): + for tag_name in ("model_path", "original_path", "saved_model_path", "file"): + try: + if ( + (not driver.hasTag(tag_name) or not driver.getTag(tag_name)) + and node.hasTag(tag_name) + and node.getTag(tag_name) + ): + driver.setTag(tag_name, node.getTag(tag_name)) + except Exception: + continue + return driver except Exception: pass diff --git a/ui/panels/app_actions.py b/ui/panels/app_actions.py index 5fd83e37..12d1cc51 100644 --- a/ui/panels/app_actions.py +++ b/ui/panels/app_actions.py @@ -35,7 +35,8 @@ class AppActions: while current and not current.isEmpty(): if current.getName() == "render": break - if current.hasTag("tree_item_type") or current.hasTag("is_model_root") or current.hasTag("is_scene_element"): + if current.hasTag("tree_item_type") or current.hasTag("is_model_root") or current.hasTag( + "is_scene_element"): return current current = current.getParent() @@ -60,8 +61,8 @@ class AppActions: return False try: has_animation = ( - model.findAllMatches("**/+Character").getNumPaths() > 0 - or model.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0 + model.findAllMatches("**/+Character").getNumPaths() > 0 + or model.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0 ) finally: try: @@ -72,14 +73,13 @@ class AppActions: except Exception: return False - def _toggle_hot_reload(self): """切换热重载状态""" if hasattr(self, 'script_manager') and self.script_manager: try: current_state = getattr(self.script_manager, 'hot_reload_enabled', False) self._set_hot_reload_enabled(not current_state) - + new_state = "启用" if not current_state else "禁用" self.add_success_message(f"热重载已{new_state}") print(f"[脚本系统] 热重载已{new_state}") @@ -93,28 +93,27 @@ class AppActions: raise RuntimeError("脚本管理器未初始化") self.script_manager.set_hot_reload_enabled(enabled) self.hotReloadEnabled = self.script_manager.hot_reload_enabled - def _create_new_script(self): """创建新脚本""" if not hasattr(self, '_new_script_name') or not self._new_script_name.strip(): self.add_error_message("请输入脚本名称") return - + script_name = self._new_script_name.strip() if not script_name.endswith('.py'): script_name += '.py' - + # 确定模板类型 template_map = { 0: "basic", - 1: "movement", + 1: "movement", 2: "rotation", 3: "scale", 4: "animation" } template_type = template_map.get(getattr(self, '_selected_template', 0), "basic") - + try: if hasattr(self, 'script_manager') and self.script_manager: result = self.script_manager.create_script_file(script_name, template_type) @@ -130,7 +129,6 @@ class AppActions: except Exception as e: self.add_error_message(f"创建脚本失败: {str(e)}") print(f"[脚本系统] 创建脚本失败: {e}") - def _refresh_scripts_list(self): """刷新脚本列表""" @@ -145,7 +143,6 @@ class AppActions: except Exception as e: self.add_error_message(f"刷新脚本列表失败: {str(e)}") print(f"[脚本系统] 刷新脚本列表失败: {e}") - def _reload_all_scripts(self): """重载所有脚本""" @@ -154,11 +151,11 @@ class AppActions: # 获取所有可用脚本并逐个重载 available_scripts = self.script_manager.get_available_scripts() success_count = 0 - + for script_name in available_scripts: if self.script_manager.reload_script(script_name): success_count += 1 - + self.add_success_message(f"重载完成: {success_count}/{len(available_scripts)} 个脚本成功") print(f"[脚本系统] 重载脚本: {success_count}/{len(available_scripts)} 成功") else: @@ -166,13 +163,11 @@ class AppActions: except Exception as e: self.add_error_message(f"重载脚本失败: {str(e)}") print(f"[脚本系统] 重载脚本失败: {e}") - def _on_script_selected(self, script_name): """处理脚本选择事件""" print(f"[脚本系统] 选择脚本: {script_name}") self.add_info_message(f"已选择脚本: {script_name}") - def _edit_script(self, script_name): """编辑脚本""" @@ -182,11 +177,11 @@ class AppActions: script_info = self.script_manager.get_script_info(script_name) if script_info and script_info.get("file"): script_path = script_info["file"] - + # 打开系统默认编辑器 import subprocess import platform - + system = platform.system() try: if system == "Windows": @@ -195,7 +190,7 @@ class AppActions: subprocess.run(['open', script_path]) else: # Linux subprocess.run(['xdg-open', script_path]) - + self.add_success_message(f"已打开脚本编辑器: {script_name}") print(f"[脚本系统] 编辑脚本: {script_path}") except Exception as e: @@ -207,16 +202,15 @@ class AppActions: except Exception as e: self.add_error_message(f"编辑脚本失败: {str(e)}") print(f"[脚本系统] 编辑脚本失败: {e}") - def _mount_script_to_selected(self, script_name): """挂载脚本到选中对象""" selected_node = self._get_selection_node() - + if not selected_node or selected_node.isEmpty(): self.add_error_message("请先选择一个对象") return - + try: if hasattr(self, 'script_manager') and self.script_manager: script_component = self.script_manager.add_script_to_object(selected_node, script_name) @@ -230,16 +224,15 @@ class AppActions: except Exception as e: self.add_error_message(f"挂载脚本失败: {str(e)}") print(f"[脚本系统] 挂载脚本失败: {e}") - def _unmount_script_from_selected(self, script_name): """从选中对象卸载脚本""" selected_node = self._get_selection_node() - + if not selected_node or selected_node.isEmpty(): self.add_error_message("请先选择一个对象") return - + try: if hasattr(self, 'script_manager') and self.script_manager: result = self.script_manager.remove_script_from_object(selected_node, script_name) @@ -253,21 +246,18 @@ class AppActions: except Exception as e: self.add_error_message(f"卸载脚本失败: {str(e)}") print(f"[脚本系统] 卸载脚本失败: {e}") - + # ==================== 菜单处理函数 ==================== - def _on_new_project(self): """处理新建项目菜单项""" self.add_info_message("打开新建项目对话框") self.show_new_project_dialog = True - def _on_open_project(self): """处理打开项目菜单项""" self.add_info_message("打开项目对话框") self.show_open_project_dialog = True - def _on_save_project(self): """处理保存项目菜单项""" @@ -278,7 +268,7 @@ class AppActions: self.add_warning_message("没有当前项目路径,请先创建或打开项目") self.show_save_as_dialog = True return - + # 直接调用保存逻辑,避免Qt依赖 if self._save_project_impl(): self.add_success_message("项目保存成功") @@ -288,7 +278,6 @@ class AppActions: self.add_error_message(f"项目保存失败: {e}") else: self.add_error_message("项目管理器未初始化") - def _on_save_as_project(self): """处理另存为项目菜单项""" @@ -311,7 +300,8 @@ class AppActions: _, active_profile = project_manager._get_active_build_profile() profile_output_dir = str((active_profile or {}).get("output_dir", "") or "").strip() if layout and profile_output_dir: - self.build_output_path = os.path.normpath(os.path.join(current_project_path, profile_output_dir.replace("/", os.sep))) + self.build_output_path = os.path.normpath( + os.path.join(current_project_path, profile_output_dir.replace("/", os.sep))) else: self.build_output_path = layout.builds_root if layout else os.path.dirname(current_project_path) else: @@ -362,12 +352,10 @@ class AppActions: self.add_error_message(f"项目打包失败: {e}") return False - def _show_about_dialog(self): """显示关于对话框。""" self.show_about_dialog = True - def _get_documentation_target(self): """返回优先级最高的本地帮助文档。""" candidate_names = [ @@ -383,7 +371,6 @@ class AppActions: return candidate_path return None - def _open_documentation(self): """打开本地项目文档。""" doc_path = self._get_documentation_target() @@ -401,57 +388,46 @@ class AppActions: except Exception as e: self.add_error_message(f"打开文档失败: {e}") return False - def _on_exit(self): """处理退出菜单项""" self.add_info_message("退出应用程序") self.userExit() - + # ==================== 键盘事件处理函数 ==================== - def _on_ctrl_pressed(self): """Ctrl键按下""" self.ctrl_pressed = True - def _on_ctrl_released(self): """Ctrl键释放""" self.ctrl_pressed = False - def _on_alt_pressed(self): """Alt键按下""" self.alt_pressed = True - def _on_alt_released(self): """Alt键释放""" self.alt_pressed = False - def _on_n_pressed(self): """N键按下 - 检查Ctrl+N组合键""" if self.ctrl_pressed: self._on_new_project() - def _on_o_pressed(self): """O键按下 - 检查Ctrl+O组合键""" if self.ctrl_pressed: self._on_open_project() - - - def _on_f4_pressed(self): """F4键按下 - 检查Alt+F4组合键""" if self.alt_pressed: self._on_exit() - + # 移除了单独的按键处理方法,现在直接使用组合键事件 - def _on_delete_pressed(self): """Delete键按下 - 删除选中节点""" @@ -464,24 +440,23 @@ class AppActions: if self.lui_manager.selected_index >= 0: self.lui_manager.selected_index = -1 print("✓ 已取消LUI组件选中") - + # 2. 取消 3D 场景选择 if hasattr(self, 'selection') and self.selection: if self._get_selection_node() or self._get_ssbo_selection_summary(): self.selection.clearSelection() print("✓ 已取消场景节点选中") - def _on_wheel_up(self): """滚轮向上滚动 - 相机前进""" try: if not self.camera_control_enabled: return - + # 检查鼠标是否在ImGui窗口上 if self._is_mouse_over_imgui(): return - + # 沿相机前向向量移动 forward = self.camera.getMat().getRow3(1) distance = 20.0 * globalClock.getDt() @@ -490,7 +465,6 @@ class AppActions: self.camera.setPos(newPos) except Exception as e: print(f"滚轮前进失败: {e}") - def _on_wheel_down(self): """滚轮向下滚动 - 相机后退""" @@ -498,7 +472,7 @@ class AppActions: # 检查鼠标是否在ImGui窗口上 if self._is_mouse_over_imgui(): return - + # 沿相机前向向量移动 forward = self.camera.getMat().getRow3(1) distance = -20.0 * globalClock.getDt() @@ -507,7 +481,6 @@ class AppActions: self.camera.setPos(newPos) except Exception as e: print(f"滚轮后退失败: {e}") - def _is_mouse_over_imgui(self): """检测鼠标是否在ImGui窗口上""" @@ -532,7 +505,6 @@ class AppActions: except Exception as e: print(f"ImGui界面检测失败: {e}") return False - def processImGuiMouseClick(self, x, y): """处理ImGui鼠标点击事件,返回是否消费了该事件""" @@ -554,7 +526,7 @@ class AppActions: pass return False - + except Exception as e: print(f"ImGui鼠标点击处理失败: {e}") return False @@ -585,54 +557,49 @@ class AppActions: def _is_point_in_known_imgui_rects(self, point): for rect_name in ( - "_resource_manager_window_rect", - "_scene_tree_window_rect", - "_property_panel_window_rect", - "_script_panel_window_rect", - "_console_window_rect", - "_toolbar_window_rect", + "_resource_manager_window_rect", + "_scene_tree_window_rect", + "_property_panel_window_rect", + "_script_panel_window_rect", + "_console_window_rect", + "_toolbar_window_rect", ): if self._point_in_rect(point, getattr(self, rect_name, None)): return True return False - + # ==================== 消息系统 ==================== - def add_message(self, text, color=(1.0, 1.0, 1.0, 1.0)): """添加消息到消息列表,同时输出到终端""" import datetime timestamp = datetime.datetime.now().strftime("%H:%M:%S") - + # 输出到终端 print(f"[{timestamp}] {text}") - + # 添加到GUI消息列表 self.messages.append({ 'text': text, 'color': color, 'timestamp': timestamp }) - + # 限制消息数量 if len(self.messages) > self.max_messages: self.messages = self.messages[-self.max_messages:] - def add_success_message(self, text): """添加成功消息""" self.add_message(f"✓ {text}", (0.176, 1.0, 0.769, 1.0)) - def add_error_message(self, text): """添加错误消息""" self.add_message(f"✗ {text}", (1.0, 0.3, 0.3, 1.0)) - def add_warning_message(self, text): """添加警告消息""" self.add_message(f"⚠ {text}", (0.953, 0.616, 0.471, 1.0)) - def add_info_message(self, text): """添加信息消息""" @@ -693,7 +660,8 @@ class AppActions: else: selected_node = None try: - selected_node = selection.getSelectedNode() if hasattr(selection, "getSelectedNode") else getattr(selection, "selectedNode", None) + selected_node = selection.getSelectedNode() if hasattr(selection, "getSelectedNode") else getattr( + selection, "selectedNode", None) except Exception: selected_node = None if not self._is_history_node_valid(selected_node, require_attached=True): @@ -746,7 +714,6 @@ class AppActions: self.add_warning_message("没有可撤销的操作") except Exception as e: self.add_error_message(f"撤销操作失败: {e}") - def _on_redo(self): """处理重做操作""" @@ -771,7 +738,6 @@ class AppActions: self.add_warning_message("没有可重做的操作") except Exception as e: self.add_error_message(f"重做操作失败: {e}") - def _on_copy(self): """处理复制操作""" @@ -779,18 +745,18 @@ class AppActions: if not hasattr(self, 'selection') or not self.selection: self.add_error_message("选择系统未初始化") return - + # 获取当前选中的节点 selected_node = self._resolve_cut_copy_node(self._get_selection_source_node()) if not selected_node: self.add_warning_message("没有选中的节点") return - + # 检查节点有效性(不能复制根节点) if selected_node.getName() == "render": self.add_warning_message("不能复制根节点") return - + # 序列化节点 if hasattr(self, 'scene_manager') and self.scene_manager: node_data = self.scene_manager.serializeNodeForCopy(selected_node) @@ -806,7 +772,6 @@ class AppActions: self.add_error_message("场景管理器未初始化") except Exception as e: self.add_error_message(f"复制操作失败: {e}") - def _on_cut(self): """处理剪切操作""" @@ -814,19 +779,19 @@ class AppActions: if not hasattr(self, 'selection') or not self.selection: self.add_error_message("选择系统未初始化") return - + # 获取当前选中的节点 selected_node = self._resolve_cut_copy_node(self._get_selection_source_node()) if not selected_node: self.add_warning_message("没有选中的节点") return - + # 检查节点有效性(不能剪切根节点和系统节点) node_name = selected_node.getName() if node_name == "render": self.add_warning_message("不能剪切根节点") return - + # 序列化节点 if hasattr(self, 'scene_manager') and self.scene_manager: node_data = self.scene_manager.serializeNodeForCopy(selected_node) @@ -835,7 +800,7 @@ class AppActions: # Cut preserves the source node references for cloning in paste self.clipboard_source_nodes = [selected_node] self.clipboard_mode = "cut" - + # 删除原节点 if self._delete_node(selected_node): self.selection.clearSelection() @@ -848,7 +813,6 @@ class AppActions: self.add_error_message("场景管理器未初始化") except Exception as e: self.add_error_message(f"剪切操作失败: {e}") - def _on_paste(self): """处理粘贴操作""" @@ -856,11 +820,11 @@ class AppActions: if not self.clipboard: self.add_warning_message("剪切板为空") return - + if not hasattr(self, 'scene_manager') or not self.scene_manager: self.add_error_message("场景管理器未初始化") return - + # 确定粘贴目标父节点 parent_node = None if hasattr(self, 'selection') and self.selection: @@ -873,11 +837,11 @@ class AppActions: else: p = selected_node.getParent() parent_node = p if p and not p.isEmpty() else self.render - + # 如果没有选中节点,使用渲染根节点 if not parent_node: parent_node = self.render - + # 反序列化并添加节点 created_any = False last_created_node = None @@ -897,10 +861,12 @@ class AppActions: else: created = source_node.copyTo(parent) if hasattr(self.scene_manager, "_generateUniqueName"): - unique_name = self.scene_manager._generateUniqueName(source_node.getName(), parent) + unique_name = self.scene_manager._generateUniqueName(source_node.getName(), + parent) created.setName(unique_name) # Preserve model source tags so later cut/paste can rebuild real model. - for _tag in ("model_path", "saved_model_path", "original_path", "file", "element_type"): + for _tag in ("model_path", "saved_model_path", "original_path", "file", + "element_type"): if source_node.hasTag(_tag): created.setTag(_tag, source_node.getTag(_tag)) # Offset slightly so the new node can be seen immediately. @@ -929,7 +895,8 @@ class AppActions: if hasattr(self.command_manager, "pop_last_command"): last_cmd = self.command_manager.pop_last_command() - if isinstance(last_cmd, DeleteNodeCommand) and getattr(last_cmd, "node", None) == source_node: + if isinstance(last_cmd, DeleteNodeCommand) and getattr(last_cmd, "node", + None) == source_node: attach_cmd.execute() self.command_manager.record_command(CompositeCommand([last_cmd, attach_cmd])) new_node = source_node @@ -991,7 +958,6 @@ class AppActions: self.clipboard_mode = "" except Exception as e: self.add_error_message(f"粘贴操作失败: {e}") - def _on_delete(self): """处理删除操作""" @@ -999,19 +965,19 @@ class AppActions: if not hasattr(self, 'selection') or not self.selection: self.add_error_message("选择系统未初始化") return - + # 获取当前选中的节点 selected_node = self._get_selection_source_node() if not selected_node: self.add_warning_message("没有选中的节点") return - + # 检查节点有效性(不能删除根节点) node_name = selected_node.getName() if node_name == "render": self.add_warning_message("不能删除根节点") return - + # 删除节点 if hasattr(self, 'scene_manager') and self.scene_manager: self._delete_node(selected_node) @@ -1021,14 +987,13 @@ class AppActions: self.add_error_message("场景管理器未初始化") except Exception as e: self.add_error_message(f"删除操作失败: {e}") - def _delete_node(self, node): """删除节点的通用方法 - 使用命令系统""" try: if not node or node.isEmpty(): return False - + node_name = node.getName() or "未命名节点" parent = node.getParent() ssbo_editor = getattr(self, "ssbo_editor", None) @@ -1070,7 +1035,7 @@ class AppActions: pass print(f"[SSBO] 已从源模型树移除并重建运行时: {node_name}") return True - + # 创建删除命令 if hasattr(self, 'command_manager') and self.command_manager: from core.Command_System import DeleteNodeCommand @@ -1088,26 +1053,25 @@ class AppActions: ssbo_editor.on_model_deleted(node) except Exception as e: print(f"[SSBO] 删除模型后清理失败: {e}") - + print(f"[删除] 成功删除节点: {node_name}") return True - + except Exception as e: print(f"[删除] 删除节点失败: {e}") return False - def _perform_node_cleanup(self, node): """执行节点清理逻辑""" try: node_name = node.getName() or "未命名节点" - + # 从场景管理器的模型列表中移除(如果是模型) if hasattr(self, 'scene_manager') and self.scene_manager: if node in self.scene_manager.models: self.scene_manager.models.remove(node) print(f"[场景管理器] 从模型列表移除: {node_name}") - + # 停止所有与该节点相关的脚本 if hasattr(self, 'script_manager') and self.script_manager: try: @@ -1117,7 +1081,7 @@ class AppActions: print(f"[脚本系统] 移除节点 {node_name} 的所有脚本") except Exception as e: print(f"[脚本系统] 移除脚本失败: {e}") - + # 清理碰撞体 if hasattr(self, 'collision_manager') and self.collision_manager: try: @@ -1125,7 +1089,7 @@ class AppActions: print(f"[碰撞系统] 移除节点 {node_name} 的碰撞体") except Exception as e: print(f"[碰撞系统] 移除碰撞体失败: {e}") - + # 清理Actor缓存(如果有动画) if hasattr(self, '_actor_cache') and node in self._actor_cache: actor = self._actor_cache[node] @@ -1141,19 +1105,18 @@ class AppActions: print(f"[动画系统] 清理Actor缓存失败: {e}") finally: del self._actor_cache[node] - + except Exception as e: print(f"[清理] 节点清理失败: {e}") - + # ==================== 对话框绘制函数 ==================== - def _create_new_project(self, name, path): """创建新项目的实际实现""" if not hasattr(self, 'project_manager') or not self.project_manager: print("✗ 项目管理器未初始化") return - + try: if self._create_new_project_impl(name, path): print(f"✓ 项目创建成功: {name}") @@ -1161,14 +1124,13 @@ class AppActions: print(f"✗ 项目创建失败: {name}") except Exception as e: print(f"✗ 项目创建失败: {e}") - def _open_project_path(self, path): """打开项目的实际实现""" if not hasattr(self, 'project_manager') or not self.project_manager: print("✗ 项目管理器未初始化") return - + try: print(f"打开项目: {path}") if self._open_project_impl(path): @@ -1177,16 +1139,14 @@ class AppActions: print(f"✗ 项目打开失败: {path}") except Exception as e: print(f"✗ 项目打开失败: {e}") - + # ==================== 项目管理具体实现 ==================== - def _save_project_impl(self): """保存项目的具体实现(不依赖Qt)""" if not hasattr(self, 'project_manager') or not self.project_manager: return False return self.project_manager.saveProject() - def _open_project_impl(self, project_path): """打开项目的具体实现(不依赖Qt)""" @@ -1202,16 +1162,15 @@ class AppActions: project_name = os.path.basename(project_path) self._update_window_title(project_name) - + print(f"✓ 项目打开成功: {project_path}") self.add_success_message(f"项目打开成功: {project_name}") return True - + except Exception as e: print(f"✗ 打开项目时发生错误: {e}") self.add_error_message(f"打开项目时发生错误: {e}") return False - def _create_new_project_impl(self, name, path): """创建新项目的具体实现(不依赖Qt)""" @@ -1225,7 +1184,6 @@ class AppActions: print(f"创建项目失败: {e}") return False - def _save_project_as_impl(self, name, path): """将当前项目保存到新的项目目录。""" if not hasattr(self, "project_manager") or not self.project_manager: @@ -1291,7 +1249,8 @@ class AppActions: if previous_project_path: self.project_manager._sync_project_script_manager(previous_project_path, reload_scripts=True) self.project_manager._sync_resource_manager_root(previous_project_path) - self._update_window_title(os.path.basename(previous_project_path) if previous_project_path else "未命名项目") + self._update_window_title( + os.path.basename(previous_project_path) if previous_project_path else "未命名项目") self.add_error_message("项目另存为失败") return False @@ -1318,7 +1277,6 @@ class AppActions: source_file = os.path.join(root, file_name) target_file = os.path.join(destination_root, file_name) shutil.copy2(source_file, target_file) - def _update_window_title(self, project_name): """更新窗口标题""" @@ -1329,9 +1287,8 @@ class AppActions: print(f"窗口标题已更新: MetaCore - {project_name}") except Exception as e: print(f"更新窗口标题失败: {e}") - + # ==================== 路径浏览器辅助方法 ==================== - def _refresh_ssbo_runtime_import_bindings(self, file_path=None, scene_package_import=False): ssbo_editor = getattr(self, 'ssbo_editor', None) @@ -1545,8 +1502,6 @@ class AppActions: except Exception: self._scene_tree_epoch = 1 animated_model = bool(file_path and self._model_file_has_animation(file_path)) - lower_path = str(file_path).lower() if file_path else "" - is_gltf_family = lower_path.endswith((".glb", ".gltf")) if animated_model: prefer_scene_manager = True ssbo_editor = getattr(self, 'ssbo_editor', None) @@ -1556,11 +1511,6 @@ class AppActions: except Exception: pass - if is_gltf_family: - fallback_model = self._import_model_via_gltf_fallback(file_path) - if fallback_model: - return fallback_model - if self.use_ssbo_mouse_picking and getattr(self, 'ssbo_editor', None): if animated_model: print(f"[AnimationImport] 检测到动画模型,跳过SSBO导入: {file_path}") @@ -1627,9 +1577,9 @@ class AppActions: except Exception: camera_count = 0 for camera_name in ( - "gizmo_overlay_cam", - "pick_camera", - "selection_outline_mask_camera", + "gizmo_overlay_cam", + "pick_camera", + "selection_outline_mask_camera", ): try: special_camera_counts[camera_name] = render.find_all_matches( @@ -1648,11 +1598,11 @@ class AppActions: interesting_tasks = [ name for name in task_names if ( - "gizmo" in str(name).lower() - or "outline" in str(name).lower() - or "pick" in str(name).lower() - or "lui" in str(name).lower() - or "canvas" in str(name).lower() + "gizmo" in str(name).lower() + or "outline" in str(name).lower() + or "pick" in str(name).lower() + or "lui" in str(name).lower() + or "canvas" in str(name).lower() ) ] @@ -1667,12 +1617,12 @@ class AppActions: pass def _import_model_with_menu_logic( - self, - file_path, - select_model=True, - set_origin=True, - show_info_message=True, - show_success_message=True, + self, + file_path, + select_model=True, + set_origin=True, + show_info_message=True, + show_success_message=True, ): """统一的单文件导入入口,保持与菜单导入一致的处理流程。""" try: @@ -1733,33 +1683,17 @@ class AppActions: if hasattr(self.scene_manager, 'models'): if getattr(self, "use_ssbo_mouse_picking", False): - try: - existing_models = [] - for existing in getattr(self.scene_manager, "models", []): - try: - if existing and not existing.isEmpty(): - existing_models.append(existing) - except Exception: - continue - if model_node and all(existing != model_node for existing in existing_models): - existing_models.append(model_node) - self.scene_manager.models = existing_models - except Exception: - self.scene_manager.models = [model_node] if model_node else [] + self.scene_manager.models = [model_node] elif model_node not in self.scene_manager.models: self.scene_manager.models.append(model_node) if select_model: if ( - getattr(self, "use_ssbo_mouse_picking", False) - and getattr(self, "ssbo_editor", None) - and getattr(self.ssbo_editor, "last_import_tree_key", None) + getattr(self, "use_ssbo_mouse_picking", False) + and getattr(self, "ssbo_editor", None) + and getattr(self.ssbo_editor, "last_import_tree_key", None) ): - self.ssbo_editor.clear_selection(sync_world_selection=False) - if hasattr(self.ssbo_editor, "_sync_editor_selection_reference"): - self.ssbo_editor._sync_editor_selection_reference(None) - if show_info_message: - self.add_info_message("模型导入完成,已保持 SSBO 静态模式以避免导入后掉帧") + self.ssbo_editor.select_node(self.ssbo_editor.last_import_tree_key) elif hasattr(self, 'selection') and self.selection: self.selection.updateSelection(model_node) @@ -1784,7 +1718,6 @@ class AppActions: """处理导入模型菜单项""" self.add_info_message("打开系统文件选择器") self.show_import_dialog = True - def _import_model(self): """导入模型的具体实现"""