接入 Studio 多点标定的会话置零与状态记录
支持显式无载荷置零、处理线程内切换模型及完整标定包快照。shared、界面检查与兼容测试通过;Studio 全量仍有一项回放停止时序测试失败。
This commit is contained in:
@@ -132,6 +132,9 @@ class AcquirePresenter(QObject):
|
||||
self._host.refresh_record_state()
|
||||
|
||||
def _push_forces(self, packet: Any):
|
||||
show_model = getattr(self._host.toolbar, "set_model_calibration_status", None)
|
||||
if callable(show_model):
|
||||
show_model(dict(getattr(packet, "calibration_status", {}) or {}))
|
||||
record = to_record_sample(packet)
|
||||
if not record.valid:
|
||||
self._host.session.last_sample_valid = False
|
||||
|
||||
@@ -911,6 +911,7 @@ class StudioMainWindow(QMainWindow):
|
||||
self.toolbar.stop_clicked.connect(self._on_stop_clicked)
|
||||
self.toolbar.record_toggled.connect(self._on_record_toggled)
|
||||
self.toolbar.reset_clicked.connect(self._on_reset)
|
||||
self.toolbar.calibration_zero_clicked.connect(self._on_calibration_zero)
|
||||
self.toolbar.units_changed.connect(self.display_ctl.on_units_changed)
|
||||
self.toolbar.settings_toggled.connect(self.layout_ctl.on_settings_toggled)
|
||||
|
||||
@@ -1846,7 +1847,7 @@ class StudioMainWindow(QMainWindow):
|
||||
t("sensor.live_calibration_invalid", reason=problem)
|
||||
)
|
||||
return
|
||||
cal_path, _device_id, problem = self._resolved_live_calibration()
|
||||
cal_path, device_id, problem = self._resolved_live_calibration()
|
||||
if problem:
|
||||
self.statusBar().showMessage(
|
||||
t("sensor.live_calibration_invalid", reason=problem)
|
||||
@@ -1859,7 +1860,10 @@ class StudioMainWindow(QMainWindow):
|
||||
if worker is None or not hasattr(worker, "set_pending_calibration"):
|
||||
self.statusBar().showMessage(t("cal.apply_no_target"))
|
||||
return
|
||||
worker.set_pending_calibration(cal_path)
|
||||
if device_id and hasattr(worker, "request_calibration_zero"):
|
||||
worker.set_pending_calibration(cal_path, device_id=device_id)
|
||||
else:
|
||||
worker.set_pending_calibration(cal_path)
|
||||
self.statusBar().showMessage(
|
||||
t("sensor.live_calibration_applied", name=cal_path.name)
|
||||
)
|
||||
@@ -2319,6 +2323,7 @@ class StudioMainWindow(QMainWindow):
|
||||
self._stop_preview(announce=False)
|
||||
|
||||
self.force_plots.reset()
|
||||
self.toolbar.set_model_calibration_status({})
|
||||
units = "N" if self.toolbar.units() == "newton" else "raw"
|
||||
self.force_plots.configure_presentation(
|
||||
sensor_type=sensor_type,
|
||||
@@ -2482,6 +2487,19 @@ class StudioMainWindow(QMainWindow):
|
||||
except Exception as exc:
|
||||
self.statusBar().showMessage(t("status.run_failed", error=str(exc)))
|
||||
|
||||
def _on_calibration_zero(self) -> None:
|
||||
"""A separate explicit unloaded operation; tracker reset never assumes no load."""
|
||||
worker = self._active_worker
|
||||
if self._session_phase != "running" or not hasattr(worker, "request_calibration_zero"):
|
||||
return
|
||||
answer = QMessageBox.question(
|
||||
self, t("multical.zero"), t("multical.zero_confirm"),
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No)
|
||||
if answer == QMessageBox.StandardButton.Yes:
|
||||
worker.request_calibration_zero()
|
||||
self.statusBar().showMessage(t("multical.zero_pending"))
|
||||
|
||||
def _reset_display(self) -> None:
|
||||
"""The rail's reset in the replay view: clear the display and start the replay over.
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ class ToolbarWidget(QWidget):
|
||||
#: Stop: end the live session (releasing the device), or leave the replay.
|
||||
stop_clicked = Signal()
|
||||
reset_clicked = Signal()
|
||||
calibration_zero_clicked = Signal()
|
||||
units_changed = Signal(str)
|
||||
record_toggled = Signal(bool)
|
||||
#: The settings area was expanded or collapsed by the user.
|
||||
@@ -282,6 +283,12 @@ class ToolbarWidget(QWidget):
|
||||
self.btn_reset.setMinimumHeight(BUTTON_HEIGHT)
|
||||
self.btn_reset.setEnabled(False)
|
||||
self.btn_reset.clicked.connect(self.reset_clicked.emit)
|
||||
self.btn_calibration_zero = QPushButton()
|
||||
self.btn_calibration_zero.setMinimumHeight(BUTTON_HEIGHT)
|
||||
self.btn_calibration_zero.clicked.connect(self.calibration_zero_clicked.emit)
|
||||
self.btn_calibration_zero.setVisible(False)
|
||||
self.model_state = ShrinkableLabel()
|
||||
self.model_state.setVisible(False)
|
||||
|
||||
self.btn_settings = QPushButton()
|
||||
self.btn_settings.setObjectName("ghostButton")
|
||||
@@ -297,6 +304,7 @@ class ToolbarWidget(QWidget):
|
||||
rail.addWidget(self.source_slot)
|
||||
rail.addWidget(self.source_summary)
|
||||
rail.addWidget(self.calibration_label)
|
||||
rail.addWidget(self.model_state)
|
||||
rail.addWidget(self.data_state)
|
||||
# Shown, the hint takes the spare width (stretch 1); hidden, the spacer does.
|
||||
rail.addWidget(self.run_hint, 1)
|
||||
@@ -306,6 +314,7 @@ class ToolbarWidget(QWidget):
|
||||
rail.addWidget(_rail(self.btn_run))
|
||||
rail.addWidget(_rail(self.btn_stop))
|
||||
rail.addWidget(_rail(self.btn_reset))
|
||||
rail.addWidget(self.btn_calibration_zero)
|
||||
rail.addWidget(_rail(self.btn_settings))
|
||||
return rail
|
||||
|
||||
@@ -393,6 +402,17 @@ class ToolbarWidget(QWidget):
|
||||
self.calibration_label.setToolTip(self.calibration_label.text())
|
||||
self.calibration_label.setVisible(bool(text))
|
||||
|
||||
def set_model_calibration_status(self, statuses: dict[str, str]) -> None:
|
||||
"""Model validity is separate from whether a calibration file is loaded."""
|
||||
text = ", ".join(f"{axis.upper()}: {t(f'multical.{value}')}" for axis, value in statuses.items())
|
||||
self.model_state.setText(text)
|
||||
self.model_state.setToolTip(text)
|
||||
self.model_state.setVisible(bool(statuses))
|
||||
self.btn_calibration_zero.setText(t("multical.zero"))
|
||||
self.btn_calibration_zero.setToolTip(t("multical.zero_confirm"))
|
||||
self.btn_calibration_zero.setVisible(bool(statuses) and self._run_role == "acquire")
|
||||
self.btn_calibration_zero.setEnabled(self._reset_available)
|
||||
|
||||
def set_data_state(self, text: str, token: str = "text_secondary") -> None:
|
||||
"""Show/clear the validity readout, coloured by a theme token."""
|
||||
text = str(text or "")
|
||||
@@ -435,6 +455,7 @@ class ToolbarWidget(QWidget):
|
||||
if wanted == self._run_role:
|
||||
return
|
||||
self._run_role = wanted
|
||||
self.set_model_calibration_status({})
|
||||
# A view switch never carries the other view's running state across.
|
||||
if self.btn_run.isChecked():
|
||||
self.btn_run.blockSignals(True)
|
||||
@@ -524,6 +545,7 @@ class ToolbarWidget(QWidget):
|
||||
"""
|
||||
self._reset_available = bool(enabled)
|
||||
self.btn_reset.setEnabled(self._reset_available)
|
||||
self.btn_calibration_zero.setEnabled(self._reset_available)
|
||||
self._refresh_reset_label()
|
||||
|
||||
def reset_target(self) -> str:
|
||||
|
||||
@@ -35,6 +35,7 @@ from orisys_app_shared.i18n import t
|
||||
from orisys_app_shared.sensors import CatalogEntry, SensorCatalog
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
@@ -200,6 +201,9 @@ class VideoAnalysisDialog(QDialog):
|
||||
self.reference_time_label.setObjectName("panelMeta")
|
||||
reference_row.addWidget(self.reference_time_label)
|
||||
calibration_form.addRow(self.reference_label, reference_row)
|
||||
self.unloaded_reference = QCheckBox(t("multical.video_zero"))
|
||||
self.unloaded_reference.setToolTip(t("multical.video_zero_tip"))
|
||||
calibration_form.addRow(self.unloaded_reference)
|
||||
root.addLayout(calibration_form)
|
||||
|
||||
self.note_label = ShrinkableLabel()
|
||||
@@ -287,6 +291,7 @@ class VideoAnalysisDialog(QDialog):
|
||||
# as one that happened.
|
||||
reference_frame=None if reference == 1 else reference,
|
||||
device_id=self._confirmed_device_id(),
|
||||
unloaded_reference_frames=30 if self.unloaded_reference.isChecked() else 0,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------ content
|
||||
|
||||
Reference in New Issue
Block a user