organize settings to dedicated sensors folder, json + assets
This commit is contained in:
@@ -2,14 +2,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import (
|
||||
QAction,
|
||||
QActionGroup,
|
||||
QApplication,
|
||||
QDockWidget,
|
||||
QFileDialog,
|
||||
QMainWindow,
|
||||
QSplitter,
|
||||
@@ -61,6 +66,7 @@ class FingerDashboardWindow(QMainWindow):
|
||||
self._frame_index = 0
|
||||
self._wall_fps = 0.0
|
||||
self._wall_fps_alpha = 0.15
|
||||
self._log_display_mode = "hidden"
|
||||
|
||||
self.setWindowTitle(layout.window_title)
|
||||
self.setStyleSheet(STYLE)
|
||||
@@ -84,7 +90,7 @@ class FingerDashboardWindow(QMainWindow):
|
||||
root_layout.setSpacing(layout.root_spacing_px)
|
||||
|
||||
setup = layout.panel_setup
|
||||
self._log_panel_visible = setup.log_panel.enabled
|
||||
self._log_panel_visible = False
|
||||
self.mesh_state = MeshViewState.from_layout(layout)
|
||||
self.mesh_viewport = MeshViewportWidget(layout=layout, mesh_state=self.mesh_state)
|
||||
arrows_slot = layout.slot("arrows")
|
||||
@@ -111,7 +117,6 @@ class FingerDashboardWindow(QMainWindow):
|
||||
|
||||
self._preferences = load_preferences()
|
||||
self.settings_panel.apply_preference_state(self._preferences)
|
||||
self._log_panel_visible = bool(self.settings_panel.show_log_check.isChecked())
|
||||
self.mesh_viewport.set_free_rotation(bool(self.settings_panel.free_rotate_check.isChecked()))
|
||||
if self.arrow_panel is not None:
|
||||
self.arrow_panel.set_background(self.settings_panel.arrow_background())
|
||||
@@ -152,11 +157,6 @@ class FingerDashboardWindow(QMainWindow):
|
||||
|
||||
self.charts_column = QSplitter(Qt.Vertical)
|
||||
self.charts_column.addWidget(self.force_charts.widget)
|
||||
if setup.log_panel.enabled:
|
||||
self.charts_column.addWidget(self.log_widget)
|
||||
self.log_widget.setVisible(self._log_panel_visible)
|
||||
self.charts_column.setStretchFactor(0, setup.charts_log_stretch)
|
||||
self.charts_column.setStretchFactor(1, setup.log_stretch)
|
||||
self.charts_column.setChildrenCollapsible(False)
|
||||
|
||||
h_splitter = QSplitter(Qt.Horizontal)
|
||||
@@ -172,18 +172,104 @@ class FingerDashboardWindow(QMainWindow):
|
||||
self.h_splitter = h_splitter
|
||||
|
||||
root_layout.addWidget(h_splitter)
|
||||
self._setup_log_dock()
|
||||
self._apply_log_display_mode(self._preferences.get("log_display_mode", "hidden"))
|
||||
|
||||
self.resize(*self._target_window_size())
|
||||
QTimer.singleShot(0, self._apply_panel_layout)
|
||||
|
||||
def _set_log_panel_visible(self, visible: bool) -> None:
|
||||
self._log_panel_visible = bool(visible)
|
||||
self.log_widget.setVisible(self._log_panel_visible)
|
||||
if hasattr(self, "log_dock"):
|
||||
self.log_dock.setVisible(self._log_panel_visible)
|
||||
QTimer.singleShot(0, self._apply_panel_layout)
|
||||
|
||||
def _setup_log_dock(self) -> None:
|
||||
self.log_dock = QDockWidget("Log", self)
|
||||
self.log_dock.setObjectName("logDock")
|
||||
self.log_dock.setWidget(self.log_widget)
|
||||
self.log_dock.setAllowedAreas(Qt.BottomDockWidgetArea | Qt.RightDockWidgetArea)
|
||||
self.addDockWidget(Qt.BottomDockWidgetArea, self.log_dock)
|
||||
self.log_dock.hide()
|
||||
self.log_dock.visibilityChanged.connect(self._on_log_dock_visibility_changed)
|
||||
|
||||
view_menu = self.menuBar().addMenu("View")
|
||||
self.log_toggle_action = QAction("Log Panel", self)
|
||||
self.log_toggle_action.setCheckable(True)
|
||||
self.log_toggle_action.triggered.connect(self._on_log_toggle_action)
|
||||
view_menu.addAction(self.log_toggle_action)
|
||||
|
||||
log_content_menu = view_menu.addMenu("Log Content")
|
||||
self.log_content_actions = QActionGroup(self)
|
||||
self.log_content_actions.setExclusive(False)
|
||||
for label, mode in (
|
||||
("Runtime log", "runtime"),
|
||||
("Bundle info", "bundle"),
|
||||
("Performance/profile", "profile"),
|
||||
):
|
||||
action = QAction(label, self)
|
||||
action.setCheckable(True)
|
||||
action.setData(mode)
|
||||
action.triggered.connect(lambda _checked=False, m=mode: self._apply_log_display_mode(m, save=True))
|
||||
self.log_content_actions.addAction(action)
|
||||
log_content_menu.addAction(action)
|
||||
|
||||
def _on_log_toggle_action(self, checked: bool) -> None:
|
||||
if checked:
|
||||
mode = self._log_display_mode if self._log_display_mode != "hidden" else "runtime"
|
||||
self._apply_log_display_mode(mode, save=True)
|
||||
else:
|
||||
self._apply_log_display_mode("hidden", save=True)
|
||||
|
||||
def _on_log_dock_visibility_changed(self, visible: bool) -> None:
|
||||
self._log_panel_visible = bool(visible)
|
||||
if hasattr(self, "log_toggle_action"):
|
||||
self.log_toggle_action.blockSignals(True)
|
||||
self.log_toggle_action.setChecked(visible)
|
||||
self.log_toggle_action.blockSignals(False)
|
||||
if not visible and self._log_display_mode != "hidden":
|
||||
self._log_display_mode = "hidden"
|
||||
if hasattr(self, "log_content_actions"):
|
||||
for action in self.log_content_actions.actions():
|
||||
action.blockSignals(True)
|
||||
action.setChecked(False)
|
||||
action.blockSignals(False)
|
||||
self._save_preferences()
|
||||
|
||||
def _apply_log_display_mode(self, mode: str | None, *, save: bool = False) -> None:
|
||||
mode = mode or "hidden"
|
||||
self._log_display_mode = mode
|
||||
if hasattr(self, "log_toggle_action"):
|
||||
self.log_toggle_action.blockSignals(True)
|
||||
self.log_toggle_action.setChecked(mode != "hidden")
|
||||
self.log_toggle_action.blockSignals(False)
|
||||
if hasattr(self, "log_content_actions"):
|
||||
for action in self.log_content_actions.actions():
|
||||
action.blockSignals(True)
|
||||
action.setChecked(action.data() == mode)
|
||||
action.blockSignals(False)
|
||||
if mode == "hidden":
|
||||
self._set_log_panel_visible(False)
|
||||
if save:
|
||||
self._save_preferences()
|
||||
return
|
||||
if mode == "bundle":
|
||||
self.log_widget.setPlainText(
|
||||
"Sensor bundle\n"
|
||||
f"Config: {self.settings_panel.config_path()}\n"
|
||||
f"Assets: {self.settings_panel.finger_export_dir()}\n"
|
||||
)
|
||||
elif mode == "profile":
|
||||
self.log_widget.setPlainText(
|
||||
"Performance/profile\n"
|
||||
"Run with --profile to print per-stage timings in the runtime log.\n"
|
||||
)
|
||||
self._set_log_panel_visible(True)
|
||||
if save:
|
||||
self._save_preferences()
|
||||
|
||||
def _on_log_visibility_changed(self, visible: bool) -> None:
|
||||
self._set_log_panel_visible(visible)
|
||||
self._save_preferences()
|
||||
self._apply_log_display_mode("runtime" if visible else "hidden", save=True)
|
||||
|
||||
def _on_free_rotation_changed(self, enabled: bool) -> None:
|
||||
self.mesh_viewport.set_free_rotation(enabled)
|
||||
@@ -196,6 +282,7 @@ class FingerDashboardWindow(QMainWindow):
|
||||
|
||||
def _save_preferences(self) -> None:
|
||||
prefs = self.settings_panel.preference_state()
|
||||
prefs["log_display_mode"] = self._log_display_mode
|
||||
self._preferences = prefs
|
||||
save_preferences(prefs)
|
||||
# Keep in-memory pipeline config aligned with panel values.
|
||||
@@ -217,7 +304,6 @@ class FingerDashboardWindow(QMainWindow):
|
||||
scale = self._layout_scale_factor()
|
||||
column_height = self._scaled_top_row_height(scale)
|
||||
charts_slot = self.layout.top_slot("charts")
|
||||
log_slot = setup.log_panel
|
||||
|
||||
for slot in setup.top_row.slots:
|
||||
if not slot.enabled:
|
||||
@@ -236,27 +322,11 @@ class FingerDashboardWindow(QMainWindow):
|
||||
settings_height,
|
||||
)
|
||||
|
||||
if (
|
||||
self._log_panel_visible
|
||||
and log_slot.enabled
|
||||
and log_slot.height_px > 0
|
||||
):
|
||||
self.log_widget.show()
|
||||
log_height = self._scaled_px(log_slot.height_px, scale)
|
||||
charts_height = self._scaled_px(charts_slot.height_px, scale)
|
||||
self.log_widget.setFixedHeight(log_height)
|
||||
self.force_charts.set_panel_size(
|
||||
self._scaled_px(charts_slot.width_px, scale),
|
||||
charts_height,
|
||||
)
|
||||
self.charts_column.setSizes([charts_height, log_height])
|
||||
else:
|
||||
self.log_widget.hide()
|
||||
self.force_charts.set_panel_size(
|
||||
self._scaled_px(charts_slot.width_px, scale),
|
||||
column_height,
|
||||
)
|
||||
self.charts_column.setSizes([column_height, 0])
|
||||
self.force_charts.set_panel_size(
|
||||
self._scaled_px(charts_slot.width_px, scale),
|
||||
column_height,
|
||||
)
|
||||
self.charts_column.setSizes([column_height])
|
||||
|
||||
self.h_splitter.setSizes(
|
||||
[self._scaled_px(slot.width_px, scale) for slot in setup.top_row.slots if slot.enabled]
|
||||
@@ -279,12 +349,6 @@ class FingerDashboardWindow(QMainWindow):
|
||||
width += margins.left() + margins.right()
|
||||
|
||||
height = layout.charts_column_height_px()
|
||||
if (
|
||||
self._log_panel_visible
|
||||
and layout.panel_setup.log_panel.enabled
|
||||
and layout.panel_setup.log_panel.height_px > 0
|
||||
):
|
||||
height += self.charts_column.handleWidth()
|
||||
height += margins.top() + margins.bottom()
|
||||
|
||||
return max(layout.window_width, width), max(layout.window_height, height)
|
||||
@@ -316,12 +380,6 @@ class FingerDashboardWindow(QMainWindow):
|
||||
base_width += max(len(enabled_slots) - 1, 0) * self.h_splitter.handleWidth()
|
||||
|
||||
base_height = self.layout.charts_column_height_px()
|
||||
if (
|
||||
self._log_panel_visible
|
||||
and self.layout.panel_setup.log_panel.enabled
|
||||
and self.layout.panel_setup.log_panel.height_px > 0
|
||||
):
|
||||
base_height += self.charts_column.handleWidth()
|
||||
if base_width <= 0 or base_height <= 0:
|
||||
return 1.0
|
||||
return min(available_width / base_width, available_height / base_height)
|
||||
@@ -602,12 +660,27 @@ class FingerDashboardWindow(QMainWindow):
|
||||
def _select_finger_assets(self) -> None:
|
||||
path = QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"选择 finger 资产目录",
|
||||
self.settings_panel.finger_export_dir() or "assets/finger",
|
||||
"选择传感器 bundle 目录",
|
||||
self.settings_panel.sensor_bundle_dir() or "sensors/finger",
|
||||
)
|
||||
if path:
|
||||
self.settings_panel.set_finger_export_dir(path)
|
||||
self.config.export_dir = path
|
||||
bundle_dir = Path(path)
|
||||
config_path = bundle_dir / "sensor.json"
|
||||
manifest_path = bundle_dir / "manifest.json"
|
||||
assets_dir = bundle_dir / "assets" / "30fps-0703"
|
||||
if manifest_path.is_file():
|
||||
try:
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
manifest = {}
|
||||
rel_assets = manifest.get("assets_dir")
|
||||
if isinstance(rel_assets, str) and rel_assets.strip():
|
||||
assets_dir = bundle_dir / rel_assets
|
||||
if config_path.is_file():
|
||||
self.settings_panel.set_config_path(str(config_path))
|
||||
self.config.config = str(config_path)
|
||||
self.settings_panel.set_finger_export_dir(str(assets_dir))
|
||||
self.config.export_dir = str(assets_dir)
|
||||
self._save_preferences()
|
||||
|
||||
def _select_csv_dir(self) -> None:
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
"""Finger-compatible sensor-bundle presets for the 3D dashboard."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from orisys.finger import DEFAULT_FINGER_EXPORT_DIR
|
||||
|
||||
|
||||
def _repo_root_candidates() -> list[Path]:
|
||||
here = Path(__file__).resolve()
|
||||
return [
|
||||
Path.cwd(),
|
||||
here.parents[3], # examples/gui/viewer3d -> repo root
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SensorBundlePreset:
|
||||
label: str
|
||||
bundle_dir: str
|
||||
config: str
|
||||
export_dir: str
|
||||
|
||||
|
||||
def _load_manifest(bundle_root: Path) -> dict:
|
||||
path = bundle_root / "manifest.json"
|
||||
if not path.is_file():
|
||||
return {}
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _bundle_from_root(bundle_root: Path, *, label: str | None = None) -> SensorBundlePreset | None:
|
||||
sensor_json = bundle_root / "sensor.json"
|
||||
if not sensor_json.is_file():
|
||||
return None
|
||||
manifest = _load_manifest(bundle_root)
|
||||
assets_dir = manifest.get("assets_dir", "assets/30fps-0703")
|
||||
if not isinstance(assets_dir, str) or not assets_dir.strip():
|
||||
assets_dir = "assets/30fps-0703"
|
||||
rel_bundle = bundle_root.as_posix()
|
||||
for root in _repo_root_candidates():
|
||||
try:
|
||||
rel_bundle = bundle_root.relative_to(root).as_posix()
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
return SensorBundlePreset(
|
||||
label=label or "ORY-FINGER",
|
||||
bundle_dir=rel_bundle,
|
||||
config=f"{rel_bundle}/sensor.json",
|
||||
export_dir=f"{rel_bundle}/{assets_dir}".replace("\\", "/"),
|
||||
)
|
||||
|
||||
|
||||
def default_finger_bundle() -> SensorBundlePreset:
|
||||
for root in _repo_root_candidates():
|
||||
preset = _bundle_from_root(root / "sensors" / "finger", label="ORY-FINGER")
|
||||
if preset is not None:
|
||||
return preset
|
||||
return SensorBundlePreset(
|
||||
label="ORY-FINGER",
|
||||
bundle_dir="sensors/finger",
|
||||
config="sensors/finger/sensor.json",
|
||||
export_dir=DEFAULT_FINGER_EXPORT_DIR,
|
||||
)
|
||||
|
||||
|
||||
def discover_sensor_bundle_presets() -> list[SensorBundlePreset]:
|
||||
"""Return finger-compatible sensor bundle presets."""
|
||||
presets = [default_finger_bundle()]
|
||||
known_dirs = {presets[0].bundle_dir.replace("\\", "/")}
|
||||
|
||||
for root in _repo_root_candidates():
|
||||
sensors_root = root / "sensors"
|
||||
if not sensors_root.is_dir():
|
||||
continue
|
||||
for child in sorted(sensors_root.iterdir()):
|
||||
if not child.is_dir():
|
||||
continue
|
||||
if child.name != "finger":
|
||||
continue
|
||||
preset = _bundle_from_root(child, label="ORY-FINGER")
|
||||
if preset is None:
|
||||
continue
|
||||
key = preset.bundle_dir.replace("\\", "/")
|
||||
if key in known_dirs:
|
||||
continue
|
||||
presets.append(preset)
|
||||
known_dirs.add(key)
|
||||
break
|
||||
|
||||
return presets
|
||||
|
||||
|
||||
def sensor_bundle_preset_index(
|
||||
config: str,
|
||||
export_dir: str,
|
||||
presets: list[SensorBundlePreset],
|
||||
) -> int:
|
||||
config = (config or "").strip().replace("\\", "/")
|
||||
export_dir = (export_dir or "").strip().replace("\\", "/")
|
||||
for idx, preset in enumerate(presets):
|
||||
if config == preset.config and export_dir == preset.export_dir:
|
||||
return idx
|
||||
if config.endswith("/sensor.json") and export_dir.endswith("/" + Path(preset.export_dir).name):
|
||||
return idx
|
||||
return -1
|
||||
@@ -15,11 +15,15 @@ from PyQt5.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from orisys.finger import DEFAULT_FINGER_EXPORT_DIR
|
||||
from viz.render_2d import ArrowBackground
|
||||
|
||||
from ..layout import DashboardLayout, LAYOUT
|
||||
from ..mesh_view_state import MeshViewState
|
||||
from ..presets import (
|
||||
SensorBundlePreset,
|
||||
discover_sensor_bundle_presets,
|
||||
sensor_bundle_preset_index,
|
||||
)
|
||||
from ..transport_icons import reset_icon, transport_icon
|
||||
|
||||
_PLAY_PAUSE_BUTTON_NAME = "btnPlayPause"
|
||||
@@ -86,7 +90,7 @@ class SettingsPanelWidget(QWidget):
|
||||
self.show_log_check = QCheckBox("Show log panel")
|
||||
self.show_log_check.setChecked(show_log)
|
||||
self.show_log_check.toggled.connect(self.log_visibility_changed.emit)
|
||||
root.addWidget(self.show_log_check)
|
||||
self.show_log_check.hide()
|
||||
|
||||
self.show_flow_arrows_check = QCheckBox("Show displacement arrows")
|
||||
self.show_flow_arrows_check.setChecked(self._mesh_state.flow_arrows_enabled)
|
||||
@@ -134,35 +138,38 @@ class SettingsPanelWidget(QWidget):
|
||||
self._video_file_path = ""
|
||||
self._updating_camera_options = False
|
||||
|
||||
root.addWidget(self._caption("Config"))
|
||||
config_row = QHBoxLayout()
|
||||
config_row.setSpacing(6)
|
||||
self.config_edit = QLineEdit("finger")
|
||||
config_row.addWidget(self.config_edit, 1)
|
||||
root.addWidget(self._caption("Sensor bundle"))
|
||||
bundle_row = QHBoxLayout()
|
||||
bundle_row.setSpacing(6)
|
||||
self.sensor_bundle_combo = QComboBox()
|
||||
self.sensor_bundle_combo.setToolTip("Select a finger sensor bundle")
|
||||
self._bundle_presets = discover_sensor_bundle_presets()
|
||||
for preset in self._bundle_presets:
|
||||
self.sensor_bundle_combo.addItem(preset.label, preset)
|
||||
self.sensor_bundle_combo.addItem("Custom…", None)
|
||||
self._updating_bundle_combo = False
|
||||
self.sensor_bundle_combo.currentIndexChanged.connect(self._on_bundle_combo_changed)
|
||||
bundle_row.addWidget(self.sensor_bundle_combo, 1)
|
||||
self.btn_browse_finger_assets = QPushButton("…")
|
||||
self.btn_browse_finger_assets.setFixedWidth(32)
|
||||
self.btn_browse_finger_assets.setToolTip("Browse sensor bundle folder")
|
||||
self.btn_browse_finger_assets.clicked.connect(self.browse_finger_assets_requested.emit)
|
||||
bundle_row.addWidget(self.btn_browse_finger_assets)
|
||||
root.addLayout(bundle_row)
|
||||
|
||||
self.config_edit = QLineEdit("sensors/finger/sensor.json")
|
||||
self.config_edit.hide()
|
||||
self.btn_browse_config = QPushButton("…")
|
||||
self.btn_browse_config.setFixedWidth(32)
|
||||
self.btn_browse_config.setToolTip("Browse config JSON")
|
||||
self.btn_browse_config.clicked.connect(self.browse_config_requested.emit)
|
||||
config_row.addWidget(self.btn_browse_config)
|
||||
root.addLayout(config_row)
|
||||
self.btn_browse_config.hide()
|
||||
|
||||
# Optional calibration path is kept for future re-enablement but hidden for now.
|
||||
self.cal_edit = QLineEdit("")
|
||||
self.cal_edit.setPlaceholderText("Optional calibration path")
|
||||
self.cal_edit.hide()
|
||||
|
||||
root.addWidget(self._caption("Finger assets"))
|
||||
finger_assets_row = QHBoxLayout()
|
||||
finger_assets_row.setSpacing(6)
|
||||
self.finger_assets_edit = QLineEdit(DEFAULT_FINGER_EXPORT_DIR)
|
||||
self.finger_assets_edit.setPlaceholderText("Blender export bundle directory")
|
||||
finger_assets_row.addWidget(self.finger_assets_edit, 1)
|
||||
self.btn_browse_finger_assets = QPushButton("…")
|
||||
self.btn_browse_finger_assets.setFixedWidth(32)
|
||||
self.btn_browse_finger_assets.setToolTip("Browse finger asset directory")
|
||||
self.btn_browse_finger_assets.clicked.connect(self.browse_finger_assets_requested.emit)
|
||||
finger_assets_row.addWidget(self.btn_browse_finger_assets)
|
||||
root.addLayout(finger_assets_row)
|
||||
self.finger_assets_edit = QLineEdit("sensors/finger/assets/30fps-0703")
|
||||
self.finger_assets_edit.hide()
|
||||
self._apply_bundle_preset(self._bundle_presets[0])
|
||||
|
||||
root.addWidget(self._caption("CSV save directory"))
|
||||
csv_dir_row = QHBoxLayout()
|
||||
@@ -337,16 +344,18 @@ class SettingsPanelWidget(QWidget):
|
||||
|
||||
def set_config_path(self, value: str) -> None:
|
||||
self.config_edit.setText(value)
|
||||
self._sync_bundle_combo_from_paths()
|
||||
|
||||
def set_calibration_path(self, value: str) -> None:
|
||||
self.cal_edit.setText(value or "")
|
||||
|
||||
def set_finger_export_dir(self, value: str) -> None:
|
||||
self.finger_assets_edit.setText(value)
|
||||
self._sync_bundle_combo_from_paths()
|
||||
|
||||
def set_finger_export_dir_enabled(self, enabled: bool) -> None:
|
||||
self.finger_assets_edit.setEnabled(enabled)
|
||||
self.btn_browse_finger_assets.setEnabled(enabled)
|
||||
self.sensor_bundle_combo.setEnabled(enabled)
|
||||
|
||||
def set_csv_save_dir(self, value: str) -> None:
|
||||
self.csv_dir_edit.setText(value)
|
||||
@@ -387,6 +396,33 @@ class SettingsPanelWidget(QWidget):
|
||||
return
|
||||
self.csv_record_toggled.emit(not self._csv_recording)
|
||||
|
||||
def _apply_bundle_preset(self, preset: SensorBundlePreset) -> None:
|
||||
self.config_edit.setText(preset.config)
|
||||
self.finger_assets_edit.setText(preset.export_dir)
|
||||
|
||||
def _on_bundle_combo_changed(self, _index: int) -> None:
|
||||
if self._updating_bundle_combo:
|
||||
return
|
||||
preset = self.sensor_bundle_combo.currentData()
|
||||
if isinstance(preset, SensorBundlePreset):
|
||||
self._apply_bundle_preset(preset)
|
||||
|
||||
def _sync_bundle_combo_from_paths(self) -> None:
|
||||
idx = sensor_bundle_preset_index(
|
||||
self.config_edit.text(),
|
||||
self.finger_assets_edit.text(),
|
||||
self._bundle_presets,
|
||||
)
|
||||
if idx < 0:
|
||||
idx = self.sensor_bundle_combo.findData(None)
|
||||
if idx < 0:
|
||||
return
|
||||
self._updating_bundle_combo = True
|
||||
self.sensor_bundle_combo.blockSignals(True)
|
||||
self.sensor_bundle_combo.setCurrentIndex(idx)
|
||||
self.sensor_bundle_combo.blockSignals(False)
|
||||
self._updating_bundle_combo = False
|
||||
|
||||
def _emit_arrow_background_changed(self, _index: int) -> None:
|
||||
value = self.arrow_background_combo.currentData()
|
||||
if value:
|
||||
@@ -413,6 +449,15 @@ class SettingsPanelWidget(QWidget):
|
||||
def finger_export_dir(self) -> str:
|
||||
return self.finger_assets_edit.text().strip()
|
||||
|
||||
def sensor_bundle_dir(self) -> str:
|
||||
preset = self.sensor_bundle_combo.currentData()
|
||||
if isinstance(preset, SensorBundlePreset):
|
||||
return preset.bundle_dir
|
||||
config = self.config_path()
|
||||
if config.endswith("/sensor.json") or config.endswith("\\sensor.json"):
|
||||
return config.rsplit("/", 1)[0].rsplit("\\", 1)[0]
|
||||
return ""
|
||||
|
||||
def csv_save_dir(self) -> str:
|
||||
return self.csv_dir_edit.text().strip()
|
||||
|
||||
@@ -420,6 +465,7 @@ class SettingsPanelWidget(QWidget):
|
||||
"""Serialize user-facing settings suitable for preference.json."""
|
||||
return {
|
||||
"video_source": self.video_source(),
|
||||
"sensor_bundle_dir": self.sensor_bundle_dir(),
|
||||
"config_path": self.config_path(),
|
||||
"finger_assets_dir": self.finger_export_dir(),
|
||||
"csv_save_dir": self.csv_save_dir(),
|
||||
@@ -465,6 +511,8 @@ class SettingsPanelWidget(QWidget):
|
||||
if isinstance(assets, str) and assets.strip():
|
||||
self.set_finger_export_dir(assets.strip())
|
||||
|
||||
self._sync_bundle_combo_from_paths()
|
||||
|
||||
csv_dir = state.get("csv_save_dir")
|
||||
if isinstance(csv_dir, str) and csv_dir.strip():
|
||||
self.set_csv_save_dir(csv_dir.strip())
|
||||
|
||||
Reference in New Issue
Block a user