待学习库ui更新

This commit is contained in:
LiXin01 2026-10-16 11:19:04 +08:00
parent 1d0b35fb3e
commit fcc43390c6
1 changed files with 101 additions and 20 deletions

View File

@ -170,10 +170,10 @@ class PendingLearningUIMixin:
values.append(name)
seen.add(name)
suggested = (suggested or "").strip()
if suggested and suggested in values:
initial = suggested
else:
initial = ""
# 最接近菜名即使不在今日菜单/主食里,也填进输入框并放到下拉最前
if suggested and suggested not in seen:
values.insert(0, suggested)
initial = suggested
return period_name, period_names, values, initial, is_period
def _today_menu_info(self):
@ -529,8 +529,11 @@ class PendingLearningUIMixin:
header.pack(fill=tk.X, padx=16, pady=(14, 6))
tk.Label(header, text="待学习菜品库", bg=COLORS["bg"], fg=COLORS["text"],
font=FONTS["h1"]).pack(side=tk.LEFT)
tk.Label(header, text=f"共 {len(items)} 条记录", bg=COLORS["bg"],
fg=COLORS["muted"], font=FONTS["small"]).pack(side=tk.LEFT, padx=10)
count_lbl = tk.Label(header, text=f"共 {len(items)} 条记录", bg=COLORS["bg"],
fg=COLORS["muted"], font=FONTS["small"])
count_lbl.pack(side=tk.LEFT, padx=10)
dialog._pending_count_label = count_lbl
dialog._pending_items = items
if items:
PillButton(header, text="全部忽略", variant="ghost", size="sm",
command=lambda: self._ignore_all_pending(items, dialog),
@ -642,7 +645,9 @@ class PendingLearningUIMixin:
actions.pack(side=tk.RIGHT, padx=(10, 0))
pid = item["id"]
PillButton(actions, text="学习", variant="primary", size="sm",
command=lambda p=pid, s=suggested: self._learn_pending_from_dialog(p, s, dialog),
command=lambda p=pid, s=suggested, row=box: self._learn_pending_from_dialog(
p, s, dialog, row=row,
),
).pack(side=tk.LEFT, padx=2)
PillButton(actions, text="忽略", variant="ghost", size="sm",
command=lambda p=pid: self._ignore_pending_from_dialog(p, dialog),
@ -683,6 +688,21 @@ class PendingLearningUIMixin:
# 保持 _pending_dialog_open / _pending_resume_live,重建列表
self.show_pending_dialog()
def _pl_place_center(self, win, width, height):
"""把弹窗放到屏幕中央,避免默认贴在左上角。"""
try:
win.update_idletasks()
sw = win.winfo_screenwidth()
sh = win.winfo_screenheight()
width = min(int(width), max(sw - 48, 240))
height = min(int(height), max(sh - 80, 180))
x = max((sw - width) // 2, 0)
y = max((sh - height) // 2 - 24, 12)
win.geometry(f"{width}x{height}+{x}+{y}")
win.lift()
except Exception:
pass
def _preview_pending_image(self, image_path, parent=None):
"""待学习库:点击缩略图查看对应大图。"""
if not image_path or not os.path.exists(image_path):
@ -699,16 +719,35 @@ class PendingLearningUIMixin:
win.title("待学习图片预览")
win.configure(bg=COLORS["bg"])
win.transient(parent or self.root)
im.thumbnail((760, 640))
try:
sw = win.winfo_screenwidth()
sh = win.winfo_screenheight()
except Exception:
sw, sh = 1280, 800
max_w = max(720, int(sw * 0.72))
max_h = max(520, int(sh * 0.72))
iw, ih = im.size
scale = min(max_w / float(iw or 1), max_h / float(ih or 1), 4.0)
if abs(scale - 1.0) > 0.02:
im = im.resize(
(max(1, int(iw * scale)), max(1, int(ih * scale))),
Image.Resampling.LANCZOS if hasattr(Image, "Resampling") else Image.LANCZOS,
)
photo = ImageTk.PhotoImage(im)
win._photo = photo
tk.Label(win, image=photo, bg=COLORS["bg"]).pack(padx=12, pady=12)
tk.Label(win, image=photo, bg=COLORS["bg"]).pack(padx=16, pady=(16, 8))
tk.Label(win, text=os.path.basename(image_path), bg=COLORS["bg"],
fg=COLORS["muted"], font=FONTS["small"]).pack()
PillButton(win, text="关闭", variant="ghost",
command=win.destroy).pack(pady=(8, 12))
command=win.destroy).pack(pady=(8, 14))
win.update_idletasks()
self._pl_place_center(
win,
max(win.winfo_reqwidth(), im.width + 48),
max(win.winfo_reqheight(), im.height + 96),
)
def _learn_pending_from_dialog(self, pending_id, suggested, dialog):
def _learn_pending_from_dialog(self, pending_id, suggested, dialog, row=None):
"""待学习库:下拉含主食小库 + 当前餐段菜品。"""
period_name, period_names, values, initial, is_period = self._pl_learn_name_choices(suggested)
@ -716,7 +755,6 @@ class PendingLearningUIMixin:
pick.title("选择菜品名称")
pick.configure(bg=COLORS["bg"])
pick.transient(dialog)
pick.grab_set()
pick.resizable(False, False)
tk.Label(pick, text="学习到哪个菜名?", bg=COLORS["bg"], fg=COLORS["text"],
@ -728,24 +766,45 @@ class PendingLearningUIMixin:
else:
tip = "暂无候选菜品可选,请手动输入菜名。"
tk.Label(pick, text=tip, bg=COLORS["bg"], fg=COLORS["muted"], font=FONTS["small"],
wraplength=340, justify=tk.LEFT).pack(padx=20, anchor=tk.W)
wraplength=420, justify=tk.LEFT).pack(padx=20, anchor=tk.W)
if values:
Chip(pick, text=f"主食+{period_name} · {len(values)} 道",
tone="ok", bg=COLORS["bg"]).pack(padx=20, pady=(8, 0), anchor=tk.W)
name_var = tk.StringVar(value=initial)
name_var = tk.StringVar(value=initial or "")
row_pick = tk.Frame(pick, bg=COLORS["bg"])
row_pick.pack(fill=tk.X, padx=20, pady=10)
combo = ttk.Combobox(
pick, textvariable=name_var, values=values,
width=34, style="Large.TEntry",
row_pick, textvariable=name_var, values=values, width=28,
)
combo.pack(padx=20, pady=10, fill=tk.X)
combo["height"] = 16
combo.pack(side=tk.LEFT, fill=tk.X, expand=True)
def _open_dropdown(_event=None):
combo.focus_set()
try:
combo.tk.call("ttk::combobox::Post", combo._w)
except Exception:
try:
combo.event_generate("<Down>")
except Exception:
pass
return "break"
PillButton(row_pick, text="选择", variant="secondary", size="sm",
command=_open_dropdown).pack(side=tk.LEFT, padx=(8, 0))
combo.focus_set()
if initial:
try:
combo.icursor("end")
except Exception:
pass
if not values:
tk.Label(pick, text="暂无可选菜名,请直接输入新菜名(将新建类别)。",
bg=COLORS["bg"], fg=COLORS["warn"], font=FONTS["caption"],
wraplength=300, justify=tk.LEFT).pack(padx=20, anchor=tk.W)
wraplength=400, justify=tk.LEFT).pack(padx=20, anchor=tk.W)
btns = tk.Frame(pick, bg=COLORS["bg"])
btns.pack(fill=tk.X, padx=20, pady=(10, 18))
@ -761,16 +820,38 @@ class PendingLearningUIMixin:
self.refresh_template_list()
self.update_pending_btn()
self._pl_toast(f"已学习为「{category}」并加入模板库", "ok")
dialog.destroy()
self.show_pending_dialog()
self._drop_pending_row(dialog, pending_id, row)
else:
messagebox.showerror("失败", "学习失败,请重试", parent=pick)
PillButton(btns, text="确定学习", variant="primary", command=_confirm).pack(side=tk.RIGHT, padx=4)
PillButton(btns, text="取消", variant="ghost", command=pick.destroy).pack(side=tk.RIGHT)
pick.bind("<Return>", lambda e: _confirm())
pick.update_idletasks()
self._pl_place_center(pick, max(pick.winfo_reqwidth(), 480), pick.winfo_reqheight())
pick.wait_window()
def _drop_pending_row(self, dialog, pending_id, row):
"""学完只去掉当前行,列表停在原位置,不整页重建。"""
items = getattr(dialog, "_pending_items", None)
if isinstance(items, list):
dialog._pending_items = [
it for it in items if it.get("id") != pending_id
]
lbl = getattr(dialog, "_pending_count_label", None)
if lbl is not None:
try:
n = len(getattr(dialog, "_pending_items", []) or [])
lbl.config(text=f"共 {n} 条记录")
except Exception:
pass
if row is not None:
try:
if row.winfo_exists():
row.destroy()
except Exception:
pass
def _ignore_pending_from_dialog(self, pending_id, dialog):
if self.manager.ignore_pending(pending_id):
self.update_pending_btn()