autocal layout first design
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render the AutoCal window offscreen to a PNG, for review.
|
||||
|
||||
python apps/autocal/tools/panel_snapshot.py --theme light --out shot.png
|
||||
|
||||
Optionally drives a simulated session first, so the shot shows live values:
|
||||
|
||||
python apps/autocal/tools/panel_snapshot.py --demo --language en_US --out shot.png
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
APP_ROOT = Path(__file__).resolve().parents[1]
|
||||
for path in (APP_ROOT / "src", APP_ROOT.parents[1] / "shared" / "src"):
|
||||
if str(path) not in sys.path:
|
||||
sys.path.insert(0, str(path))
|
||||
|
||||
os.environ.setdefault("ORISYS_APP_ID", "autocal")
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
if sys.platform == "win32" and "QT_QPA_FONTDIR" not in os.environ:
|
||||
_fonts = Path(os.environ.get("WINDIR", r"C:\Windows")) / "Fonts"
|
||||
if _fonts.is_dir():
|
||||
os.environ["QT_QPA_FONTDIR"] = str(_fonts)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--theme", default="dark", choices=("dark", "light"))
|
||||
parser.add_argument("--language", default="zh_CN", choices=("zh_CN", "en_US"))
|
||||
parser.add_argument("--size", default="1600x900", help="WIDTHxHEIGHT")
|
||||
parser.add_argument("--out", default="autocal.png")
|
||||
parser.add_argument(
|
||||
"--tab",
|
||||
default="axes",
|
||||
help="workspace tab to show: axes, sequence, sensor, surface, points",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--compare",
|
||||
action="store_true",
|
||||
help="render every tab, named <out stem>_<tab>.<ext>, so the layout can be "
|
||||
"reviewed in one pass",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--demo",
|
||||
action="store_true",
|
||||
help="drive a simulated session so the shot shows live values",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
width, height = (int(part) for part in args.size.lower().split("x"))
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from orisys_app_shared.i18n import set_locale
|
||||
from orisys_app_shared.theme import build_qss
|
||||
|
||||
from orisys_autocal.motion import ConnectionSpec, DemoTransport
|
||||
from orisys_autocal.preferences import load_preferences
|
||||
from orisys_autocal.ui.bus_panel import default_bas_dir
|
||||
from orisys_autocal.ui.window import AutoCalMainWindow
|
||||
|
||||
set_locale(args.language)
|
||||
app = QApplication.instance() or QApplication([])
|
||||
app.setStyleSheet(build_qss(args.theme))
|
||||
|
||||
prefs = load_preferences()
|
||||
prefs["theme"] = args.theme
|
||||
prefs["language"] = args.language
|
||||
prefs["show_diagnostics"] = False
|
||||
|
||||
window = AutoCalMainWindow(prefs=prefs)
|
||||
window.poller.service._transport = DemoTransport() # noqa: SLF001
|
||||
window.resize(width, height)
|
||||
window.show()
|
||||
app.processEvents()
|
||||
|
||||
if args.demo:
|
||||
window.connect_controller(ConnectionSpec(mode="demo"), label="connect")
|
||||
_wait_for(app, lambda: window._last_snapshot.connected, 6.0)
|
||||
window.bus_panel.set_bas_path(str(default_bas_dir() / "ECAT.bas"))
|
||||
window._on_download_bas()
|
||||
_wait_for(app, lambda: window._last_snapshot.bus.init_status == 1, 8.0)
|
||||
window.axis_panel.set_selected_axis(0)
|
||||
window.axis_panel.target_edit.setText("1000")
|
||||
window._on_move_abs(0, 1000.0)
|
||||
_wait_for(app, lambda: window._last_snapshot.axis(0).dpos != 0, 4.0)
|
||||
|
||||
app.processEvents()
|
||||
|
||||
if args.compare:
|
||||
return _render_every_tab(window, app, args, width, height)
|
||||
|
||||
window.workspace.set_current_key(args.tab)
|
||||
app.processEvents()
|
||||
output = Path(args.out).resolve()
|
||||
if not window.grab().save(str(output)):
|
||||
print(f"could not write {output}", file=sys.stderr)
|
||||
return 1
|
||||
print(
|
||||
f"wrote {output} ({width}x{height}, theme={args.theme}, "
|
||||
f"lang={args.language}, tab={args.tab})"
|
||||
)
|
||||
|
||||
window.close()
|
||||
app.processEvents()
|
||||
return 0
|
||||
|
||||
|
||||
def _render_every_tab(window, app, args, width: int, height: int) -> int:
|
||||
"""One image per workspace tab, so the whole layout can be reviewed at once."""
|
||||
base = Path(args.out).resolve()
|
||||
failures = 0
|
||||
for key in window.workspace.page_keys():
|
||||
window.workspace.set_current_key(key)
|
||||
app.processEvents()
|
||||
target = base.with_name(f"{base.stem}_{key}{base.suffix or '.png'}")
|
||||
if window.grab().save(str(target)):
|
||||
print(f"wrote {target} ({width}x{height}, tab={key})")
|
||||
else:
|
||||
print(f"could not write {target}", file=sys.stderr)
|
||||
failures += 1
|
||||
window.close()
|
||||
app.processEvents()
|
||||
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__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user