回放并入主窗口,并修掉开场尖峰 / 录制时长 / 录制帧率 / 回放被摄像头覆盖等 9 项评审问题 - What: 回放不再是独立窗口——删除 PlaybackDialog,拆成 replay.py::ReplayController(读盘、游标、深度重算,只发信号)与 ui/replay_bar.py(录制选择、可拖拽进度条、单位、重算深度、提示行),品牌轨「历史回放」直接换主窗口布局,文件菜单两条入口都切到该视图,曲线改读 RecordingCurveSource(区间按需读盘 + 缓存包络),2D 面板切「原始图像 / 深度图」;传感器与标定合并到 SensorDialog(类型/配置下拉 + 当前生效标定 + 最近导出系数),标定面板加纵向滚动区、九按钮两行、六列表头 ResizeToContents。评审 9 项:1) 开场尖峰两个根因——push() 遇单位变化只记 _unit 而保留旧单位样本(raw 计数被画在牛顿轴上),改为「切换即换单位」:清缓冲按新单位重建、触发切换的那一帧仍刷数值卡;_start_run 先 session_store.reset() 再 set_store(),新会话不再带上次的尾巴(复现脚本 .repro/probe_item1.py:旧 39 样本峰值 9.841 → 新 37 样本峰值 2.390)。2) 箭头页脚改为「箭头按位移放大 ×{factor}」。3) 表头单位芯片由只读 QLabel 改为可点按钮,一次点击同时改曲线、数值卡与设置区下拉。4-6) 全程总览/框选/返回最新 改为 ui/icons.py 按主题令牌运行时绘制的图标,tooltip 与 accessibleName 保留文字,全程总览区域名仍是文字。7) stats() 在录制进行中即返回 _last_t - _t0,记录条开始计时(此前只在 _finalise 写、整场停在 00:00.0)。8) 容器 fps 改用传感器配置的速率(packet.fps 是"每秒完成多少帧"的计数器,突发/文件源会读到 48),视频按 1/fps 节流并把超前的帧计入 resampled_video(frames.csv/forces.csv 一行不少,报告写明原因),session.json 同时记 fps 与 measured_fps。9) _replay_owns_image() 让回放视图独占 2D 面板,实时帧不再覆盖;push() 在录制源挂载时连单位都不切,曲线/数值卡/单位一并归录制。附带:数值卡改读游标处样本(_readout_at_cursor)、回放按录制实际有的单位打开(_replay_unit_for + ReplayController.set_unit,避免 raw-only 录制在 newton 偏好下画三张空图)、视频速率下限。 - Why: 用户评审 9 点,其中 1/7/8/9 是代码缺陷(开场尖峰、录制时长不更新、录制 48fps 与实际 30fps 不符、回放时 2D 面板仍被摄像头刷新),2-6 是可读性问题(×2 含义不明、单位与视图控制应可点/应符号化)。 - Verification: ui_check.py 524 项 0 失败(新增 chart header and unit chip / acquisition correctness / replay owns the 2D pane 三段)、test_ui_smoke.py 69 项 0 失败(新增 7 个回归用例:单位切换不混单位、单位芯片、图标语义、录制中时长、传感器帧率+节流、回放独占面板、回放游标读数与单位回退)、drive_shared.py 146 项 0 失败、locale_studio.py(268 键齐全、zh==en、prefs 对齐)、reproduce.py、ui_snapshot.py 四张复核图(深色实时/回放、浅色 1280x720 实时与标定)。 - Not in this change: 录制文件格式(forces.csv 列序、frames.csv、session.json 路径与分段规则)不变,节流只影响"哪些帧进视频",并显式计数上报;三维面板仍是占位符。
This commit is contained in:
@@ -157,6 +157,26 @@ outputs/recordings/<UTC 时间戳>_session<NNNN>/
|
||||
- `raw_frame` 在入队时**必须拷贝**:packet 里的帧可能复用 SDK 的采集缓冲,直接持有引用
|
||||
会让录制内容随后续帧改变。
|
||||
|
||||
### 6.1 录制速率与时长(视频 30 fps、时长实时递增)
|
||||
|
||||
`session.json` 的 `video.fps` 是**容器速率**,必须等于传感器的采集速率,取自传感器配置
|
||||
(`config/sensors/<type>/sensor.json` 的 `fps`,box 为 30),**不是** packet 里那个
|
||||
`fps` 字段 —— 后者是"管线每秒完成多少帧"的计数器,开流瞬间的缓冲突发或视频文件源会让
|
||||
它读到 48,用它写 30 fps 的容器就等于把回放播成了快进。这是"传感器 30 fps、录制结果
|
||||
48 fps"的直接原因。
|
||||
|
||||
- `StudioMainWindow._video_fps()`:实时相机 → 传感器配置的 `fps`;视频文件源 → 实测值;
|
||||
实时相机的实测值另存 `video.measured_fps`,两者都进 `session.json`,对不上时看得见。
|
||||
- **视频按速率节流,CSV 一行不少**(`ExperimentRecorder._video_frame_due`):比
|
||||
`1/fps` 提前超过 `VIDEO_PACING_TOLERANCE`(0.5 个周期)到达的帧**只进
|
||||
`frames.csv` / `forces.csv`,不进视频**,并计入 `stats.resampled_video`;报告里会写成
|
||||
"N frame(s) were delivered faster than the 30 fps recording rate, so they are in
|
||||
frames.csv but not in the video"。这不是丢数据,是显式记账。
|
||||
- 节流表的调度基准取"上一帧排定时刻 + 周期"与"当前时间戳"的**较晚者**,所以卡顿之后不会
|
||||
用一串补帧追进度。
|
||||
- `stats()` 的 `duration_s` 在录制**进行中**就返回 `_last_t - _t0`(此前只有 `_finalise`
|
||||
才写,界面上的录制时长因此整场停在 00:00.0);`forces.csv` 的行数与时间轴不受影响。
|
||||
|
||||
### 已知环境限制
|
||||
|
||||
本机未安装 pytest,且沙箱拒绝写入工作区之外的路径,`conftest.py` 的 `tmp_path` 因此
|
||||
@@ -274,11 +294,21 @@ outputs/recordings/<UTC 时间戳>_session<NNNN>/
|
||||
|
||||
### 回放界面
|
||||
|
||||
`ui/playback_dialog.py`,入口在「文件 → 打开录制回放…/回放最近一次录制」。
|
||||
回放**不再是一个独立窗口**:`ui/playback_dialog.py` 已删除,拆成
|
||||
`replay.py::ReplayController`(读盘 + 游标 + 深度重算)与 `ui/replay_bar.py::ReplayBar`
|
||||
(录制选择 + 进度条 + 单位 + 提示),入口在「文件 → 打开录制目录…/回放最近一次录制」
|
||||
与品牌轨的「历史回放」。布局与关闭流程见 `docs/shell-lifecycle.md` §2。
|
||||
|
||||
- **一个游标驱动三处**:移动位置同时更新视频帧、深度面板与三张曲线的游标线;
|
||||
并且通过 `cursor_moved` 信号同步到主界面的曲线面板。
|
||||
- 单位可切换(raw/N),数据直接来自录制目录的 `forces.csv`。
|
||||
- **回放按录制实际有的单位打开**:录制里 `FN_N` 等列全空(没有标定)时退回 raw,
|
||||
否则选 N 会画出三张空图;`raw` 一定存在(那是设备测到的值)。
|
||||
单位由 `ReplayController.set_unit` 一处决定,回放条、曲线与设置区下拉同时改。
|
||||
- **数值卡读的是游标处的那一帧**(`ForcePlotsPanel._readout_at_cursor`):浏览历史或回放
|
||||
时,三个大数字是播放头所指的样本,而不是实时采集的最后一个样本 —— 否则一张 7.5e5
|
||||
原始计数的曲线旁边会写着实时的 `0.0`,看起来就像"回放根本没打开"。
|
||||
实时视图仍是"最新样本就是游标",所以跟随状态下不额外读盘。
|
||||
- 帧未被写入视频时(记录队列曾溢出)明确提示,而不是显示上一帧冒充当前帧。
|
||||
- **深度重算在守护线程上执行**,UI 线程立即返回。实测 `_on_recompute_depth` 返回耗时
|
||||
0.001 s,而 SDK 调用仍在后台跑。
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Studio 外壳:视图与关闭流程
|
||||
|
||||
这份文档记录主窗口的两件"外壳"行为:三个顶层视图如何切换面板,以及窗口关闭后进程
|
||||
如何回到命令行。两者都不属于记录通道,但都会直接影响可用性。
|
||||
这份文档记录主窗口的三件"外壳"行为:三个顶层视图如何切换面板、回放如何并入主窗口
|
||||
(含可拖拽进度条),以及窗口关闭后进程如何回到命令行。它们都不属于记录通道,但都会
|
||||
直接影响可用性。
|
||||
|
||||
---
|
||||
|
||||
@@ -10,31 +11,136 @@
|
||||
品牌轨上的三个导航项不打开新窗口,而是切换主窗口的布局
|
||||
(`StudioMainWindow._apply_view_preset`,预设在 `VIEW_PRESETS` / `VIEW_SPLITTER_SIZES`):
|
||||
|
||||
| 视图 | 面板 | 曲线模式 | 分割比例(3D, 2D, 曲线, 标定) |
|
||||
| 视图 | 面板 | 曲线 | 比例(3D,2D,曲线,标定) |
|
||||
|---|---|---|---|
|
||||
| 实时监测 `live` | 2D 图像 + 力曲线(+ 手指三维) | 跟随实时 | `0, 360, 1080, 0` |
|
||||
| 历史回放 `replay` | 2D 图像 + 力曲线 | 进入全程浏览 | `0, 320, 1120, 0` |
|
||||
| 历史回放 `replay` | 2D 图像 + 力曲线 + **回放条** | 读打开的录制;未打开时仍是实时跟随 | `0, 320, 1120, 0` |
|
||||
| 标定 `calibration` | 标定面板 + 力曲线 | 不变 | `0, 0, 560, 880` |
|
||||
| 自由 `custom`(启动默认) | 由视图菜单决定 | 不变 | 用户在分割条上摆的位置 |
|
||||
|
||||
- **为什么标定视图给标定面板 880 px**:六列诊断表头加九个动作按钮,在 300–400 px 的
|
||||
三栏布局里必然被挤压(原来的症状就是按钮文字被切成「排 除」「设为验证点」)。面板自身
|
||||
还有纵向滚动区,窗口再矮也够得到「应用到标定」。
|
||||
- **为什么导航不再自动打开回放对话框**:回放对话框(视频 + 离线深度重算 + 同一游标)仍
|
||||
从「文件」菜单打开;导航的职责是把"看历史"这件事在主界面里摆好——全程浏览 + 2D 图像,
|
||||
立刻可以拖时间轴。
|
||||
- **`custom` 不点亮任何导航项**:手工摆出来的布局不属于三个视图中的任何一个,点亮一个
|
||||
会误报状态。
|
||||
三栏布局里必然被挤压。标定面板自身还有纵向滚动区,窗口再矮也够得到「应用到设备」。
|
||||
- **`custom` 不点亮任何导航项**:手工摆出来的布局不属于三个视图中的任何一个。
|
||||
- **`视图 → 标定面板` 与「标定」导航是同一个开关**:勾上进入标定视图,取消回到实时视图。
|
||||
不允许出现"面板已隐藏但导航还亮着"。
|
||||
- **布局菜单里的「自由布局 / 二维模式」已删除**:导航视图 + 分割条已经覆盖它们,而
|
||||
「深度图 + 曲线」由 2D 面板自带的「深度图」模式一键完成;`layout_preset` 偏好一并移除。
|
||||
|
||||
回放对话框的三颗按钮曾经完全没有文字——`PlaybackDialog.retranslate()` 写好了却从没被
|
||||
调用过。现在它在 `__init__` 末尾调用一次(`test_the_playback_dialog_labels_its_buttons`
|
||||
守住这一点)。
|
||||
### 1.1 采集来源随视图,不再是「来源」下拉
|
||||
|
||||
`ToolbarWidget` 的状态轨上有一个来源槽(`source_slot`):
|
||||
|
||||
| 情形 | 轨上显示 |
|
||||
|---|---|
|
||||
| 实时监测 + 摄像机 | `摄像机 [编号] [搜索]` |
|
||||
| 实时监测 + 视频文件 | `使用摄像机` 按钮 + 摘要里的 `视频文件: xxx.mp4` |
|
||||
| 历史回放 | 不占位(回放条里有录制选择与路径) |
|
||||
|
||||
「用视频文件跑一次采集」仍从「文件 → 打开视频…」进入,`source_mode` / `video_path`
|
||||
偏好保留,但不再有一个需要先选「来源」才能用的下拉。
|
||||
|
||||
### 1.2 传感器与标定共用一个弹窗
|
||||
|
||||
轨上只有一个摘要按钮(`BOX · box/sensor.json`,`ToolbarWidget.btn_sensor`);
|
||||
点击打开 `apps/studio/src/orisys_studio/ui/sensor_dialog.py::SensorDialog`:
|
||||
|
||||
- 传感器类型 / 传感器配置两个下拉(原 `SensorPickerWidget`,仍是窗口的属性,
|
||||
因此 `sensor_type()` / `config_name()` 与信号语义不变);
|
||||
- 「当前生效的标定」:设备确认的版本(`_active_calibration_id`)与已标定轴;
|
||||
- 「最近导出的系数」:文件、大小、时间与文件身份(类型 / SN / 配置,来自
|
||||
`calibration_identity()`);导出/应用按钮转调 `CalPanel.export_coefficients()` /
|
||||
`apply_coefficients()`,实现只有一份。
|
||||
|
||||
弹窗常驻一个实例、每次打开 `refresh()`,所以设备刚确认的标定立刻可见。
|
||||
|
||||
---
|
||||
|
||||
## 2. 关闭流程:窗口关掉后进程必须回到命令行
|
||||
## 2. 回放并入主窗口(含进度条)
|
||||
|
||||
回放**不再是一个独立窗口**。`PlaybackDialog` 已删除,它被拆成两半:
|
||||
|
||||
- `apps/studio/src/orisys_studio/replay.py::ReplayController(QObject)`:录制读取、游标、
|
||||
深度重算(daemon 线程 + 120 ms 轮询、请求绑定 `(_record_serial, seq, frame)`、旧结果一律丢弃)、
|
||||
深度色标(按录制锁存,与实时同一规则)、回放提示(降采样 / 跨版本 / 深度状态)。
|
||||
它不画任何东西,只发信号:`position_changed` / `frames_changed` /
|
||||
`status_changed` / `recording_changed`。
|
||||
- `apps/studio/src/orisys_studio/ui/replay_bar.py::ReplayBar`:录制选择、录制信息、
|
||||
**可拖拽的 QSlider 进度条**、`pos / dur` 读数、单位切换、重算深度、以及第二行的提示。
|
||||
|
||||
主窗口把两者接起来:
|
||||
|
||||
- 曲线:`ForcePlotsPanel.set_recording_source(reader)` 用 `RecordingCurveSource` 直接读
|
||||
录制目录(区间按需读盘 + 缓存包络),所以全程总览、拖动、缩放、框选都在同一套曲线上;
|
||||
表头显示「回放录制(浏览)」,`返回最新` 在录制模式下禁用。
|
||||
- **回放按录制实际有的单位打开**(`_replay_unit_for`):`FN_N` 等列全空的录制退回 raw,
|
||||
否则在 newton 偏好下会画出三张空图、每张数值卡一个横杠——看起来就是"回放没加载"。
|
||||
单位由 `ReplayController.set_unit` 一处决定,回放条、曲线、数值卡与设置区下拉一起改。
|
||||
- 2D 面板:`ArrowPanel.set_source_kind("replay")` 把视图下拉换成「原始图像 / 深度图」
|
||||
(录制里没有光流,不提供箭头),页脚显示「录制视频 → 深度重算」并隐藏放大倍率与有效区域;
|
||||
深度模式显示按录制锁存的色条与范围。
|
||||
- 游标是同一个:拖进度条 → 视频帧 + 曲线游标;拖曲线游标 → 回放位置。
|
||||
- **数值卡读游标处的那一帧**(`ForcePlotsPanel._readout_at_cursor`)。浏览历史或回放时,
|
||||
三个大数字是播放头所指的样本;跟随实时时"最新样本就是游标",不额外读盘。旧写法在回放
|
||||
时显示实时采集的最后一个样本,于是一张 7.5e5 原始计数的曲线旁边写着实时的 `0.0`。
|
||||
- **2D 面板归当前视图的来源所有**(`_replay_owns_image()`):回放视图打开录制后,采集线程
|
||||
仍然在跑,但 `_on_run_tick` / `_on_preview_tick` 不再把实时帧画进 2D 面板,
|
||||
`_render_run_image` 直接返回。否则回放画面会被摄像头帧一帧一帧盖掉("回放时好像用的是
|
||||
摄像头")。切回实时视图才把面板交还给实时帧。
|
||||
- 离开回放视图:曲线挂回实时 store 并恢复跟随,回放条隐藏,**已打开的录制保留**。
|
||||
- 文件菜单两条入口(`打开录制目录…` / `回放最近一次录制`)都只是切到回放视图并加载录制,
|
||||
不再弹第二个窗口。
|
||||
|
||||
`apps/studio/tools/ui_snapshot.py --scene replay` 会合成一段短录制并打开它,用于人工复核
|
||||
进度条与回放布局(不写业务数据,也不需要硬件)。
|
||||
|
||||
### 曲线缩放与默认模式
|
||||
|
||||
- 三条曲线在**所有**模式下 `mouseEnabled(x=True)`:滚轮缩放、左键平移。手势信号用
|
||||
pyqtgraph 的 `sigRangeChangedManually`(只有真实滚轮/拖动才会发),不是
|
||||
`sigXRangeChanged`——后者在窗口缩放沿 X 轴联动传播时也会发,会把"缩放窗口"误判成
|
||||
"用户开始浏览"。y 轴仍按配置的固定量程。
|
||||
- 浏览模式下共享光标线可拖动,拖动即移动播放头(回放视图里就是回放位置)。
|
||||
- 启动、新一次运行、切换单位/传感器、切回「实时监测」都保证回到「跟随实时」;
|
||||
只有打开录制后的回放视图才处于浏览状态。
|
||||
|
||||
### 曲线表头:动作是图标,单位是开关
|
||||
|
||||
表头右侧三个动作(`ForcePlotsPanel._header_button`)只画符号、不写字,符号由
|
||||
`apps/studio/src/orisys_studio/ui/icons.py` 在运行时用当前主题的颜色绘制
|
||||
(`zoom_fit` 取景框+角标、`box_zoom` 虚线框、`latest` 箭头进入竖条),因此**没有新增色值**,
|
||||
换主题时按令牌重新着色,也不会出现位图在不同底色上发灰:
|
||||
|
||||
| 动作 | 符号 | 文字(tooltip / 无障碍名) |
|
||||
|---|---|---|
|
||||
| 全程总览 | `zoom_fit` | `chart.overview` |
|
||||
| 框选 | `box_zoom` | `chart.box_zoom` |
|
||||
| 返回最新 | `latest` | `chart.return_latest` |
|
||||
|
||||
- **符号不替代语义**:每个按钮都带 tooltip 与 `accessibleName`,`ui_check.py` 的
|
||||
`chart header and unit chip` 段逐项断言 `text()==""` 且这三样齐全——只有符号没有说明
|
||||
等于把动作藏起来。全程总览**区域名**(`window_caption`)仍是文字,它标注的是一块区域。
|
||||
- **单位芯片是可点的按钮**(`QPushButton#unitBadge`,不再是 `QLabel`):点一下就切换
|
||||
`N` ⇄ `raw`,同一条命令(`_set_display_unit`)同时改曲线、数值卡与设置区下拉,三处永远
|
||||
一致。旧写法只有「raw/N」两个字,看起来像只读状态。
|
||||
- 箭头图页脚的放大倍率写成「箭头按位移放大 ×2」,不再只有一个「×2」。
|
||||
|
||||
### 单位切换是"开关",不是"重新解释"
|
||||
|
||||
曲线缓冲区里的每个数都带着它被测量时的单位。设备在头几帧还没确认标定时,显示会退到
|
||||
raw;标定一到位就切到 N —— 这一次切换**丢弃**缓冲区里的旧单位样本(会话存储仍然保留
|
||||
它们,`RecordingCurveSource` / `SessionStore` 可重建),而不是把 raw 计数画到牛顿轴上。
|
||||
旧写法只把 `_unit` 记成新单位、样本原样留着,于是每次开始采集的第一个画面都是一根
|
||||
~1e5 的尖峰。触发切换的那一帧本身仍然会刷新数值卡(早期实现里它被 `return` 吃掉了),
|
||||
而浏览/回放时卡片改为按新单位重读游标处的样本。
|
||||
|
||||
同样地,`_start_run` 现在**先** `session_store.reset()` 再 `force_plots.set_store(...)`:
|
||||
反过来的话新会话开局会带着上一次会话的尾巴。
|
||||
|
||||
切换单位后(`set_units`)立即按新单位重读游标处的样本,因为浏览/回放时数值卡说的是游标,
|
||||
不是实时样本——否则换一下单位,卡片会短暂显示上一次实时读数。
|
||||
|
||||
---
|
||||
|
||||
## 3. 关闭流程:窗口关掉后进程必须回到命令行
|
||||
|
||||
关闭顺序(`StudioMainWindow.closeEvent` → `main()`):
|
||||
|
||||
@@ -66,14 +172,14 @@ os._exit(code)
|
||||
|
||||
---
|
||||
|
||||
## 3. 复核方式
|
||||
## 4. 复核方式
|
||||
|
||||
```powershell
|
||||
$py = "D:\orisys_ws\orisys-app\.venv-build\Scripts\python.exe"
|
||||
$env:PYTHONPATH = "D:\orisys_ws\orisys-app\shared\src;D:\orisys_ws\orisys-app\apps\studio\src"
|
||||
cd D:\orisys_ws\orisys-app\apps\studio
|
||||
|
||||
& $py tools/ui_check.py # 含 top area / instrument panels / clean shutdown
|
||||
& $py tools/ui_check.py # 含 top area / views / instrument panels / replay / clean shutdown
|
||||
& $py tools/ui_snapshot.py --demo --scene replay --out outputs/_snap_replay.png
|
||||
& $py tools/ui_snapshot.py --demo --preset calibration --out outputs/_snap_calibration.png
|
||||
& $py tools/ui_snapshot.py --demo --scene recording --out outputs/_snap_recording.png
|
||||
```
|
||||
|
||||
@@ -178,22 +178,24 @@ flow_ramp = 画布色, #6E4AD0, #FA02F3, #FF8A6B, #D8FF00
|
||||
| 轨 | 高度 | 承载 |
|
||||
|---|---|---|
|
||||
| 品牌轨(`QFrame#brandBar`) | 28 px | logo + 字标 + 三个顶层视图(实时监测 / 历史回放 / 标定)+ 右侧产品名 |
|
||||
| 状态轨(`ToolbarWidget` 的 rail) | ~30 px | **传感器 / 传感器配置**、阶段、采集来源、标定状态、数据有效性、记录状态、记录/运行/重置/设置 |
|
||||
| 设置区(默认收起) | 0 / ~40 px | 来源、摄像机编号、视频文件、单位、**记录模式** |
|
||||
| 状态轨(`ToolbarWidget` 的 rail) | ~30 px | **传感器摘要按钮**、阶段、**当前视图的来源选择**(摄像机编号 + 搜索 / 使用摄像机)、标定状态、数据有效性、记录状态、记录/运行/重置/设置 |
|
||||
| 设置区(默认收起) | 0 / ~40 px | 单位、记录模式、预览 |
|
||||
| 回放条(仅历史回放视图) | ~50 px | 录制选择、录制信息、进度条、位置、单位、重算深度、提示行 |
|
||||
|
||||
三点规则:
|
||||
四点规则:
|
||||
|
||||
1. **传感器选择在状态轨最左侧,且两个下拉都带标签**。`box / mini / finger` 是产品
|
||||
(传感器)类型,它决定后面可选的配置文件;原来只有一个「配置」下拉,看不出在切什么。
|
||||
现在写成「传感器」+「传感器配置」两个带 tooltip 的下拉,产品型号另有一个徽标
|
||||
(`FINGER` 不得被截断成 `INGE`,所以徽标只有最小宽度)。
|
||||
2. **导航按钮在品牌底上取品牌前景色**。`navButton` 用 `brand_bar_fg_muted`,当前视图用
|
||||
1. **传感器区是一个摘要按钮,不是两个下拉**。轨上只有 `BOX · box/sensor.json`;类型、配置
|
||||
与「当前生效的标定 / 最近导出的系数」都在它打开的 `SensorDialog` 里(见
|
||||
`docs/shell-lifecycle.md` §1.2)。摘要按钮属于状态轨,因此文本必须完整显示(见第 5 条)。
|
||||
2. **来源选择随视图**:实时视图显示摄像机编号 + 搜索;用视频文件采集时显示「使用摄像机」;
|
||||
回放视图不占位(回放条里有录制选择)。没有「来源」下拉,因此没有"先选来源才能用"的状态。
|
||||
3. **导航按钮在品牌底上取品牌前景色**。`navButton` 用 `brand_bar_fg_muted`,当前视图用
|
||||
`brand_bar_fg` + `navActive="true"`;两者都在 §2 的品牌底对比度审计里(7.67 / 12.94),
|
||||
不新增色值。
|
||||
3. **状态轨上的语义色一律算在 `toolbar_bg` 上审计**。记录状态(`status_idle` /
|
||||
4. **状态轨上的语义色一律算在 `toolbar_bg` 上审计**。记录状态(`status_idle` /
|
||||
`status_running` / `status_connecting` / `status_error`)、丢帧提示(`warning`)与数据
|
||||
有效性(`success` / `warning`)此前只检查 `panel_bg`,而它们实际画在顶栏底色上。
|
||||
4. **同一时刻只有一个实心按钮**(`ToolbarWidget._refresh_emphasis`):
|
||||
5. **同一时刻只有一个实心按钮**(`ToolbarWidget._refresh_emphasis`):
|
||||
|
||||
| 状态 | 实心 | 描边 |
|
||||
|---|---|---|
|
||||
@@ -202,18 +204,22 @@ flow_ramp = 画布色, #6E4AD0, #FA02F3, #FF8A6B, #D8FF00
|
||||
| 采集中 | `开始记录`(`primaryButton`) | `停止`(`dangerOutline`) |
|
||||
| 记录中 | `停止记录`(`dangerButton`) | `停止`(`dangerOutline`) |
|
||||
|
||||
`dangerOutline` 是新增样式:透明底 + `danger` 文字与描边,hover/pressed 才填充
|
||||
`danger_fill`;`:checked`(即"采集正在跑")仍保持描边——否则一圈红边会在会话开始后
|
||||
悄悄变成整块红色,那正是这次要修掉的观感。
|
||||
`dangerOutline`:透明底 + `danger` 文字与描边,hover/pressed 才填充 `danger_fill`;
|
||||
`:checked`(即"采集正在跑")仍保持描边——否则一圈红边会在会话开始后悄悄变成整块红色。
|
||||
|
||||
原反馈里"提高停止按钮优先级"的诉求没有丢:停止始终是红色、始终一键可达,只是不再
|
||||
与当前主操作争夺同一时刻的唯一实心位置。
|
||||
|
||||
5. **状态轨上的事实不得被压缩**(`ui_check.rail_squeezed`)。中文、英文、1280 与 1440
|
||||
6. **状态轨上的事实不得被压缩**(`ui_check.rail_squeezed`)。中文、英文、1280 与 1440
|
||||
四种组合下,轨上的按钮与下拉都必须显示完整文本;只有带 tooltip 的 `ShrinkableLabel`
|
||||
允许在窗口极窄时被裁切。这也是"记录模式"下拉被挪进设置区的原因:英文的
|
||||
`Start recording` + `Sensor config` 与它挤在同一行时,第一个被压缩的就是按钮文字。
|
||||
|
||||
7. **曲线表头只有符号,符号的颜色仍来自令牌**。全程总览 / 框选 / 返回最新三个按钮是
|
||||
纯图标(`QPushButton[iconOnly="true"]`),图标由 `ui/icons.py` 用 `text_secondary`
|
||||
实时绘制、切主题时按新令牌重画,因此配色审计的结论对它们同样成立,也不需要新增色值。
|
||||
单位芯片 `QPushButton#unitBadge` 用 `card_bg` 底 + `card_border` 描边 + `text_secondary`
|
||||
文字,hover 才换成 `text_primary` 文字与 `accent` 描边,它是"可点的状态"而不是实心主
|
||||
按钮,所以不参与 §6.5 的"同一时刻只有一个实心按钮"计数(`QLabel#unitBadge` 仍是通道
|
||||
徽标与传感器缩略图的样式,两者靠控件类型区分)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 三个顶层视图 = 同一窗口的三套布局
|
||||
|
||||
Reference in New Issue
Block a user