From dcabd547fa44dfcf4b68d53941ddcc34a40e58db Mon Sep 17 00:00:00 2001 From: ykcheungab Date: Sun, 18 Oct 2026 19:20:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E7=AE=A1=20Studio=20UI=20=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=E4=B8=8E=E5=89=A9=E4=BD=99=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 承接工作区现行审查,保留客户决定与摄像机、实机和延迟待办。文档检查及迁移完整性核对通过。 --- .../docs/orisys-studio-ui-review-2026-10.md | 277 ++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 apps/studio/docs/orisys-studio-ui-review-2026-10.md diff --git a/apps/studio/docs/orisys-studio-ui-review-2026-10.md b/apps/studio/docs/orisys-studio-ui-review-2026-10.md new file mode 100644 index 0000000..65b2f01 --- /dev/null +++ b/apps/studio/docs/orisys-studio-ui-review-2026-10.md @@ -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).