This commit is contained in:
wanghanbo 2026-10-12 16:15:31 +08:00
parent f8c60f0aa4
commit 792f296bdc
1 changed files with 103 additions and 8 deletions

View File

@ -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()