Files
orisys-app/apps/studio/docs/orisys-studio-ui-review-2026-10.md
ykcheungab dcabd547fa 接管 Studio UI 审查与剩余需求
承接工作区现行审查,保留客户决定与摄像机、实机和延迟待办。文档检查及迁移完整性核对通过。
2026-10-18 19:20:30 +08:00

278 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).