删掉回放的五个转发、重复的空格快捷键与重复的等待循环。
回放调用点直接走 replay_ctl,场景工具的路径引导收敛到 _paths,panel_snapshot 复用 panel_check 的 _wait_for,report_from 收成模块私有的 _report_from。
This commit is contained in:
@@ -15,6 +15,12 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# The wait loop lives with the checker; this tool used to carry its own copy.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
|
from panel_check import _wait_for # noqa: E402
|
||||||
|
|
||||||
APP_ROOT = Path(__file__).resolve().parents[1]
|
APP_ROOT = Path(__file__).resolve().parents[1]
|
||||||
for path in (APP_ROOT / "src", APP_ROOT.parents[1] / "shared" / "src"):
|
for path in (APP_ROOT / "src", APP_ROOT.parents[1] / "shared" / "src"):
|
||||||
if str(path) not in sys.path:
|
if str(path) not in sys.path:
|
||||||
@@ -151,17 +157,5 @@ def _render_every_tab(window, app, args, width: int, height: int) -> int:
|
|||||||
return 1 if failures else 0
|
return 1 if failures else 0
|
||||||
|
|
||||||
|
|
||||||
def _wait_for(app, predicate, timeout: float) -> bool:
|
|
||||||
import time
|
|
||||||
|
|
||||||
deadline = time.monotonic() + timeout
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
app.processEvents()
|
|
||||||
if predicate():
|
|
||||||
return True
|
|
||||||
time.sleep(0.02)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
raise SystemExit(main())
|
raise SystemExit(main())
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
重算是否在跑)与 2D 渲染属于窗口级的 `ReplayController`(见
|
重算是否在跑)与 2D 渲染属于窗口级的 `ReplayController`(见
|
||||||
`docs/architecture/studio-architecture.md` 建议 1)。
|
`docs/architecture/studio-architecture.md` 建议 1)。
|
||||||
- **选视频即选录制**:状态轨的「选择录制视频…」(`toolbar.btn_replay_video`)与
|
- **选视频即选录制**:状态轨的「选择录制视频…」(`toolbar.btn_replay_video`)与
|
||||||
「文件 → 打开视频…」都走 `_load_replay_video(path)`——视频所在目录就是录制目录,
|
「文件 → 打开视频…」都走 `ReplayController.load_video(path)`——视频所在目录就是录制目录,
|
||||||
于是曲线自动配对同目录的 `forces.csv`。视频与 CSV 仍然同时记录(录制格式不变)。
|
于是曲线自动配对同目录的 `forces.csv`。视频与 CSV 仍然同时记录(录制格式不变)。
|
||||||
- 曲线:`ForcePlotsPanel.set_recording_source(reader)` 用 `RecordingCurveSource` 直接读
|
- 曲线:`ForcePlotsPanel.set_recording_source(reader)` 用 `RecordingCurveSource` 直接读
|
||||||
录制目录(区间按需读盘 + 缓存包络),所以全程总览、拖动、缩放、框选都在同一套曲线上;
|
录制目录(区间按需读盘 + 缓存包络),所以全程总览、拖动、缩放、框选都在同一套曲线上;
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
- **数值卡读游标处的那一帧**(`ForcePlotsPanel._readout_at_cursor`)。浏览历史或回放时,
|
- **数值卡读游标处的那一帧**(`ForcePlotsPanel._readout_at_cursor`)。浏览历史或回放时,
|
||||||
三个大数字是播放头所指的样本;跟随实时时"最新样本就是游标",不额外读盘。旧写法在回放
|
三个大数字是播放头所指的样本;跟随实时时"最新样本就是游标",不额外读盘。旧写法在回放
|
||||||
时显示实时采集的最后一个样本,于是一张 7.5e5 原始计数的曲线旁边写着实时的 `0.0`。
|
时显示实时采集的最后一个样本,于是一张 7.5e5 原始计数的曲线旁边写着实时的 `0.0`。
|
||||||
- **2D 面板归当前视图的来源所有**(`_replay_owns_image()`):回放视图打开录制后,采集线程
|
- **2D 面板归当前视图的来源所有**(`ReplayController.owns_image()`):回放视图打开录制后,采集线程
|
||||||
仍然在跑,但 `_on_run_tick` / `_on_preview_tick` 不再把实时帧画进 2D 面板,
|
仍然在跑,但 `_on_run_tick` / `_on_preview_tick` 不再把实时帧画进 2D 面板,
|
||||||
`_render_run_image` 直接返回,否则回放画面会被摄像头帧一帧一帧盖掉。切回实时视图才把
|
`_render_run_image` 直接返回,否则回放画面会被摄像头帧一帧一帧盖掉。切回实时视图才把
|
||||||
面板交还给实时帧。
|
面板交还给实时帧。
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class AnalysisReport:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def report_from(outcome: AnalysisOutcome | None, *, cancelled: bool) -> AnalysisReport:
|
def _report_from(outcome: AnalysisOutcome | None, *, cancelled: bool) -> AnalysisReport:
|
||||||
"""Turn a service outcome into the surface's report."""
|
"""Turn a service outcome into the surface's report."""
|
||||||
if outcome is None:
|
if outcome is None:
|
||||||
return AnalysisReport(
|
return AnalysisReport(
|
||||||
@@ -178,7 +178,7 @@ class AnalysisWorker(QObject):
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self._service = None
|
self._service = None
|
||||||
cancelled = self._cancel.is_set()
|
cancelled = self._cancel.is_set()
|
||||||
report = report_from(outcome, cancelled=cancelled)
|
report = _report_from(outcome, cancelled=cancelled)
|
||||||
if outcome is None and not cancelled:
|
if outcome is None and not cancelled:
|
||||||
report = AnalysisReport(
|
report = AnalysisReport(
|
||||||
outcome=None,
|
outcome=None,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ from orisys_app_shared.scene3d.visual_policy import (
|
|||||||
policy_for_settings,
|
policy_for_settings,
|
||||||
select_flow_samples,
|
select_flow_samples,
|
||||||
)
|
)
|
||||||
from orisys_studio.analysis_worker import AnalysisWorker, report_from
|
from orisys_studio.analysis_worker import AnalysisWorker, _report_from
|
||||||
from orisys_studio.replay import MODE_ANALYSIS, MODE_RECORDING
|
from orisys_studio.replay import MODE_ANALYSIS, MODE_RECORDING
|
||||||
from orisys_studio.replay_analysis import (
|
from orisys_studio.replay_analysis import (
|
||||||
AnalysisSession,
|
AnalysisSession,
|
||||||
@@ -1342,7 +1342,7 @@ def test_the_worker_reports_progress_and_a_cancelled_outcome():
|
|||||||
report = reports[-1][1]
|
report = reports[-1][1]
|
||||||
assert report.cancelled is True and report.complete is False
|
assert report.cancelled is True and report.complete is False
|
||||||
assert report.frames_written == 2
|
assert report.frames_written == 2
|
||||||
assert report_from(None, cancelled=True).cancelled is True
|
assert _report_from(None, cancelled=True).cancelled is True
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------ the depth view
|
# ------------------------------------------------------------------ the depth view
|
||||||
|
|||||||
@@ -1109,7 +1109,7 @@ def test_the_replay_view_shows_the_pane_and_says_where_the_field_comes_from(
|
|||||||
"""
|
"""
|
||||||
window = _show(make_window(sensor_type="finger"), (1440, 880))
|
window = _show(make_window(sensor_type="finger"), (1440, 880))
|
||||||
try:
|
try:
|
||||||
assert window._load_recording(Path(replay_recording))
|
assert window.replay_ctl.load_recording(Path(replay_recording))
|
||||||
assert window._view_preset == "replay"
|
assert window._view_preset == "replay"
|
||||||
pane = window.mesh3d_panel
|
pane = window.mesh3d_panel
|
||||||
assert pane.isVisible(), "the 3D pane is hidden in the replay view"
|
assert pane.isVisible(), "the 3D pane is hidden in the replay view"
|
||||||
|
|||||||
@@ -2084,9 +2084,9 @@ def main() -> int:
|
|||||||
app.processEvents()
|
app.processEvents()
|
||||||
pane_window.brand_bar.btn_playback.click()
|
pane_window.brand_bar.btn_playback.click()
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
check(pane_window._load_recording(recording), "the replay could not open the recording")
|
check(pane_window.replay_ctl.load_recording(recording), "the replay could not open the recording")
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
check(pane_window._replay_owns_image(), "the replay does not own the 2D pane")
|
check(pane_window.replay_ctl.owns_image(), "the replay does not own the 2D pane")
|
||||||
# The recording has no calibrated axis, so newton would be an empty chart.
|
# The recording has no calibrated axis, so newton would be an empty chart.
|
||||||
check(
|
check(
|
||||||
pane_window.force_plots.unit() == "raw",
|
pane_window.force_plots.unit() == "raw",
|
||||||
@@ -2139,7 +2139,7 @@ def main() -> int:
|
|||||||
pane_window.brand_bar.btn_live.click()
|
pane_window.brand_bar.btn_live.click()
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
check(
|
check(
|
||||||
not pane_window._replay_owns_image(),
|
not pane_window.replay_ctl.owns_image(),
|
||||||
"the live view still hands the 2D pane to the replay",
|
"the live view still hands the 2D pane to the replay",
|
||||||
)
|
)
|
||||||
pane_window._on_run_tick()
|
pane_window._on_run_tick()
|
||||||
@@ -3187,7 +3187,7 @@ def main() -> int:
|
|||||||
"the replay view still offers the camera picker",
|
"the replay view still offers the camera picker",
|
||||||
)
|
)
|
||||||
check(
|
check(
|
||||||
replay_window._load_recording(recording),
|
replay_window.replay_ctl.load_recording(recording),
|
||||||
"the replay view could not open the recording",
|
"the replay view could not open the recording",
|
||||||
)
|
)
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
@@ -3238,7 +3238,7 @@ def main() -> int:
|
|||||||
_prompt_module.VideoAnalysisDialog.exec = _answer_prompt
|
_prompt_module.VideoAnalysisDialog.exec = _answer_prompt
|
||||||
try:
|
try:
|
||||||
check(
|
check(
|
||||||
other_window._load_replay_video(bare_video) is True,
|
other_window.replay_ctl.load_video(bare_video) is True,
|
||||||
"a playable video with no recording beside it was refused",
|
"a playable video with no recording beside it was refused",
|
||||||
)
|
)
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
@@ -3652,7 +3652,7 @@ def main() -> int:
|
|||||||
# Leaving the replay view returns the charts to the live session and hides
|
# Leaving the replay view returns the charts to the live session and hides
|
||||||
# the bar: the replay stays available, it just stops being on screen.
|
# the bar: the replay stays available, it just stops being on screen.
|
||||||
check(
|
check(
|
||||||
replay_window._load_recording(recording),
|
replay_window.replay_ctl.load_recording(recording),
|
||||||
"the replay view could not be re-opened",
|
"the replay view could not be re-opened",
|
||||||
)
|
)
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ def _run_scene(window, app, scene: str):
|
|||||||
for _ in range(4):
|
for _ in range(4):
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
recording = _replay_data(window, app)
|
recording = _replay_data(window, app)
|
||||||
window._load_recording(recording)
|
window.replay_ctl.load_recording(recording)
|
||||||
# Park the playhead where the bar and the charts show a real position.
|
# Park the playhead where the bar and the charts show a real position.
|
||||||
window.replay.set_position(window.replay.duration_s() * 0.4)
|
window.replay.set_position(window.replay.duration_s() * 0.4)
|
||||||
window.arrow_panel.set_view_mode("recorded")
|
window.arrow_panel.set_view_mode("recorded")
|
||||||
|
|||||||
@@ -518,7 +518,7 @@ shared 全部 **262 项**。`ui_check.py` **566 项检查 0 失败**(新增 "3
|
|||||||
`act_recompute` 同一个开关),2D 面板的 note 直接指路。
|
`act_recompute` 同一个开关),2D 面板的 note 直接指路。
|
||||||
- **实时界面禁止回放入口**:新增 `_sync_replay_actions()`——`act_open_video`/`act_playback`/
|
- **实时界面禁止回放入口**:新增 `_sync_replay_actions()`——`act_open_video`/`act_playback`/
|
||||||
`act_playback_latest` 只在历史回放视图可用,否则禁用 + tooltip 说明原因;
|
`act_playback_latest` 只在历史回放视图可用,否则禁用 + tooltip 说明原因;
|
||||||
`_load_replay_video()`/`_open_latest_recording()` 在非 replay 预设**拒绝并提示**,不再把采集源偷偷
|
`load_video()`/`open_latest_recording()` 在非 replay 预设**拒绝并提示**,不再把采集源偷偷
|
||||||
切成文件。实时视图用视频**当采集源**("回到摄像机")是另一项既有能力,保留不变。
|
切成文件。实时视图用视频**当采集源**("回到摄像机")是另一项既有能力,保留不变。
|
||||||
- locale:`panels.replay_raw_note`/`frame_missing_note`/`field_flow`/`field_depth`/`recompute_pipeline(_tip)`、
|
- locale:`panels.replay_raw_note`/`frame_missing_note`/`field_flow`/`field_depth`/`recompute_pipeline(_tip)`、
|
||||||
`toolbar.recompute(_tip)`/`replay_view_only`(中英各一套)。
|
`toolbar.recompute(_tip)`/`replay_view_only`(中英各一套)。
|
||||||
@@ -529,7 +529,7 @@ shared 全部 **262 项**。`ui_check.py` **566 项检查 0 失败**(新增 "3
|
|||||||
|---|---|
|
|---|---|
|
||||||
| 后端单一 | `shared/tests/test_image_backend.py` 15 项:三类来源的 mode 计划、`missing`/note、模式回退、**与旧渲染逐像素一致**(arrows 对 `draw_arrow_display`、depth 对 `depth_map_to_bgr` 带主题色带)、原始帧不缩放不替图、缺 depth → None、缺 flow → 底图 |
|
| 后端单一 | `shared/tests/test_image_backend.py` 15 项:三类来源的 mode 计划、`missing`/note、模式回退、**与旧渲染逐像素一致**(arrows 对 `draw_arrow_display`、depth 对 `depth_map_to_bgr` 带主题色带)、原始帧不缩放不替图、缺 depth → None、缺 flow → 底图 |
|
||||||
| 路径一致 | `test_ui_smoke.py`:同一段视频在**回放**路径只出 `recorded` 且有说明行、在**重算/实时**路径出三种派生视图且都经过同一个 `render_image`(spy 断言来源与模式);"全应用只有一处 BGR→QImage"的结构断言 |
|
| 路径一致 | `test_ui_smoke.py`:同一段视频在**回放**路径只出 `recorded` 且有说明行、在**重算/实时**路径出三种派生视图且都经过同一个 `render_image`(spy 断言来源与模式);"全应用只有一处 BGR→QImage"的结构断言 |
|
||||||
| 回放入口收口 | 实时预设下三个文件菜单动作禁用且 tooltip 带原因、rail 无回放/重算按钮、`_load_replay_video()` 返回 False 且 `source_mode()`/播放定时器不变、空格不启动回放;切到历史回放后三者可用、裸视频仍能打开、rail 的重算按钮可点并同步 `act_recompute` |
|
| 回放入口收口 | 实时预设下三个文件菜单动作禁用且 tooltip 带原因、rail 无回放/重算按钮、`ReplayController.load_video()` 返回 False 且 `source_mode()`/播放定时器不变、空格不启动回放;切到历史回放后三者可用、裸视频仍能打开、rail 的重算按钮可点并同步 `act_recompute` |
|
||||||
| 不回归 | 缺深度仍报"本帧没有深度数据"(不回退显示普通形变图);只导入视频仍可播放(既有用例) |
|
| 不回归 | 缺深度仍报"本帧没有深度数据"(不回退显示普通形变图);只导入视频仍可播放(既有用例) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -666,9 +666,9 @@ shared 全部 **262 项**。`ui_check.py` **566 项检查 0 失败**(新增 "3
|
|||||||
`bench_scene.py --package …`、`soak_scene.py --no-window --minutes 10` 报告。板卡不在手边时按"未跑"
|
`bench_scene.py --package …`、`soak_scene.py --no-window --minutes 10` 报告。板卡不在手边时按"未跑"
|
||||||
记录,**不得**写成通过。
|
记录,**不得**写成通过。
|
||||||
3. **真机待验证一项**(阶段 6 遗留):算法链失败时 SDK 的 `VRAW`(光流)是否也过期。当前取保守解释。
|
3. **真机待验证一项**(阶段 6 遗留):算法链失败时 SDK 的 `VRAW`(光流)是否也过期。当前取保守解释。
|
||||||
4. **阶段 10 的过渡转发**:窗口里那约 40 个与旧名同名的成员(`_apply_view_preset`、`_set_display_unit`、
|
4. **阶段 10 的过渡转发**:窗口里三十余个与旧名同名的成员(`_apply_view_preset`、`_set_display_unit`、
|
||||||
`_play_timer`、`_recompute_running`、`_load_recording` …)是给 tests/`ui_check` 的桥,不是最终形态。
|
`_play_timer`、`_recompute_running` …)是给 tests/`ui_check` 的桥,非最终形态。下一次改动落在哪个
|
||||||
下一次改动落在哪个控制器,就把该控制器的转发与测试调用点改成 `self.<controller>.*`。
|
控制器,就把它的转发与测试调用点改成 `self.<controller>.*`。
|
||||||
5. **仍留在窗口的胶水**:预览(`_start_preview`/`_stop_preview`/`_on_preview_tick`)与 2D 渲染
|
5. **仍留在窗口的胶水**:预览(`_start_preview`/`_stop_preview`/`_on_preview_tick`)与 2D 渲染
|
||||||
(`_show_image_frame`/`_render_run_image`/`_depth_range`)。它们同时属于面板与相机,暂时没有第二个
|
(`_show_image_frame`/`_render_run_image`/`_depth_range`)。它们同时属于面板与相机,暂时没有第二个
|
||||||
变更驱动;硬拆只会把耦合搬家。
|
变更驱动;硬拆只会把耦合搬家。
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
"""Put the studio and shared source trees on ``sys.path`` for the scene tools.
|
||||||
|
|
||||||
|
These tools are run as scripts out of this directory, but the tests load them by
|
||||||
|
file location (``spec_from_file_location`` + ``exec_module``), which leaves this
|
||||||
|
directory off ``sys.path``. Each tool therefore adds it before importing here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap_paths() -> None:
|
||||||
|
"""Make ``orisys_studio`` and ``orisys_app_shared`` importable from the repo."""
|
||||||
|
here = Path(__file__).resolve()
|
||||||
|
repo = here.parents[2]
|
||||||
|
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
||||||
|
if path.is_dir() and str(path) not in sys.path:
|
||||||
|
sys.path.insert(0, str(path))
|
||||||
@@ -37,13 +37,11 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Tests load this tool by file location, so add our own directory to the path.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
def _bootstrap_paths() -> None:
|
from _paths import bootstrap_paths # noqa: E402
|
||||||
here = Path(__file__).resolve()
|
|
||||||
repo = here.parents[2]
|
|
||||||
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
|
||||||
if path.is_dir() and str(path) not in sys.path:
|
|
||||||
sys.path.insert(0, str(path))
|
|
||||||
|
|
||||||
|
|
||||||
def _percentiles(values: list[float]) -> dict[str, float]:
|
def _percentiles(values: list[float]) -> dict[str, float]:
|
||||||
@@ -604,7 +602,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
parser.add_argument("--json", action="store_true")
|
parser.add_argument("--json", action="store_true")
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_bootstrap_paths()
|
bootstrap_paths()
|
||||||
width, _, height = args.size.partition("x")
|
width, _, height = args.size.partition("x")
|
||||||
if args.cpu_only:
|
if args.cpu_only:
|
||||||
report, code = measure_cpu(
|
report, code = measure_cpu(
|
||||||
|
|||||||
@@ -43,13 +43,11 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Tests load this tool by file location, so add our own directory to the path.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
def _bootstrap_paths() -> None:
|
from _paths import bootstrap_paths # noqa: E402
|
||||||
here = Path(__file__).resolve()
|
|
||||||
repo = here.parents[2]
|
|
||||||
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
|
||||||
if path.is_dir() and str(path) not in sys.path:
|
|
||||||
sys.path.insert(0, str(path))
|
|
||||||
|
|
||||||
|
|
||||||
def _platform_lock_from_args(args):
|
def _platform_lock_from_args(args):
|
||||||
@@ -189,7 +187,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
parser.add_argument("--json", action="store_true")
|
parser.add_argument("--json", action="store_true")
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_bootstrap_paths()
|
bootstrap_paths()
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from orisys_app_shared.scene3d.contracts import ThreeDError
|
from orisys_app_shared.scene3d.contracts import ThreeDError
|
||||||
from orisys_app_shared.scene3d.legacy import LegacyFingerImporter
|
from orisys_app_shared.scene3d.legacy import LegacyFingerImporter
|
||||||
|
|||||||
@@ -26,20 +26,18 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Tests load this tool by file location, so add our own directory to the path.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
|
from _paths import bootstrap_paths # noqa: E402
|
||||||
|
|
||||||
#: Exit codes: distinct values so a CI script can tell the two failures apart.
|
#: Exit codes: distinct values so a CI script can tell the two failures apart.
|
||||||
EXIT_OK = 0
|
EXIT_OK = 0
|
||||||
EXIT_SOFTWARE = 3
|
EXIT_SOFTWARE = 3
|
||||||
EXIT_NO_CONTEXT = 4
|
EXIT_NO_CONTEXT = 4
|
||||||
|
|
||||||
|
|
||||||
def _bootstrap_paths() -> None:
|
|
||||||
here = Path(__file__).resolve()
|
|
||||||
repo = here.parents[2]
|
|
||||||
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
|
||||||
if path.is_dir() and str(path) not in sys.path:
|
|
||||||
sys.path.insert(0, str(path))
|
|
||||||
|
|
||||||
|
|
||||||
def collect(profile: str, *, keep_open_s: float) -> dict:
|
def collect(profile: str, *, keep_open_s: float) -> dict:
|
||||||
"""Create a real window, let Qt initialise it, and report what it got."""
|
"""Create a real window, let Qt initialise it, and report what it got."""
|
||||||
from orisys_studio.scene3d.gl_backend import (
|
from orisys_studio.scene3d.gl_backend import (
|
||||||
@@ -189,7 +187,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
)
|
)
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_bootstrap_paths()
|
bootstrap_paths()
|
||||||
report = collect(args.profile, keep_open_s=0.6 if args.windowed else 0.3)
|
report = collect(args.profile, keep_open_s=0.6 if args.windowed else 0.3)
|
||||||
context = report.get("context")
|
context = report.get("context")
|
||||||
if not context or not context.get("valid"):
|
if not context or not context.get("valid"):
|
||||||
|
|||||||
@@ -37,13 +37,11 @@ import threading
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Tests load this tool by file location, so add our own directory to the path.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
def _bootstrap_paths() -> None:
|
from _paths import bootstrap_paths # noqa: E402
|
||||||
here = Path(__file__).resolve()
|
|
||||||
repo = here.parents[2]
|
|
||||||
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
|
||||||
if path.is_dir() and str(path) not in sys.path:
|
|
||||||
sys.path.insert(0, str(path))
|
|
||||||
|
|
||||||
|
|
||||||
def resident_bytes() -> int:
|
def resident_bytes() -> int:
|
||||||
@@ -519,7 +517,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
parser.add_argument("--json", action="store_true")
|
parser.add_argument("--json", action="store_true")
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_bootstrap_paths()
|
bootstrap_paths()
|
||||||
report, code = run(
|
report, code = run(
|
||||||
minutes=args.minutes,
|
minutes=args.minutes,
|
||||||
fps=args.fps,
|
fps=args.fps,
|
||||||
|
|||||||
@@ -25,13 +25,11 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Tests load this tool by file location, so add our own directory to the path.
|
||||||
|
if str(Path(__file__).resolve().parent) not in sys.path:
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
def _bootstrap_paths() -> None:
|
from _paths import bootstrap_paths # noqa: E402
|
||||||
here = Path(__file__).resolve()
|
|
||||||
repo = here.parents[2]
|
|
||||||
for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"):
|
|
||||||
if path.is_dir() and str(path) not in sys.path:
|
|
||||||
sys.path.insert(0, str(path))
|
|
||||||
|
|
||||||
|
|
||||||
def _legacy_report(args) -> tuple[int, dict]:
|
def _legacy_report(args) -> tuple[int, dict]:
|
||||||
@@ -308,7 +306,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
parser.add_argument("--json", action="store_true", help="machine-readable output")
|
parser.add_argument("--json", action="store_true", help="machine-readable output")
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_bootstrap_paths()
|
bootstrap_paths()
|
||||||
if args.legacy_dir is not None:
|
if args.legacy_dir is not None:
|
||||||
if args.map is None:
|
if args.map is None:
|
||||||
print("--legacy-dir also needs --map", file=sys.stderr)
|
print("--legacy-dir also needs --map", file=sys.stderr)
|
||||||
|
|||||||
Reference in New Issue
Block a user