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

16 KiB
Raw Blame History

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. Everything else below is still a proposal. A touch-screen audit is in section D.

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 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, palette, bundles and recording. Inspect the current window/controller and relevant UI tests, pause tests and UI checker 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 and fix plan.