diff --git a/blenderpython/suw_core/deletion_manager.py b/blenderpython/suw_core/deletion_manager.py index 18d58b9..87ff1d0 100644 --- a/blenderpython/suw_core/deletion_manager.py +++ b/blenderpython/suw_core/deletion_manager.py @@ -13,8 +13,52 @@ from .data_manager import data_manager, get_data_manager import time import logging import threading +import sys from typing import Dict, Any, List, Optional +# 配置日志系统 + + +def setup_logging(): + """配置日志系统,确保在Blender控制台中能看到输出""" + try: + # 获取根日志记录器 + root_logger = logging.getLogger() + + # 如果已经有处理器,不重复配置 + if root_logger.handlers: + return + + # 设置日志级别 + root_logger.setLevel(logging.INFO) + + # 创建控制台处理器 + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setLevel(logging.INFO) + + # 创建格式化器 + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + console_handler.setFormatter(formatter) + + # 添加处理器到根日志记录器 + root_logger.addHandler(console_handler) + + # 特别配置SUW相关的日志记录器 + suw_logger = logging.getLogger('suw_core') + suw_logger.setLevel(logging.INFO) + + print("✅ 日志系统配置完成") + + except Exception as e: + print(f"❌ 日志系统配置失败: {e}") + + +# 在模块加载时自动配置日志 +setup_logging() + # 设置日志 logger = logging.getLogger(__name__) @@ -94,32 +138,39 @@ class DeletionManager: # 删除zones(如果是uid或zid类型) if typ in ["uid", "zid"]: # 【修复】对于zid类型,使用完整的删除逻辑来删除子对象 - if typ == "zid": - logger.info(f"🗑️ 删除区域: uid={uid}, zid={oid}") - result = self._del_zone_complete(uid, oid) - deleted_count += result if result is not None else 0 - logger.info(f"✅ 删除区域完成: {result} 个对象") - else: - # 对于uid类型,删除所有区域 - zones = self.data_manager.get_zones(data) - zones_deleted = self._del_entities_by_type( - zones, typ, oid, uid) - deleted_count += zones_deleted - logger.info(f"✅ 删除zones: {zones_deleted} 个") - # 删除parts - parts = self.data_manager.get_parts(data) - parts_deleted = self._del_entities_by_type( - parts, typ, oid, uid) - deleted_count += parts_deleted - logger.info(f"✅ 删除parts: {parts_deleted} 个") + logger.info(f"🗑️ 删除区域: uid={uid}, zid={oid}") + result = self._del_zone_complete(uid, oid) + deleted_count += result if result is not None else 0 + logger.info(f"✅ 删除区域完成: {result} 个对象") - # 删除hardwares - hardwares = self.data_manager.get_hardwares(data) - hardwares_deleted = self._del_entities_by_type( - hardwares, typ, oid, uid) - deleted_count += hardwares_deleted - logger.info(f"✅ 删除hardwares: {hardwares_deleted} 个") + # 【修复】删除zone时,同时删除该zone相关的所有parts + logger.info( + f"🗑️ 删除区域相关的所有parts: uid={uid}, zid={oid}") + parts = self.data_manager.get_parts(data) + if parts: + # 查找并删除与该zone相关的所有parts + parts_to_delete = [] + for cp, part_obj in parts.items(): + try: + if part_obj and hasattr(part_obj, 'get'): + part_zid = part_obj.get( + "sw_zid") + if part_zid == oid: + parts_to_delete.append(cp) + except Exception as e: + logger.debug(f"检查part属性时发生错误: {e}") + continue + + # 删除找到的parts + for cp in parts_to_delete: + logger.info( + f"🗑️ 删除zone相关的part: uid={uid}, zid={oid}, cp={cp}") + result = self._del_part_complete( + uid, cp) + deleted_count += result if result is not None else 0 + logger.info( + f"✅ 删除part完成: cp={cp}, 删除数量={result}") # 【修复】与Ruby版本保持一致:清理labels(如果是uid或zid类型) if typ in ["uid", "zid"]: @@ -196,11 +247,11 @@ class DeletionManager: zid = data.get("zid") elements = data.get("children", []) - logger.info(f" Zone_{zid} 数据: uid={uid}, 元素数量={len(elements)}") + logger.info(f" Zone_{uid} 数据: uid={uid}, 元素数量={len(elements)}") # 【修复】不再创建线框立方体,直接创建六个面组成Zone # 创建一个空的组对象作为Zone容器 - group = bpy.data.objects.new(f"Zone_{zid}", None) + group = bpy.data.objects.new(f"Zone_{uid}", None) group.empty_display_type = 'PLAIN_AXES' # 改为PLAIN_AXES,不显示线框 bpy.context.scene.collection.objects.link(group) @@ -211,7 +262,7 @@ class DeletionManager: group["sw_typ"] = "zone" # 改为"zone"而不是"zid" # 【调试】打印设置的属性 - logger.info(f"🔧 Zone_{zid} 属性设置:") + logger.info(f"🔧 Zone_{uid} 属性设置:") logger.info(f" sw_uid: {group.get('sw_uid')}") logger.info(f" sw_zid: {group.get('sw_zid')}") logger.info(f" sw_zip: {group.get('sw_zip')}") @@ -261,7 +312,7 @@ class DeletionManager: group["sw_created_faces"] = len(created_faces) group["sw_failed_faces"] = len(failed_faces) - logger.info(f"📊 Zone_{zid} 创建统计:") + logger.info(f"📊 Zone_{uid} 创建统计:") logger.info(f" 成功创建: {len(created_faces)} 个面") logger.info(f" 创建失败: {len(failed_faces)} 个面") if failed_faces: @@ -552,8 +603,9 @@ class DeletionManager: obj.get("sw_typ") == "zone"): zone_objects.append(obj) logger.info(f"🎯 找到Zone对象(属性匹配): {obj.name}") + # 【修复】同时检查命名格式匹配 - elif obj.name == f"Zone_{zid}": + elif obj.name == f"Zone_{uid}": zone_objects.append(obj) logger.info(f"🎯 找到Zone对象(命名匹配): {obj.name}") except Exception as e: @@ -572,361 +624,66 @@ class DeletionManager: logger.info(f"ℹ️ 未找到Zone对象,删除操作完成: uid={uid}, zid={zid}") return 0 - # 删除找到的Zone对象 + def collect_all_children(obj, collected=None): + if collected is None: + collected = [] + children = list(obj.children) + for child in children: + collect_all_children(child, collected) + collected.append(obj) + return collected + for zone_obj in zone_objects: try: - # 检查对象是否仍然有效 - if not self._is_object_valid(zone_obj): - logger.debug(f"Zone对象已无效,跳过: {zone_obj.name}") - continue + zone_name = zone_obj.name if hasattr( + zone_obj, 'name') else 'unknown' + logger.info(f"🔄 删除失败,回退到收集并批量删除所有层级对象: {zone_name}") + # 收集所有要删除的对象(包括自身和所有子孙) + all_to_delete = collect_all_children(zone_obj) - # 递归删除所有子对象 - children_to_delete = list(zone_obj.children) - logger.info(f"📦 找到 {len(children_to_delete)} 个子对象需要删除") - - for child in children_to_delete: + # 先解除所有对象的父子关系和依赖 + for obj in all_to_delete: try: - if self._is_object_valid(child): - logger.info(f"🗑️ 删除Zone子对象: {child.name}") - if self._delete_object_safe(child): - deleted_count += 1 - else: - logger.debug(f"子对象已无效,跳过: {child.name}") - except Exception as e: - logger.debug(f"删除子对象时发生错误: {e}") + # 解除父对象 + obj.parent = None + # 清空约束 + if hasattr(obj, "constraints"): + for c in list(obj.constraints): + obj.constraints.remove(c) + # 清空驱动 + if hasattr(obj, "animation_data") and obj.animation_data: + obj.animation_data_clear() + # 清空修饰器 + if hasattr(obj, "modifiers"): + for m in list(obj.modifiers): + obj.modifiers.remove(m) + # 清空材质 + if hasattr(obj.data, "materials"): + obj.data.materials.clear() + except Exception as pre_del_error: + logger.debug(f"删除前解除依赖失败: {pre_del_error}") - # 删除Zone对象本身 - logger.info(f"🗑️ 删除Zone对象: {zone_obj.name}") - if self._delete_object_safe(zone_obj): - deleted_count += 1 - - except Exception as e: - logger.error( - f"删除Zone对象失败: {zone_obj.name if hasattr(zone_obj, 'name') else 'unknown'}, 错误: {e}") - - # 【修复4】使用data_manager的删除方法,确保删除所有相关对象 - if self.data_manager: - try: - zones = self.data_manager.get_zones({'uid': uid}) - if zones and zid in zones: - # 删除数据结构中的引用 - del zones[zid] - logger.info(f"✅ 从zones数据结构中移除: uid={uid}, zid={zid}") - - # 使用data_manager的删除方法删除所有相关对象 - blender_deleted = self.data_manager._delete_blender_objects_by_type( - "zid", zid, uid) - deleted_count += blender_deleted - logger.info(f"✅ 通过data_manager删除 {blender_deleted} 个对象") - except Exception as e: - logger.error(f"使用data_manager删除时发生错误: {e}") - - # 更新统计 - self.deletion_stats["zones_deleted"] += 1 - self.deletion_stats["objects_deleted"] += deleted_count - - logger.info( - f"✅ 区域删除完成: uid={uid}, zid={zid}, 共删除 {deleted_count} 个对象") - - return deleted_count - - except Exception as e: - logger.error(f"删除区域失败 uid={uid}, zid={zid}: {e}") - self.deletion_stats["deletion_errors"] += 1 - import traceback - logger.error(traceback.format_exc()) - return 0 - - def _del_part_complete(self, uid: str, cp: int): - """完整删除部件 - 与c04完全对称的删除逻辑""" - try: - logger.info(f"🗑️ 开始删除部件: uid={uid}, cp={cp}") - - # 【数据结构优先策略】先从数据结构获取信息 - parts = self.data_manager.get_parts({'uid': uid}) - part_exists_in_data = cp in parts - - if part_exists_in_data: - part = parts[cp] - logger.info(f"📊 数据结构中找到部件: {part.name if part else 'None'}") - - # 获取创建记录(如果存在) - created_objects = part.get( - "sw_created_objects", {}) if part else {} - - # 1. 清理材质引用(对应c04的材质设置) - for material_name in created_objects.get("materials", []): - material = bpy.data.materials.get(material_name) - if material: - logger.info(f"🗑️ 清理材质引用: {material_name}") - # 不删除材质本身,只清理引用 - - # 2. 删除板材(对应c04的板材创建) - for board_name in created_objects.get("boards", []): - board = bpy.data.objects.get(board_name) - if board: - logger.info(f"🗑️ 删除记录的板材: {board_name}") - self._delete_object_safe(board) - else: - logger.info(f"📊 数据结构中未找到部件: uid={uid}, cp={cp}") - part = None - - # 3. 查找Blender中的Part对象 - part_name = f"Part_{cp}" - part_obj = bpy.data.objects.get(part_name) - - deleted_objects_count = 0 - - if part_obj: - logger.info(f"🎯 在Blender中找到部件对象: {part_name}") - - # 3. 递归删除所有子对象 (对应各种板材创建方法) - children_to_delete = list(part_obj.children) - logger.info(f"📦 找到 {len(children_to_delete)} 个子对象需要删除") - - for child in children_to_delete: - try: - # 在删除前记录名称,避免删除后访问 - child_name = child.name if hasattr( - child, 'name') else 'unknown' - logger.info(f"🗑️ 删除Part子对象: {child_name}") - - # 检查是否是板材 - try: - if child.get("sw_face_type") == "board": - logger.info(f"📋 删除板材对象: {child_name}") - except (ReferenceError, AttributeError): - # 对象可能已经被删除 - pass - - success = self._delete_object_safe(child) - if success: - deleted_objects_count += 1 - logger.info(f"✅ 子对象删除成功: {child_name}") + deleted_in_this_zone = 0 + for obj in reversed(all_to_delete): # 先删子孙,最后删自己 + obj_name = obj.name if hasattr( + obj, 'name') else 'unknown' + if self._delete_object_safe(obj): + deleted_in_this_zone += 1 + logger.info(f"✅ 对象删除成功: {obj_name}") else: - logger.warning(f"⚠️ 子对象删除失败: {child_name}") - - except (ReferenceError, AttributeError) as e: - logger.warning(f"⚠️ 子对象已被删除,跳过: {e}") - # 对象已被删除,计为成功 - deleted_objects_count += 1 - - # 4. 删除Part对象本身 - try: - logger.info(f"🗑️ 删除Part对象: {part_name}") - success = self._delete_object_safe(part_obj) - if success: - deleted_objects_count += 1 - logger.info(f"✅ Part对象删除成功: {part_name}") - else: - logger.warning(f"⚠️ Part对象删除失败: {part_name}") - except (ReferenceError, AttributeError) as e: - logger.warning(f"⚠️ Part对象已被删除,跳过: {e}") - # 对象已被删除,计为成功 - deleted_objects_count += 1 - else: - logger.warning(f"❌ Part对象不存在: {part_name}") - - # 5. 全面搜索并删除所有可能的相关板材对象 - board_patterns = [ - f"Board_Part_{cp}", # 标准板材 - f"Board_Part_{cp}_default", # 默认板材 - f"Board_Surface_Part_{cp}", # 表面板材 - ] - - # 搜索带时间戳的板材 - if BLENDER_AVAILABLE: - all_objects = list(bpy.data.objects) - for obj in all_objects: - # 检查是否是该Part的板材(包含时间戳的情况) - if obj.name.startswith(f"Board_Part_{cp}_") and obj.name != f"Board_Part_{cp}_default": - logger.info(f"🔍 发现时间戳板材: {obj.name}") - board_patterns.append(obj.name) - - orphaned_boards_deleted = 0 - for pattern in board_patterns: - board_obj = bpy.data.objects.get(pattern) - if board_obj: - try: - logger.info(f"🗑️ 删除孤立板材: {pattern}") - success = self._delete_object_safe(board_obj) - if success: - orphaned_boards_deleted += 1 - logger.info(f"✅ 孤立板材删除成功: {pattern}") - else: - logger.warning(f"⚠️ 孤立板材删除失败: {pattern}") - except (ReferenceError, AttributeError) as e: - logger.warning(f"⚠️ 孤立板材已被删除,跳过: {pattern}, {e}") - # 对象已被删除,计为成功 - orphaned_boards_deleted += 1 - - # 6. 搜索所有可能的相关对象(基于属性) - if BLENDER_AVAILABLE: - attribute_based_objects = [] - for obj in bpy.data.objects: - # 检查对象属性 - if (obj.get("sw_uid") == uid and obj.get("sw_cp") == cp) or \ - (obj.get("sw_face_type") == "board" and f"Part_{cp}" in obj.name): - attribute_based_objects.append(obj) - - if attribute_based_objects: - logger.info( - f"🔍 通过属性找到 {len(attribute_based_objects)} 个相关对象") - for obj in attribute_based_objects: - try: - obj_name = obj.name if hasattr( - obj, 'name') else 'unknown' - if obj_name not in [o.name for o in [part_obj] + (list(part_obj.children) if part_obj else [])]: - logger.info(f"🗑️ 删除属性相关对象: {obj_name}") - success = self._delete_object_safe(obj) - if success: - orphaned_boards_deleted += 1 - logger.info(f"✅ 属性相关对象删除成功: {obj_name}") - except (ReferenceError, AttributeError) as e: - logger.warning(f"⚠️ 属性相关对象已被删除,跳过: {e}") - # 对象已被删除,计为成功 - orphaned_boards_deleted += 1 - - # 7. 最后清理数据结构(对应c04的数据结构存储) - if part_exists_in_data: - del parts[cp] - logger.info(f"✅ 从parts数据结构中移除: uid={uid}, cp={cp}") - - # 检查是否清空了整个uid的parts - if not parts: - logger.info(f"📊 uid={uid} 的所有部件已清空") - else: - logger.info(f"📊 数据结构中没有需要清理的部件引用: uid={uid}, cp={cp}") - - # 8. 显示所有剩余的Part对象用于调试 - if BLENDER_AVAILABLE: - remaining_parts = [ - obj for obj in bpy.data.objects if obj.name.startswith("Part_")] - if remaining_parts: - logger.info( - f"🔍 场景中剩余的Part对象: {[obj.name for obj in remaining_parts]}") - else: - logger.info("🔍 场景中没有剩余的Part对象") - - total_deleted = deleted_objects_count + orphaned_boards_deleted - self.deletion_stats["parts_deleted"] += 1 - self.deletion_stats["objects_deleted"] += total_deleted - - logger.info( - f"🎉 部件删除完成: uid={uid}, cp={cp}, 共删除 {total_deleted} 个对象") - return total_deleted # 返回删除数量 - - except Exception as e: - logger.error(f"❌ 删除部件失败 uid={uid}, cp={cp}: {e}") - self.deletion_stats["deletion_errors"] += 1 - import traceback - logger.error(traceback.format_exc()) - return None # 返回None表示失败 - - def _del_hardware_complete(self, uid: str, hw_id: int): - """完整删除硬件 - 对应c08的hardware创建逻辑""" - try: - logger.info(f"🗑️ 开始删除硬件: uid={uid}, hw_id={hw_id}") - - # 从数据结构中查找并删除硬件对象 - if (hasattr(self.data_manager, 'hardwares') and - uid in self.data_manager.hardwares and hw_id in self.data_manager.hardwares[uid]): - hw_obj = self.data_manager.hardwares[uid][hw_id] - if hw_obj and self._is_object_valid(hw_obj): - logger.info(f"删除硬件对象: {hw_obj.name}") - self._delete_object_safe(hw_obj) - - # 从数据结构中移除 - del self.data_manager.hardwares[uid][hw_id] - logger.info(f"✅ 从hardwares数据结构中移除: uid={uid}, hw_id={hw_id}") - else: - logger.warning(f"硬件不存在: uid={uid}, hw_id={hw_id}") - - # 更新统计 - self.deletion_stats["hardwares_deleted"] += 1 - - logger.info(f"✅ 硬件删除完成: uid={uid}, hw_id={hw_id}") - return 1 # 返回1表示成功 - - except Exception as e: - logger.error(f"删除硬件失败 uid={uid}, hw_id={hw_id}: {e}") - self.deletion_stats["deletion_errors"] += 1 - import traceback - logger.error(traceback.format_exc()) - return None # 返回None表示失败 - - def _del_other_entity_safe(self, data: Dict[str, Any], uid: str, typ: str, oid: int): - """删除其他类型实体 - 兼容旧逻辑""" - try: - logger.info(f"🗑️ 删除其他实体: uid={uid}, typ={typ}, oid={oid}") - - # 获取相应的实体集合 - if typ == "work": - # 工作实体,可能需要特殊处理 - logger.info(f"删除工作实体: uid={uid}, oid={oid}") - # 这里可以添加具体的工作实体删除逻辑 - elif typ == "pull": - # 拉手实体,可能需要特殊处理 - logger.info(f"删除拉手实体: uid={uid}, oid={oid}") - # 这里可以添加具体的拉手实体删除逻辑 - else: - logger.warning(f"未知实体类型: {typ}") - - logger.info(f"✅ 其他实体删除完成: uid={uid}, typ={typ}, oid={oid}") - return 1 # 返回1表示成功 - - except Exception as e: - logger.error(f"删除其他实体失败 uid={uid}, typ={typ}, oid={oid}: {e}") - self.deletion_stats["deletion_errors"] += 1 - import traceback - logger.error(traceback.format_exc()) - return None # 返回None表示失败 - - def _del_wall_entity_safe(self, data: Dict[str, Any], uid: str, oid: int): - """安全删除墙体实体""" - try: - logger.info(f"删除墙体实体: uid={uid}, oid={oid}") - - if not BLENDER_AVAILABLE: - return - - # 查找并删除墙体对象 - objects_to_delete = [] - for obj in list(bpy.data.objects): - try: - if not self._is_object_valid(obj): - continue - - # 检查是否是墙体对象 - obj_uid = obj.get("sw_uid") - obj_oid = obj.get("sw_oid") - obj_type = obj.get("sw_typ") - - if obj_uid == uid and obj_oid == oid and obj_type == "wall": - objects_to_delete.append(obj) - logger.debug(f"标记删除墙体对象: {obj.name}") - - except Exception as e: - logger.warning(f"检查墙体对象失败: {e}") - continue - - # 删除找到的墙体对象 - deleted_count = 0 - for obj in objects_to_delete: - try: - if self._delete_object_safe(obj): - deleted_count += 1 - except Exception as e: - logger.error( - f"删除墙体对象失败 {obj.name if hasattr(obj, 'name') else 'unknown'}: {e}") + logger.warning(f"⚠️ 对象删除失败: {obj_name}") + deleted_count += deleted_in_this_zone + logger.info(f"✅ 批量删除了 {deleted_in_this_zone} 个对象") + except Exception as fallback_error: + logger.error(f"回退删除也失败: {fallback_error}") self.deletion_stats["objects_deleted"] += deleted_count logger.info( - f"墙体删除完成: {deleted_count}/{len(objects_to_delete)} 个对象") + f"区域删除完成: {deleted_count} 个对象") return deleted_count # 返回删除数量 except Exception as e: - logger.error(f"删除墙体实体失败: {e}") + logger.error(f"删除区域实体失败: {e}") self.deletion_stats["deletion_errors"] += 1 import traceback logger.error(traceback.format_exc()) @@ -989,20 +746,41 @@ class DeletionManager: logger.debug(f"对象不在bpy.data.objects中,跳过删除: {obj.name}") return True # 对象已经不在数据中,视为删除成功 - # 【修复4】安全删除对象 + # 【修复4】安全删除对象 - 添加更多错误处理 try: + # 在删除前记录对象名称 + obj_name = obj.name if hasattr(obj, 'name') else 'unknown' + + # 检查对象是否仍然有效 + if not self._is_object_valid(obj): + logger.debug(f"对象在删除前已无效: {obj_name}") + return True + + # 执行删除 bpy.data.objects.remove(obj, do_unlink=True) - logger.debug( - f"✅ 成功删除对象: {obj.name if hasattr(obj, 'name') else 'unknown'}") + logger.debug(f"✅ 成功删除对象: {obj_name}") return True + except Exception as e: - logger.error( - f"删除对象失败: {obj.name if hasattr(obj, 'name') else 'unknown'}, 错误: {e}") - return False + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug( + f"对象已被移除: {obj.name if hasattr(obj, 'name') else 'unknown'}") + return True # 对象已被移除,视为删除成功 + else: + logger.error( + f"删除对象失败: {obj.name if hasattr(obj, 'name') else 'unknown'}, 错误: {e}") + return False except Exception as e: - logger.error(f"安全删除对象时发生错误: {e}") - return False + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug( + f"对象已被移除: {obj.name if hasattr(obj, 'name') else 'unknown'}") + return True # 对象已被移除,视为删除成功 + else: + logger.error(f"安全删除对象时发生错误: {e}") + return False def _cleanup_orphaned_meshes(self): """清理孤立的网格数据 - 改进版本""" @@ -1014,15 +792,30 @@ class DeletionManager: if mesh.users == 0: meshes_to_remove.append(mesh) except Exception as e: - logger.debug(f"检查网格时发生错误: {e}") - continue + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug(f"网格已被移除,跳过清理") + continue + else: + logger.debug(f"检查网格时发生错误: {e}") + continue for mesh in meshes_to_remove: try: + # 再次检查网格是否仍然有效 + if not self._is_object_valid(mesh): + logger.debug(f"网格已无效,跳过删除") + continue + bpy.data.meshes.remove(mesh) logger.debug(f"清理孤立网格: {mesh.name}") except Exception as e: - logger.debug(f"删除孤立网格失败: {e}") + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug(f"网格已被移除,跳过删除") + continue + else: + logger.debug(f"删除孤立网格失败: {e}") # 【修复】更安全的材质清理逻辑 materials_to_remove = [] @@ -1035,8 +828,13 @@ class DeletionManager: if material.users == 0: materials_to_remove.append(material) except Exception as e: - logger.debug(f"检查材质时发生错误: {e}") - continue + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug(f"材质已被移除,跳过清理") + continue + else: + logger.debug(f"检查材质时发生错误: {e}") + continue for material in materials_to_remove: try: @@ -1045,7 +843,12 @@ class DeletionManager: bpy.data.materials.remove(material) logger.debug(f"清理孤立材质: {material.name}") except Exception as e: - logger.debug(f"删除孤立材质失败: {e}") + # 检查是否是"StructRNA has been removed"错误 + if "StructRNA" in str(e) and "removed" in str(e): + logger.debug(f"材质已被移除,跳过删除") + continue + else: + logger.debug(f"删除孤立材质失败: {e}") except Exception as e: logger.debug(f"清理孤立数据时发生错误: {e}") @@ -1352,8 +1155,8 @@ class DeletionManager: except Exception as e: logger.error(f"删除Blender对象失败: {obj.name}, 错误: {e}") - # 清理孤立的网格数据 - self._cleanup_orphaned_meshes() + # 清理孤立的网格数据 注释 + # self._cleanup_orphaned_meshes() except Exception as e: logger.error(f"删除Blender对象时发生错误: {e}") diff --git a/blenderpython/suw_core/part_creator.py b/blenderpython/suw_core/part_creator.py index cc375f7..851ef57 100644 --- a/blenderpython/suw_core/part_creator.py +++ b/blenderpython/suw_core/part_creator.py @@ -84,8 +84,8 @@ class PartCreator: logger.warning(f"清理无效的部件引用: {root}") del parts[root] - # 【修复3】创建部件容器 - part_name = f"Part_{root}" + # 【修复3】创建部件容器 - 修改命名格式为Part_{uid}_{cp} + part_name = f"Part_{uid}_{root}" part = bpy.data.objects.new(part_name, None) bpy.context.scene.collection.objects.link(part) @@ -96,10 +96,19 @@ class PartCreator: part["sw_cp"] = root part["sw_typ"] = "part" - # 【修复5】存储部件到数据结构 - parts[root] = part + # 【新增】设置Part对象的父对象为Zone对象 + zone_name = f"Zone_{uid}" + zone_obj = bpy.data.objects.get(zone_name) + if zone_obj: + part.parent = zone_obj + logger.info(f"✅ 设置Part对象 {part_name} 的父对象为Zone: {zone_name}") + else: + logger.warning(f"⚠️ 未找到Zone对象: {zone_name},Part对象将没有父对象") - logger.info(f"✅ 部件存储到数据结构: uid={uid}, cp={root}") + # 【修复5】存储部件到数据结构 - 使用data_manager.add_part()方法 + self.data_manager.add_part(uid, root, part) + logger.info( + f"✅ 使用data_manager.add_part()存储部件数据: uid={uid}, cp={root}") # 【修复6】处理finals数据 finals = data.get("finals", []) @@ -116,26 +125,26 @@ class PartCreator: logger.info( f"✅ 板材 {i+1}/{len(finals)} 创建成功: {board.name}") - # 【修复7】减少依赖图更新频率 - if i % 5 == 0: - bpy.context.view_layer.update() + # 【修复7】移除频繁的依赖图更新,避免评估过程中的错误 + # if i % 5 == 0: + # bpy.context.view_layer.update() else: logger.warning(f"⚠️ 板材 {i+1}/{len(finals)} 创建失败") except Exception as e: logger.error(f"❌ 创建板材 {i+1}/{len(finals)} 失败: {e}") - # 【修复8】单个板材失败时的恢复 + # 【修复8】单个板材失败时的恢复 - 移除依赖图更新 try: import gc gc.collect() - bpy.context.view_layer.update() + # bpy.context.view_layer.update() # 移除这行 except: pass logger.info(f"📊 板材创建统计: {created_boards}/{len(finals)} 成功") - # 【修复9】最终清理 + # 【修复9】最终清理 - 移除依赖图更新 try: - bpy.context.view_layer.update() + # bpy.context.view_layer.update() # 移除这行 import gc gc.collect() except Exception as cleanup_error: @@ -285,7 +294,7 @@ class PartCreator: except Exception as e: logger.error(f"关联材质失败: {e}") - # 【修复5】启用UV + # 【修复5】启用UV - 移除依赖图更新 self.enable_uv_for_board(board) return board @@ -319,8 +328,8 @@ class PartCreator: # 确保UV层是活动的 mesh.uv_layers.active = uv_layer - # 更新网格数据 - mesh.calc_loop_triangles() + # 更新网格数据 - 移除可能导致依赖图更新的操作 + # mesh.calc_loop_triangles() # 移除这行 # 为立方体创建基本UV坐标 if len(mesh.polygons) == 6: # 标准立方体 @@ -338,8 +347,8 @@ class PartCreator: for loop in mesh.loops: uv_layer.data[loop.index].uv = (0.5, 0.5) - # 更新网格 - mesh.update() + # 更新网格 - 移除可能导致依赖图更新的操作 + # mesh.update() # 移除这行 except Exception as e: logger.error(f"启用UV失败: {e}")