From 94fa69043b441a4c8403f7c73aef068a87974203 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 29 Oct 2025 17:55:54 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E9=9B=86=E5=8D=A1bug=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- preview.py | 64 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/preview.py b/preview.py index 68482c8..179be11 100644 --- a/preview.py +++ b/preview.py @@ -322,11 +322,11 @@ class PreviewWindow: pil_image = Image.fromarray(resized_frame) photo = ImageTk.PhotoImage(image=pil_image) - # 多重引用保护,防止被GC回收 - self.photo_objects[idx] = photo # 按索引保存 - self.photo_objects_list.append(photo) # 在列表中保存(防止GC) + # 保存引用(按索引保存,方便查找) + self.photo_objects[idx] = photo - images_to_draw.append((photo, center_x, center_y)) + # 添加到绘制列表(在列表中保存引用,防止GC) + images_to_draw.append((photo, center_x, center_y, idx)) texts_to_draw.append((center_x, y + 15, name, 'white')) frame_idx += 1 @@ -353,31 +353,57 @@ class PreviewWindow: print(f" 当前保存的照片对象数量: {len(self.photo_objects_list)}") if images_to_draw: - # 创建一个临时列表保存所有photo引用,防止在绘制时被GC - photos_to_keep = [] - for i, (photo, x, y) in enumerate(images_to_draw): + # 先收集所有photo对象到列表中,确保引用不丢失 + photos_current_frame = [] + + for i, item in enumerate(images_to_draw): + if len(item) == 4: + photo, x, y, idx = item + else: + # 兼容旧格式(如果还有的话) + photo, x, y = item[:3] + idx = None + try: # 确保坐标在画布范围内 if 0 <= x <= canvas_width and 0 <= y <= canvas_height: - # 在绘制前保存引用 - photos_to_keep.append(photo) + # 先保存引用到列表(防止GC) + photos_current_frame.append(photo) + + # 然后绘制 canvas.create_image(x, y, image=photo, anchor='center') + if self.debug_count <= 3 and i == 0: - print(f" 绘制图像 #{i} 到位置 ({x}, {y}), photo id: {id(photo)}") + print(f" 绘制图像 #{i} (idx={idx}) 到位置 ({x}, {y})") else: if self.debug_count <= 3: print(f" ⚠️ 图像 #{i} 位置 ({x}, {y}) 超出画布范围") except Exception as e: - print(f" 绘制图像 #{i} 错误: {e}") - import traceback - traceback.print_exc() + error_msg = str(e).lower() + print(f" ❌ 绘制图像 #{i} 时出错: {type(e).__name__}: {e}") + + if "pyimage" in error_msg: + # PhotoImage 对象被 GC 了,尝试恢复 + if self.debug_count <= 5: + print(f" ⚠️ PhotoImage 对象丢失,尝试从字典恢复 (idx={idx})") + if idx is not None and idx in self.photo_objects: + try: + photo = self.photo_objects[idx] + photos_current_frame.append(photo) + canvas.create_image(x, y, image=photo, anchor='center') + if self.debug_count <= 5: + print(f" ✅ 已从字典恢复并重新绘制") + except Exception as e2: + if self.debug_count <= 5: + print(f" ❌ 恢复失败: {e2}") + else: + # 其他类型的错误,打印完整堆栈 + import traceback + traceback.print_exc() - # 确保照片对象不被回收(保存引用到实例变量) - # 只保留最近的一批照片对象,避免内存泄漏 - self.photo_objects_list = photos_to_keep[:] # 复制列表 - # 限制列表大小,只保留最近的20个对象 - if len(self.photo_objects_list) > 20: - self.photo_objects_list = self.photo_objects_list[-20:] + # 保存当前帧的所有photo引用(替换旧的列表,只保留当前帧) + # 这样确保正在显示的photo对象不会被GC + self.photo_objects_list = photos_current_frame[:] # 复制列表 else: # 如果没有图像,至少显示一些提示 if self.debug_count <= 3: