C09,C03,C04暂无问题

This commit is contained in:
libtxixi 2025-07-19 17:36:38 +08:00
parent d732c50b3d
commit 7e44892dfe
2 changed files with 217 additions and 405 deletions

View File

@ -13,8 +13,52 @@ from .data_manager import data_manager, get_data_manager
import time import time
import logging import logging
import threading import threading
import sys
from typing import Dict, Any, List, Optional 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__) logger = logging.getLogger(__name__)
@ -94,32 +138,39 @@ class DeletionManager:
# 删除zones如果是uid或zid类型 # 删除zones如果是uid或zid类型
if typ in ["uid", "zid"]: if typ in ["uid", "zid"]:
# 【修复】对于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 logger.info(f"🗑️ 删除区域: uid={uid}, zid={oid}")
parts = self.data_manager.get_parts(data) result = self._del_zone_complete(uid, oid)
parts_deleted = self._del_entities_by_type( deleted_count += result if result is not None else 0
parts, typ, oid, uid) logger.info(f"✅ 删除区域完成: {result} 个对象")
deleted_count += parts_deleted
logger.info(f"✅ 删除parts: {parts_deleted}")
# 删除hardwares # 【修复】删除zone时同时删除该zone相关的所有parts
hardwares = self.data_manager.get_hardwares(data) logger.info(
hardwares_deleted = self._del_entities_by_type( f"🗑️ 删除区域相关的所有parts: uid={uid}, zid={oid}")
hardwares, typ, oid, uid) parts = self.data_manager.get_parts(data)
deleted_count += hardwares_deleted if parts:
logger.info(f"✅ 删除hardwares: {hardwares_deleted}") # 查找并删除与该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类型 # 【修复】与Ruby版本保持一致清理labels如果是uid或zid类型
if typ in ["uid", "zid"]: if typ in ["uid", "zid"]:
@ -196,11 +247,11 @@ class DeletionManager:
zid = data.get("zid") zid = data.get("zid")
elements = data.get("children", []) elements = data.get("children", [])
logger.info(f" Zone_{zid} 数据: uid={uid}, 元素数量={len(elements)}") logger.info(f" Zone_{uid} 数据: uid={uid}, 元素数量={len(elements)}")
# 【修复】不再创建线框立方体直接创建六个面组成Zone # 【修复】不再创建线框立方体直接创建六个面组成Zone
# 创建一个空的组对象作为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不显示线框 group.empty_display_type = 'PLAIN_AXES' # 改为PLAIN_AXES不显示线框
bpy.context.scene.collection.objects.link(group) bpy.context.scene.collection.objects.link(group)
@ -211,7 +262,7 @@ class DeletionManager:
group["sw_typ"] = "zone" # 改为"zone"而不是"zid" 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_uid: {group.get('sw_uid')}")
logger.info(f" sw_zid: {group.get('sw_zid')}") logger.info(f" sw_zid: {group.get('sw_zid')}")
logger.info(f" sw_zip: {group.get('sw_zip')}") logger.info(f" sw_zip: {group.get('sw_zip')}")
@ -261,7 +312,7 @@ class DeletionManager:
group["sw_created_faces"] = len(created_faces) group["sw_created_faces"] = len(created_faces)
group["sw_failed_faces"] = len(failed_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(created_faces)} 个面")
logger.info(f" 创建失败: {len(failed_faces)} 个面") logger.info(f" 创建失败: {len(failed_faces)} 个面")
if failed_faces: if failed_faces:
@ -552,8 +603,9 @@ class DeletionManager:
obj.get("sw_typ") == "zone"): obj.get("sw_typ") == "zone"):
zone_objects.append(obj) zone_objects.append(obj)
logger.info(f"🎯 找到Zone对象(属性匹配): {obj.name}") logger.info(f"🎯 找到Zone对象(属性匹配): {obj.name}")
# 【修复】同时检查命名格式匹配 # 【修复】同时检查命名格式匹配
elif obj.name == f"Zone_{zid}": elif obj.name == f"Zone_{uid}":
zone_objects.append(obj) zone_objects.append(obj)
logger.info(f"🎯 找到Zone对象(命名匹配): {obj.name}") logger.info(f"🎯 找到Zone对象(命名匹配): {obj.name}")
except Exception as e: except Exception as e:
@ -572,361 +624,66 @@ class DeletionManager:
logger.info(f" 未找到Zone对象删除操作完成: uid={uid}, zid={zid}") logger.info(f" 未找到Zone对象删除操作完成: uid={uid}, zid={zid}")
return 0 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: for zone_obj in zone_objects:
try: try:
# 检查对象是否仍然有效 zone_name = zone_obj.name if hasattr(
if not self._is_object_valid(zone_obj): zone_obj, 'name') else 'unknown'
logger.debug(f"Zone对象已无效跳过: {zone_obj.name}") logger.info(f"🔄 删除失败,回退到收集并批量删除所有层级对象: {zone_name}")
continue # 收集所有要删除的对象(包括自身和所有子孙)
all_to_delete = collect_all_children(zone_obj)
# 递归删除所有子对象 # 先解除所有对象的父子关系和依赖
children_to_delete = list(zone_obj.children) for obj in all_to_delete:
logger.info(f"📦 找到 {len(children_to_delete)} 个子对象需要删除")
for child in children_to_delete:
try: try:
if self._is_object_valid(child): # 解除父对象
logger.info(f"🗑️ 删除Zone子对象: {child.name}") obj.parent = None
if self._delete_object_safe(child): # 清空约束
deleted_count += 1 if hasattr(obj, "constraints"):
else: for c in list(obj.constraints):
logger.debug(f"子对象已无效,跳过: {child.name}") obj.constraints.remove(c)
except Exception as e: # 清空驱动
logger.debug(f"删除子对象时发生错误: {e}") 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对象本身 deleted_in_this_zone = 0
logger.info(f"🗑️ 删除Zone对象: {zone_obj.name}") for obj in reversed(all_to_delete): # 先删子孙,最后删自己
if self._delete_object_safe(zone_obj): obj_name = obj.name if hasattr(
deleted_count += 1 obj, 'name') else 'unknown'
if self._delete_object_safe(obj):
except Exception as e: deleted_in_this_zone += 1
logger.error( logger.info(f"✅ 对象删除成功: {obj_name}")
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}")
else: else:
logger.warning(f"⚠️ 子对象删除失败: {child_name}") logger.warning(f"⚠️ 对象删除失败: {obj_name}")
deleted_count += deleted_in_this_zone
except (ReferenceError, AttributeError) as e: logger.info(f"✅ 批量删除了 {deleted_in_this_zone} 个对象")
logger.warning(f"⚠️ 子对象已被删除,跳过: {e}") except Exception as fallback_error:
# 对象已被删除,计为成功 logger.error(f"回退删除也失败: {fallback_error}")
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}")
self.deletion_stats["objects_deleted"] += deleted_count self.deletion_stats["objects_deleted"] += deleted_count
logger.info( logger.info(
f"墙体删除完成: {deleted_count}/{len(objects_to_delete)} 个对象") f"区域删除完成: {deleted_count} 个对象")
return deleted_count # 返回删除数量 return deleted_count # 返回删除数量
except Exception as e: except Exception as e:
logger.error(f"删除墙体实体失败: {e}") logger.error(f"删除区域实体失败: {e}")
self.deletion_stats["deletion_errors"] += 1 self.deletion_stats["deletion_errors"] += 1
import traceback import traceback
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@ -989,20 +746,41 @@ class DeletionManager:
logger.debug(f"对象不在bpy.data.objects中跳过删除: {obj.name}") logger.debug(f"对象不在bpy.data.objects中跳过删除: {obj.name}")
return True # 对象已经不在数据中,视为删除成功 return True # 对象已经不在数据中,视为删除成功
# 【修复4】安全删除对象 # 【修复4】安全删除对象 - 添加更多错误处理
try: 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) bpy.data.objects.remove(obj, do_unlink=True)
logger.debug( logger.debug(f"✅ 成功删除对象: {obj_name}")
f"✅ 成功删除对象: {obj.name if hasattr(obj, 'name') else 'unknown'}")
return True return True
except Exception as e: except Exception as e:
logger.error( # 检查是否是"StructRNA has been removed"错误
f"删除对象失败: {obj.name if hasattr(obj, 'name') else 'unknown'}, 错误: {e}") if "StructRNA" in str(e) and "removed" in str(e):
return False 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: except Exception as e:
logger.error(f"安全删除对象时发生错误: {e}") # 检查是否是"StructRNA has been removed"错误
return False 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): def _cleanup_orphaned_meshes(self):
"""清理孤立的网格数据 - 改进版本""" """清理孤立的网格数据 - 改进版本"""
@ -1014,15 +792,30 @@ class DeletionManager:
if mesh.users == 0: if mesh.users == 0:
meshes_to_remove.append(mesh) meshes_to_remove.append(mesh)
except Exception as e: except Exception as e:
logger.debug(f"检查网格时发生错误: {e}") # 检查是否是"StructRNA has been removed"错误
continue 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: for mesh in meshes_to_remove:
try: try:
# 再次检查网格是否仍然有效
if not self._is_object_valid(mesh):
logger.debug(f"网格已无效,跳过删除")
continue
bpy.data.meshes.remove(mesh) bpy.data.meshes.remove(mesh)
logger.debug(f"清理孤立网格: {mesh.name}") logger.debug(f"清理孤立网格: {mesh.name}")
except Exception as e: 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 = [] materials_to_remove = []
@ -1035,8 +828,13 @@ class DeletionManager:
if material.users == 0: if material.users == 0:
materials_to_remove.append(material) materials_to_remove.append(material)
except Exception as e: except Exception as e:
logger.debug(f"检查材质时发生错误: {e}") # 检查是否是"StructRNA has been removed"错误
continue 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: for material in materials_to_remove:
try: try:
@ -1045,7 +843,12 @@ class DeletionManager:
bpy.data.materials.remove(material) bpy.data.materials.remove(material)
logger.debug(f"清理孤立材质: {material.name}") logger.debug(f"清理孤立材质: {material.name}")
except Exception as e: 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: except Exception as e:
logger.debug(f"清理孤立数据时发生错误: {e}") logger.debug(f"清理孤立数据时发生错误: {e}")
@ -1352,8 +1155,8 @@ class DeletionManager:
except Exception as e: except Exception as e:
logger.error(f"删除Blender对象失败: {obj.name}, 错误: {e}") logger.error(f"删除Blender对象失败: {obj.name}, 错误: {e}")
# 清理孤立的网格数据 # 清理孤立的网格数据 注释
self._cleanup_orphaned_meshes() # self._cleanup_orphaned_meshes()
except Exception as e: except Exception as e:
logger.error(f"删除Blender对象时发生错误: {e}") logger.error(f"删除Blender对象时发生错误: {e}")

