Skip to content

Commit 04da309

Browse files
committed
Refine custom processor UI controls
Renames and repurposes processor control UI to a custom-processor toggle tied to processor selection, including a combined status/toggle row that only appears when a processor is chosen. Processor instantiation and status updates are now gated by the new `_custom_processor_enabled` logic, and disabled selections are explicitly reported without loading a plugin. Updated the recording paths UI test to use the new checkbox.
1 parent 767bc8f commit 04da309

2 files changed

Lines changed: 66 additions & 43 deletions

File tree

‎dlclivegui/gui/main_window.py‎

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,34 @@ def _build_dlc_group(self) -> QGroupBox:
481481
processing_sttgs = lyts.make_two_field_row(
482482
"Inference camera",
483483
self.dlc_camera_combo,
484-
"Processor",
484+
"Custom processor",
485485
self.processor_combo,
486486
key_width=None,
487487
)
488488
self.dlc_camera_combo.update_shrink_width()
489489
form.addRow(processing_sttgs)
490490

491+
self.processor_status_label = QLabel("Processor: No clients | Recording: No")
492+
self.processor_status_label.setWordWrap(True)
493+
# form.addRow("Processor Status", self.processor_status_label)
494+
self.use_custom_proc_checkbox = QCheckBox("Use custom processor")
495+
self.use_custom_proc_checkbox.setChecked(False)
496+
self.use_custom_proc_checkbox.setToolTip(
497+
"If enabled, the GUI will load and interact with the selected processor plugin.\n"
498+
)
499+
self.processor_toggle_row = lyts.make_two_field_row(
500+
"Processor status",
501+
self.processor_status_label,
502+
None,
503+
self.use_custom_proc_checkbox,
504+
key_width=None,
505+
left_stretch=0,
506+
right_stretch=0,
507+
style_values=False,
508+
)
509+
self.processor_toggle_row.setVisible(False) # Hide until a processor is selected
510+
form.addRow(self.processor_toggle_row)
511+
491512
# Wrap inference buttons in a widget to prevent shifting
492513
inference_button_widget = QWidget()
493514
inference_buttons = QHBoxLayout(inference_button_widget)
@@ -508,17 +529,6 @@ def _build_dlc_group(self) -> QGroupBox:
508529
# self.show_predictions_checkbox.setChecked(True)
509530
# form.addRow(self.show_predictions_checkbox)
510531

511-
self.allow_processor_ctrl_checkbox = QCheckBox("Allow processor-based control")
512-
self.allow_processor_ctrl_checkbox.setChecked(False)
513-
self.allow_processor_ctrl_checkbox.setToolTip(
514-
"If enabled, the GUI will load and interact with the selected processor plugin.\n"
515-
)
516-
form.addRow(self.allow_processor_ctrl_checkbox)
517-
518-
self.processor_status_label = QLabel("Processor: No clients | Recording: No")
519-
self.processor_status_label.setWordWrap(True)
520-
form.addRow("Processor Status", self.processor_status_label)
521-
522532
return group
523533

524534
def _build_recording_group(self) -> QGroupBox:
@@ -801,8 +811,8 @@ def _connect_signals(self) -> None:
801811
self._dlc.initialized.connect(self._on_dlc_initialised)
802812
self.dlc_camera_combo.currentIndexChanged.connect(self._on_dlc_camera_changed)
803813
self.dlc_camera_combo.currentTextChanged.connect(self.dlc_camera_combo.update_shrink_width)
804-
self.allow_processor_ctrl_checkbox.stateChanged.connect(lambda _s: self._update_dlc_controls_enabled())
805-
self.allow_processor_ctrl_checkbox.stateChanged.connect(lambda _s: self._update_processor_status())
814+
self.processor_combo.currentIndexChanged.connect(self._on_processor_selection_changed)
815+
self.use_custom_proc_checkbox.stateChanged.connect(lambda _s: self._update_processor_status())
806816

807817
# Recording settings
808818
## Session name persistence + preview updates
@@ -1133,9 +1143,11 @@ def _action_open_recording_folder(self) -> None:
11331143
logger.error(f"Failed to open folder: {exc}")
11341144
self.statusBar().showMessage("Could not open recording folder.", 5000)
11351145

1136-
def _processor_control_enabled(self) -> bool:
1146+
def _custom_processor_enabled(self) -> bool:
11371147
return bool(
1138-
getattr(self, "allow_processor_ctrl_checkbox", None) and self.allow_processor_ctrl_checkbox.isChecked()
1148+
getattr(self, "use_custom_proc_checkbox", None)
1149+
and self.use_custom_proc_checkbox.isChecked()
1150+
and self.processor_combo.currentData() is not None
11391151
)
11401152

11411153
def _refresh_processors(self) -> None:
@@ -1710,23 +1722,20 @@ def _configure_dlc(self) -> bool:
17101722

