Files
orisys-app/apps/studio/docs/ui-palette.md
T
ykcheungab 35e86ec1f7 界面改用「精密仪器工作台」布局:顶栏压缩、视图切布局、标定与回放面板不再裁切
- What: 品牌条 40→28px 并加 实时监测/历史回放/标定 导航;导航改为切换主界面三套布局(实时=2D图像+曲线、回放=自动全程浏览、标定=标定面板 880px+曲线,2D 图像整体缩小);传感器选择(传感器 / 传感器配置 两个带 tooltip 的下拉 + 产品徽标)移到状态轨最左侧,记录模式移入设置区;新增 RecordStatusChip(记录中时长、丢帧、写入异常)与「同一时刻只有一个实心按钮」(新增 dangerOutline 描边危险样式);三轴读数移到曲线上方(16pt 等宽)、只保留最下方时间轴、网格淡化、全程浏览条可拖动;三轴配色统一为蓝/橙/绿并把语义色对比度审计扩展到 toolbar_bg;箭头页脚显示放大倍率与有效区域,深度模式才显示色条与范围;左侧面板改名「2D 图像」且固定显示图像宽高比;标定面板加纵向滚动区、九按钮分两行、六列表头 ResizeToContents;PlaybackDialog 补上从未调用的 retranslate;main() 在事件循环结束后 flush 日志并 os._exit,避免设备 SDK 原生线程把进程挂在解释器收尾。
- Why: 评审提出的 7 点:顶部占高与装饰过多、记录状态不突出、配置切换看不懂且藏得太深、切视图应换主界面布局、标定与回放面板文字被裁、关窗后命令行不退出、2D 图像无固定宽高比且名字不符。
- Verification: ui_check.py(430 项,新增 top area / instrument panels / clean shutdown 段,含中英×1280/1440 的轨不挤压检查)、test_ui_smoke.py(57 项,新增视图切换、标定不裁切、2D 宽高比、回放按钮文字、传感器选择器等用例)、drive_shared.py(146 项)、locale_studio.py(261 键齐全、zh==en、prefs 对齐)、reproduce.py、ui_snapshot.py 四张复核图。
- Not in this change: 录制文件格式、CSV/MP4 写入路径与记录通道数据流不变;三维面板仍是占位符;回放对话框(含离线深度重算)本身只修了按钮文字。
2026-09-13 18:54:31 +08:00

