ykcheungab
d4b66c65a1
Unify 2D arrows on the shared painter.
...
Drop orisys.util.draw_arrows; box live and replay both use
_paint_shared_flow_arrows with sdk_box_policy when no policy is passed.
Recovered box flow uses the same inverse as finger. Verified with
shared/tests/test_image_backend.py arrow/idle/recovered cases.
2026-10-09 13:34:10 +08:00
ykcheungab
091d0fda35
三维未激活采样显示灰点,二维增加深度叠箭头。
...
分析对话框默认使用传感器对话框保存的标定,关闭按钮改为保存。框选曲线时数值卡片仍显示光标读数。相关单测通过。
2026-10-08 17:04:30 +08:00
ykcheungab
3d2fb0c4eb
让直播标定文件从传感器对话框进入打开链路。
...
对话框可选配置默认、原始力或浏览文件,并按配置条目写入 prefs;点运行时把 cal_path(Finger 含 device_id)交给 worker,采集中则热更新。对话框与打开链路单测通过。
2026-10-08 16:37:26 +08:00
ykcheungab
19ae1f4968
三维显示与二维共用深度色标,配置名改为 Finger
...
箭头阈值写入设置;去掉网格告警。三维色标改为立刻采用二维量程(不再 P5/P95 缓变)。选择器显示 Finger。已用 pane UI、shared-scale 与 catalog 单测验证。
2026-10-08 16:10:58 +08:00
ykcheungab
f2d7b3e83c
三维设置加箭头阈值,静默处理网格冲突
...
对话框写回 flow_on_px(二维三维共用),去掉 analysis_grid_mismatch 告警,缩短 UV/形变等工程腔文案并认 source=analysis。已用 pane UI 与 controller 单测验证。
2026-10-08 16:01:15 +08:00
ykcheungab
f2bbab76a6
缩短 Studio 叠字与重复说明,色标只保留量程
...
去掉 depth_empty 与无用选区峰值文案,并修正「视图 → 分析视频」。已用 locale 单测与 depth_scale_text 核对;含证据录制的 wiring 用例本机跳过。
2026-10-08 15:41:30 +08:00
ykcheungab
48624dec99
让 Finger 视频分析使用所选的力标定文件。
...
分析链不再在打开前拒绝标定,而是把文件作为 cal_path 交给 FingerRuntime,并用操作者确认的设备编号核对文件里的 SN。标定传递测试与对话框 SN 校验通过。
2026-10-08 15:12:08 +08:00
ykcheungab
7afd3169fd
把 Studio 2D 画面与深度色标抽到 FrameView。
...
深度闩、域掩码与 _show_image_frame 离开主窗口;ImageSink 方法名保留为转发。朝向与深度闩测试、以及 monkeypatch render_image 的画面测试通过。
2026-10-08 10:39:40 +08:00
ykcheungab
3e45ecdc3c
把分析视频和分析设置挪到独立的「分析」菜单。
...
菜单栏为文件 | 采集 | 分析 | 视图 | 帮助。提示里指向这两项的路径已改成「分析」。
2026-10-07 21:36:13 +08:00
ykcheungab
154a84ddae
把运行/预览/暂停等采集控制挪到「采集」菜单。
...
菜单栏顺序为文件 | 采集 | 视图 | 帮助;复位视图、主题与语言仍在视图。快捷键不变。
2026-10-07 21:31:02 +08:00
ykcheungab
92e6de6913
收紧绘图设置为两页,并把 FN 牛顿下限固定为 0。
...
手指 2D 方向改到 ArrowPanel 设置对话框;归零阈值不变。已用 display_prefs、finger_2d_orientation 与绘图对话框相关 smoke 测试验证。
2026-10-07 20:48:48 +08:00
ykcheungab
b1cd37148c
删掉回放的五个转发、重复的空格快捷键与重复的等待循环。
...
回放调用点直接走 replay_ctl,场景工具的路径引导收敛到 _paths,panel_snapshot
复用 panel_check 的 _wait_for,report_from 收成模块私有的 _report_from。
2026-10-07 12:25:48 +08:00
ykcheungab
86996694b4
统一力值阈值处理并保留原始采样
...
移除自动显示基线,使逐轴阈值统一作用于曲线、读数与记录输出,标定拟合继续使用原始采样。
共享层全量测试和相关 Studio 测试通过。
2026-10-07 12:25:12 +08:00
ykcheungab
6194778a6f
收起复用与完成态分析横幅。
...
来源栏已显示结果状态,横幅约 1.5 秒后只隐藏控件并保留 state()。已用 apps/studio/tests/test_analysis_progress.py 验证。
2026-10-07 09:37:08 +08:00
ykcheungab
97e80d3d38
为二维与三维深度色标增加每帧自适应选项。
...
默认仍锁定本会话首帧,勾选后两视图按当前帧有效深度共用同一范围。已用深度色标与显示设置测试验证。
2026-10-07 09:34:57 +08:00
ykcheungab
425f0dc9e2
统一 2D/3D 深度色条为同一量程。
...
将 2D 闩锁的深度色彩区间推给 3D 自动着色,固定量程仍跟旋钮。用 test_shared_depth_scale 与深度闩锁冒烟用例验证。
2026-10-06 21:31:53 +08:00
ykcheungab
829d2c1b53
回放叠加改用展开底图,并统一 2D/3D 投递。
...
箭头/采集图像与 live 的 processed_frame 对齐;deliver_measurement 先画 2D 再提交同一包给 3D。已用 overlay、scene3d 与 replay 相关测试验证。
2026-10-06 21:21:37 +08:00
ykcheungab
321e52f47a
改善 Studio 回放布局与传感器切换体验
...
缩短界面提示,保存上次视频目录,并防止旧结果绘入 Finger 3D。针对性测试及可见界面复测通过。
2026-10-06 20:18:50 +08:00
ykcheungab
425fa0d44d
Fix replay 2D black canvas and journey UX clarity
...
Stored analysis overlays now use the decoded frame; latch resets and draw failures surface. Verified with bare-video, replay-wiring, visual_policy, and analysis-progress tests.
2026-10-06 16:26:47 +08:00
ykcheungab
d11441cd79
Show only the bundle's name in the sensor configuration row
...
The row carried the origin, the calibration selection and the SDK's verdict, which a 30 px
line cannot explain; it is now the descriptor's display_name, else the folder name, and the
sensor dialog states the rest under the selector — name and folder, profile, shape, root,
verdict, calibration and every finding — with a suffix only where two rows would otherwise
read alike. Verified with the studio suite, the catalog tests in shared/tests, ui_check and
the lint gate.
2026-10-05 17:01:42 +08:00
ykcheungab
92b2f2fcd7
裸视频可分析:生成工作区、设置提示、逐帧力与按来源选画笔
...
pass 只认 frames.csv 与 session.json,裸视频此前因此只能播放;现在为它在应用自己的目录里生成
这两份文件并复用既有链路,链路重算的力按帧存入结果并标注为「重算」,box 的空白箭头画布改按帧
来源走共享画笔。
apps/studio/tests 497 项、shared/tests 471 项(2 个已知墙钟失败)、autocal 358 项、ui_check 634 项
均 0 失败,lint_gate 与 doc_lint 无发现;真实 GL 快照见 .scratch/bare-video/snapshots。
2026-10-05 16:57:02 +08:00
ykcheungab
c0a10e6adb
Present replay 3D with the cursor and open Analyze on a remembered video
...
Replay left the 3D pane on the rest mesh or a sparse overlay, and Analyze skipped the setup dialog unless the file was re-picked. Live Run no longer starts a chain on a video path. Verified with Studio tests for flush, overlay mask, Analyze open, live-video refusal, and locales.
2026-10-05 16:52:27 +08:00
ykcheungab
3433eb0fec
Record the live box arrow grid so replay matches live paint
...
Box analysis used the finger sample grid, so replay packed arrows into the contact zone. The pass now stores draw_arrows cells; stale box caches miss and re-run. Verified with shared and Studio tests, including a live-vs-replay canvas check.
2026-10-05 15:01:48 +08:00
ykcheungab
28e955a817
把 Studio 主机协议改成单向端口,并让「从视频重算」走分析入口
...
控制器经 AcquireGate / ImageSink / ShellNotes 与信号接线,采集 tick 用 acquire_presenter 上屏;三维测试证明分析结果已喂 3D 后,重算并入 on_analysis_requested。
验证:apps/studio 下相关 pytest 与 tools/ui_check.py(634 checks)通过。
2026-10-05 14:00:24 +08:00
ykcheungab
25ccffd814
Discover sensor bundles from the SDK catalog, not a product list
...
The picker listed a hard-coded product list and treated any *.json beside sensor.json as a
configuration, so a folder of stray JSON could be offered as a sensor. Discovery now scans the
shipped root and a writable user root beside it, keeps the SDK's verdict for every entry
(listing rejected ones with their reason) and flags duplicate profile_ids instead of shadowing
them; verified by shared/tests and apps/studio/tests.
2026-10-05 09:30:04 +08:00
ykcheungab
a92a2ba237
Read the analysis cache before refusing a pass, and own the cache root
...
Opening a recording while the session that recorded it was still live never reached
``stored_result()``, so the panel reported no result over a recording whose result was on
disk. The guard now follows the cache read, results move to the app's derived-data root
(swept at startup, the old in-recording root ignored), and the panel states which of the
four no-result states it is in, with the pass's own reason.
Verified: apps/studio 462/0/18, shared 431/0/1, autocal 358, ui_check 628/0, both gates
clean, real-GL snapshots per state.
2026-10-04 16:53:50 +08:00
ykcheungab
1aa454311f
Make the cached analysis views reachable over a recording
...
Replay moved the cursor onto the result's timeline, so the recording's own
picture was not offered at all and ``use_recording()`` had no caller. Adopting a
result now keeps the cursor on the recording, opens the panel on a derived view,
and serves scrubs, playback and display changes from the cache (Part B §9).
Verified: apps/studio 455 items / 0 failed / 18 skipped, shared 427 / 0 / 1,
autocal 358, ui_check 628 checks / 0 failures, lint gate clean.
2026-10-04 16:04:02 +08:00
ykcheungab
9e20fd0277
Store the depth channel in an analysis result and serve it to the 2D depth view
...
A replay could draw arrows but not depth, and the recording view said there was
nothing to draw even when a result existed. Depth is now quantised per frame
(14 B empty, 486 B at the worst frame seen), read from the cache by the same
draw path live uses, and the raw-recording note appears only when no result
exists. Format version 2; see apps/studio/docs/replay-analysis.md.
2026-10-04 14:11:22 +08:00
ykcheungab
cf49b0836e
Serve replay playback from a cached analysis result
...
Replay re-ran the SDK chain on every seek; Part A's pass stores the flow
samples once. Opening a recording now reuses a compatible result or runs the
pass behind a progress/cancel surface, and one cursor with two modes
(recorded CSV, analysed) feeds the charts, the 2D canvas and the 3D pane by
frame id. A threshold, colour or idle-dot change redraws from the stored
frames and writes nothing; a cancelled pass is reported and never played.
Verified: apps/studio/tests 440 items / 0 failed / 18 skipped (15 new, no
existing assertion changed); shared 417 / 0 / 1; autocal 358.
2026-10-04 13:38:03 +08:00
ykcheungab
6fb2f72c6e
Make the rail reset zero the sensor in live and the display in replay
...
The replay view disabled a control whose display reset is exactly what an
operator wants there, and the bare "Reset" hid that the live one zeroes the
sensor tracker; the label, tooltip and status line now name the target, and
Ctrl+R follows the view. Verified with the studio suite, ui_check (628
checks) and real-GL rail snapshots of both views.
2026-10-04 11:09:22 +08:00
ykcheungab
dda6c74fb7
fix the finger 2D canvas orientation and the 3D arrow size
...
The unrolled canvas put the finger's +x where the 3D default view puts screen-left,
and the 3D flow arrows rasterised as 0.23 px hairlines because their stroke was 2 %
of their own sub-millimetre length. The 2D presentation is now the derived half turn
(persisted, set from the plot-settings dialog) and the 3D stroke is one canvas pixel
— the width the 2D canvas draws — verified numerically and with real-GL snapshots.
2026-10-04 10:04:51 +08:00
ykcheungab
9b1a3fb922
state the finger 3D camera presets in the design export's frame
...
The default view came from the mesh's own face normal and sat 24.6 degrees off the
design export's frame; re-applying any preset also adopted its baked distance, so the
part overflowed the viewport (measured 2.16 x 2.12 of it). Presets are now the frame's
own views, every preset is reframed through the pane's existing fit, and that fit
projects the model's bounds instead of a bounding sphere. Verified: the frame axes map
to screen up, left and viewer to 1e-6, the model fills 0.60-0.95 of the viewport at two
sizes, and the recompiled package keeps its geometry, domain and source-geometry ids.
2026-10-01 16:56:36 +08:00
ykcheungab
eb70ac7638
add the Studio element manifest and its window-matching test
...
ui-layout.md named the arrangement's owners but nothing knew which elements exist, so a
view switch could silently take a running session's tracker reset away: docs/ui-elements.json
now states all 195 by region, pane and view, ui/elements.py loads it, and
tools/ui_elements_dump.py drafts it.
Verified: apps/studio 393 items (13 new) and ui_check 600/0, autocal 358, shared 390 with its
2 known wall-clock failures, lint and doc gates clean; a removed element, a renamed
objectName and a new named widget each fail the suite.
2026-10-01 15:18:16 +08:00
ykcheungab
e0156a14c6
fix Studio reset, 3D base colour and arrow-trigger defects
...
The tracker reset was decided by which panel was on screen rather than by the
session, the automatic colour range moved the 3D surface's base colour on the first
press, and the 2D canvas and the 3D overlay applied different flow thresholds (622
arrows against 432 on a real recording). ui_check 600/0; apps/studio 380 items and
shared 390 items with no failures; real-GL 18 items, only the pre-existing
colour-scale failure; AutoCal 358 passed; both views now select identical arrows on
all 42 frames of a real video.
2026-10-01 14:40:33 +08:00
ykcheungab
720bd81031
fixed window flashing bug
2026-09-28 21:17:26 +08:00
ykcheungab
fa2703968e
added ruff test
2026-09-27 19:20:43 +08:00
ykcheungab
bcab2790bf
modify 3d view to sync / identical to 2d view
2026-09-27 10:42:38 +08:00
ykcheungab
3daee6d407
fixed display problem
2026-09-25 17:53:07 +08:00
ykcheungab
12781854ca
fixed commetn from songxiao
2026-09-25 17:46:37 +08:00
ykcheungab
ccedb9ddd9
migrate pyqt5 to pyside6
2026-09-21 09:34:56 +08:00
ykcheungab
e276b5de0f
autocal layout first design
2026-09-20 21:15:39 +08:00
ykcheungab
c538dbf074
fixed label under 3d view
2026-09-15 21:50:37 +08:00
ykcheungab
c2293d4d41
界面:三维可选「深度响应 / 光流幅值」上色(默认深度),并加一个「诊断信息」开关收拾面板脚下的文字
...
- What(颜色来源): `DisplaySettings` 增 `field`(`"depth"` 默认 / `"flow"`),随显示设置持久化
(`scene3d/prefs.py`:新增 `FIELD_CHOICES`/`DEFAULT_FIELD`,手改文件里的非法值修回默认)。
`FingerFrameAdapter` 按所选字段先试、失败再用另一个,并把"换了量"这件事记在
`last_substitution`(新诊断码 `field_substituted`,info 级);两个字段都拿不到时,
`last_diagnostic` 取**信息量最大**的那条(域不符/过期/未声明 > 没这个字段),而不是"所选字段"的
那条空话。设置对话框新增「颜色来源」下拉,下面一行 `field_note` 直接写"这一帧画的是 X,不是所选的 Y";
图例(`legend_label`)永远标注**实际**画出来的量,所以设定与画面不一致时两处都能看出来。
深度响应 `[1]`、光流幅值 `[px]`,两者都是 `approximate`——代码与文案都不把它们说成毫米。
- What(诊断信息开关): 视图菜单新增 `诊断信息`(可勾选、持久化 `show_diagnostics`、**默认关**,
由 `DisplayPrefsController` 持有并推给两个面板)。关掉后:2D 面板只留"画面 + 计划说明 + 深度色标",
三条解释流水线的标签(箭头按位移放大、原始图像 → 算法输出、全部帧)隐藏;3D 面板只留
色条 + 一行图例 + 来源,info 级诊断隐藏。**永不隐藏**:warning/error 级诊断(测量是否过期、
字段是否本帧的、是否与设备核对过——`geometry_unverified` 虽然是 info 也白名单保留)与
深度色标。3D 图例改成 `ShrinkableLabel`(一行、裁切 + tooltip,不再折行),色标数值从 4 位有效数字
收到 3 位("0.0334 – 0.899")。设置对话框控件宽度 88 → 108 px(不再需要迁就 240 px 面板)。
- What(取景脚本): `tools/ui_snapshot.py --scene flow3d` 现在同时给 flow 与 depth(各带 provenance),
所以「颜色来源」开关有东西可切;再加 `.repro/shot_variants.py` 一次出四张图
(深度版 / 光流版 / 诊断开 / 设置对话框)。
- What(测试): `test_scene3d_adapters.py` 增 3 项(默认深度、换量时给出 `field_substituted` 并在恢复后
清掉、depth 不在场景栅格时按 `field_domain_mismatch` 拒绝而不是缩放);`test_scene3d_view.py` 增
1 项(field 的持久化与修复,并把 `field in FIELD_CHOICES` 加进"手改文件会被修复"的参数化);
`test_scene3d_pane_ui.py` 增 3 项(两种量可选 + 说明行 + 恢复的偏好会移动下拉;诊断开关只隐藏 info、
保留 warning 与"未核对";2D 脚注三标签的显隐与窗口开关一致);`test_display_prefs.py` 增 1 项
(开关推到两个面板、写偏好)。`test_ui_smoke.py` 与 `tools/ui_check.py` 里"箭头图例可见"的四条断言
改为按新规则检查(默认隐藏、开诊断后可见、关掉再隐藏)。
- Why: ①"深度"比"光流"更贴近操作员口中的"压得多深",但两者都只是近似量,所以做成开关而不是替换——
并且换量必须说出来(否则画面会被读成所选的量)。②面板脚下的解释文字是"内部管线"而不是"画面是什么",
拍视频与日常看都只需要后者;用一个开关收起,比做一个滚动日志面板诚实(这些是每帧状态,不是事件)。
- Verification: pytest shared/tests apps/studio/tests(offscreen)658 passed / 0 failed / 15 skipped;
真 GL 组(gl_smoke + view + pane_ui)70 passed / 0 failed / 1 skipped;
`tools/ui_check.py` 590 项检查 0 失败;`.repro/locale_check.py` 0 缺失(新增 7 个 key 双语齐备)。
目视验收:同一批合成数据渲染的四张图(深色主题)——深度版(默认)、光流版、诊断打开、设置对话框。
- Not in this change: 不改采集/记录/契约之外的三维渲染;不做页签容器与窄窗口回退;曲线量程仍是出厂 ±20 N。
真机待验证:手指路径上 `packet.depth` 的栅格是否就是场景的 300×280(若不是,面板会写明并回退到光流)。
2026-09-15 21:23:18 +08:00
ykcheungab
0b87e3e36d
界面:三维显示设置对话框的关闭按钮说中文(Qt 自带翻译没有被安装)
...
- What: `Scene3dSettingsDialog.retranslate()` 把 `QDialogButtonBox` 的 Close 按钮文字设为
`t("dialog.close")`;两个 locale 各加一个 close 键。
- Why: 应用没有安装 Qt 自带的翻译文件,于是按钮框在满屏中文里显示一个英文 Close;对话框是这个
面板唯一的出口按钮,不该是外来语。
- Verification: `.repro/locale_check.py` 0 缺失(新键双语齐备);对话框截图目视确认
(266x531,结尾按钮为「关闭」)。
- Not in this change: 不动其它对话框(它们用自己的按钮,本来就有中文文案)。
2026-09-15 20:57:21 +08:00
ykcheungab
650578d738
界面:三维面板只留画面与图例,全部配置进「三维显示设置」对话框;三维占比提为最宽
...
- What(隐藏配置): `ThreeDPanel` 把**所有设置**收进一个块
(`controls_widget`:总开关、观察方向、显示外壳、放大形变+增益、色标模式与范围、显示帧率、
复位按钮、显示统计),面板本身只保留读图需要的东西:色条 + 一行紧凑图例(数量/自动色标范围/
来源)+ 状态行。新增 `scene3d/settings_dialog.py` 的 `Scene3dSettingsDialog`:非模态
(调参时画面还在动),承载的就是面板那套控件本身(同一批 widget,信号通路不变),
关闭只是隐藏(控件必须活着,下一次读设置还要用)。
两个入口:视图菜单新增 `三维显示设置…`(`act_scene3d_settings` → `Mesh3dPanel.open_settings()`)
与面板标题栏的齿轮;无三维可配时(box 传感器 / 无 GL / 无场景包)菜单项会把面板上的原因
原样提示出来,而不是弹一个空对话框。标题栏另有「复位视角」图标(新画的 `reset_view` 图标,
环形箭头,随主题着色)。
- What(提高占比): live/replay 预设把三维面板设为最宽:`VIEW_SPLITTER_SIZES` (620, 300, 520)、
`DEFAULT_SPLITTER_SIZES` (620, 320, 500)、`DEFAULT_3D_PANE_WIDTH` 340 → 620、
`MIN_PANEL_WIDTHS["mesh3d"]` 260 → 200(面板里没有表单要放了,画面才是重点)、
`ViewLayoutController.apply_panel_visibility` 的拉伸权重 2 → 5(2D 3、曲线 4)。
1440×880 实测:三维约 430 px(原约 280)、2D 约 265、曲线约 320。
- What(拍视频/评审用): `tools/ui_snapshot.py` 新增 `--scene flow3d`:驱动一批**带真实光流场**的
packets(适配器只接受声明了 provenance 的字段),所以三维面板真的有颜色、曲线也真的在动;
这是本次界面评审与演示视频的取景脚本。配 `--sensor finger --panels 3d --preset live`。
- What(测试): 新增 `apps/studio/tests/test_scene3d_pane_ui.py`(5 项):面板只有图例没有设置、
对话框承载的是面板自己的控件且设置信号照旧、关闭只隐藏不销毁、标题栏两个动作可用、
无场景时 `open_settings()` 返回 False、`reset_view` 转发、`reset_view` 图标两种主题都能画、
视图菜单项能打开对话框(无 GL 时给原因)。`test_view_layout.py` 增一条"出厂 live 布局里
三维最宽"的断言(并更正我自己那条 340 的旧期望)。
- 文档: `studio-3d-stages.md` 的「尚未完成」第 1 条(阶段 8 第二批)改写为"部分已做",
写清已完成的部分与仍留后的四项(页签容器/窄窗口回退/曲线量程开关/独立诊断抽屉)。
- Why: 三维面板原来下半屏全是表单,画面被挤成一小块——拍视频要的是"三维是主角"。
配置不是不需要,而是不该和画面抢地方;放进对话框后画面变大、参数反而更好找(一处看全)。
- Verification: pytest shared/tests apps/studio/tests(offscreen)650 passed / **2 failed** / 15 skipped,
两个失败都是 `shared/tests/test_worker_frame_rate.py` 的**墙钟计时**用例(30 fps 设备被测成
24–25 fps):已在 `d539ce9` 的干净 worktree 上复现同样的失败,属本机负载(DSH/Cursor/飞书 占 CPU)
而非本次改动;真 GL 用例 `test_scene3d_gl_smoke` + `test_scene3d_view` 61 项 0 失败(1 跳过);
`tools/ui_check.py` 587 项检查 0 失败;`.repro/locale_check.py` 0 缺失(新增 6 个 key 双语齐备)。
目视验收:`--scene flow3d` 渲染的 1440×880 截图(拆分前后各一张)见提交说明附言。
- Not in this change: 不改三维渲染/契约/记录;不做页签容器与窄窗口回退;
曲线量程仍是出厂 ±20 N(演示取景脚本会临时调窄,产品默认值另议)。
2026-09-15 20:55:33 +08:00
ykcheungab
81e39ca868
修复:设备的"清零已生效"确认从未被记录(worker 的 on_reset_applied 没有接线)
...
- What: `SessionController.ensure_workers` 现在把 `on_reset_applied=self.note_reset_applied`
交给两个 worker,并在控制器上补回 `note_reset_applied()`(转发给记录通道的
`recording.note_reset`)。窗口侧保留 `_on_reset_applied` 作为一层转发——tests 通过它
模拟设备确认。
- Fix: 这是一个真实缺陷,不是重构副作用。请求路径一直是通的(`_on_reset` →
worker.reset()),确认路径却从来没有人接:worker 的 `on_reset_applied` 默认 None,
于是设备回报的 frame_id / mono_t 被丢掉,`recording.note_reset` 不会执行,
`_drain_reset_events()` 也永远没有事件可发。后果是真机上按「清零」只会看到「指令已发出」,
录制里不会留下 zeroing 事件(`session.json` 的 events 少一条),而 test_ui_smoke 里那条
`test_a_zeroing_is_recorded_only_after_the_worker_confirms_it` 之所以一直是绿的,是因为
它自己把 `stuck.on_reset_applied = window._on_reset_applied` 手工接上了——测试补上了
产品缺的那根线。
- What: 新增 apps/studio/tests/test_session_controller.py:8 个用例直接驱动控制器
(假 worker、假记录通道)——清零确认与标定确认都会到达记录、两个 worker 都挂上记录
sink 与三维 sink、detach_sinks 会摘掉、一次运行从 connecting 到 running 到 idle 的
相位与遥测(结束后旧遥测不可读、session id 递增)、connecting 时停止会被如实告知、
慢连接提示只说一次、丢帧批次只报一次、采集率短少必须有实测值才判定且一会话只说一次。
- Why: 记录要说真话。清零在实验里是一个事件("这一刻起力被归零"),设备既然回报了
它就应当进入记录;把接线补在控制器(A2 搬进来的那个所有者)而不是窗口里,也让
"谁在跑、报了什么"这条链路只有一个所有者。
- Verification: pytest shared/tests apps/studio/tests(offscreen)644 passed / 0 failed /
15 skipped(原 636 + 新增 8);apps/studio/tools/ui_check.py 587 项检查 0 失败;
.repro/locale_check.py 0 缺失。真机上的验收仍未做(本机没有设备),
已知边界:SDK worker 的 `_on_reset_applied` 只在 `sensor.reset()` 返回后调用,
异常路径本来就不会报告确认——这一点没有改动。
- Not in this change: 不改变 `_on_reset` 的请求语义与状态栏文案;`_drain_reset_events`
仍在窗口(它只是把事件变成一行状态)。
2026-09-15 09:58:45 +08:00
ykcheungab
c62452ec4b
拆分收尾:标定归属规则移入 CalibrationService(app_window 2340→2304 行)
...
- What: `apps/studio/src/orisys_studio/calibration_service.py` 新增三条**无状态**规则,
它们原来长在窗口里:`identity_of(path)`(这份系数文件声称属于哪台设备——用 SDK 自己的
解析器读,与设备将要加载的是同一份文档;读不出来就什么都不声称)、`same_path(a, b)`
(同一个配置路径的不同写法不算不一致;两个空值也不算)、
`target_mismatch(path, *, sensor_type, config_ref)`(在确认框之前就说清楚"这份标定
看起来不属于这台设备"的理由)。服务本来就是这个文件工作的所有者,规则回到了它这里,
并且不需要窗口就能测。
- What: 窗口保留 `calibration_identity` / `calibration_target_mismatch` 两个转发
(tests 与 tools/ui_check.py 通过窗口驱动它们),删除 `_same_path`。
`_on_calibration_apply` 仍留在窗口:它要弹确认框、要把路径交给采集 worker,那是交互
与会话,不是关于文件的规则;`_calibration_summary`、`_drain_calibration_events`、
`_drain_reset_events`、`_on_calibration_point/cancelled/exported` 同理(面板与状态栏
的胶水)。
- What: 新增 apps/studio/tests/test_calibration_service.py:4 个用例——身份读出
sensor_type/sn/config_ref、读不出来的文件既无身份也无"不匹配"指控(不能把"不知道"
当成"不一致")、另一台设备/另一份配置会被逐条报出而同一台不会、同一路径的不同写法
不算不一致而非路径值算。
- Why: 这是 app_window 拆分计划里最后一块「有独立规则可搬」的部分。搬完之后
"这份标定是谁的"只有一处实现,窗口只剩对话与转发。
- Verification: pytest shared/tests apps/studio/tests(offscreen)636 passed / 0 failed /
15 skipped(原 632 + 新增 4);apps/studio/tools/ui_check.py 587 项检查 0 失败;
.repro/locale_check.py 0 缺失。行为等价(同一份实现 + 一层转发)。
- Not in this change: 预览(_start_preview/_stop_preview/_on_preview_tick)与 2D 渲染
(_render_run_image/_show_image_frame/_depth_range)仍留在窗口——它们是面板与相机的
胶水,不属于任何一个已拆出的变更驱动;已知缺陷(本次未修,另行处理):worker 的
on_reset_applied 从未接到 recording.note_reset,真机上按"清零"不会记录确认事件。
2026-09-15 09:54:04 +08:00
ykcheungab
0a42a452dd
拆分 C2:显示偏好移入 DisplayPrefsController(app_window 2347→2340 行 / 净减 110 行代码)
...
- What: 新增 apps/studio/src/orisys_studio/display_prefs.py:把「同一个测量值可以怎么
显示」搬进 DisplayPrefsController——单位(newton / raw,含工具栏、曲线图、重算引擎、
数值卡四处同步)、量程与时间窗(含默认值、应用到面板、供对话框读取)、箭头显示
(按传感器决定旋转是否可用、图例倍率、旋转 90°)、2D 视图模式切换后的重绘、以及
rail 上的帧率读数开关。控制器自己拥有 show_fps;其余偏好仍写在 preferences 里。
`DisplayHost` Protocol 写明它向窗口借的五件事:_persist_prefs / _refresh_status_rail /
_latest_fps(算滤波设计用的采样率)/ _drop_render_marker + _repaint_live_frame
(2D 视图模式变了要重建那一帧)。
_on_plot_settings *不搬*:它要开 QDialog、还要把滤波系数交给采集 worker,那是会话与
窗口的事,不是显示偏好;它现在只向控制器取对话框要显示的值、把结果交回去应用。
- What: 窗口侧保留过渡 shim(_set_display_unit / _on_units_changed / _on_chart_unit_toggle /
_on_image_view_mode_changed / _on_show_fps_toggled / _apply_plot_prefs_to_panel /
_device_sample_rate / _apply_arrow_rotation_for_sensor / _apply_arrow_legend /
_draw_style / _on_rotate_arrows,以及 _show_fps 可读写属性),供 tests 与
tools/ui_check.py 继续驱动窗口(其中 window._on_plot_settings 仍可被测试整体替换)。
窗口内部连线(toolbar.units_changed、arrow_panel.view_mode_changed、
force_plots.unit_toggle_requested、act_show_fps.toggled、act_rotate_arrows.triggered)
改为直连控制器;_sync_menu_checks 里的箭头部分也走控制器。
- What: 新增 apps/studio/tests/test_display_prefs.py:11 个用例——单位四处同步、
工具栏改单位时不回写工具栏(防回声)、图表单位芯片翻转、对话框值取默认值、
应用量程写偏好与面板、采样率优先用实测值再回落存储值与默认值、不可旋转的传感器
不提供旋转且不写偏好、可旋转的重启后记住角度、图例倍率随 draw style 变化、
切换 2D 模式只重绘一次、帧率开关同时更新偏好与 rail。
- Why: 「数字怎么显示」是 app_window 的第四个独立变更驱动:改一个显示偏好不该碰会话、
回放或布局。搬完之后这条规则的唯一所有者在 display_prefs 里,且可以脱离窗口单测。
- Verification: pytest shared/tests apps/studio/tests(offscreen)632 passed / 0 failed /
15 skipped(原 621 + 新增 11);apps/studio/tools/ui_check.py 587 项检查 0 失败;
.repro/locale_check.py 0 缺失。行为等价(搬运 + 一层转发)。
- Not in this change: 标定交互(_on_calibration_apply / 身份与不匹配检查 / 导出与事件)
仍未拆分;预览(_start_preview/_stop_preview/_on_preview_tick)留在窗口;
_depth_range/_depth_scale_text 与 _show_image_frame 同属 2D 渲染,留在窗口;
docs 的行数/方法数指标待全部切片落定后统一刷新。
2026-09-15 09:50:25 +08:00
ykcheungab
aecde67c73
拆分 C1:面板布局与视图预设移入 ViewLayoutController(app_window 2499→2347 行)
...
- What: 新增 apps/studio/src/orisys_studio/view_layout.py:把「一个视图是什么」从
app_window 搬进 ViewLayoutController——面板可见性与最小宽度、splitter 尺寸与 stretch、
三个预设(live / replay / calibration)的切换、视图菜单三个勾选项的状态、3D 面板的
开与关(含它看哪个传感器、恢复持久化的显示设置)、设置抽屉的展开状态。
控制器自己拥有 view_preset / show_cal / show_finger_3d 三个状态(初值直接取自
preferences,窗口不再留第二份),并直接驱动面板;`LayoutHost` Protocol 写明它向窗口
借的四件事:_persist_prefs / _update_run_enabled / _refresh_status_rail /
_current_source_kind(以及 3D 的 _device_geometry_provider)。
预设切换同时通知 ReplayController(暂停 transport、把曲线图交还实时会话),
这是 B 之后两个控制器之间唯一的方向性依赖,写在构造参数里而不是隐式查找。
- What: 窗口侧保留过渡 shim(_view_preset / _show_cal / _show_finger_3d 可读写属性,
_apply_view_preset / _apply_panel_visibility / _shows_3d / _splitter_sizes_for /
_set_settings_expanded / _sync_settings_action / _sync_3d_source / _restore_3d_settings /
view_preset()),供 tests 与 tools/ui_check.py 继续驱动窗口;窗口内部调用点
(brand_bar 导航、mesh3d_panel.enabledChanged、toolbar.settings_toggled、
_build_menus 里的三个 QAction、_on_sensor_type_changed、_sync_menu_checks)改为直连
控制器。_sync_menu_checks 只保留主题/语言两项(那是窗口的状态),视图三项交给
layout_ctl.sync_view_checks()。
- What: 新增 apps/studio/tests/test_view_layout.py:10 个用例,用假面板/假 splitter/
假 host 直接测布局规则——预设决定面板组合与 rail 角色、未知预设回落 custom、离开回放
视图会停 transport 并清掉重算标签、3D 面板需要 finger + 开关且隐藏时不建 GL 视图、
预设不给 3D 留宽度时从最宽面板取且总量不变、标定面板开关即视图开关、3D 开关不自我
驱动(no-op 不写偏好)、菜单勾选跟随屏幕且 blockSignals、设置抽屉三处同步、
3D 面板拿到正确的传感器与密钥。
- Why: 「家具」是 app_window 的第三个独立变更驱动。搬完之后「哪个视图显示哪些面板」
只有一处规则,窗口不再同时持有布局状态与面板。布局与回放两个控制器的依赖是显式的
单向调用。
- Verification: pytest shared/tests apps/studio/tests(offscreen)621 passed / 0 failed /
15 skipped(原 611 + 新增 10);apps/studio/tools/ui_check.py 587 项检查 0 失败;
.repro/locale_check.py 0 缺失。行为等价(搬运 + 一层转发)。
- Not in this change: C2 显示偏好(单位/量程/箭头/2D 模式/fps)与标定交互仍未拆分;
预览(_start_preview/_stop_preview/_on_preview_tick)留在窗口,它既不是会话也不是布局;
docs 的行数/方法数指标待全部切片落定后统一刷新。
2026-09-15 09:45:05 +08:00
ykcheungab
fbf808d878
拆分 B:回放策略移入 ReplayController(app_window 2837→2499 行)
...
- What: 新增 apps/studio/src/orisys_studio/replay_controller.py:回放视图的**策略**
(哪些入口能打开视频、transport 何时是播放何时是采集、从视频重算对面板与采集
管线做什么)从 app_window 搬进 ReplayController,共 30 个公开方法。replay.py 仍是
**引擎**(reader / 游标 / 异步深度重算),本模块是它外面的状态机。
它直接驱动的对象只有五个:toolbar 的回放控件、曲线图(时间轴)、2D 面板、三维面板的
来源标签、五个菜单项;其余依赖用 ReplayHost Protocol 写明(状态栏、轨道、视图切换、
显示单位、2D 唯一入口 _show_image_frame、采集管线 _start_run/_stop_run)——这些是
窗口级操作,所以留在窗口,并由 Protocol 把边界写清楚,而不是让控制器伸手进"上帝对象"。
- What: 播放时钟与定时器(33 ms)、_recompute_running、入口校验、菜单启用与提示、
重算开关的全部规则都随之上移;窗口侧保留过渡 shim(_load_recording / _load_replay_video /
_render_replay_frame / _start_playback / _pause_playback / _on_play_shortcut /
_stop_replay_activity / _replay_owns_image / _can_recompute / _play_timer /
_recompute_running)供 tests、tools/ui_check.py、tools/ui_snapshot.py 继续驱动窗口。
窗口内部调用点(_apply_view_preset、_update_run_enabled、_sync_menu_checks、_on_run_toggled、
_on_stop_shortcut、_on_reset、菜单与工具栏连线)改为直接调用 self.replay_ctl.*。
删除窗口里已搬走的 _on_browse_video、_on_replay_position、_on_recording_changed、
_wire_replay、_on_recompute_toggled 等,以及随之为空的导入(time、QFileDialog、
SOURCE_REPLAY、PLAYBACK_TICK_MS)。顺带删掉一个从未接线的死方法
_on_replay_unit_toggled(与 _on_chart_unit_toggle 重复,无人调用)。
- What: 新增 apps/studio/tests/test_replay_controller.py:8 个用例,用假的 host/engine/
panel 直接测策略——播放在末尾从头开始、零长录制不播、playhead 跟墙钟并在末尾停下、
重算开关打开时 transport 启动管线而非播放、2D 面板归属、裸视频被接受/在实时视图被拒、
无标定录制回落到 raw、回放入口只在回放视图存在。
- Fix: 重算结束的 flag 由它自己的所有者清除(transport 暂停、开关关闭、stop_activity
三条路径),不再依赖窗口的 _stop_run 顺手清;效果是"重算结束后停止按钮正确变为不可用",
test_ui_smoke 里那个"手动补状态"的注释随之删除。
- Why: 回放是 app_window 的第二个独立变更驱动:录制格式变一次是引擎的事,操作员流程
变一次不该动 2500 行窗口。搬完之后回放策略可单独阅读、单独测,窗口只剩装配与转发。
- Verification: pytest shared/tests apps/studio/tests(offscreen)611 passed / 0 failed /
15 skipped(原 603 + 新增 8);apps/studio/tools/ui_check.py 587 项检查 0 失败;
.repro/locale_check.py 0 缺失。行为等价(搬运 + 一层转发)。
- Not in this change: C1 视图排列、C2 显示偏好、标定交互仍未拆分;docs 的行数/方法数
指标待全部切片落定后统一刷新。真机 GL 冒烟仍无法复跑(本机创建不出 GL 上下文,
eglError 3005),13 例跳过、probe 用例按机器实况失败,与本次改动无关。
2026-09-15 09:38:40 +08:00