fix: 修复停止按钮死循环+EasyOCR预热误触发+历史消息时间戳过滤错误 (Task #1638)
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只能看到已发送消息,未来时戳必然来自过去日期
This commit is contained in:
@@ -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)
|
||||
|
||||
+14
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user