fix: 修复急停后GUI开关状态不更新+EasyOCR不恢复+无法重启 (Task #1640)
gui.py: - _on_emergency_stop: 急停后立即通知 OCR 管理器 set_monitor_active(False) - _update_stop_ui: 用 deselect() 替代 _variable.set(False),更可靠地更新开关视觉状态 - _update_stop_ui: 添加20秒安全超时,强制重置 _stop_in_progress(防止 stop() 挂起导致永远无法重启) - _toggle_monitor: 添加安全检查,monitor 已停止但 _stop_in_progress=True 时强制重置 monitor.py: - _emergency_stop: 设置 _stopping=True 防止 stop() 重复执行清理(join已死线程+重复杀EasyOCR) - _emergency_stop: 完成后重置 _stopping=False,确保后续 start() 正常执行 根因分析: 1. GUI开关不更新:CTkSwitch._variable.set(False) 在 customtkinter 中不可靠,改用 deselect() 2. 急停后无法重启:stop() 中 _release_memory() 可能挂起(torch.cuda/EmptyWorkingSet), 导致 _stop_in_progress 永远不重置,_toggle_monitor 拒绝重启 3. EasyOCR不恢复:急停后未通知 OCR 管理器,引擎状态不一致,description:提交修改}
This commit is contained in:
@@ -815,8 +815,14 @@ class MsgBotApp(ctk.CTk):
|
||||
return
|
||||
# ⭐ 防止 stop 过程中被 re-toggle 导致死循环
|
||||
if self._stop_in_progress:
|
||||
return
|
||||
|
||||
# ⭐ 安全检查:如果 monitor 已经停止(_running=False),说明 stop() 可能挂起,
|
||||
# 强制重置 _stop_in_progress,允许用户重新启动。
|
||||
if not self.monitor.is_running:
|
||||
logger.warning("[gui] _stop_in_progress=True 但 monitor 已停止,强制重置")
|
||||
self._stop_in_progress = False
|
||||
else:
|
||||
return
|
||||
|
||||
enabled = self._toggle_btn.get()
|
||||
if enabled:
|
||||
# 检查知识库是否已加载
|
||||
@@ -825,7 +831,12 @@ class MsgBotApp(ctk.CTk):
|
||||
# 设置标志防止 deselect() 触发回调
|
||||
self._suppress_toggle_callback = True
|
||||
try:
|
||||
self._toggle_btn._variable.set(False)
|
||||
self._toggle_btn.deselect()
|
||||
except Exception:
|
||||
try:
|
||||
self._toggle_btn._variable.set(False)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.after(100, lambda: setattr(self, '_suppress_toggle_callback', False))
|
||||
return
|
||||
@@ -1444,6 +1455,13 @@ class MsgBotApp(ctk.CTk):
|
||||
def _on_emergency_stop(self):
|
||||
"""双击 ESC 急停的回调(从 daemon 线程调用,日志队列安全)。"""
|
||||
self._log_queue.put("⚠️⚠️⚠️ 双击ESC急停!消息监控已强制终止")
|
||||
# ⭐ 通知 OCR 管理器:监控已停止,开始计时释放引擎
|
||||
# 这步必须在 daemon 线程里同步做(self.after 可能延迟),
|
||||
# 否则 OCR 引擎不会被正确释放,重启后 EasyOCR 无法初始化。
|
||||
try:
|
||||
self._ocr_manager.set_monitor_active(False)
|
||||
except Exception as e:
|
||||
logger.warning(f"[gui] 急停通知 OCR 管理器失败: {e}")
|
||||
# 安全更新 toggle 状态(主线程 after 调度)
|
||||
self.after(0, self._update_stop_ui)
|
||||
|
||||
@@ -1467,14 +1485,30 @@ class MsgBotApp(ctk.CTk):
|
||||
self._suppress_toggle_callback = True
|
||||
self._stop_in_progress = True # ⭐ 防止死循环
|
||||
try:
|
||||
# 直接设置开关变量值,不触发回调
|
||||
self._toggle_btn._variable.set(False)
|
||||
# ⭐ 用 deselect() 替代 _variable.set(False)——customtkinter 的
|
||||
# CTkSwitch.deselect() 内部同时更新视觉状态和变量,更可靠。
|
||||
self._toggle_btn.deselect()
|
||||
except Exception:
|
||||
# 降级:直接设置变量值
|
||||
try:
|
||||
self._toggle_btn._variable.set(False)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
# 延迟重置标志,确保回调已被跳过
|
||||
self.after(100, lambda: setattr(self, '_suppress_toggle_callback', False))
|
||||
|
||||
self._status_label.configure(text="⏸ 急停")
|
||||
|
||||
self._status_label.configure(text="⏸ 急停", text_color="#e74c3c")
|
||||
self._log_queue.put("🔄 可点击「启用监控」重新启动")
|
||||
|
||||
# ⭐ 安全超时:如果 _stop_in_progress 在 20s 内没被重置(stop() 挂起),
|
||||
# 强制重置,确保用户永远能重新启动。
|
||||
def _safety_reset_stop_flag():
|
||||
if self._stop_in_progress:
|
||||
logger.warning("[gui] _stop_in_progress 安全超时重置(stop() 可能挂起)")
|
||||
self._stop_in_progress = False
|
||||
self.after(20000, _safety_reset_stop_flag)
|
||||
|
||||
# ⭐ UI 立即更新;stop() 在后台线程执行,主线程保持响应,不再触发冻结弹窗。
|
||||
threading.Thread(target=self._stop_monitor_async, daemon=True, name="gui-stop").start()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user