Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions dlclivegui/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def _build_dlc_group(self) -> QGroupBox:
# Processor selection
processor_path_layout = QHBoxLayout()
self.processor_folder_edit = QLineEdit()
self.processor_folder_edit.setText(default_processors_dir())
self.processor_folder_edit.setText(self._settings_store.get_processor_folder(default=default_processors_dir()))
processor_path_layout.addWidget(self.processor_folder_edit)

self.browse_processor_folder_button = QPushButton("Browse...")
Expand Down Expand Up @@ -1081,10 +1081,11 @@ def _action_browse_directory(self) -> None:

def _action_browse_processor_folder(self) -> None:
"""Browse for processor folder."""
current_path = self.processor_folder_edit.text() or default_processors_dir()
current_path = self.processor_folder_edit.text().strip() or default_processors_dir()
directory = QFileDialog.getExistingDirectory(self, "Select processor folder", current_path)
if directory:
self.processor_folder_edit.setText(directory)
self._settings_store.set_processor_folder(directory)
self._refresh_processors()

def _action_open_recording_folder(self) -> None:
Expand Down Expand Up @@ -1138,10 +1139,17 @@ def _refresh_processors(self) -> None:
self.processor_combo.addItem("No Processor", None)

selected_folder = self.processor_folder_edit.text().strip()
if Path(selected_folder).exists():
self._scanned_processors = scan_processor_folder(selected_folder)
selected_path = Path(selected_folder).expanduser() if selected_folder else None

if selected_path is not None and selected_path.is_dir():
resolved_folder = str(selected_path.resolve())
self._settings_store.set_processor_folder(resolved_folder)
self._scanned_processors = scan_processor_folder(resolved_folder)
source_text = resolved_folder
else:
self._scanned_processors = scan_processor_package("dlclivegui.processors")
source_text = "package dlclivegui.processors"

self._processor_keys = list(self._scanned_processors.keys())

for key in self._processor_keys:
Expand All @@ -1150,9 +1158,7 @@ def _refresh_processors(self) -> None:
self.processor_combo.addItem(display_name, key)

self.processor_combo.update_shrink_width()
self.statusBar().showMessage(
f"Found {len(self._processor_keys)} processor(s) in package dlclivegui.processors", 3000
)
self.statusBar().showMessage(f"Found {len(self._processor_keys)} processor(s) in {source_text}", 3000)

# ------------------------------------------------------------------
# Recording path preview and session name persistence
Expand Down Expand Up @@ -1728,24 +1734,28 @@ def _update_inference_buttons(self) -> None:
def _update_dlc_controls_enabled(self) -> None:
"""Enable/disable DLC settings based on inference state."""
allow_changes = not self._dlc_active
processor_controls = allow_changes and self._processor_control_enabled()

widgets = [
self.model_path_edit,
self.browse_model_button,
self.dlc_camera_combo,
# self.additional_options_edit,
]

processor_widgets = [
self.processor_folder_edit,
self.browse_processor_folder_button,
self.refresh_processors_button,
self.processor_combo,
]

for widget in widgets:
widget.setEnabled(allow_changes)

for widget in processor_widgets:
widget.setEnabled(processor_controls)
widget.setEnabled(allow_changes)

if hasattr(self, "allow_processor_ctrl_checkbox"):
self.allow_processor_ctrl_checkbox.setEnabled(allow_changes)

def _update_camera_controls_enabled(self) -> None:
multi_cam_recording = self._rec_manager.is_active
Expand Down Expand Up @@ -2151,6 +2161,9 @@ def closeEvent(self, event: QCloseEvent) -> None: # pragma: no cover - GUI beha

# Remember model path on exit
self._model_path_store.save_if_valid(self.model_path_edit.text().strip())
# Remember processor folder on exit
if hasattr(self, "processor_folder_edit"):
self._settings_store.set_processor_folder(self.processor_folder_edit.text().strip())

# Close the window
super().closeEvent(event)
9 changes: 8 additions & 1 deletion dlclivegui/gui/recording_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ def write_frame(self, cam_id: str, frame: np.ndarray, timestamp: float | None =
try:
rec.write(frame, timestamp=timestamp if timestamp is not None else time.time())
except Exception as exc:
log.warning("Failed to write frame for %s: %s", cam_id, exc)
log.warning(
"Failed to write frame for %s: %s: %s frame_shape=%s dtype=%s",
cam_id,
type(exc).__name__,
str(exc) or repr(exc),
getattr(frame, "shape", None),
getattr(frame, "dtype", None),
)
try:
rec.stop()
except Exception:
Expand Down
10 changes: 3 additions & 7 deletions dlclivegui/processors/PLUGIN_SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ Processors are Python classes (typically subclasses of `dlclive.Processor`) that

### Useful files

- `dlclivegui/processors/dlc_processor_socket.py` — Example socket-based processor base class + examples
- `dlclivegui/processors/dlc_processor_socket.py` — Example socket-based processor base class
- `dlclivegui/processors/examples.py` — Example processor implementations (e.g., One-Euro filter)
- `dlclivegui/processors/processor_utils.py` — Scanning + instantiation helpers used by the GUI

---
Expand Down Expand Up @@ -204,12 +205,7 @@ The built-in `BaseProcessorSocket` (in `dlc_processor_socket.py`) demonstrates a

```python
from dlclive import Processor

PROCESSOR_REGISTRY = {}

def register_processor(cls):
PROCESSOR_REGISTRY[getattr(cls, "PROCESSOR_ID", cls.__name__)] = cls
return cls
from dlclivegui.processors import register_processor, PROCESSOR_REGISTRY

@register_processor
class MyNewProcessor(Processor):
Expand Down
3 changes: 3 additions & 0 deletions dlclivegui/processors/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .registry import PROCESSOR_REGISTRY, register_processor

__all__ = ["register_processor", "PROCESSOR_REGISTRY"]
Loading