fix(autocal): 将标定记录总览接入主窗口
合并旧占位页为标定记录页,并统一采集页的数据查看入口。通过45项相关测试、离屏布局检查及代码和文档门禁。
This commit is contained in:
@@ -36,6 +36,7 @@ class CalibrationPanel(QWidget):
|
||||
"""One operator page. No motion commands are issued by this widget."""
|
||||
|
||||
loadBundleRequested = Signal()
|
||||
reviewRequested = Signal(str)
|
||||
|
||||
def __init__(self, source_panel: QWidget, parent: QWidget | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
@@ -351,19 +352,13 @@ class CalibrationPanel(QWidget):
|
||||
self._sync()
|
||||
|
||||
def _review(self) -> None:
|
||||
from .record_overview_dialog import RecordOverviewDialog
|
||||
|
||||
session = self.trial.session
|
||||
directory = QFileDialog.getExistingDirectory(
|
||||
self, t("review.open"),
|
||||
str(session.directory) if session is not None else self.output_path.text(),
|
||||
)
|
||||
if directory:
|
||||
try:
|
||||
dialog = RecordOverviewDialog(Path(directory), self)
|
||||
dialog.exec()
|
||||
except (OSError, ValueError, KeyError, TypeError) as exc:
|
||||
self.result_label.setText(t("calibration.error").format(detail=exc))
|
||||
self.reviewRequested.emit(directory)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
self._timer.stop()
|
||||
|
||||
@@ -8,7 +8,7 @@ from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QColor, QPainter
|
||||
from PySide6.QtWidgets import (
|
||||
QComboBox, QDialog, QFileDialog, QHBoxLayout, QHeaderView, QLabel,
|
||||
QPushButton, QTableWidget, QTableWidgetItem, QVBoxLayout,
|
||||
QPushButton, QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget,
|
||||
)
|
||||
|
||||
from ..record_overview import load_overview, overview_rows, read_plan, save_overview
|
||||
@@ -16,7 +16,63 @@ from ..session_review import review_session
|
||||
from .session_review_dialog import SessionReviewDialog
|
||||
|
||||
|
||||
class RecordOverviewPage(QWidget):
|
||||
"""Keep the existing record viewer inside the main task workspace."""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.overview = None
|
||||
self.directory = Path.cwd()
|
||||
self.content_layout = QVBoxLayout(self)
|
||||
self.open_button = QPushButton(self)
|
||||
self.open_button.setObjectName("openCalibrationRecords")
|
||||
self.open_button.clicked.connect(self._open)
|
||||
self.path_label = QLabel(self)
|
||||
self.path_label.setWordWrap(True)
|
||||
self.message = QLabel(self)
|
||||
self.message.setWordWrap(True)
|
||||
self.content_layout.addWidget(self.open_button)
|
||||
self.content_layout.addWidget(self.path_label)
|
||||
self.content_layout.addWidget(self.message)
|
||||
self.content_layout.addStretch(1)
|
||||
self.retranslate()
|
||||
|
||||
def retranslate(self):
|
||||
self.open_button.setText(t("overview.open_session"))
|
||||
if self.overview is None:
|
||||
self.path_label.setText(t("overview.empty"))
|
||||
|
||||
def _open(self):
|
||||
path = QFileDialog.getExistingDirectory(self, t("overview.open_session"), str(self.directory))
|
||||
if path:
|
||||
self.load_session(Path(path))
|
||||
|
||||
def load_session(self, directory: Path):
|
||||
try:
|
||||
overview = RecordOverviewDialog(directory, self)
|
||||
except (OSError, ValueError, KeyError, TypeError) as exc:
|
||||
self.message.setText(t("calibration.error").format(detail=exc))
|
||||
return
|
||||
overview.setWindowFlags(Qt.WindowType.Widget)
|
||||
if self.overview is not None:
|
||||
self.content_layout.removeWidget(self.overview)
|
||||
self.overview.hide()
|
||||
self.overview.deleteLater()
|
||||
else:
|
||||
self.content_layout.takeAt(self.content_layout.count() - 1)
|
||||
self.overview = overview
|
||||
self.directory = directory
|
||||
self.path_label.setText(str(directory))
|
||||
self.message.clear()
|
||||
self.content_layout.addWidget(overview, 1)
|
||||
overview.show()
|
||||
|
||||
|
||||
class RecordOverviewDialog(QDialog):
|
||||
def reject(self):
|
||||
if self.isWindow():
|
||||
super().reject()
|
||||
|
||||
def __init__(self, directory: Path, parent=None):
|
||||
super().__init__(parent)
|
||||
self.directory = directory
|
||||
|
||||
@@ -305,6 +305,7 @@ class AutoCalMainWindow(QMainWindow):
|
||||
body,
|
||||
sensor_panel=self.calibration_panel,
|
||||
)
|
||||
self.calibration_panel.reviewRequested.connect(self._on_review_records)
|
||||
|
||||
self.main_splitter = QSplitter(Qt.Orientation.Horizontal, body)
|
||||
self.main_splitter.addWidget(self.left_rail)
|
||||
@@ -325,6 +326,10 @@ class AutoCalMainWindow(QMainWindow):
|
||||
|
||||
self._wire_signals()
|
||||
|
||||
def _on_review_records(self, directory: str) -> None:
|
||||
self.workspace.page("records").load_session(Path(directory))
|
||||
self.workspace.set_current_key("records")
|
||||
|
||||
def _wire_signals(self) -> None:
|
||||
self.connection_panel.connectRequested.connect(self._on_connect)
|
||||
self.connection_panel.disconnectRequested.connect(self._on_disconnect)
|
||||
|
||||
@@ -4,10 +4,7 @@ One tab per task the operator does on the platform, with the motion status strip
|
||||
living *outside* this widget so a tab change cannot hide the state that decides
|
||||
whether it is safe to act.
|
||||
|
||||
Pages that are not built yet are present as labelled placeholders rather than
|
||||
missing: the layout is being reviewed for operator use, and a tab that appears
|
||||
only later would change the layout under review. Each placeholder names what
|
||||
will fill it, so nobody mistakes an empty page for a broken one.
|
||||
Calibration records share one page for coverage, sensitivity and window review.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -23,25 +20,23 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from .axis_panel import AxisPanel
|
||||
from .sequence_panel import SequencePanel
|
||||
from .record_overview_dialog import RecordOverviewPage
|
||||
|
||||
#: Tab identity -> locale key for its label. Order is the display order, and it
|
||||
#: follows the work: commission the axes, describe the sequence, watch the
|
||||
#: sensor, look at the surface, then record calibration points.
|
||||
#: sensor, capture data, then review calibration records.
|
||||
TABS: tuple[tuple[str, str], ...] = (
|
||||
("axes", "workspace.tab_axes"),
|
||||
("sequence", "workspace.tab_sequence"),
|
||||
("finger", "workspace.tab_finger"),
|
||||
("sensor", "workspace.tab_sensor"),
|
||||
("surface", "workspace.tab_surface"),
|
||||
("points", "workspace.tab_points"),
|
||||
("records", "workspace.tab_records"),
|
||||
)
|
||||
|
||||
#: Tabs whose content does not exist yet, with the locale key that explains what
|
||||
#: will appear there and when.
|
||||
_PLACEHOLDERS: dict[str, str] = {
|
||||
"sensor": "workspace.reserved_sensor",
|
||||
"surface": "workspace.reserved_surface",
|
||||
"points": "workspace.reserved_points",
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +88,7 @@ class WorkspaceTabs(QTabWidget):
|
||||
self._pages["finger"] = finger_panel
|
||||
if sensor_panel is not None:
|
||||
self._pages["sensor"] = sensor_panel
|
||||
self._pages["records"] = RecordOverviewPage(self)
|
||||
for key, locale_key in _PLACEHOLDERS.items():
|
||||
if key in self._pages:
|
||||
continue
|
||||
@@ -134,6 +130,7 @@ class WorkspaceTabs(QTabWidget):
|
||||
page = self._pages[key]
|
||||
if isinstance(page, PlaceholderPage):
|
||||
page.retranslate()
|
||||
self._pages["records"].retranslate()
|
||||
|
||||
def _on_current_changed(self, index: int) -> None:
|
||||
keys = self.page_keys()
|
||||
|
||||
Reference in New Issue
Block a user