界面:三维可选「深度响应 / 光流幅值」上色(默认深度),并加一个「诊断信息」开关收拾面板脚下的文字
- 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(若不是,面板会写明并回退到光流)。
This commit is contained in:
@@ -175,6 +175,10 @@ class ArrowPanel(QFrame):
|
||||
#: panel behaved before the plans existed.
|
||||
self._modes: tuple[str, ...] = ()
|
||||
self._plan_note = ""
|
||||
#: Whether the footer explains the *pipeline* (magnification, source → output,
|
||||
#: which frames) or only what the picture is. Off by default; the window's
|
||||
#: 「诊断信息」 switch owns it.
|
||||
self._show_diagnostics = False
|
||||
self.view_combo = QComboBox()
|
||||
self.view_combo.setMinimumWidth(140)
|
||||
self.view_combo.currentIndexChanged.connect(self._on_view_index_changed)
|
||||
@@ -398,17 +402,34 @@ class ArrowPanel(QFrame):
|
||||
What is shown follows the *modes this frame supports*: the magnification and
|
||||
the valid area describe a flow drawing, so they are shown only when an arrow
|
||||
mode is on offer.
|
||||
|
||||
The three labels that describe the *pipeline* — how much the arrows are
|
||||
magnified, "raw image → algorithm output", which frames — follow the window's
|
||||
「诊断信息」 switch instead: they explain the inside of the pipeline, not what the
|
||||
picture is, and under the canvas they are the first thing to become noise. What
|
||||
stays regardless is what the picture *is*: the plan's own line ("this recording
|
||||
stores only the raw camera image"), the depth scale, and the colour bar.
|
||||
"""
|
||||
depth = self.view_mode() == DEPTH
|
||||
modes = self.view_modes()
|
||||
has_arrows = ARROW_PURE in modes or ARROW_STITCHED in modes
|
||||
self.scale_label.setVisible(not depth and has_arrows)
|
||||
self.pipeline_label.setVisible(not depth)
|
||||
self.area_label.setVisible(not depth and has_arrows)
|
||||
explain = bool(self._show_diagnostics)
|
||||
self.scale_label.setVisible(explain and not depth and has_arrows)
|
||||
self.pipeline_label.setVisible(explain and not depth)
|
||||
self.area_label.setVisible(explain and not depth and has_arrows)
|
||||
self.colorbar.setVisible(depth)
|
||||
self.depth_scale_label.setVisible(depth and bool(self._depth_scale_text))
|
||||
self.note_label.setVisible(bool(self._plan_note))
|
||||
|
||||
def set_diagnostics_visible(self, visible: bool) -> None:
|
||||
"""How much the panel explains the pipeline around the picture (see the window's
|
||||
「诊断信息」 switch)."""
|
||||
self._show_diagnostics = bool(visible)
|
||||
self._refresh_footer_visibility()
|
||||
|
||||
def diagnostics_visible(self) -> bool:
|
||||
return bool(self._show_diagnostics)
|
||||
|
||||
def _populate_view_combo(self) -> None:
|
||||
current = self.view_combo.currentData()
|
||||
self.view_combo.blockSignals(True)
|
||||
|
||||
@@ -108,6 +108,9 @@ class Mesh3dPanel(QFrame):
|
||||
self._pending_settings = None
|
||||
self._message_kind = _UNAVAILABLE
|
||||
self._message_args: dict[str, str] = {"reason": ""}
|
||||
#: Whether the pane explains itself (the window's 「诊断信息」 switch), held here
|
||||
#: until the view exists (see ``set_diagnostics_visible``).
|
||||
self._show_diagnostics = False
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
root.setContentsMargins(0, 0, 0, 0)
|
||||
@@ -167,6 +170,24 @@ class Mesh3dPanel(QFrame):
|
||||
if self._view is not None:
|
||||
self._view.reset_view()
|
||||
|
||||
def set_diagnostics_visible(self, visible: bool) -> None:
|
||||
"""How much the 3D pane explains itself (see ``DisplayPrefsController``).
|
||||
|
||||
Forwarded to the view's own panel, which is where the legend and the state line
|
||||
live; held here because the view is built on first use and has to inherit the
|
||||
switch when it appears.
|
||||
"""
|
||||
self._show_diagnostics = bool(visible)
|
||||
self._apply_diagnostics()
|
||||
|
||||
def diagnostics_visible(self) -> bool:
|
||||
return bool(self._show_diagnostics)
|
||||
|
||||
def _apply_diagnostics(self) -> None:
|
||||
panel = getattr(self._view, "panel", None) if self._view is not None else None
|
||||
if panel is not None:
|
||||
panel.set_diagnostics_visible(self._show_diagnostics)
|
||||
|
||||
def set_theme(self, theme: str) -> None:
|
||||
"""Re-tint the header icons: they are painted from the theme's own colours."""
|
||||
self._theme = str(theme)
|
||||
@@ -251,6 +272,9 @@ class Mesh3dPanel(QFrame):
|
||||
# The kind was decided before the view existed (the window sets it when the
|
||||
# view preset changes), so it is pushed again here rather than lost.
|
||||
view.set_source_kind(self._source_kind)
|
||||
panel = getattr(view, "panel", None)
|
||||
if panel is not None:
|
||||
panel.set_diagnostics_visible(self._show_diagnostics)
|
||||
if not view.available:
|
||||
self._show_message(_UNAVAILABLE, reason=view.reason)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user