258 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Orisys Studio 配色规范
本文件记录客户端的品牌配色体系、它与 UI 设计稿的关系,以及每一处有意偏离设计稿
的理由。所有色值都存放在主题令牌文件里,控件代码中**不允许出现十六进制颜色**:
```
orisys-app/shared/src/orisys_app_shared/theme/colors_dark.json
orisys-app/shared/src/orisys_app_shared/theme/colors_light.json
```
规则由 `apps/studio/tools/ui_check.py` 的 `audit_palette()` 机械校验,pytest 套件
(`tests/test_ui_smoke.py::test_palette_rules_hold`)复用它,因此只有一个真值来源。
---
## 1. 设计稿色号 → 令牌映射
| 设计稿 | 角色 | 落地令牌 |
|---|---|---|
| `#003893` | 品牌主色 / 底图色 | `accent`、`plot_fn`(浅色);`arrow_canvas` 使用了更深的 logo 藏青 |
| `#4D62BE` | 交互蓝 | `accent_hover`(浅色)、`accent`(深色)、`focus_ring`(浅色) |
| `#12315D` | logo 实际藏青(见 §3.1) | `brand_bar_bg`、`arrow_canvas`(浅色) |
| `#FA02F3` | 洋红 | `flow_ramp` 第 3 档(原值保留) |
| `#FE5853` | 珊瑚 | `flow_ramp` 第 4 档(提亮为 `#FF8A6B`,见 §3.2);`danger` 家族取自同一色相 |
| `#D8FF00` | 柠檬绿 | `flow_ramp` 第 5 档(原值保留)、`plot_fy`(深色) |
| `#D5D5D5` | 应用画布 | **未采用**,改为 `#E1E6EF`(见 §3.3) |
### 1.1 三轴分类色(蓝 / 橙 / 绿)
界面改版把三个力通道统一成"蓝 = 法向、橙 = 切向 X、绿 = 切向 Y"。它们是
**分类色**(三条曲线各自一个身份),与 §4 的箭头色带(顺序色带)是两件事,因此落在
`plot_fn` / `plot_fx` / `plot_fy` 三个令牌上。这三个令牌同时被实时曲线、录制回放、
标定面板与标定点详情对话框引用,换值即全局统一。
| 主题 | `plot_fn`(法向,蓝) | `plot_fx`(切向 X,橙) | `plot_fy`(切向 Y,绿) |
|---|---|---|---|
| 深色 | `#6FA8FF` | `#FF9A4D` | `#7CE0A0` |
| 浅色 | `#003893`(品牌藏青,见 §3.1) | `#B45309` | `#2E7D32` |
浅色主题的 FN 保持品牌藏青 `#003893`(与 `accent` 同值),这样品牌蓝在三轴里仍是
"主通道";橙色与绿色在浅底上取深色相,才够对比度。深色主题的霓虹洋红/柠檬绿被替换,
原因是它们是**色带**用色:作为分类色时洋红与橙的色相差偏小,而柠檬绿在浅底上不可用
(§2.1)。替换后三个通道在灰度打印下也靠亮度分开,不再依赖单一色相。
---
## 2. 实测对比度(WCAG)
对比度用标准相对亮度公式实算(`tools/ui_check.py` 中的 `contrast_ratio`)。
正文门槛 4.5:1,图形/控件/状态指示门槛 3.0:1。
### 2.1 设计稿本身的实测结果
| 组合 | 比值 | 判定 |
|---|---|---|
| 白字 on `#003893` | 10.57 | AAA ✅ |
| 白字 on `#4D62BE` | 5.52 | AA ✅ |
| `#4D62BE` on `#D5D5D5` | 3.76 | 图形达标,12–13px 文字**不达标** ⚠️ |
| `#D5D5D5` vs 白卡 | 1.47 | 卡片层次偏弱 ⚠️ |
| `#D8FF00` on `#D5D5D5` | 1.27 | 不可用 ❌ |
| `#D8FF00` on `#003893` | 9.17 | 极佳 ✅ |
| `#FA02F3` on `#003893` | 3.20 | 达标 ✅ |
| `#FE5853` on `#003893` | 3.39 | 达标 ✅ |
| **`#FA02F3` vs `#FE5853`** | **1.06** | **色带中段的亮度平台 ❌** |
| 白字 on `#FE5853` | 3.12 | 珊瑚色不能承载白字 ❌ |
设计稿的色相选择是可靠的;三个霓虹色的**相对亮度**为 0.268 → 0.287 → 0.861,
色相沿 302° → 2° → 69° 单调前进 127°,作为"位移幅值 → 颜色"的顺序色带是正确用法。
问题在于中段:洋红与珊瑚亮度几乎相同(1.06:1),只靠 60° 色相差区分,在灰度
打印与色觉障碍下会糊成一片。
### 2.2 落地后
浅色主题(节选):白字 on `accent` 10.57,白字 on `accent_hover` 5.52,
`plot_fn/fx/fy` 对 `plot_bg` 分别 10.11 / 4.80 / 4.90,
`brand_bar_fg` on `brand_bar_bg` 12.94,`input_border` 对四种底均 ≥3.08。
深色主题(节选):白字 on `accent` 5.52,`accent` 对 `panel_bg` 3.09(贴线但仍达标),
`plot_fn/fx/fy` 对 `plot_bg` 分别 7.53 / 8.62 / 11.25,`focus_ring` 对 `input_bg` 6.51。
语义色(`danger` / `warning` / `success` / `status_*`)额外对 `toolbar_bg` 审计:它们会
出现在顶栏状态行上(记录状态、数据有效性、阶段指示),实测浅色 4.65–6.66、深色 5.60–9.13,
全部 ≥4.5。在这一步之前它们只在 `panel_bg` / `card_bg` 上被检查,顶栏是一个未经审计的底色。
三通道可分辨性(色相差 / 相对亮度比):
| 主题 | FN vs FX | FN vs FY | FX vs FY |
|---|---|---|---|
| 浅色 | 168.8° / 2.10 | 94.1° / 2.06 | 97.1° / 1.02 |
| 深色 | 169.7° / 1.14 | 74.7° / 1.49 | 115.6° / 1.31 |
规则是"色相差 ≥90° **或** 相对亮度比 ≥1.2",两主题均满足(深色 FN vs FY 靠 1.49 的亮度比
过关,浅色 FX vs FY 靠 97.1° 的色相差过关)。颜色在任何情况下都不是唯一编码:每个曲线有
文字标签,每张数值卡有同色通道徽标与通道名。
---
## 3. 对设计稿的四处有意偏离
### 3.1 品牌藏青用 logo 实际取色 `#12315D`
`logo.png` 与 `logo_topbar.png` 采样后字形的实际颜色都是 `#12315D`
(H215 S80.6% V36.5%),而设计稿写的是 `#003893`(H217 但 S100% V57.6%)。
两者同色相家族,但后者明显更亮更艳。顶栏品牌条与浅色主题的箭头画布采用 logo
实际色,以保证"标识色"与"界面色"一致。
### 3.2 色带第 4 档由 `#FE5853` 提亮为 `#FF8A6B`
见 §2.1:保留设计稿珊瑚色会在色带中段留下 1.06:1 的亮度平台。提亮后色带相对亮度
为 **0.031 → 0.128 → 0.268 → 0.405 → 0.861**,严格单调。
第 3 档洋红与第 5 档柠檬绿**保留设计稿原值不动**。
### 3.3 画布 `#D5D5D5` → `#E1E6EF`
`#D5D5D5` 是无彩色(H=0 S=0% L=84%)。现代浅色画布普遍在 96–98% 亮度,目的是让
白卡"浮起来";而在 `#D5D5D5` 上白卡只有 1.47:1,且 Qt 的 QSS 不支持 `box-shadow`,
卡片只能靠 1px 描边分离,观感会发平、发旧。改为品牌微蓝灰后:
- 保留设计稿"灰底 + 白卡"的角色分工;
- `#4D62BE` 在画布上的对比度从 3.76 升到 4.41。
### 3.4 补齐语义色与深色主题
设计稿没有 success/warning/error/info,也只有浅色版;本应用默认主题是深色。
同时原深色主题的语义色是一整套 Google Material 色
(`#8ab4f8` / `#34a853` / `#fbbc04` / `#ea4335` / `#1a73e8`),对一个测量仪器
产品属于即时可识别的品牌冲突,已全部替换为品牌衍生色。
`danger` 在深色主题被拆成两个令牌:`danger`(亮,用于卡片上的红字与状态指示)
与 `danger_fill`(暗,用于承载白字的危险按钮)—— 一个色值无法同时满足两个方向。
---
## 4. 品牌化箭头色带
设计稿的霓虹三色是**一张 colormap**(按位移幅值着色),不是三个分类色。因此它落在
箭头可视化上,而不是 UI 控件上:
```
flow_ramp = 画布色, #6E4AD0, #FA02F3, #FF8A6B, #D8FF00
```
- 第 1 档等于画布色,这样**零位移区域自然融入背景**;
- 由 `orisys_app_shared.acquisition.arrows.flow_colormap(theme)` 生成 256 档
BGR 查找表,经 `draw_arrows(colormap=...)` 注入 SDK(**SDK 无需改动**);
- LUT 缓存的键是 ramp 字符串本身,因此切换主题后立刻生效;
- 非法 ramp 永不抛异常,回退到内置默认色带。
箭头画布在两个主题里都是品牌深蓝(浅 `#12315D` / 深 `#0B1830`),这也是霓虹色带
唯一可用的背景 —— 色带第 3 档起对画布均 ≥3.0:1。**绘图区(`plot_bg`)保持浅色**,
所以浅色主题下霓虹只出现在箭头画布上,力曲线使用浅色安全的品牌三色。
---
## 5. 有记录在案的豁免
以下项目**不**被 `audit_palette()` 检查,理由是它们属于装饰而非状态指示:
| 项 | 现状 | 理由 |
|---|---|---|
| 装饰性 1px 容器描边 | `card_border` / `panel_border` / `plot_grid` / `separator`(1.16–1.45:1) | WCAG 1.4.11 针对交互控件的边界与状态指示;容器轮廓属于装饰 |
| 禁用态文字 | 深色 `text_muted` on `button_bg` = 4.07 | WCAG 1.4.3 明确豁免禁用控件 |
输入框的静止描边**不在豁免之列** —— 它被强制要求对 `input_bg`/`panel_bg`/`card_bg`/`toolbar_bg`
四种底均 ≥3.0:1(实测 3.08–4.09),这与 Material 3 描边式输入框的做法一致。焦点态同样由
`focus_ring` 保证 ≥3.0:1。
深色主题的 `accent` 对 `panel_bg` 为 3.09:1(设计稿原值 `#4D62BE`),贴线但达标;
若后续视觉上偏软,可单点提升为 `#5468C6`(白字仍为 5.03)。
---
## 6. 顶栏:一件仪器只有一条读数线
顶部由两条细轨组成,合计在 1440×880 下不超过 78 px(`ui_check.py` 的 `top area` 段与
`tests/test_ui_smoke.py::test_top_area_stays_compact` 都断言这一点):
| 轨 | 高度 | 承载 |
|---|---|---|
| 品牌轨(`QFrame#brandBar`) | 28 px | logo + 字标 + 三个顶层视图(实时监测 / 历史回放 / 标定)+ 右侧产品名 |
| 状态轨(`ToolbarWidget` 的 rail) | ~30 px | **传感器 / 传感器配置**、阶段、采集来源、标定状态、数据有效性、记录状态、记录/运行/重置/设置 |
| 设置区(默认收起) | 0 / ~40 px | 来源、摄像机编号、视频文件、单位、**记录模式** |
三点规则:
1. **传感器选择在状态轨最左侧,且两个下拉都带标签**。`box / mini / finger` 是产品
(传感器)类型,它决定后面可选的配置文件;原来只有一个「配置」下拉,看不出在切什么。
现在写成「传感器」+「传感器配置」两个带 tooltip 的下拉,产品型号另有一个徽标
(`FINGER` 不得被截断成 `INGE`,所以徽标只有最小宽度)。
2. **导航按钮在品牌底上取品牌前景色**。`navButton` 用 `brand_bar_fg_muted`,当前视图用
`brand_bar_fg` + `navActive="true"`;两者都在 §2 的品牌底对比度审计里(7.67 / 12.94),
不新增色值。
3. **状态轨上的语义色一律算在 `toolbar_bg` 上审计**。记录状态(`status_idle` /
`status_running` / `status_connecting` / `status_error`)、丢帧提示(`warning`)与数据
有效性(`success` / `warning`)此前只检查 `panel_bg`,而它们实际画在顶栏底色上。
4. **同一时刻只有一个实心按钮**(`ToolbarWidget._refresh_emphasis`):
| 状态 | 实心 | 描边 |
|---|---|---|
| 空闲 | `运行`(`primaryButton`) | — |
| 连接中 | `取消连接`(`dangerButton`) | — |
| 采集中 | `开始记录`(`primaryButton`) | `停止`(`dangerOutline`) |
| 记录中 | `停止记录`(`dangerButton`) | `停止`(`dangerOutline`) |
`dangerOutline` 是新增样式:透明底 + `danger` 文字与描边,hover/pressed 才填充
`danger_fill`;`:checked`(即"采集正在跑")仍保持描边——否则一圈红边会在会话开始后
悄悄变成整块红色,那正是这次要修掉的观感。
原反馈里"提高停止按钮优先级"的诉求没有丢:停止始终是红色、始终一键可达,只是不再
与当前主操作争夺同一时刻的唯一实心位置。
5. **状态轨上的事实不得被压缩**(`ui_check.rail_squeezed`)。中文、英文、1280 与 1440
四种组合下,轨上的按钮与下拉都必须显示完整文本;只有带 tooltip 的 `ShrinkableLabel`
允许在窗口极窄时被裁切。这也是"记录模式"下拉被挪进设置区的原因:英文的
`Start recording` + `Sensor config` 与它挤在同一行时,第一个被压缩的就是按钮文字。
---
## 7. 三个顶层视图 = 同一窗口的三套布局
导航不再打开对话框,而是切换主窗口的布局(`StudioMainWindow._apply_view_preset`):
| 视图 | 面板 | 曲线模式 | 分割比例(3D, 2D, 曲线, 标定) |
|---|---|---|---|
| 实时监测 | 2D 图像 + 力曲线(+ 手指三维) | 跟随实时 | `0, 360, 1080, 0` |
| 历史回放 | 2D 图像 + 力曲线 | 进入全程浏览 | `0, 320, 1120, 0` |
| 标定 | 标定面板 + 力曲线 | 不变 | `0, 0, 560, 880` |
| 自由(`custom`,启动默认) | 由视图菜单决定 | 不变 | 用户在分割条上摆的位置 |
- 标定视图给标定面板 880 px:六列诊断表头与九个动作按钮不再被挤压;标定面板自身还有
纵向滚动区,窗口再矮也够得到「应用到设备」。
- 标定面板列宽改为 `ResizeToContents`:诊断列头宁可让表格横向滚动,也不截成
「FN 均」这种看不懂的缩写。
- 2D 图像区域固定为所显示图像的宽高比(`AspectImageLabel`)。横向分割器**不会**向子控件
询问 `heightForWidth`,所以宽高比由 `ArrowPanel.resizeEvent` 主动施加;box 传感器的
画面旋转 90° 显示,因此其显示宽高比是 9:16 而不是 16:9。
- `视图 → 标定面板` 与「标定」导航是同一个开关:勾上进入标定视图,取消回到实时视图,
不会留下"面板已隐藏但导航还亮着"的矛盾状态。
---
## 8. 复核方式
```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 # 含 palette 规则段落
& $py tools/ui_snapshot.py --theme dark --lang zh_CN --demo --out outputs/_snap_dark.png
& $py tools/ui_snapshot.py --theme light --lang en_US --sensor finger --panels cal,3d --size 1600x900 --demo --out outputs/_snap_light.png
& $py tools/ui_snapshot.py --theme dark --lang zh_CN --demo --scene recording --out outputs/_snap_recording.png
& $py tools/ui_snapshot.py --theme dark --lang zh_CN --demo --preset calibration --out outputs/_snap_calibration.png
```
视图切换与关闭流程另见 `apps/studio/docs/shell-lifecycle.md`。