接管 Studio UI 审查与剩余需求

承接工作区现行审查,保留客户决定与摄像机、实机和延迟待办。文档检查及迁移完整性核对通过。
This commit is contained in:
ykcheungab
2026-10-18 19:20:30 +08:00
parent 9a3886b8cf
commit dcabd547fa
@@ -0,0 +1,277 @@
# Studio — UI review: logic, look and information clarity (2026-10)
Status (2026-10-18): L1, L2 (rail hint and empty-state copy), L3 and A1 are implemented
in the reviewed `orisys-app` worktree; see [Implemented](#implemented-2026-10-18). Everything else below
is still a proposal. A touch-screen audit is in [section D](#d-touch-screen-readiness).
Maintenance moved to `apps/studio/docs` on 2026-10-18. This move preserves the dated review;
current implementation and commit status must be rechecked before starting an item.
Source history: workspace commit `5ab0beb`, same filename.
This review covers live, replay and calibration UI. An operator
meeting it cold cannot tell *what to do next* or *why a control is grey*. The biggest wins are not
visual polish: they are (1) making the next action obvious, (2) removing engineering vocabulary
from operator-facing text, and (3) fixing one regression of the September customer feedback that is
still visible in the default configuration (see L1).
Evidence: Six screenshots of the source build (`apps/studio/run.py`, 2026-10-18, dark theme,
`zh_CN`, sensor `finger`, no device attached): live view, 绘图设置, 三维显示设置, 手指 2D 显示方向,
传感器与标定, and the 标定 view. Copies are in workspace-root `.scratch/studio-ui-review-claude/shots/`
(disposable). Findings were checked against `orisys-app` `main` at `dc5a6f8`.
Not covered: the replay view, the light theme, a running session with live data, and the
`en_US` locale — each needs its own pass.
Severity: **P1** misleads or blocks the operator · **P2** slows or confuses · **P3** polish.
---
## Top priorities
| # | finding | sev | effort |
|---|---|---|---|
| L1 | FN curve still sits on the FX frame and has no 0 label by default | P1 | S |
| L2 | Nothing says *why* 运行 is disabled, and the rail offers no way to pick the video it waits for | P1 | S |
| L3 | Enabled and disabled buttons look almost the same, so the whole rail reads as "off" | P1 | S |
| C1 | 传感器与标定 shows internal state (`degraded`, `shipped`, absolute paths, an English SDK warning) | P1 | S–M |
| L4 | Calibration lives in two places with overlapping buttons (dialog and 标定 view) | P2 | M |
| A1 | Checked checkboxes are a filled square with no tick | P2 | S |
---
## A. UI logic
### L1 — the "no origin / FN on the FX frame" bug is still the default (P1)
The September review
items 5 and 19 were closed by making the FN lower bound configurable, but both defaults are `0.0`
(`ui/plot_settings_dialog.py:43,48`) and the N-mode floor was then deliberately fixed at 0
(`92e6de6`, 2026-10-07). With the view ending exactly at 0, pyqtgraph does not draw the 0 label and
the idle FN trace lies on the border it shares with FX (first screenshot).
*Done:* the configured range and ticks are unchanged (the 0 N decision stands); the *view* gets an
8 % margin beyond each bound (`force_plots._display_range`), never crossing zero when the range
excludes it, and bound labels may draw past the axis end.
### L2 — disabled 运行 never explains itself (P1)
The rail shows `使用摄像机`, which is the
*video-mode* group (`toolbar.py:193-200`): the current source is a video file, none is chosen, so 运行
is disabled. The only hint is a tooltip (`run_disabled_video`). The video group contains no
"choose video" button; that lives only in 文件 → 打开视频…. The 2D empty state says
"请选择视频或摄像机后点击「运行」" but nothing near it does either.
*Suggest:* show the source on the rail as one segmented control **摄像机 | 视频文件** followed by
the camera id or the file name with a `选择…` button. Put the disabled reason as visible text next
to 运行 (e.g. "未选择视频"), and make the 2D empty state contain the two actions as buttons.
### L3 — state is not legible on the rail (P1)
`QPushButton#primaryButton:disabled` uses
`panel_border` fill and muted text (`theme/qss.py:243`), which on `#0E1320` looks the same as an
enabled neutral button. In the screenshot 开始记录, 运行, 停止 and 重置传感器 are all disabled, and
it is not obvious.
*Suggest:* disabled = no fill, dashed or 40 % opacity border. Only one filled accent button at a
time — 运行 when idle, 停止 while running. Move 开始记录 after 运行 (you can only record a running
session) and keep it hidden or clearly inert until a session runs.
### L4 — two calibration homes (P2)
导出系数… and 应用到设备 appear both in the 传感器与标定 dialog and
at the bottom of the 标定 view; the dialog also has 保存, whose relation to 应用到设备 is unclear.
*Suggest:* the dialog chooses *which* calibration to use; the 标定 view *produces* one. Remove
export/apply from the dialog, or replace them with a link to the 标定 view.
### L5 — calibration workflow has no order (P2)
The 标定 view presents everything at once:
channel, weight (default `0.0000` N, a value that can never be valid), duration, 记录, a 0 %
progress bar, an empty table, 序列号, five row actions and four file actions.
*Suggest:* a three-step layout — ① channel + weight + capture, ② the point table with "fit needs
≥ 2 points (current 0)", ③ serial number + save/export/apply. Disable row actions until a row is
selected; separate 全部清空 from the others and confirm it. Rename 记录 to **采集样本** so it is not
confused with the session 开始记录 on the rail.
### L6 — inconsistent apply models (P2)
绘图设置 has 确定/取消; 三维显示设置 applies live and has
only 关闭; 传感器与标定 has 保存 and no cancel. Pick one rule: live-apply dialogs get 关闭 only and
say "更改立即生效"; deferred dialogs get 确定/取消.
### L7 — controls that live in the wrong place (P3)
显示三维视图 sits inside the 3D view's own
settings (it belongs in 视图 menu / layout); 复位视角 appears both as ↻ in the pane header and as a
button in the dialog; the settings toggle on the rail (设置), the ⚙ on every pane and the 视图 menu
are three entry points with no rule for which holds what.
### L8 — disabled fields without a reason (P2)
In 绘图设置, FN 上限 / 剪切上限 / 归零阈值(N)are
greyed because the sensor is uncalibrated; in 三维显示设置, 色标 and 范围 are greyed. Add one line
under each group, e.g. "牛顿量程需要先选择标定".
---
## B. Information clarity
### C1 — internal vocabulary in the sensor dialog (P1)
The header block shows
`配置档案 orisys.finger.r01 · 描述符配置包 · 来源 shipped`, an absolute path, `SDK 校验: degraded`,
and an untranslated SDK message `no calibration selected — calibrated force is unavailable; raw
force is available`. *Suggest:* one status line for operators ("可以运行 · 未标定,只能显示原始力"),
with the descriptor, root path and SDK check behind a 详情 expander. Map SDK warning codes to locale
strings instead of passing text through.
### C2 — terminology drift (P2)
One quantity has several names:
| concept | names seen | suggest |
|---|---|---|
| shear force | 切向力 (cards), 剪切 (绘图设置) | 切向力 everywhere |
| uncalibrated unit | `raw` (cards, badge), 原始 (settings) | 原始 in `zh_CN` |
| calibration for live use | 直播用标定 ("live-stream") | 实时采集用标定 |
| 3D pane | 三维网格 (title), 三维表面视图 (empty state) | 三维表面 |
| capture a calibration sample | 记录 | 采集样本 |
Also word order: "FN 原始上限" vs "原始剪切上限".
### C3 — numbers that are hard to read (P2)
Raw axes print `750000`, `-250000`; use `750k` or
thousands separators. The x-axis has no unit (s). 全程总览 is a label *below* an empty 0–1.0
overview; hide the overview until there is data, and title it above.
### C4 — the rail summarises the wrong things (P3)
`FINGER · sensor.json` shows a file name; an
operator needs the sensor and the calibration ("Finger · 未标定"). `使用摄像机` reads as a status
("using the camera") although it is an action to switch to the camera.
### C5 — explanation in a title bar (P3)
"手指 2D 显示方向(只影响 2D 画面:3D 视图仍用设计坐标系)" is
truncated as a window title. Title "2D 显示方向"; move the note into the dialog body.
### C6 — developer telemetry in an operator dialog (P3)
"已显示 1 · 未赶上显示的帧 0 · 合成 0.0 ms ·
绘制 1.8 ms" belongs in 帮助 → 诊断 or behind `show_fps`.
---
## C. Aesthetic
### A1 — checkbox and radio states (P2)
`QCheckBox::indicator:checked` paints a solid accent square
with no tick (`qss.py:481-484`); at 15 px it reads as "disabled" or "indeterminate". Add a tick
image, as the spin-box arrows already do (`_arrow_image`). In the 标定 view the selected FN radio
renders as a large rounded square ring, unlike the circles beside it.
### A2 — too much chrome, weak primary navigation (P2)
Menu bar + brand bar + rail use about
130 px of a 745 px window. The brand bar repeats the product name (ORISYS left, Orisys Studio right),
while the actual mode switch 实时 / 回放 / 标定 is the smallest text on screen. *Suggest:* merge
brand and tabs into one row, drop the right-hand name, and make the tabs ≥ 13 px with a filled
selected state.
### A3 — 3D pane (P2)
The header background stops at about 160 px, leaving a two-tone strip; the
pane is flush with the window edge while the other panes have margins; an empty strip sits under
the view. The idle mesh is a flat grey silhouette with no shading, outline or axis indicator, so it
reads as a placeholder blob. Add basic lighting or an edge outline and a small orientation gizmo.
### A4 — chart styling (P3)
Subplot left axes and frames are bright white and heavier than the
grid, and adjacent subplots draw double borders. Use one muted axis colour, a single separator
between subplots, and an accent zero line (now the reference line of the chart). The four
icon-only buttons in the chart header (→|, ⛶, ⬚, ⚙) need either labels or a grouped "视图" menu.
### A5 — focus looks like selection (P3)
The 2D mode combo and the 时间窗口 field open with an
accent border that is the keyboard focus ring; it suggests they are selected or in error. Use a
subtler ring, or do not give initial focus to fields.
### A6 — alignment and density (P3)
In 三维显示设置 labels wrap ("深度显示阈值") and spin boxes are
narrower than combos; in 标定 the "砝码重量 (N)" and "采集时长" labels are not on one edge. Use a
two-column form layout with fixed label width. In 绘图设置 split the fields into two columns,
**牛顿** and **原始**, so the disabled half is self-evident.
---
## D. Touch-screen readiness
Measured from the stylesheet and widgets; target ≈ 40 px for primary touch targets.
| # | finding | where | suggestion |
|---|---|---|---|
| T1 | Rail buttons are ~24 px tall; nav tabs 18 px with 11 px text; unit chip 18 px | `qss.py` `[rail="true"]`, `#navButton`, `#unitBadge` | a **touch density** preference (`compact`/`touch`) that raises these to 36–40 px; `ui_check`'s `top_area_limit = 78` would need a per-density limit — a product decision, not a bug |
| T2 | Essential information lives in tooltips (full source path, chart-header icon meaning, disabled reasons) — no hover on touch | `toolbar.py`, `force_plots.py` | visible text or a tap-to-reveal info button; the run reason is now on the rail (done) |
| T3 | Chart and 3D zoom are mouse-wheel only; no pinch | `force_plots.py` (`setMouseEnabled`), `scene3d/widget.py` `wheelEvent` | `QPinchGesture`, or on-screen +/− and fit buttons |
| T4 | No kinetic scrolling in scroll areas, tables or the sensor-dialog text | no `QScroller` use in `apps/studio` / `shared` | `QScroller.grabGesture(viewport, TouchGesture)` on each scroll area |
| T5 | Splitter handles are 6 px | `qss.py` `QSplitter::handle` | ≥ 12 px handles in touch density |
| T6 | Spin-box arrows are two half-height targets (~20 px) | `QAbstractSpinBox` | in touch density: side-by-side −/+ buttons, or rely on the numeric keyboard |
*Done in this change:* checkbox/radio indicators 18 px with taller rows, spin arrows 22 px wide,
scrollbars 12 px, menu rows +4 px, combo-popup rows ≥ 30 px.
---
## Implemented (2026-10-18)
| item | change |
|---|---|
| L1 | `force_plots._display_range`: 8 % view margin, zero-safe; labels may extend past the axis end |
| L2 | rail `run_hint` shows the blocking reason in acquire views (replay keeps its empty-state text); live + video reason no longer asks for a file; 2D empty state rewritten |
| L3 | disabled buttons: no fill, dashed outline (all four button styles) |
| A1 | checkbox tick image; checked radios keep their 20 px outer size (border no longer grows them into rounded squares) |
| — | `en_US` `panels.replay_empty` named a button label that does not exist; `ui_check` stub worker updated to the real `start()` keywords |
Verification: Linux offscreen (PySide6 6.12) against an untouched HEAD copy with the same stub fix —
studio suite fails exactly HEAD's set minus one (the replay-text test now passes), shared identical,
`ui_check` 631 checks with the same 2 failures as HEAD (one is Linux font metrics; "replay view is
not the live arrangement" is pre-existing and also fails on Windows). Windows run of the targeted
tests: App-root `.scratch/studio-ui-quick-fixes/`.
---
## Carried-forward requirements and remaining work
The [September plan](../../../../docs/archive/studio/orisys-studio-fix-plan.md) records these
customer decisions; they remain requirements, not newly verified implementation claims:
- Pause computation and display while keeping the camera connected.
- Keep `finger-mems` configuration in Studio, derived from box without calibration; no SDK addition.
- Keep camera preferences in `config/`, use app-local discovery and scan at start-up.
- Keep force settings per channel and separate device/tracker reset from view reset.
- Treat housing as a separate part, hidden by default while debugging.
| remaining item | status and acceptance boundary |
|---|---|
| September C: camera list and auto-pick | Previously not started; reconfirm current code. Persist names/preferences, scan off the GUI thread and avoid scans while a camera is in use. Stubbed tests must cover selection, missing stored IDs and UI responsiveness. |
| September display requests | Recheck valid-mask boundary stability, palette/LUT parity, per-axis unit labels and 2D/3D arrow appearance before dispatch; do not infer completion from the old plan. |
| Device registration and orientation | September items 8, 14, 15 and 17 still need device evidence. A device session needs the equipment, bounds and stop conditions required by workspace rules. |
| Latency | Item 9 needs paired input/configuration and frame-identity evidence for packet-to-paint delay. Synthetic or offscreen checks alone do not establish live-device latency. |
Current contracts: [layout](ui-layout.md), [palette](ui-palette.md), [bundles](sensor-bundles.md)
and [recording](record-channel.md). Inspect the current [window/controller](../src/orisys_studio/app_window.py)
and relevant [UI tests](../tests/test_ui_smoke.py), [pause tests](../tests/test_pause_controls.py)
and [UI checker](../tools/ui_check.py) before changing them. These are owners and check entry points,
not evidence that this migration exercised the UI or hardware.
## Suggested order
1. ~~L1, L2, L3, A1~~ — done.
2. C1, C2 — one locale sweep plus an SDK-code → message map.
3. L4, L5 — calibration restructure; needs a short agreement on the workflow first.
4. A2–A6, L6–L8, C3–C6 — polish, batchable with `ui_check.py` assertions (rail elision at
1280×720 still applies to any rail change).
Historical sources: [end-user comments](../../../../docs/archive/studio/orisys-studio-enduser-comments.md)
and [fix plan](../../../../docs/archive/studio/orisys-studio-fix-plan.md).