From 792f296bdcdd3b082b9145833bebc7205ffd4cee Mon Sep 17 00:00:00 2001 From: wanghanbo <1411232085@qq.com> Date: Mon, 12 Oct 2026 16:15:31 +0800 Subject: [PATCH] =?UTF-8?q?ui=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/classifier_ui.py | 111 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/ui/classifier_ui.py b/ui/classifier_ui.py index 2610e11..9f5d61e 100644 --- a/ui/classifier_ui.py +++ b/ui/classifier_ui.py @@ -6,8 +6,8 @@ 界面结构(结算机):: ┌ 顶栏:标题 + 状态徽章 + 设置 ─────────────────────┐ - ├ 左:已点菜品清单 ─────────┬─ 右:绿提示 + 大预览 ─┤ - │ 缩略图 / 菜名 / 数量 │ │ 流畅预览 + 识别框叠加 │ + ├ 左:识别图 + 已点清单 ─────┬─ 右:绿提示 + 大预览 ─┤ + │ 识别整图 / 菜名 / 数量 │ │ 流畅预览 + 识别框叠加 │ ├──────────────────────────┴───────────────────────┤ │ 底栏:暂停/继续扫描|选图识别|清空|设置 │ └──────────────────────────────────────────────────┘ @@ -111,6 +111,8 @@ class BaseDishApp(PendingLearningUIMixin): self._dock_thumb_refs = [] self._cart = [] self._cart_thumbs = [] + self._cart_result_image = None # 左侧已点区展示的识别整图路径 + self._cart_result_photo = None self._template_filter = "" self._template_page = 0 self._template_page_size = 40 # 大库分页,避免一次渲染上千行闪退 @@ -1317,6 +1319,23 @@ class BaseDishApp(PendingLearningUIMixin): font=FONTS["small"]) self.cart_count_label.pack(side=tk.RIGHT) + # 识别整图:放在清单上方空白区 + img_wrap = tk.Frame( + panel, bg=c["pos_row_alt"], + highlightbackground=c["border"], highlightthickness=1, + height=168, + ) + img_wrap.pack(fill=tk.X, padx=8, pady=(0, 8)) + img_wrap.pack_propagate(False) + self.cart_result_label = tk.Label( + img_wrap, + text="识别图片将显示在这里", + bg=c["pos_row_alt"], fg=c["muted"], + font=FONTS["small"], + compound=tk.CENTER, + ) + self.cart_result_label.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) + cols = tk.Frame(panel, bg=c["pos_row_alt"]) cols.pack(fill=tk.X) for text, w in (("菜品", 22), ("数量", 10), ("", 4)): @@ -2546,11 +2565,12 @@ class BaseDishApp(PendingLearningUIMixin): written=pending_written, elapsed=stable_elapsed, committed=already_committed, hold_s=hold, same_order=order_unchanged, + frame_ref=frame, ): # 只更新叠加框 + 左侧清单;预览继续刷实时帧 self._live_overlay_dishes = list(ds or []) - # 菜品未变:仍刷新预览框,但左侧保持/同步同一批菜,不视为新单 - self.render_result_dock(ds, path) + # 有已识别菜时更新左侧识别整图(新单用存盘图,否则用当前帧) + self.render_result_dock(ds, path, frame_bgr=frame_ref) try: self.update_pending_btn() except Exception: @@ -2578,9 +2598,9 @@ class BaseDishApp(PendingLearningUIMixin): ) if written: - self._toast(f"已记入待学习 {written} 个未知菜", "warn") + self._toast(f"已记入待学习并上报 {written} 个未知菜", "warn") self._set_result_text( - f"未知菜已记入待学习({written})|左侧仅显示已识别", + f"未知菜已记入待学习并上报({written})|左侧仅显示已识别", "warn", ) @@ -2626,6 +2646,8 @@ class BaseDishApp(PendingLearningUIMixin): self._last_dishes = [] self._live_overlay_dishes = [] self._reset_order_gate() + self._cart_result_image = None + self._render_cart_result_image() self.render_cart() if not self._preview_enabled and not self._live_enabled and hasattr(self, "img_label"): self.img_label.config( @@ -2635,6 +2657,74 @@ class BaseDishApp(PendingLearningUIMixin): self.img_label.image = None self._set_result_text("已清空本次已点菜品", "ok") + def _set_cart_result_image(self, image_path=None, frame_bgr=None, dishes=None): + """更新左侧已点区识别整图;优先磁盘路径,否则用内存帧(可叠识别框)。""" + if image_path and os.path.exists(str(image_path)): + self._cart_result_image = os.path.abspath(str(image_path)) + self._render_cart_result_image() + return + if frame_bgr is not None: + try: + os.makedirs("images", exist_ok=True) + path = os.path.abspath(os.path.join("images", "_cart_result_preview.jpg")) + saved = False + if dishes: + try: + rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) + im = Image.fromarray(rgb) + im = self._draw_live_overlay_pil(im, dishes) + im.convert("RGB").save(path, quality=88) + saved = os.path.isfile(path) + except Exception as exc: + print(f"[cart] overlay preview failed: {exc}") + if not saved: + saved = bool(cv2.imwrite(path, frame_bgr)) + if saved: + self._cart_result_image = path + self._render_cart_result_image() + return + except Exception as exc: + print(f"[cart] preview from frame failed: {exc}") + # 无新图则保持当前展示 + + def _render_cart_result_image(self): + if not hasattr(self, "cart_result_label"): + return + path = getattr(self, "_cart_result_image", None) + c = COLORS + if not path or not os.path.exists(str(path)): + self.cart_result_label.config( + text="识别图片将显示在这里", image="", + bg=c["pos_row_alt"], fg=c["muted"], + ) + self._cart_result_photo = None + return + try: + with open(path, "rb") as f: + im = Image.open(f).convert("RGB") + max_w, max_h = 300, 156 + try: + w = max(self.cart_result_label.winfo_width(), 120) + if w > 40: + max_w = min(w - 8, 360) + except Exception: + pass + try: + resample = Image.Resampling.LANCZOS + except Exception: + resample = Image.LANCZOS + im.thumbnail((max_w, max_h), resample) + photo = ImageTk.PhotoImage(im) + self._cart_result_photo = photo + self.cart_result_label.config(text="", image=photo, bg=c["surface"]) + except Exception as exc: + print(f"[cart] render result image failed: {exc}") + self.cart_result_label.config( + text="识别图片加载失败", image="", + bg=c["pos_row_alt"], fg=c["muted"], + ) + self._cart_result_photo = None + def fill_cart_from_dishes(self, dishes): """左侧清单只放已识别菜;未知仅留在预览标注 / 待学习流程。 @@ -2738,8 +2828,13 @@ class BaseDishApp(PendingLearningUIMixin): if hasattr(self, "cart_count_label"): self.cart_count_label.config(text=f"{len(self._cart)} 项") - def render_result_dock(self, dishes, labeled_path=None): - """兼容旧调用:识别结果写入购物车。""" + def render_result_dock(self, dishes, labeled_path=None, frame_bgr=None): + """识别结果写入购物车,并刷新左侧识别整图。""" + known = [d for d in (dishes or []) if not d.get("is_unknown")] + if known: + self._set_cart_result_image( + labeled_path, frame_bgr=frame_bgr, dishes=dishes, + ) self.fill_cart_from_dishes(dishes) self.render_cart()