Skip to content

Commit c97d0d2

Browse files
committed
Lock processor settings during DLC inference
Disable all DLC and processor configuration widgets consistently while inference is active, including the processor-control checkbox. Refactor processor discovery into shared helpers that detect direct and indirect `dlclive.Processor` subclasses, standardize metadata extraction, and reuse the same fallback logic for package scans and file-based loading.
1 parent 2b3d8a5 commit c97d0d2

2 files changed

Lines changed: 67 additions & 39 deletions

File tree

dlclivegui/gui/main_window.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,24 +1657,28 @@ def _update_inference_buttons(self) -> None:
16571657
def _update_dlc_controls_enabled(self) -> None:
16581658
"""Enable/disable DLC settings based on inference state."""
16591659
allow_changes = not self._dlc_active
1660-
processor_controls = allow_changes and self._processor_control_enabled()
16611660

16621661
widgets = [
16631662
self.model_path_edit,
16641663
self.browse_model_button,
16651664
self.dlc_camera_combo,
1666-
# self.additional_options_edit,
16671665
]
1666+
16681667
processor_widgets = [
16691668
self.processor_folder_edit,
16701669
self.browse_processor_folder_button,
16711670
self.refresh_processors_button,
16721671
self.processor_combo,
16731672
]
1673+
16741674
for widget in widgets:
16751675
widget.setEnabled(allow_changes)
1676+
16761677
for widget in processor_widgets:
1677-
widget.setEnabled(processor_controls)
1678+
widget.setEnabled(allow_changes)
1679+
1680+
if hasattr(self, "allow_processor_ctrl_checkbox"):
1681+
self.allow_processor_ctrl_checkbox.setEnabled(allow_changes)
16781682

16791683
def _update_camera_controls_enabled(self) -> None:
16801684
multi_cam_recording = self._rec_manager.is_active

dlclivegui/processors/processor_utils.py

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,64 @@ def default_processors_dir() -> str:
1717
return str(path)
1818

1919

20+
def _processor_base_class():
21+
from dlclive import Processor
22+
23+
return Processor
24+
25+
26+
def _is_processor_subclass(obj, *, include_base: bool = False) -> bool:
27+
"""Return True for dlclive.Processor subclasses, including indirect subclasses."""
28+
if not inspect.isclass(obj):
29+
return False
30+
31+
try:
32+
processor_base = _processor_base_class()
33+
except Exception:
34+
logger.exception("Could not import dlclive.Processor")
35+
return False
36+
37+
try:
38+
if obj is processor_base:
39+
return bool(include_base)
40+
return issubclass(obj, processor_base)
41+
except TypeError:
42+
return False
43+
44+
45+
def _processor_info_from_class(cls, fallback_name: str) -> dict:
46+
return {
47+
"class": cls,
48+
"name": getattr(cls, "PROCESSOR_NAME", fallback_name),
49+
"description": getattr(cls, "PROCESSOR_DESCRIPTION", ""),
50+
"params": getattr(cls, "PROCESSOR_PARAMS", {}),
51+
}
52+
53+
54+
def discover_processor_classes(module, *, only_defined_in_module: bool = True) -> dict[str, dict]:
55+
"""Discover dlclive.Processor subclasses in a module.
56+
57+
Includes indirect subclasses of Processor.
58+
59+
Args:
60+
module: Imported Python module.
61+
only_defined_in_module: If True, ignore Processor subclasses imported
62+
from other modules to avoid duplicate registry entries.
63+
"""
64+
processors: dict[str, dict] = {}
65+
66+
for name, obj in inspect.getmembers(module, inspect.isclass):
67+
if only_defined_in_module and getattr(obj, "__module__", None) != module.__name__:
68+
continue
69+
70+
if not _is_processor_subclass(obj):
71+
continue
72+
73+
processors[name] = _processor_info_from_class(obj, name)
74+
75+
return processors
76+
77+
2078
def scan_processor_folder(folder_path):
2179
all_processors = {}
2280
folder = Path(folder_path)
@@ -65,22 +123,7 @@ def scan_processor_package(package_name: str = "dlclivegui.processors") -> dict[
65123
processors = mod.get_available_processors()
66124
else:
67125
# Fallback: scan for dlclive.Processor subclasses
68-
from dlclive import Processor
69-
70-
processors = {}
71-
for attr_name in dir(mod):
72-
obj = getattr(mod, attr_name)
73-
try:
74-
if isinstance(obj, type) and obj is not Processor and issubclass(obj, Processor):
75-
processors[attr_name] = {
76-
"class": obj,
77-
"name": getattr(obj, "PROCESSOR_NAME", attr_name),
78-
"description": getattr(obj, "PROCESSOR_DESCRIPTION", ""),
79-
"params": getattr(obj, "PROCESSOR_PARAMS", {}),
80-
}
81-
except Exception:
82-
# Non-class or weird metaclass; ignore
83-
pass
126+
processors = discover_processor_classes(mod)
84127

85128
# Normalize into your “file::class” shape
86129
module_file = mod.__name__.split(".")[-1] + ".py"
@@ -131,26 +174,7 @@ def load_processors_from_file(file_path: str | Path):
131174
return processors
132175

133176
# Fallback path: discover subclasses of dlclive.Processor
134-
from dlclive import Processor
135-
136-
processors: dict[str, dict] = {}
137-
for name, obj in inspect.getmembers(module, inspect.isclass):
138-
if obj is Processor:
139-
continue
140-
# Guard: module might define other classes; only include Processor subclasses
141-
try:
142-
if issubclass(obj, Processor):
143-
processors[name] = {
144-
"class": obj,
145-
"name": getattr(obj, "PROCESSOR_NAME", name),
146-
"description": getattr(obj, "PROCESSOR_DESCRIPTION", ""),
147-
"params": getattr(obj, "PROCESSOR_PARAMS", {}),
148-
}
149-
except Exception:
150-
# Some "classes" can fail issubclass checks; ignore safely
151-
continue
152-
153-
return processors
177+
return discover_processor_classes(module)
154178

155179
except Exception:
156180
# Full traceback helps a ton when a plugin fails to import

0 commit comments

Comments
 (0)