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 ,
8788class DLCLiveMainWindow (QMainWindow ):
8889 """Main application window."""
8990
91+ _recording_stopped_async = Signal ()
92+
9093 def __init__ (self , config : ApplicationSettings | None = None ):
9194 super ().__init__ ()
9295 self .setWindowTitle ("DeepLabCut Live GUI" )
@@ -178,6 +181,9 @@ def __init__(self, config: ApplicationSettings | None = None):
178181 self ._dlc_tile_scale : tuple [float , float ] = (1.0 , 1.0 ) # (scale_x, scale_y)
179182 # Display flag (decoupled from frame capture for performance)
180183 self ._display_dirty : bool = False
184+ # Recording state
185+ self ._recording_stopping = False
186+ self ._recording_stopped_async .connect (self ._on_recording_stopped_async )
181187
182188 self ._load_icons ()
183189 self ._preview_pixmap = QPixmap (LOGO_ALPHA )
@@ -641,11 +647,11 @@ def _build_recording_group(self) -> QGroupBox:
641647 form .addRow (grid )
642648
643649 # Recording options
644- self .record_with_overlays_checkbox = QCheckBox ("Record video with overlays" )
645- self .record_with_overlays_checkbox .setToolTip (
646- "Enable to include pose overlays in recorded video (keypoints & bounding boxes)"
647- )
648- self .record_with_overlays_checkbox .setChecked (False )
650+ # self.record_with_overlays_checkbox = QCheckBox("Record video with overlays")
651+ # self.record_with_overlays_checkbox.setToolTip(
652+ # "Enable to include pose overlays in recorded video (keypoints & bounding boxes)"
653+ # )
654+ # self.record_with_overlays_checkbox.setChecked(False)
649655
650656 self .fast_encoding_checkbox = QCheckBox ("Use faster encoding parameters" )
651657 self .fast_encoding_checkbox .setToolTip (
@@ -658,7 +664,7 @@ def _build_recording_group(self) -> QGroupBox:
658664 recording_options = QWidget ()
659665 recording_options_layout = QHBoxLayout (recording_options )
660666 recording_options_layout .setContentsMargins (0 , 0 , 0 , 0 )
661- recording_options_layout .addWidget (self .record_with_overlays_checkbox )
667+ # recording_options_layout.addWidget(self.record_with_overlays_checkbox)
662668 recording_options_layout .addWidget (self .fast_encoding_checkbox )
663669 recording_options_layout .addStretch (1 )
664670
@@ -1454,8 +1460,8 @@ def _on_recording_frame_ready(
14541460 if not self ._rec_manager .is_active :
14551461 return
14561462
1457- if self .record_with_overlays_checkbox .isChecked ():
1458- frame = self ._render_overlays_for_recording (camera_id , frame )
1463+ # if self.record_with_overlays_checkbox.isChecked():
1464+ # frame = self._render_overlays_for_recording(camera_id, frame)
14591465
14601466 self ._rec_manager .write_frame (camera_id , frame , timestamp , timestamp_metadata = timestamp_metadata )
14611467
@@ -1611,9 +1617,35 @@ def _stop_multi_camera_recording(self) -> None:
16111617 if not self ._rec_manager .is_active :
16121618 return
16131619
1614- self .multi_camera_controller .set_recording_frame_do_emit (False )
1620+ if getattr (self , "_recording_stopping" , False ):
1621+ return
1622+
1623+ self ._recording_stopping = True
16151624
1616- self ._rec_manager .stop_all ()
1625+ self .start_record_button .setEnabled (False )
1626+ self .stop_record_button .setEnabled (False )
1627+ self .statusBar ().showMessage ("Stopping multi-camera recording…" , 3000 )
1628+
1629+ # Stop frame emission immediately so no new frames enter recording pipeline.
1630+ try :
1631+ self .multi_camera_controller .set_recording_frame_do_emit (False )
1632+ except Exception :
1633+ logger .exception ("Failed to disable recording frame emission" )
1634+
1635+ def worker ():
1636+ try :
1637+ self ._rec_manager .stop_all ()
1638+ finally :
1639+ self ._recording_stopped_async .emit ()
1640+
1641+ threading .Thread (
1642+ target = worker ,
1643+ name = "StopRecordingWorker" ,
1644+ daemon = True ,
1645+ ).start ()
1646+
1647+ def _on_recording_stopped_async (self ) -> None :
1648+ self ._recording_stopping = False
16171649 self .start_record_button .setEnabled (True )
16181650 self .stop_record_button .setEnabled (False )
16191651 self .statusBar ().showMessage ("Multi-camera recording stopped" , 3000 )
0 commit comments