|
|
@@ -36,9 +36,10 @@ class MainWindow(QMainWindow):
|
|
|
voice_signal = Signal(str)
|
|
|
camera_signal_signal = Signal(dict)
|
|
|
|
|
|
- def __init__(self, config: Config):
|
|
|
+ def __init__(self, config: Config, config_path: str = ""):
|
|
|
super().__init__()
|
|
|
self.config = config
|
|
|
+ self._config_path = config_path
|
|
|
self.buckets = config.buckets
|
|
|
self.active_bucket: BucketModel = None
|
|
|
self.switch_mode = SwitchMode.AUTO
|
|
|
@@ -79,7 +80,7 @@ class MainWindow(QMainWindow):
|
|
|
self.ai_enabled = True
|
|
|
self._last_left_draw: list = []
|
|
|
self._last_right_draw: list = []
|
|
|
- self._voice_queue = queue.Queue()
|
|
|
+ self._voice_queue = queue.Queue(maxsize=32)
|
|
|
self._voice_thread = threading.Thread(target=self._voice_worker, daemon=True)
|
|
|
self._voice_thread.start()
|
|
|
self._voices_dir = self._get_voices_dir()
|
|
|
@@ -91,11 +92,13 @@ class MainWindow(QMainWindow):
|
|
|
self._rtsp_retry_delay_left: int = 1
|
|
|
self._rtsp_retry_delay_right: int = 1
|
|
|
self._last_mqtt_connect_attempt: float = 0.0
|
|
|
+ self._last_mqtt_process: float = 0.0
|
|
|
|
|
|
# 摄像头信号监控
|
|
|
self._camera_signal: dict = {} # ip → 延迟(ms), -1=不可达
|
|
|
self._bucket_signal_labels: dict = {} # bucket.id → QLabel
|
|
|
self._last_signal_check: float = 0.0
|
|
|
+ self._signal_check_thread: threading.Thread = None
|
|
|
|
|
|
# 连接跨线程信号到主线程槽
|
|
|
self.rfid_tag_signal.connect(self._on_rfid_tag_impl)
|
|
|
@@ -454,7 +457,10 @@ class MainWindow(QMainWindow):
|
|
|
self.voice_signal.emit(msg)
|
|
|
|
|
|
def _speak_impl(self, msg: str):
|
|
|
- self._voice_queue.put(msg)
|
|
|
+ try:
|
|
|
+ self._voice_queue.put_nowait(msg)
|
|
|
+ except queue.Full:
|
|
|
+ pass
|
|
|
|
|
|
@staticmethod
|
|
|
def _get_voices_dir() -> str:
|
|
|
@@ -694,8 +700,8 @@ class MainWindow(QMainWindow):
|
|
|
timeout = self.config.video.rtsp_timeout
|
|
|
player = RTSPPlayer(cam.rtsp_url, w, h, timeout_sec=timeout)
|
|
|
player.frame_ready.connect(label.setPixmap)
|
|
|
- player.first_frame_received.connect(lambda: self._on_rtsp_retry_success(side))
|
|
|
- player.timeout.connect(lambda: self.on_player_timeout(label, player, side))
|
|
|
+ player.first_frame_received.connect(lambda s=side: self._on_rtsp_retry_success(s))
|
|
|
+ player.timeout.connect(lambda l=label, p=player, s=side: self.on_player_timeout(l, p, s))
|
|
|
old = self.left_player if side == "left" else self.right_player
|
|
|
if old:
|
|
|
try:
|
|
|
@@ -866,8 +872,8 @@ class MainWindow(QMainWindow):
|
|
|
timeout = self.config.video.rtsp_timeout
|
|
|
player = RTSPPlayer(left_cam.rtsp_url, w, h, timeout_sec=timeout)
|
|
|
player.frame_ready.connect(self.left_label.setPixmap)
|
|
|
- player.first_frame_received.connect(lambda: self.hide_loading_on_label(self.left_label))
|
|
|
- player.timeout.connect(lambda: self.on_player_timeout(self.left_label, player, "left"))
|
|
|
+ player.first_frame_received.connect(lambda p=player: self.hide_loading_on_label(self.left_label))
|
|
|
+ player.timeout.connect(lambda p=player: self.on_player_timeout(self.left_label, p, "left"))
|
|
|
self.left_player = player
|
|
|
player.start()
|
|
|
|
|
|
@@ -877,8 +883,8 @@ class MainWindow(QMainWindow):
|
|
|
timeout = self.config.video.rtsp_timeout
|
|
|
player = RTSPPlayer(right_cam.rtsp_url, w, h, timeout_sec=timeout)
|
|
|
player.frame_ready.connect(self.right_label.setPixmap)
|
|
|
- player.first_frame_received.connect(lambda: self.hide_loading_on_label(self.right_label))
|
|
|
- player.timeout.connect(lambda: self.on_player_timeout(self.right_label, player, "right"))
|
|
|
+ player.first_frame_received.connect(lambda p=player: self.hide_loading_on_label(self.right_label))
|
|
|
+ player.timeout.connect(lambda p=player: self.on_player_timeout(self.right_label, p, "right"))
|
|
|
self.right_player = player
|
|
|
player.start()
|
|
|
else:
|
|
|
@@ -1000,6 +1006,10 @@ class MainWindow(QMainWindow):
|
|
|
return
|
|
|
if not self.ai_enabled:
|
|
|
return
|
|
|
+ now = time.time()
|
|
|
+ if now - self._last_mqtt_process < 0.03:
|
|
|
+ return
|
|
|
+ self._last_mqtt_process = now
|
|
|
for cam in self.active_bucket.cameras:
|
|
|
if cam.topic == topic:
|
|
|
self.update_video_size()
|
|
|
@@ -1199,7 +1209,9 @@ class MainWindow(QMainWindow):
|
|
|
# 每 30 秒检测一次摄像头信号
|
|
|
if now - self._last_signal_check > 30:
|
|
|
self._last_signal_check = now
|
|
|
- threading.Thread(target=self._check_all_cameras, daemon=True).start()
|
|
|
+ if not getattr(self, '_signal_check_thread', None) or not self._signal_check_thread.is_alive():
|
|
|
+ self._signal_check_thread = threading.Thread(target=self._check_all_cameras, daemon=True)
|
|
|
+ self._signal_check_thread.start()
|
|
|
|
|
|
def _collect_camera_ips(self) -> list:
|
|
|
ips = set()
|
|
|
@@ -1276,16 +1288,14 @@ class MainWindow(QMainWindow):
|
|
|
def _save_config(self):
|
|
|
import configparser
|
|
|
cfg = configparser.ConfigParser()
|
|
|
- config_name = "config.dev.ini" if "--prod" not in sys.argv else "config.prod.ini"
|
|
|
- config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), config_name)
|
|
|
- cfg.read(config_path, encoding="utf-8")
|
|
|
+ cfg.read(self._config_path, encoding="utf-8")
|
|
|
if not cfg.has_section("AI"):
|
|
|
cfg.add_section("AI")
|
|
|
val = "true" if self.config.ai.tts_enabled else "false"
|
|
|
cfg.set("AI", "tts_enabled", val)
|
|
|
- with open(config_path, "w", encoding="utf-8") as f:
|
|
|
+ with open(self._config_path, "w", encoding="utf-8") as f:
|
|
|
cfg.write(f)
|
|
|
- logger.info(f"TTS 设置已保存: {config_path} → tts_enabled={val}")
|
|
|
+ logger.info(f"TTS 设置已保存: {self._config_path} → tts_enabled={val}")
|
|
|
|
|
|
@staticmethod
|
|
|
def _autostart_config_dir():
|