import os import tkinter as tk from tkinter import filedialog, messagebox from typing import Optional import cv2 from PIL import Image, ImageTk from ..config_loader import load_config from ..io_utils import read_image_bgr from ..pipeline import EAN13Recognizer class TkEAN13App: def __init__(self) -> None: self.config = load_config() self.root = tk.Tk() self.root.title(self.config["app"]["ui"]["window_title"]) self.image_panel = tk.Label(self.root) self.image_panel.pack(side=tk.TOP, padx=10, pady=10) btn_frame = tk.Frame(self.root) btn_frame.pack(side=tk.TOP, pady=5) self.btn_open = tk.Button(btn_frame, text="选择图片", command=self.on_open) self.btn_open.pack(side=tk.LEFT, padx=5) self.btn_recognize = tk.Button(btn_frame, text="识别条码", command=self.on_recognize) self.btn_recognize.pack(side=tk.LEFT, padx=5) self.btn_camera = tk.Button(btn_frame, text="摄像头识别", command=self.on_camera) self.btn_camera.pack(side=tk.LEFT, padx=5) self.result_var = tk.StringVar(value="结果:-") self.result_label = tk.Label(self.root, textvariable=self.result_var) self.result_label.pack(side=tk.TOP, pady=5) self.current_image_path: Optional[str] = None self.recognizer = EAN13Recognizer() self.cap = None self.cam_running = False def on_open(self) -> None: path = filedialog.askopenfilename( title="选择条码图片", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.bmp;*.tif;*.tiff")], ) if not path: return self.current_image_path = path img_bgr = read_image_bgr(path) if img_bgr is None: messagebox.showerror("错误", "无法读取图片") return img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 限制显示尺寸 max_w = 800 h, w = img_rgb.shape[:2] if w > max_w: scale = max_w / float(w) img_rgb = cv2.resize(img_rgb, (max_w, int(h * scale)), interpolation=cv2.INTER_AREA) im = Image.fromarray(img_rgb) self.tk_img = ImageTk.PhotoImage(im) self.image_panel.configure(image=self.tk_img) self.result_var.set("结果:-") def on_recognize(self) -> None: if not self.current_image_path: messagebox.showinfo("提示", "请先选择图片") return result = self.recognizer.recognize_any_from_path(self.current_image_path) ean13 = result.get("ean13", "") if ean13: self.result_var.set(f"结果:EAN-13 {ean13}") return others = result.get("others", []) if others: first = others[0] self.result_var.set(f"结果:{first.get('type')} {first.get('code')}") return self.result_var.set("结果:未识别") def on_camera(self) -> None: if self.cam_running: # 若已在运行,视为停止 self.stop_camera() return cam_cfg = self.config.get("camera", {}) index = int(cam_cfg.get("index", 0)) self.cap = cv2.VideoCapture(index, cv2.CAP_DSHOW) if not self.cap.isOpened(): messagebox.showerror("错误", f"无法打开摄像头 index={index}") self.cap.release() self.cap = None return # 设置分辨率(尽力设置) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, float(cam_cfg.get("width", 1280))) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, float(cam_cfg.get("height", 720))) self.cam_running = True self.result_var.set("结果:摄像头开启,正在识别...") self.btn_camera.configure(text="停止摄像头") self._camera_loop() def stop_camera(self) -> None: self.cam_running = False try: if self.cap is not None: self.cap.release() finally: self.cap = None self.btn_camera.configure(text="摄像头识别") def _camera_loop(self) -> None: if not self.cam_running or self.cap is None: return ret, frame = self.cap.read() if not ret: # 读取失败,稍后重试 self.root.after(int(self.config.get("camera", {}).get("interval_ms", 80)), self._camera_loop) return # 显示到面板 show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) max_w = 800 h, w = show.shape[:2] if w > max_w: scale = max_w / float(w) show = cv2.resize(show, (max_w, int(h * scale)), interpolation=cv2.INTER_AREA) im = Image.fromarray(show) self.tk_img = ImageTk.PhotoImage(im) self.image_panel.configure(image=self.tk_img) # 识别 result = self.recognizer.recognize_any_from_image(frame) ean13 = result.get("ean13", "") if ean13: self.result_var.set(f"结果:EAN-13 {ean13}") self.stop_camera() return others = result.get("others", []) if others: first = others[0] self.result_var.set(f"结果:{first.get('type')} {first.get('code')}") self.stop_camera() return # 未识别,继续下一帧 self.root.after(int(self.config.get("camera", {}).get("interval_ms", 80)), self._camera_loop) def run(self) -> None: self.root.mainloop() def main() -> None: app = TkEAN13App() app.run() if __name__ == "__main__": main()