17111723
# Instantiate processor if selected
17121724
processor = None
1713-
if self._processor_control_enabled():
1714-
selected_key = self.processor_combo.currentData()
1715-
if selected_key is not None and self._scanned_processors:
1716-
try:
1717-
# For now, instantiate with no parameters
1718-
processor = instantiate_from_scan(self._scanned_processors, selected_key)
1719-
processor_name = self._scanned_processors[selected_key]["name"]
1720-
self.statusBar().showMessage(f"Loaded processor: {processor_name}", 3000)
1721-
except Exception as e:
1722-
error_msg = f"Failed to instantiate processor: {e}"
1723-
self._show_error(error_msg)
1724-
logger.error(error_msg)
1725-
return False
1726-
else:
1727-
selected_key = self.processor_combo.currentData()
1728-
if selected_key is not None:
1729-
self.statusBar().showMessage(f"Processor selection ignored (control disabled): {selected_key}", 3000)
1725+
selected_key = self.processor_combo.currentData()
1726+
if self._custom_processor_enabled():
1727+
try:
1728+
# For now, instantiate with no parameters
1729+
processor = instantiate_from_scan(self._scanned_processors, selected_key)
1730+
processor_name = self._scanned_processors[selected_key]["name"]
1731+
self.statusBar().showMessage(f"Loaded processor: {processor_name}", 3000)
1732+
except Exception as e:
1733+
error_msg = f"Failed to instantiate processor: {e}"
1734+
self._show_error(error_msg)
1735+
logger.error(error_msg)
1736+
return False
1737+
elif selected_key is not None:
1738+
self.statusBar().showMessage(f"Custom processor disabled: {selected_key}", 3000)
17301739

17311740
self._dlc.configure(settings, processor=processor)
17321741
self._model_path_store.save_if_valid(settings.model_path)
@@ -1760,8 +1769,8 @@ def _update_dlc_controls_enabled(self) -> None:
17601769
for widget in processor_widgets:
17611770
widget.setEnabled(allow_changes)
17621771

1763-
if hasattr(self, "allow_processor_ctrl_checkbox"):
1764-
self.allow_processor_ctrl_checkbox.setEnabled(allow_changes)
1772+
if hasattr(self, "use_custom_proc_checkbox"):
1773+
self.use_custom_proc_checkbox.setEnabled(allow_changes)
17651774

17661775
def _update_camera_controls_enabled(self) -> None:
17671776
multi_cam_recording = self._rec_manager.is_active
@@ -1851,7 +1860,7 @@ def _update_metrics(self) -> None:
18511860
self.dlc_stats_label.setText("DLC processor idle")
18521861

18531862
# Update processor status (connection and recording state)
1854-
if hasattr(self, "processor_status_label") and self._processor_control_enabled():
1863+
if hasattr(self, "processor_status_label") and self._custom_processor_enabled():
18551864
self._update_processor_status()
18561865

18571866
# --- Recorder stats ---
@@ -1863,26 +1872,40 @@ def _update_metrics(self) -> None:
18631872
else:
18641873
self.recording_stats_label.setText(self._last_recorder_summary)
18651874

1875+
def _on_processor_selection_changed(
1876+
self,
1877+
_index: int,
1878+
) -> None:
1879+
"""Enable custom processing when a processor is selected."""
1880+
has_selection = self.processor_combo.currentData() is not None
1881+
self.processor_toggle_row.setVisible(has_selection)
1882+
1883+
self.use_custom_proc_checkbox.blockSignals(True)
1884+
self.use_custom_proc_checkbox.setChecked(has_selection)
1885+
self.use_custom_proc_checkbox.blockSignals(False)
1886+
1887+
self._update_processor_status()
1888+
18661889
def _update_processor_status(self) -> None:
18671890
"""Update processor connection and recording status, handle auto-recording."""
1868-
if not self._processor_control_enabled():
1869-
self.processor_status_label.setText("Processor control disabled")
1891+
if not self._custom_processor_enabled():
1892+
self.processor_status_label.setText("Disabled")
18701893
return
18711894

18721895
if not self._dlc_active or not self._dlc_initialized:
1873-
self.processor_status_label.setText("Processor: Not active")
1896+
self.processor_status_label.setText("Not active")
18741897
return
18751898

18761899
# Get processor instance from _dlc
18771900
processor = self._dlc._processor
18781901

18791902
if processor is None:
1880-
self.processor_status_label.setText("Processor: None loaded")
1903+
self.processor_status_label.setText("None loaded")
18811904
return
18821905

18831906
# Check if processor has the required attributes (socket-based processors)
18841907
if not hasattr(processor, "conns") or not hasattr(processor, "_recording"):
1885-
self.processor_status_label.setText("Processor: No status info")
1908+
self.processor_status_label.setText("No status info")
18861909
return
18871910

18881911
# Get connection count and recording state
@@ -1895,7 +1918,7 @@ def _update_processor_status(self) -> None:
18951918
self.processor_status_label.setText(f"Clients: {client_str} | Recording: {recording_str}")
18961919

18971920
# Handle auto-recording based on processor's video recording flag
1898-
if hasattr(processor, "_vid_recording") and self.allow_processor_ctrl_checkbox.isChecked():
1921+
if hasattr(processor, "_vid_recording") and self.use_custom_proc_checkbox.isChecked():
18991922
current_vid_recording = processor.video_recording
19001923

19011924
# Check if video recording state changed

‎tests/gui/test_recording_paths_ui.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def test_processor_overrides_session_name_and_persists(window, start_all_spy, mo
139139
# Arrange window state so processor status logic runs
140140
window._dlc_active = True
141141
window._dlc_initialized = True
142-
window.allow_processor_ctrl_checkbox.setChecked(True)
142+
window.use_custom_proc_checkbox.setChecked(True)
143143

144144
# Patch start_recording to avoid preview start/timers
145145
monkeypatch.setattr(window, "_start_recording", lambda: window._start_multi_camera_recording())

0 commit comments

Comments
 (0)