View File

@ -84,8 +84,8 @@ class PartCreator:
logger.warning(f"清理无效的部件引用: {root}") logger.warning(f"清理无效的部件引用: {root}")
del parts[root] del parts[root]
# 【修复3】创建部件容器 # 【修复3】创建部件容器 - 修改命名格式为Part_{uid}_{cp}
part_name = f"Part_{root}" part_name = f"Part_{uid}_{root}"
part = bpy.data.objects.new(part_name, None) part = bpy.data.objects.new(part_name, None)
bpy.context.scene.collection.objects.link(part) bpy.context.scene.collection.objects.link(part)
@ -96,10 +96,19 @@ class PartCreator:
part["sw_cp"] = root part["sw_cp"] = root
part["sw_typ"] = "part" part["sw_typ"] = "part"
# 【修复5】存储部件到数据结构 # 【新增】设置Part对象的父对象为Zone对象
parts[root] = part 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数据 # 【修复6】处理finals数据
finals = data.get("finals", []) finals = data.get("finals", [])
@ -116,26 +125,26 @@ class PartCreator:
logger.info( logger.info(
f"✅ 板材 {i+1}/{len(finals)} 创建成功: {board.name}") f"✅ 板材 {i+1}/{len(finals)} 创建成功: {board.name}")
# 【修复7】减少依赖图更新频率 # 【修复7】移除频繁的依赖图更新,避免评估过程中的错误
if i % 5 == 0: # if i % 5 == 0:
bpy.context.view_layer.update() # bpy.context.view_layer.update()
else: else:
logger.warning(f"⚠️ 板材 {i+1}/{len(finals)} 创建失败") logger.warning(f"⚠️ 板材 {i+1}/{len(finals)} 创建失败")
except Exception as e: except Exception as e:
logger.error(f"❌ 创建板材 {i+1}/{len(finals)} 失败: {e}") logger.error(f"❌ 创建板材 {i+1}/{len(finals)} 失败: {e}")
# 【修复8】单个板材失败时的恢复 # 【修复8】单个板材失败时的恢复 - 移除依赖图更新
try: try:
import gc import gc
gc.collect() gc.collect()
bpy.context.view_layer.update() # bpy.context.view_layer.update() # 移除这行
except: except:
pass pass
logger.info(f"📊 板材创建统计: {created_boards}/{len(finals)} 成功") logger.info(f"📊 板材创建统计: {created_boards}/{len(finals)} 成功")
# 【修复9】最终清理 # 【修复9】最终清理 - 移除依赖图更新
try: try:
bpy.context.view_layer.update() # bpy.context.view_layer.update() # 移除这行
import gc import gc
gc.collect() gc.collect()
except Exception as cleanup_error: except Exception as cleanup_error:
@ -285,7 +294,7 @@ class PartCreator:
except Exception as e: except Exception as e:
logger.error(f"关联材质失败: {e}") logger.error(f"关联材质失败: {e}")
# 【修复5】启用UV # 【修复5】启用UV - 移除依赖图更新
self.enable_uv_for_board(board) self.enable_uv_for_board(board)
return board return board
@ -319,8 +328,8 @@ class PartCreator:
# 确保UV层是活动的 # 确保UV层是活动的
mesh.uv_layers.active = uv_layer mesh.uv_layers.active = uv_layer
# 更新网格数据 # 更新网格数据 - 移除可能导致依赖图更新的操作
mesh.calc_loop_triangles() # mesh.calc_loop_triangles() # 移除这行
# 为立方体创建基本UV坐标 # 为立方体创建基本UV坐标
if len(mesh.polygons) == 6: # 标准立方体 if len(mesh.polygons) == 6: # 标准立方体
@ -338,8 +347,8 @@ class PartCreator:
for loop in mesh.loops: for loop in mesh.loops:
uv_layer.data[loop.index].uv = (0.5, 0.5) uv_layer.data[loop.index].uv = (0.5, 0.5)
# 更新网格 # 更新网格 - 移除可能导致依赖图更新的操作
mesh.update() # mesh.update() # 移除这行
except Exception as e: except Exception as e:
logger.error(f"启用UV失败: {e}") logger.error(f"启用UV失败: {e}")