2
This commit is contained in:
2
backend/txm/app/ui/__init__.py
Normal file
2
backend/txm/app/ui/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
BIN
backend/txm/app/ui/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
backend/txm/app/ui/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
backend/txm/app/ui/__pycache__/tk_app.cpython-39.pyc
Normal file
BIN
backend/txm/app/ui/__pycache__/tk_app.cpython-39.pyc
Normal file
Binary file not shown.
162
backend/txm/app/ui/tk_app.py
Normal file
162
backend/txm/app/ui/tk_app.py
Normal file
@@ -0,0 +1,162 @@
|
||||
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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user