diff --git a/preview.py b/preview.py index ca89278..68482c8 100644 --- a/preview.py +++ b/preview.py @@ -194,8 +194,9 @@ class PreviewWindow: canvas = Canvas(root, bg='black', width=preview_width, height=preview_height) canvas.pack(fill=tk.BOTH, expand=True) - # 存储图像对象 - self.photo_objects = {} + # 存储图像对象(使用列表保存所有PhotoImage引用,防止GC) + self.photo_objects_list = [] + self.photo_objects = {} # 按索引映射 # 用于控制调试输出(只打印前几次) self.debug_count = 0 @@ -219,10 +220,27 @@ class PreviewWindow: root.after(33, update_frames_once) return + # 重新读取配置,确保获取最新值(修复配置读取问题) + config_manager.load_config() + display = config_manager.config.get('display', {}) + current_preview_width = display.get('preview_width', 1000) + current_preview_height = display.get('preview_height', 700) + current_columns = display.get('preview_columns', 2) + current_rows = display.get('preview_rows', 2) + + # 验证配置值是否有效 + if current_preview_width < 100 or current_preview_height < 100: + current_preview_width = 1000 + current_preview_height = 700 + if current_columns < 1 or current_columns > 10: + current_columns = 2 + if current_rows < 1 or current_rows > 10: + current_rows = 2 + # 计算每个预览窗口的位置和大小 - # 直接使用配置值作为画布尺寸(macOS上窗口尺寸可能在显示前返回默认值) - canvas_width = preview_width - canvas_height = preview_height + # 直接使用配置值作为画布尺寸 + canvas_width = current_preview_width + canvas_height = current_preview_height # 尝试获取实际的窗口尺寸,如果有效则使用(大于配置值说明可能被手动调整了) try: @@ -237,8 +255,8 @@ class PreviewWindow: except: pass # 如果获取失败,使用配置值 - cell_width = max(10, canvas_width // columns) - cell_height = max(10, canvas_height // rows) + cell_width = max(10, canvas_width // current_columns) + cell_height = max(10, canvas_height // current_rows) # 先收集所有需要显示的图像 images_to_draw = [] @@ -250,8 +268,8 @@ class PreviewWindow: texts_to_draw.append((canvas_width // 2, canvas_height // 2, "未找到采集卡", 'red')) for idx in self.caps.keys(): - row = frame_idx // columns - col = frame_idx % columns + row = frame_idx // current_columns + col = frame_idx % current_columns x = col * cell_width y = row * cell_height center_x = x + cell_width // 2 @@ -289,7 +307,7 @@ class PreviewWindow: self.debug_count += 1 if self.debug_count <= 3: # 只打印前3次 print(f"🔍 预览调试 #{self.debug_count}:") - print(f" 配置尺寸: {preview_width}x{preview_height}") + print(f" 配置尺寸: {current_preview_width}x{current_preview_height}") print(f" 实际画布: {canvas_width}x{canvas_height}") print(f" 单元格大小: {cell_width}x{cell_height}") print(f" 原始帧: {w}x{h}") @@ -303,8 +321,10 @@ class PreviewWindow: resized_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(resized_frame) photo = ImageTk.PhotoImage(image=pil_image) - # 保持引用,避免被GC - self.photo_objects[idx] = photo + + # 多重引用保护,防止被GC回收 + self.photo_objects[idx] = photo # 按索引保存 + self.photo_objects_list.append(photo) # 在列表中保存(防止GC) images_to_draw.append((photo, center_x, center_y)) texts_to_draw.append((center_x, y + 15, name, 'white')) @@ -324,21 +344,26 @@ class PreviewWindow: texts_to_draw.append((center_x, center_y, f"{name}\n等待画面...", 'gray')) frame_idx += 1 - # 清空画布 + # 清空画布(保留photo引用,只清空画布内容) canvas.delete("all") # 先绘制所有图像(底层) if self.debug_count <= 3: print(f"📊 绘制状态: {len(images_to_draw)} 个图像, {len(texts_to_draw)} 个文本, 画布={canvas_width}x{canvas_height}") + 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): try: # 确保坐标在画布范围内 if 0 <= x <= canvas_width and 0 <= y <= canvas_height: + # 在绘制前保存引用 + photos_to_keep.append(photo) canvas.create_image(x, y, image=photo, anchor='center') if self.debug_count <= 3 and i == 0: - print(f" 绘制图像 #{i} 到位置 ({x}, {y})") + print(f" 绘制图像 #{i} 到位置 ({x}, {y}), photo id: {id(photo)}") else: if self.debug_count <= 3: print(f" ⚠️ 图像 #{i} 位置 ({x}, {y}) 超出画布范围") @@ -346,6 +371,13 @@ class PreviewWindow: print(f" 绘制图像 #{i} 错误: {e}") 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:] else: # 如果没有图像,至少显示一些提示 if self.debug_count <= 3: @@ -377,7 +409,7 @@ class PreviewWindow: # 绘制分割线,区分不同的采集卡窗口 try: # 绘制垂直分割线(列之间的分割线) - for col in range(1, columns): + for col in range(1, current_columns): x = col * cell_width canvas.create_line( x, 0, @@ -388,7 +420,7 @@ class PreviewWindow: ) # 绘制水平分割线(行之间的分割线) - for row in range(1, rows): + for row in range(1, current_rows): y = row * cell_height canvas.create_line( 0, y, @@ -409,14 +441,20 @@ class PreviewWindow: def on_canvas_click(event): """点击画布事件""" - window_width = root.winfo_width() - window_height = root.winfo_height() - cell_width = window_width // columns - cell_height = window_height // rows + # 重新读取配置以获取最新的columns和rows + config_manager.load_config() + display = config_manager.config.get('display', {}) + click_columns = display.get('preview_columns', 2) + click_rows = display.get('preview_rows', 2) + + window_width = root.winfo_width() if root.winfo_width() > 1 else preview_width + window_height = root.winfo_height() if root.winfo_height() > 1 else preview_height + cell_width = window_width // click_columns + cell_height = window_height // click_rows col = int(event.x // cell_width) row = int(event.y // cell_height) - index = row * columns + col + index = row * click_columns + col # 找到对应的配置 if index < len(self.caps):