66import json
77import logging
88import os
9+ import threading
910import time
1011from pathlib import Path
1112
1213import cv2
1314import numpy as np
14- from PySide6 .QtCore import QRect , QSettings , Qt , QTimer , QUrl
15+ from PySide6 .QtCore import QRect , QSettings , Qt , QTimer , QUrl , Signal
1516from PySide6 .QtGui import (
1617 QAction ,
1718 QActionGroup ,
8889class DLCLiveMainWindow (QMainWindow ):
8990 """Main application window."""
9091
92+ _recording_stopped_async = Signal ()
93+
9194 def __init__ (self , config : ApplicationSettings | None = None ):
9295 super ().__init__ ()
9396 self .setWindowTitle ("DeepLabCut Live GUI" )
@@ -179,6 +182,9 @@ def __init__(self, config: ApplicationSettings | None = None):
179182 self ._dlc_tile_scale : tuple [float , float ] = (1.0 , 1.0 ) # (scale_x, scale_y)
180183 # Display flag (decoupled from frame capture for performance)
181184 self ._display_dirty : bool = False
185+ # Recording state
186+ self ._recording_stopping = False
187+ self ._recording_stopped_async .connect (self ._on_recording_stopped_async )
182188
183189 self ._load_icons ()
184190 self ._preview_pixmap = QPixmap (LOGO_ALPHA )
@@ -642,11 +648,11 @@ def _build_recording_group(self) -> QGroupBox:
642648 form .addRow (grid )
643649
644650 # Recording options
645- self .record_with_overlays_checkbox = QCheckBox ("Record video with overlays" )
646- self .record_with_overlays_checkbox .setToolTip (
647- "Enable to include pose overlays in recorded video (keypoints & bounding boxes)"
648- )
649- self .record_with_overlays_checkbox .setChecked (False )
651+ # self.record_with_overlays_checkbox = QCheckBox("Record video with overlays")
652+ # self.record_with_overlays_checkbox.setToolTip(
653+ # "Enable to include pose overlays in recorded video (keypoints & bounding boxes)"
654+ # )
655+ # self.record_with_overlays_checkbox.setChecked(False)
650656
651657 self .fast_encoding_checkbox = QCheckBox ("Use faster encoding parameters" )
652658 self .fast_encoding_checkbox .setToolTip (
@@ -659,7 +665,7 @@ def _build_recording_group(self) -> QGroupBox:
659665 recording_options = QWidget ()
660666 recording_options_layout = QHBoxLayout (recording_options )
661667 recording_options_layout .setContentsMargins (0 , 0 , 0 , 0 )
662- recording_options_layout .addWidget (self .record_with_overlays_checkbox )
668+ # recording_options_layout.addWidget(self.record_with_overlays_checkbox)
663669 recording_options_layout .addWidget (self .fast_encoding_checkbox )
664670 recording_options_layout .addStretch (1 )
665671
@@ -1458,8 +1464,8 @@ def _on_recording_frame_ready(
14581464 if not self ._rec_manager .is_active :
14591465 return
14601466
1461- if self .record_with_overlays_checkbox .isChecked ():
1462- frame = self ._render_overlays_for_recording (camera_id , frame )
1467+ # if self.record_with_overlays_checkbox.isChecked():
1468+ # frame = self._render_overlays_for_recording(camera_id, frame)
14631469
14641470 self ._rec_manager .write_frame (camera_id , frame , timestamp , timestamp_metadata = timestamp_metadata )
14651471
@@ -1615,9 +1621,35 @@ def _stop_multi_camera_recording(self) -> None:
16151621 if not self ._rec_manager .is_active :
16161622 return
16171623
1618- self .multi_camera_controller .set_recording_frame_do_emit (False )
1624+ if getattr (self , "_recording_stopping" , False ):
1625+ return
1626+
1627+ self ._recording_stopping = True
16191628
1620- self ._rec_manager .stop_all ()
1629+ self .start_record_button .setEnabled (False )
1630+ self .stop_record_button .setEnabled (False )
1631+ self .statusBar ().showMessage ("Stopping multi-camera recording…" , 3000 )
1632+
1633+ # Stop frame emission immediately so no new frames enter recording pipeline.
1634+ try :
1635+ self .multi_camera_controller .set_recording_frame_do_emit (False )
1636+ except Exception :
1637+ logger .exception ("Failed to disable recording frame emission" )
1638+
1639+ def worker ():
1640+ try :
1641+ self ._rec_manager .stop_all ()
1642+ finally :
1643+ self ._recording_stopped_async .emit ()
1644+
1645+ threading .Thread (
1646+ target = worker ,
1647+ name = "StopRecordingWorker" ,
1648+ daemon = True ,
1649+ ).start ()
1650+
1651+ def _on_recording_stopped_async (self ) -> None :
1652+ self ._recording_stopping = False
16211653 self .start_record_button .setEnabled (True )
16221654 self .stop_record_button .setEnabled (False )
16231655 self .statusBar ().showMessage ("Multi-camera recording stopped" , 3000 )
0 commit comments