@@ -417,6 +417,12 @@ def _build_dlc_group(self) -> QGroupBox:
417417 self .browse_processor_folder_button = QPushButton ("Browse..." )
418418 self .browse_processor_folder_button .setIcon (self .style ().standardIcon (QStyle .StandardPixmap .SP_DirOpenIcon ))
419419 self .browse_processor_folder_button .clicked .connect (self ._action_browse_processor_folder )
420+ self .browse_processor_folder_button .setToolTip (
421+ "Select the folder to scan for DLC processor plugins."
422+ "<br>Plugins must inherit from dlclive.Processor, see dlclivegui.processors for more details."
423+ "<br><b>Important:</b> External processors are Python code that will be imported during discovery."
424+ "<br>Only use processor plugins from trusted sources to avoid security risks."
425+ )
420426 processor_path_layout .addWidget (self .browse_processor_folder_button )
421427
422428 self .refresh_processors_button = QPushButton ("Refresh" )
@@ -457,12 +463,12 @@ def _build_dlc_group(self) -> QGroupBox:
457463 self .show_predictions_checkbox .setChecked (True )
458464 form .addRow (self .show_predictions_checkbox )
459465
460- self .auto_record_checkbox = QCheckBox ("Auto-record video on processor command " )
461- self .auto_record_checkbox .setChecked (False )
462- self .auto_record_checkbox .setToolTip (
463- "Automatically start/stop video recording when processor receives video recording commands "
466+ self .allow_processor_ctrl_checkbox = QCheckBox ("Allow processor-based control " )
467+ self .allow_processor_ctrl_checkbox .setChecked (False )
468+ self .allow_processor_ctrl_checkbox .setToolTip (
469+ "If enabled, the GUI will load and interact with the selected processor plugin. \n "
464470 )
465- form .addRow (self .auto_record_checkbox )
471+ form .addRow (self .allow_processor_ctrl_checkbox )
466472
467473 self .processor_status_label = QLabel ("Processor: No clients | Recording: No" )
468474 self .processor_status_label .setWordWrap (True )
@@ -958,6 +964,11 @@ def _action_open_recording_folder(self) -> None:
958964 logger .error (f"Failed to open folder: { exc } " )
959965 self .statusBar ().showMessage ("Could not open recording folder." , 5000 )
960966
967+ def _processor_control_enabled (self ) -> bool :
968+ return bool (
969+ getattr (self , "allow_processor_ctrl_checkbox" , None ) and self .allow_processor_ctrl_checkbox .isChecked ()
970+ )
971+
961972 def _refresh_processors (self ) -> None :
962973 self .processor_combo .clear ()
963974 self .processor_combo .addItem ("No Processor" , None )
@@ -1481,20 +1492,23 @@ def _configure_dlc(self) -> bool:
14811492
14821493 # Instantiate processor if selected
14831494 processor = None
1484- selected_key = self .processor_combo .currentData ()
1485- if selected_key is not None and self ._scanned_processors :
1486- try :
1487- # For now, instantiate with no parameters
1488- # TODO: Add parameter dialog for processors that need params
1489- # or pass kwargs from config ?
1490- processor = instantiate_from_scan (self ._scanned_processors , selected_key )
1491- processor_name = self ._scanned_processors [selected_key ]["name" ]
1492- self .statusBar ().showMessage (f"Loaded processor: { processor_name } " , 3000 )
1493- except Exception as e :
1494- error_msg = f"Failed to instantiate processor: { e } "
1495- self ._show_error (error_msg )
1496- logger .error (error_msg )
1497- return False
1495+ if self ._processor_control_enabled ():
1496+ selected_key = self .processor_combo .currentData ()
1497+ if selected_key is not None and self ._scanned_processors :
1498+ try :
1499+ # For now, instantiate with no parameters
1500+ processor = instantiate_from_scan (self ._scanned_processors , selected_key )
1501+ processor_name = self ._scanned_processors [selected_key ]["name" ]
1502+ self .statusBar ().showMessage (f"Loaded processor: { processor_name } " , 3000 )
1503+ except Exception as e :
1504+ error_msg = f"Failed to instantiate processor: { e } "
1505+ self ._show_error (error_msg )
1506+ logger .error (error_msg )
1507+ return False
1508+ else :
1509+ selected_key = self .processor_combo .currentData ()
1510+ if selected_key is not None :
1511+ self .statusBar ().showMessage (f"Processor selection ignored (control disabled): { selected_key } " , 3000 )
14981512
14991513 self ._dlc .configure (settings , processor = processor )
15001514 self ._model_path_store .save_if_valid (settings .model_path )
@@ -1508,18 +1522,24 @@ def _update_inference_buttons(self) -> None:
15081522 def _update_dlc_controls_enabled (self ) -> None :
15091523 """Enable/disable DLC settings based on inference state."""
15101524 allow_changes = not self ._dlc_active
1525+ processor_controls = allow_changes and self ._processor_control_enabled ()
1526+
15111527 widgets = [
15121528 self .model_path_edit ,
15131529 self .browse_model_button ,
1530+ self .dlc_camera_combo ,
1531+ # self.additional_options_edit,
1532+ ]
1533+ processor_widgets = [
15141534 self .processor_folder_edit ,
15151535 self .browse_processor_folder_button ,
15161536 self .refresh_processors_button ,
15171537 self .processor_combo ,
1518- # self.additional_options_edit,
1519- self .dlc_camera_combo ,
15201538 ]
15211539 for widget in widgets :
15221540 widget .setEnabled (allow_changes )
1541+ for widget in processor_widgets :
1542+ widget .setEnabled (processor_controls )
15231543
15241544 def _update_camera_controls_enabled (self ) -> None :
15251545 multi_cam_recording = self ._rec_manager .is_active
@@ -1606,7 +1626,7 @@ def _update_metrics(self) -> None:
16061626 self .dlc_stats_label .setText ("DLC processor idle" )
16071627
16081628 # Update processor status (connection and recording state)
1609- if hasattr (self , "processor_status_label" ):
1629+ if hasattr (self , "processor_status_label" ) and self . _processor_control_enabled () :
16101630 self ._update_processor_status ()
16111631
16121632 # --- Recorder stats ---
@@ -1620,6 +1640,10 @@ def _update_metrics(self) -> None:
16201640
16211641 def _update_processor_status (self ) -> None :
16221642 """Update processor connection and recording status, handle auto-recording."""
1643+ if not self ._processor_control_enabled ():
1644+ self .processor_status_label .setText ("Processor control disabled" )
1645+ return
1646+
16231647 if not self ._dlc_active or not self ._dlc_initialized :
16241648 self .processor_status_label .setText ("Processor: Not active" )
16251649 return
@@ -1646,7 +1670,7 @@ def _update_processor_status(self) -> None:
16461670 self .processor_status_label .setText (f"Clients: { client_str } | Recording: { recording_str } " )
16471671
16481672 # Handle auto-recording based on processor's video recording flag
1649- if hasattr (processor , "_vid_recording" ) and self .auto_record_checkbox .isChecked ():
1673+ if hasattr (processor , "_vid_recording" ) and self .allow_processor_ctrl_checkbox .isChecked ():
16501674 current_vid_recording = processor .video_recording
16511675
16521676 # Check if video recording state changed
0 commit comments