From 0552df5287401da94b5218f6df3d11401e689525 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Sun, 30 Aug 2026 14:44:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E6=AD=BB=E5=BE=AA=E7=8E=AF+EasyOCR=E9=A2=84?= =?UTF-8?q?=E7=83=AD=E8=AF=AF=E8=A7=A6=E5=8F=91+=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=97=B6=E9=97=B4=E6=88=B3=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E9=94=99=E8=AF=AF=20(Task=20#1638)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gui.py: - 添加 _stop_in_progress 标志防止停止/启动死循环 - _toggle_monitor 检查 _stop_in_progress 标志防止重入 - _update_stop_ui 和 _stop_monitor_async 中正确设置和重置标志 monitor.py: - 添加 _easyocr_warmup_generation 代际计数器防止死循环中旧预热线程错误初始化 EasyOCR - 预热线程检查代际匹配防止跨 start/stop 周期的旧线程执行 - 移除 has_past 分支中的 skip_future_check=True,修复历史消息误判为新鲜的bug 根因:skip_future_check 让未来时戳(如14:09看到21:15)通过检查, 但OCR只能看到已发送消息,未来时戳必然来自过去日期 --- gui.py | 10 ++++++++++ monitor.py | 17 ++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/gui.py b/gui.py index 3d020fe..b3b560c 100644 --- a/gui.py +++ b/gui.py @@ -601,6 +601,8 @@ class MsgBotApp(ctk.CTk): # ⭐ 防止 _update_stop_ui() 中 deselect() 触发 _toggle_monitor() 回调的标志 self._suppress_toggle_callback = False + # ⭐ 防止停止/启动死循环:stop过程中禁止toggle重新触发start + self._stop_in_progress = False self._build_ui() @@ -811,6 +813,9 @@ class MsgBotApp(ctk.CTk): # ⭐ 防止 _update_stop_ui() 中 deselect() 触发的回调 if self._suppress_toggle_callback: return + # ⭐ 防止 stop 过程中被 re-toggle 导致死循环 + if self._stop_in_progress: + return enabled = self._toggle_btn.get() if enabled: @@ -832,6 +837,7 @@ class MsgBotApp(ctk.CTk): self._ocr_manager.set_monitor_active(True) else: # ⭐ UI 立即更新;stop() 在后台线程执行,主线程保持响应,不再触发冻结弹窗 + self._stop_in_progress = True # ⭐ 防止 stop 过程中被 re-toggle 导致死循环 self._status_label.configure(text="⏸ 已暂停") self._log("⏸ 消息监控已暂停") # 通知OCR管理器:监控已停止,开始计时 @@ -1451,11 +1457,15 @@ class MsgBotApp(ctk.CTk): self.monitor.stop() except Exception as e: logger.warning(f"[gui] 后台 stop 异常: {e}") + finally: + # ⭐ stop 完成后在主线程重置标志,允许下一次 toggle + self.after(0, lambda: setattr(self, '_stop_in_progress', False)) def _update_stop_ui(self): """主线程更新急停后的 UI(stop 放到后台线程,主线程不冻结)。""" # ⭐ 设置标志防止 deselect() 触发 _toggle_monitor() 回调 self._suppress_toggle_callback = True + self._stop_in_progress = True # ⭐ 防止死循环 try: # 直接设置开关变量值,不触发回调 self._toggle_btn._variable.set(False) diff --git a/monitor.py b/monitor.py index d22f85b..726a6f8 100644 --- a/monitor.py +++ b/monitor.py @@ -779,6 +779,8 @@ class MessageMonitor: # ⭐ 停止信号事件:set() 后所有 time.sleep / 阻塞操作应立即响应退出 # 替代原 _running bool 标志:Event.wait(timeout) 可被 set() 唤醒,time.sleep 不行 self._stop_event = threading.Event() + # ⭐ EasyOCR 预热线程代际计数器:防止死循环中旧预热线程在新 start() 后醒来错误初始化 EasyOCR + self._easyocr_warmup_generation = 0 def _load_replied_texts_from_db(self): """从消息历史数据库加载最近的bot回复到 _replied_texts, @@ -965,9 +967,13 @@ class MessageMonitor: # 第3批:EasyOCR预热(延迟1s,最重的操作放最后) if _HAS_EASYOCR and not self._easyocr_broken: + # ⭐ 代际计数器:防止死循环中旧预热线程在新 start() 后醒来错误初始化 EasyOCR + self._easyocr_warmup_generation += 1 + _gen = self._easyocr_warmup_generation def _delayed_easyocr_start(): time.sleep(1.0) - if self._running: + # ⭐ 双重检查:self._running + 代际匹配,防止死循环中的旧线程执行 + if self._running and self._easyocr_warmup_generation == _gen: self._gui_log("🔧 EasyOCR 子进程初始化中...") self._ensure_easyocr_subprocess() threading.Thread(target=_delayed_easyocr_start, daemon=True, name="easyocr-delay").start() @@ -6651,8 +6657,13 @@ class MessageMonitor: _has_fresh_ts = False for ts in timestamps: try: - # 过去日期+未来时间戳组合 → 日期识别错误 → 跳过未来时间戳检查 - if not self._is_timestamp_old(ts, 300, skip_future_check=True): + # ⭐ Task #1638 修复:移除 skip_future_check=True + # 之前的 skip_future_check 逻辑假定"过去日期+未来时间戳=日期识别错误", + # 但OCR只能看到已发送的消息,未来时戳不可能来自今天。 + # 例:14:09 看到 21:15 → 这是昨天的消息,不应放行。 + # 正常 _is_timestamp_old 会用 60s 容差处理时钟偏移, + # 超过容差的"未来"时戳一律判为旧消息。 + if not self._is_timestamp_old(ts, 300): _has_fresh_ts = True logger.debug(f"[ocr] 过滤器1结果: 过去日期+新鲜时间戳{ts}(≤300s),放行") break