采集卡bug修复

This commit is contained in:
Ray
2025-10-29 17:50:33 +08:00
parent ed636f68f6
commit 754501b933

View File

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