ui界面版本

This commit is contained in:
Ray
2025-10-29 10:49:38 +08:00
parent 8294cab51b
commit 3f1dd4e8c1
12 changed files with 1659 additions and 94 deletions

229
preview.py Normal file
View File

@@ -0,0 +1,229 @@
import cv2
import tkinter as tk
from tkinter import Canvas
from PIL import Image, ImageTk
import threading
import numpy as np
from config import config_manager
class PreviewWindow:
"""采集卡预览窗口"""
def __init__(self):
self.config = config_manager.config
self.caps = {}
self.frames = {}
self.large_window = None
self.running = True
def init_cameras(self):
"""初始化所有相机"""
for i, group in enumerate(self.config['groups']):
if group.get('active', True): # 只加载活动的配置
try:
cap = cv2.VideoCapture(group['camera_index'], cv2.CAP_DSHOW)
if cap.isOpened():
cap.set(cv2.CAP_PROP_FRAME_WIDTH, group['camera_width'])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, group['camera_height'])
self.caps[i] = {
'cap': cap,
'group': group,
'name': group['name']
}
except Exception as e:
print(f"无法打开相机 {group['camera_index']}: {e}")
def capture_frames(self):
"""捕获帧"""
while self.running:
for idx, data in self.caps.items():
ret, frame = data['cap'].read()
if ret:
# 裁剪到配置的区域
height, width = frame.shape[:2]
crop_top = 30
crop_bottom = min(crop_top + 720, height)
crop_width = min(1280, width)
frame = frame[crop_top:crop_bottom, 0:crop_width]
# 转换颜色空间
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.frames[idx] = frame_rgb
def create_grid_window(self):
"""创建网格窗口"""
root = tk.Tk()
root.title("采集卡预览 - 点击放大")
root.geometry("1000x700")
# 获取显示配置
display = self.config.get('display', {})
preview_width = display.get('preview_width', 640)
preview_height = display.get('preview_height', 360)
columns = display.get('preview_columns', 2)
rows = display.get('preview_rows', 2)
canvas = Canvas(root, bg='black')
canvas.pack(fill=tk.BOTH, expand=True)
# 存储图像对象
self.photo_objects = {}
def update_frames():
"""更新帧"""
while self.running:
try:
canvas.delete("all")
# 计算每个预览窗口的位置和大小
window_width = root.winfo_width() if root.winfo_width() > 1 else 1000
window_height = root.winfo_height() if root.winfo_height() > 1 else 700
cell_width = window_width // columns
cell_height = window_height // rows
frame_idx = 0
for idx in self.caps.keys():
if idx in self.frames:
row = frame_idx // columns
col = frame_idx % columns
x = col * cell_width
y = row * cell_height
# 调整图像大小
frame = self.frames[idx]
h, w = frame.shape[:2]
scale = min(cell_width / w, cell_height / h) * 0.9
new_w = int(w * scale)
new_h = int(h * scale)
resized_frame = cv2.resize(frame, (new_w, new_h))
# 转换为PIL图像
pil_image = Image.fromarray(resized_frame)
photo = ImageTk.PhotoImage(image=pil_image)
self.photo_objects[idx] = photo
# 绘制图像
center_x = x + cell_width // 2
center_y = y + cell_height // 2
canvas.create_image(center_x, center_y, image=photo, anchor='center')
# 绘制标签
name = self.caps[idx]['name']
canvas.create_text(center_x, y + 20, text=name, fill='white', font=('Arial', 12, 'bold'))
frame_idx += 1
canvas.update()
tk._default_root.update_idletasks()
except Exception as e:
print(f"更新帧错误: {e}")
import time
time.sleep(0.03) # 约30fps
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
col = int(event.x // cell_width)
row = int(event.y // cell_height)
index = row * columns + col
# 找到对应的配置
if index < len(self.caps):
idx = list(self.caps.keys())[index]
self.show_large_window(idx)
canvas.bind('<Button-1>', on_canvas_click)
# 启动更新线程
update_thread = threading.Thread(target=update_frames, daemon=True)
update_thread.start()
def on_closing():
"""关闭窗口"""
self.running = False
for data in self.caps.values():
data['cap'].release()
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
def show_large_window(self, idx):
"""显示大窗口"""
if self.large_window is not None and self.large_window.winfo_exists():
self.large_window.destroy()
self.large_window = tk.Tk()
self.large_window.title(f"放大视图 - {self.caps[idx]['name']}")
self.large_window.geometry("1280x720")
canvas = Canvas(self.large_window, bg='black')
canvas.pack(fill=tk.BOTH, expand=True)
photo_obj = {}
def update_large_frame():
"""更新大窗口帧"""
while self.running:
try:
if idx in self.frames and self.large_window.winfo_exists():
canvas.delete("all")
frame = self.frames[idx]
h, w = frame.shape[:2]
# 调整到窗口大小
window_width = self.large_window.winfo_width() if self.large_window.winfo_width() > 1 else 1280
window_height = self.large_window.winfo_height() if self.large_window.winfo_height() > 1 else 720
scale = min(window_width / w, window_height / h)
new_w = int(w * scale)
new_h = int(h * scale)
resized_frame = cv2.resize(frame, (new_w, new_h))
pil_image = Image.fromarray(resized_frame)
photo = ImageTk.PhotoImage(image=pil_image)
photo_obj['img'] = photo
canvas.delete("all")
canvas.create_image(window_width // 2, window_height // 2, image=photo, anchor='center')
self.large_window.update()
else:
break
except Exception as e:
print(f"更新大窗口错误: {e}")
break
import time
time.sleep(0.03)
update_thread = threading.Thread(target=update_large_frame, daemon=True)
update_thread.start()
self.large_window.mainloop()
def run(self):
"""运行预览"""
self.init_cameras()
# 启动捕获线程
capture_thread = threading.Thread(target=self.capture_frames, daemon=True)
capture_thread.start()
# 创建并显示网格窗口
import time
time.sleep(0.5) # 等待几帧
self.create_grid_window()
if __name__ == "__main__":
preview = PreviewWindow()
preview.run()