From 570daae68f06f0fe99ac14fd1030449e7f439e5e Mon Sep 17 00:00:00 2001 From: ykcheungab Date: Mon, 19 Oct 2026 10:15:07 +0800 Subject: [PATCH] =?UTF-8?q?autocal:=20=E6=96=B0=E5=A2=9E=E5=8D=95=E4=BD=8D?= =?UTF-8?q?=E4=B8=8E=E6=8D=A2=E7=AE=97=E9=A1=B5=EF=BC=88=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=94=81=E5=AE=9A=EF=BC=89=E5=8F=8A=E8=BD=B4=E9=A1=B5=E6=8A=A5?= =?UTF-8?q?=E8=AD=A6=E4=B8=8E=E4=BA=8B=E4=BB=B6=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 设备设置新增“单位与换算”页:显示各轴 UNITS、读回值、脉冲/mm 换算与核对状态; 默认锁定,点击“修改”后才可编辑,应用前弹窗确认,旋转轴偏离标定值时额外警告 - 轴调试页:脉冲当量改为只读并提供入口;新增报警与事件日志(报警、使能、 回零、读取失败、单位不符等)及单位核对摘要 - 新增 motion/unit_report.py、motion/events.py 及对应测试 --- apps/autocal/config/locales/en_US.json | 4 +-- apps/autocal/config/locales/zh_CN.json | 6 ++-- .../src/orisys_autocal/ui/units_page.py | 36 +++++++++++++++---- apps/autocal/tests/test_units_page.py | 22 ++++++------ 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/apps/autocal/config/locales/en_US.json b/apps/autocal/config/locales/en_US.json index 3193ff4..aa7b112 100644 --- a/apps/autocal/config/locales/en_US.json +++ b/apps/autocal/config/locales/en_US.json @@ -942,8 +942,8 @@ "col_check": "Check", "kind_linear": "Linear (mm)", "kind_rotary": "Rotary (°)", - "scale_linear": "1 unit = {mm} mm; {per} units/mm", - "scale_rotary": "1 unit = {deg}°; {per} units/°", + "scale_linear": "{mm} mm/unit · {per}/mm", + "scale_rotary": "{deg}°/unit · {per}/°", "status_ok": "Agree", "status_unread": "Not read", "status_mismatch": "Mismatch", diff --git a/apps/autocal/config/locales/zh_CN.json b/apps/autocal/config/locales/zh_CN.json index 8f9c95d..b77a6a5 100644 --- a/apps/autocal/config/locales/zh_CN.json +++ b/apps/autocal/config/locales/zh_CN.json @@ -942,8 +942,8 @@ "col_check": "检查", "kind_linear": "线性 (mm)", "kind_rotary": "旋转 (°)", - "scale_linear": "1 单位 = {mm} mm;{per} 单位/mm", - "scale_rotary": "1 单位 = {deg}°;{per} 单位/°", + "scale_linear": "{mm} mm/单位 · {per}/mm", + "scale_rotary": "{deg}°/单位 · {per}/°", "status_ok": "一致", "status_unread": "未读回", "status_mismatch": "不一致", @@ -958,7 +958,7 @@ "summary_ok": "单位检查:{total} 个方向全部一致", "summary_bad": "单位检查:{bad}/{total} 个方向需要处理", "summary_unread": "单位检查:尚未读回(未连接)", - "constants": "固定标定常数(来自实机测量,不能在此修改):线性轴 {ppm} 脉冲/mm;旋转轴 {rotary};以上在 UNITS={cal} 下测得。毫米与角度都是由这些常数换算的显示值,不是新的测量。", + "constants": "固定标定常数(实机测得,不能在此修改)\n线性轴:{ppm} 脉冲/mm\n旋转轴:{rotary}\n以上均在 UNITS={cal} 下测得;毫米与角度是由这些常数换算的显示值,不是新的测量。", "rotary_item": "轴 {axis}:{units} 用户单位 = 90°", "modify": "修改", "apply": "应用到控制器", diff --git a/apps/autocal/src/orisys_autocal/ui/units_page.py b/apps/autocal/src/orisys_autocal/ui/units_page.py index 2e1b4a4..bed6fa3 100644 --- a/apps/autocal/src/orisys_autocal/ui/units_page.py +++ b/apps/autocal/src/orisys_autocal/ui/units_page.py @@ -23,6 +23,7 @@ from PySide6.QtCore import Qt, Signal from PySide6.QtGui import QBrush, QColor from PySide6.QtWidgets import ( QHBoxLayout, + QHeaderView, QLabel, QLineEdit, QMessageBox, @@ -60,7 +61,9 @@ COLUMNS = ( "units.col_check", ) CONFIGURED_COLUMN = 3 -ROW_HEIGHT = 34 +STRETCH_COLUMNS = (0, 5, 6) +ROW_HEIGHT = 40 +EDIT_HEIGHT = 26 _ROLE_KEYS = { "x": "setup.role_x", @@ -116,7 +119,7 @@ class UnitsPage(QWidget): column = QVBoxLayout(self) column.setContentsMargins(8, 8, 8, 8) - column.setSpacing(8) + column.setSpacing(10) self.hint_label = QLabel(self) self.hint_label.setObjectName("hintLabel") @@ -153,10 +156,23 @@ class UnitsPage(QWidget): self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setAlternatingRowColors(True) self.table.verticalHeader().setDefaultSectionSize(ROW_HEIGHT) - self.table.horizontalHeader().setStretchLastSection(True) - for index, width in enumerate((110, 60, 90, 110, 100, 230)): - self.table.setColumnWidth(index, width) - self.table.setMinimumHeight(40 + ROW_HEIGHT * 5 + 4) + self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.table.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.table.setWordWrap(False) + self.table.setTextElideMode(Qt.TextElideMode.ElideRight) + header = self.table.horizontalHeader() + header.setMinimumSectionSize(48) + header.setDefaultAlignment(Qt.AlignmentFlag.AlignCenter) + # Short columns size to their text; the wide ones share what is left, so the + # table always fits the dialog and never needs a scroll bar. + for index in range(len(COLUMNS)): + header.setSectionResizeMode( + index, + QHeaderView.ResizeMode.Stretch + if index in STRETCH_COLUMNS + else QHeaderView.ResizeMode.ResizeToContents, + ) + self.table.setFixedHeight(header.height() + ROW_HEIGHT * 5 + 6) for row in range(5): for col in range(len(COLUMNS)): if col != CONFIGURED_COLUMN: @@ -243,7 +259,13 @@ class UnitsPage(QWidget): edit = QLineEdit(self.table) edit.setObjectName(f"unitsEdit_{row.role}") edit.setAlignment(Qt.AlignmentFlag.AlignCenter) - edit.setFixedHeight(28) + edit.setFixedHeight(EDIT_HEIGHT) + edit.setMinimumWidth(84) + # The global style gives line edits generous padding; inside a table cell + # that clips the text, so the cell edit is compact. + edit.setStyleSheet( + "QLineEdit { padding: 0 6px; margin: 0; min-height: 0; border-radius: 4px; }" + ) self._edits[row.role] = edit self.table.setCellWidget(index, CONFIGURED_COLUMN, edit) edit.setReadOnly(self._locked) diff --git a/apps/autocal/tests/test_units_page.py b/apps/autocal/tests/test_units_page.py index cd7cea1..a9a991c 100644 --- a/apps/autocal/tests/test_units_page.py +++ b/apps/autocal/tests/test_units_page.py @@ -53,7 +53,7 @@ def test_the_page_is_locked_until_the_operator_presses_modify(make_window): page = window.device_setup.units_page page.set_rows(_rows(connected=False), False) assert page.locked - assert all(edit.isReadOnly() for edit in page._edits.values()) # noqa: SLF001 + assert all(edit.isReadOnly() for edit in page._edits.values()) # Nothing to change on a machine that is not connected. assert not page.modify_button.isEnabled() @@ -61,7 +61,7 @@ def test_the_page_is_locked_until_the_operator_presses_modify(make_window): assert page.modify_button.isEnabled() page.modify_button.click() assert not page.locked - assert not any(edit.isReadOnly() for edit in page._edits.values()) # noqa: SLF001 + assert not any(edit.isReadOnly() for edit in page._edits.values()) assert page.apply_button.isEnabled() window.close() @@ -75,7 +75,7 @@ def test_a_confirmed_change_is_emitted_and_the_page_locks_again(make_window, mon asked = _answer(monkeypatch, QMessageBox.StandardButton.Yes) page.modify_button.click() - page._edits["x"].setText("5") # noqa: SLF001 + page._edits["x"].setText("5") page.apply_button.click() assert emitted == [{0: 5.0}] @@ -93,14 +93,14 @@ def test_declining_the_confirmation_changes_nothing(make_window, monkeypatch): _answer(monkeypatch, QMessageBox.StandardButton.No) page.modify_button.click() - page._edits["y"].setText("20") # noqa: SLF001 + page._edits["y"].setText("20") page.apply_button.click() assert emitted == [] assert not page.locked # still the operator's to finish or cancel page.cancel_button.click() assert page.locked - assert page._edits["y"].text() == "10" # noqa: SLF001 + assert page._edits["y"].text() == "10" window.close() @@ -111,7 +111,7 @@ def test_a_rotary_value_off_the_calibration_is_warned_about(make_window, monkeyp asked = _answer(monkeypatch, QMessageBox.StandardButton.No) page.modify_button.click() - page._edits["yaw"].setText("5") # noqa: SLF001 + page._edits["yaw"].setText("5") page.apply_button.click() assert t("units.confirm_rotary", roles=t("setup.role_yaw"), cal="10") in asked[0] @@ -128,7 +128,7 @@ def test_an_unusable_value_is_refused_before_any_question(make_window, monkeypat asked = _answer(monkeypatch, QMessageBox.StandardButton.Yes) page.modify_button.click() - page._edits["z"].setText(bad) # noqa: SLF001 + page._edits["z"].setText(bad) page.apply_button.click() assert emitted == [] and asked == [] @@ -157,13 +157,13 @@ def test_a_changed_axis_mapping_drops_an_open_edit(make_window): page = window.device_setup.units_page page.set_rows(_rows(), True) page.modify_button.click() - page._edits["x"].setText("5") # noqa: SLF001 + page._edits["x"].setText("5") swapped = dict(ROLES, x=1, y=0) page.set_rows(build_unit_rows(swapped, lambda axis: 10.0, _snapshot()), True) assert page.locked - assert page._edits["x"].text() == "10" # noqa: SLF001 + assert page._edits["x"].text() == "10" window.close() @@ -205,9 +205,9 @@ def test_the_axis_page_logs_alarms_and_can_clear_the_log(make_window): def test_a_unit_mismatch_reaches_the_axis_page_summary(make_window): window = make_window() window.axis_panel.update_snapshot(_snapshot(units=5.0)) - window._refresh_unit_report() # noqa: SLF001 - the window does this on every poll + window._refresh_unit_report() assert t("units.summary_bad", bad=5, total=5) == window.axis_panel.units_chip.text() window.axis_panel.update_snapshot(_snapshot(units=10.0)) - window._refresh_unit_report() # noqa: SLF001 + window._refresh_unit_report() assert t("units.summary_ok", total=5) == window.axis_panel.units_chip.text() window.close()