接管 Studio UI 审查与剩余需求
承接工作区现行审查,保留客户决定与摄像机、实机和延迟待办。文档检查及迁移完整性核对通过。
This commit is contained in:
@@ -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).
|
||||
Reference in New Issue
Block a user