改善 Studio 回放布局与传感器切换体验
缩短界面提示,保存上次视频目录,并防止旧结果绘入 Finger 3D。针对性测试及可见界面复测通过。
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""The two video pickers share the last directory chosen by the operator."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
import orisys_studio.replay_controller as replay_controller
|
||||
|
||||
|
||||
def test_both_video_pickers_recall_the_last_chosen_directory(
|
||||
make_window, monkeypatch, tmp_path
|
||||
):
|
||||
window = make_window(sensor_type="box")
|
||||
window.show()
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
|
||||
first = tmp_path / "first"
|
||||
second = tmp_path / "second"
|
||||
first.mkdir()
|
||||
second.mkdir()
|
||||
window.prefs["last_video_directory"] = str(first)
|
||||
chosen = [second / "first.mp4", first / "second.mp4"]
|
||||
starts: list[str] = []
|
||||
|
||||
def choose(_parent, _title, start, _filter):
|
||||
starts.append(start)
|
||||
return str(chosen[len(starts) - 1]), ""
|
||||
|
||||
monkeypatch.setattr(replay_controller.QFileDialog, "getOpenFileName", choose)
|
||||
monkeypatch.setattr(window.replay_ctl, "load_video", lambda _video: True)
|
||||
|
||||
window.replay_ctl.on_open_recording()
|
||||
assert starts == [str(first)]
|
||||
assert window.prefs["last_video_directory"] == str(second)
|
||||
|
||||
window.replay_ctl.on_browse_video()
|
||||
assert starts == [str(first), str(second)]
|
||||
assert window.prefs["last_video_directory"] == str(first)
|
||||
saved = json.loads((tmp_path / "preference.json").read_text(encoding="utf-8"))
|
||||
assert saved["last_video_directory"] == str(first)
|
||||
|
||||
|
||||
def test_missing_remembered_directory_falls_back_to_current_video(make_window, tmp_path):
|
||||
window = make_window(sensor_type="box")
|
||||
current = tmp_path / "clip.mp4"
|
||||
current.write_bytes(b"fixture")
|
||||
window.prefs["last_video_directory"] = str(tmp_path / "removed")
|
||||
window.toolbar.set_video_path(str(current))
|
||||
|
||||
assert window.replay_ctl._video_dialog_directory(Path.home()) == str(tmp_path)
|
||||
@@ -51,6 +51,45 @@ def _pick(window, sensor_type: str) -> None:
|
||||
combo.setCurrentIndex(combo.findData(sensor_type))
|
||||
|
||||
|
||||
def test_switching_to_finger_enables_3d_after_an_earlier_off_choice(qt_app, make_window):
|
||||
window = make_window(sensor_type="box", show_finger_3d=False)
|
||||
window.show()
|
||||
qt_app.processEvents()
|
||||
try:
|
||||
assert not window._show_finger_3d
|
||||
_pick(window, "finger")
|
||||
qt_app.processEvents()
|
||||
assert window._show_finger_3d
|
||||
assert window.prefs["show_finger_3d"] is True
|
||||
assert window.mesh3d_panel.isVisible()
|
||||
assert window.act_finger_3d.isChecked()
|
||||
|
||||
window.layout_ctl.on_finger_3d_toggled(False)
|
||||
assert not window._show_finger_3d
|
||||
_pick(window, "box")
|
||||
_pick(window, "finger")
|
||||
qt_app.processEvents()
|
||||
assert window.mesh3d_panel.isVisible()
|
||||
assert window.act_finger_3d.isChecked()
|
||||
finally:
|
||||
window.close()
|
||||
|
||||
|
||||
def test_replay_keeps_the_marker_image_readable_in_a_1197px_window(qt_app, make_window):
|
||||
window = make_window(sensor_type="finger", show_finger_3d=True)
|
||||
window.resize(1197, 820)
|
||||
window.show()
|
||||
qt_app.processEvents()
|
||||
try:
|
||||
window.brand_bar.btn_playback.click()
|
||||
qt_app.processEvents()
|
||||
assert window.mesh3d_panel.isVisible()
|
||||
assert window.arrow_panel.width() >= 380
|
||||
assert window.force_plots.width() > window.arrow_panel.width()
|
||||
finally:
|
||||
window.close()
|
||||
|
||||
|
||||
def test_reserving_the_native_handle_costs_nothing_at_construction(qt_app, make_window):
|
||||
"""The handle is reserved, and reserving it does not build the pane.
|
||||
|
||||
|
||||
@@ -2830,6 +2830,88 @@ def test_the_rail_reads_the_rate_and_what_it_cost(qt_app, make_window):
|
||||
window.close()
|
||||
|
||||
|
||||
def test_replay_without_a_video_names_the_visible_picker(qt_app, make_window):
|
||||
from orisys_app_shared.i18n import t
|
||||
|
||||
window = make_window(sensor_type="box")
|
||||
window.brand_bar.btn_playback.click()
|
||||
qt_app.processEvents()
|
||||
|
||||
assert window.arrow_panel.image_label.text() == t("panels.replay_empty")
|
||||
assert window.toolbar.btn_replay_video.text() in t("panels.replay_empty")
|
||||
window.close()
|
||||
|
||||
|
||||
def test_a_remembered_video_is_not_described_as_open(make_window):
|
||||
from orisys_app_shared.i18n import t
|
||||
|
||||
window = make_window(sensor_type="finger", video_path="D:/clips/old.mp4")
|
||||
window.brand_bar.btn_playback.click()
|
||||
window._refresh_status_rail()
|
||||
|
||||
assert not window.replay.has_video()
|
||||
assert window.toolbar.source_summary.text() == t(
|
||||
"status.rail_recent_video", name="old.mp4"
|
||||
)
|
||||
|
||||
|
||||
def test_the_rail_puts_result_identity_before_a_long_video_name(
|
||||
qt_app, make_window, monkeypatch
|
||||
):
|
||||
from types import SimpleNamespace
|
||||
|
||||
from orisys_app_shared.i18n import t
|
||||
|
||||
window = make_window(sensor_type="finger")
|
||||
window.resize(1197, 752)
|
||||
window.show()
|
||||
window.brand_bar.btn_playback.click()
|
||||
window.toolbar.set_video_path("D:/very-long-recording-name-for-a-box-sensor.mp4")
|
||||
session = SimpleNamespace(
|
||||
reader=SimpleNamespace(provenance=SimpleNamespace(sensor_type="box"))
|
||||
)
|
||||
monkeypatch.setattr(window.replay_ctl, "available_result", lambda: session)
|
||||
window._refresh_status_rail()
|
||||
qt_app.processEvents()
|
||||
|
||||
prefix = t("status.rail_result_profile", type="box")
|
||||
label = window.toolbar.source_summary
|
||||
assert label.text().startswith(prefix), label.text()
|
||||
assert label.fontMetrics().horizontalAdvance(prefix) <= label.width(), (
|
||||
"the result's sensor disappears from the visible rail"
|
||||
)
|
||||
window.close()
|
||||
|
||||
|
||||
def test_old_result_depth_is_not_sent_to_the_new_sensor_scene(make_window, monkeypatch):
|
||||
from types import SimpleNamespace
|
||||
|
||||
window = make_window(sensor_type="finger")
|
||||
session = SimpleNamespace(
|
||||
reader=SimpleNamespace(provenance=SimpleNamespace(sensor_type="box")),
|
||||
paint_policy=lambda: None,
|
||||
)
|
||||
monkeypatch.setattr(window.replay_ctl, "available_result", lambda: session)
|
||||
submitted = []
|
||||
fake_view = SimpleNamespace(
|
||||
set_overlay_policy=lambda _policy: None,
|
||||
submit_packet=submitted.append,
|
||||
)
|
||||
panel = window.mesh3d_panel
|
||||
real_view = panel._view
|
||||
panel._view = fake_view
|
||||
monkeypatch.setattr(window, "_connect_analysis_display", lambda _panel: True)
|
||||
monkeypatch.setattr(window, "_tick_analysis_3d", lambda: None)
|
||||
try:
|
||||
window._show_analysis_frame("old box packet")
|
||||
assert submitted == []
|
||||
session.reader.provenance.sensor_type = "finger"
|
||||
window._show_analysis_frame("finger packet")
|
||||
assert submitted == ["finger packet"]
|
||||
finally:
|
||||
panel._view = real_view
|
||||
|
||||
|
||||
def test_the_camera_probe_is_usable_without_a_camera(capsys):
|
||||
"""The probe has to be safe to run on a machine with no camera attached."""
|
||||
import camera_probe
|
||||
|
||||
@@ -304,9 +304,23 @@ def test_the_shipped_live_layout_makes_the_3d_pane_the_widest_panel():
|
||||
assert mesh > charts, DEFAULT_SPLITTER_SIZES
|
||||
live_mesh, live_image, live_charts, _cal_live = LIVE["live"]
|
||||
assert live_mesh > live_image and live_mesh > live_charts, LIVE["live"]
|
||||
assert LIVE["replay"] == LIVE["live"], (
|
||||
"the replay view is the live layout with another source"
|
||||
assert LIVE["replay"][2] > LIVE["replay"][0], (
|
||||
"history browsing needs more chart width than 3D width"
|
||||
)
|
||||
assert LIVE["live"][1] >= 400 and LIVE["replay"][1] >= 400, (
|
||||
"the 2D marker picture is too narrow to inspect"
|
||||
)
|
||||
|
||||
|
||||
def test_panel_stretch_keeps_each_views_priority_after_resizing():
|
||||
controller, parts = _controller(prefs={"view_preset": "live"}, sensor_type="finger")
|
||||
controller.show_finger_3d = True
|
||||
|
||||
controller.apply_panel_visibility()
|
||||
assert dict(parts["splitter"].stretches) == {0: 5, 1: 4, 2: 4, 3: 0}
|
||||
|
||||
controller.apply_preset("replay")
|
||||
assert dict(parts["splitter"].stretches) == {0: 4, 1: 4, 2: 5, 3: 0}
|
||||
|
||||
|
||||
def test_the_calibration_panel_toggle_is_the_calibration_view_switch():
|
||||
|
||||
Reference in New Issue
Block a user