autocal layout first design
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
"""AutoCal entrypoint: logging, locale, theme, then the window."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
# The app id must be set before anything imports orisys_app_shared.paths, because
|
||||
# the config/output roots are resolved on first use and cached by callers.
|
||||
os.environ.setdefault("ORISYS_APP_ID", "autocal")
|
||||
|
||||
|
||||
def _force_utf8_streams() -> None:
|
||||
"""Make stdout/stderr UTF-8 where the platform lets us.
|
||||
|
||||
The controller's error strings and every panel label are Chinese, and a
|
||||
Windows console defaults to a legacy code page that cannot encode them.
|
||||
A failed reconfigure is not fatal — the log file is UTF-8 regardless.
|
||||
"""
|
||||
for stream in (sys.stdout, sys.stderr):
|
||||
reconfigure = getattr(stream, "reconfigure", None)
|
||||
if reconfigure is None:
|
||||
continue
|
||||
try:
|
||||
reconfigure(encoding="utf-8", errors="replace")
|
||||
except (ValueError, OSError): # pragma: no cover - stream already detached
|
||||
pass
|
||||
|
||||
|
||||
def _pyside_version() -> str:
|
||||
import PySide6
|
||||
|
||||
return getattr(PySide6, "__version__", "unknown")
|
||||
|
||||
|
||||
def _parse_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(prog="orisys-autocal", description=__doc__)
|
||||
parser.add_argument(
|
||||
"--demo",
|
||||
action="store_true",
|
||||
help="start on the simulated platform (no hardware, no controller DLL)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
default="INFO",
|
||||
choices=("DEBUG", "INFO", "WARNING", "ERROR"),
|
||||
help="console and file log level",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
args = _parse_args(list(argv if argv is not None else sys.argv[1:]))
|
||||
_force_utf8_streams()
|
||||
|
||||
from orisys_app_shared import API_VERSION, __version__ as shared_version
|
||||
from orisys_app_shared.compat import check_shared_compat
|
||||
from orisys_app_shared.i18n import set_locale
|
||||
from orisys_app_shared.logging import init_logging, install_exception_hooks
|
||||
from orisys_app_shared.theme import build_qss
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from . import REQUIRES_SHARED_API, __version__ as app_version
|
||||
from .motion import ConnectionSpec, DemoTransport
|
||||
from .preferences import load_preferences
|
||||
from .ui.window import AutoCalMainWindow
|
||||
|
||||
init_logging("autocal", level=getattr(logging, args.log_level))
|
||||
install_exception_hooks()
|
||||
check_shared_compat(REQUIRES_SHARED_API)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
prefs = load_preferences()
|
||||
language = str(prefs.get("language") or "zh_CN")
|
||||
theme = str(prefs.get("theme") or "dark")
|
||||
set_locale(language)
|
||||
|
||||
app = QApplication(sys.argv[:1])
|
||||
app.setApplicationName("Orisys AutoCal")
|
||||
app.setApplicationVersion(app_version)
|
||||
app.setStyleSheet(build_qss(theme))
|
||||
|
||||
transport = None
|
||||
if args.demo:
|
||||
transport = DemoTransport()
|
||||
connection = dict(prefs.get("connection") or {})
|
||||
connection["mode"] = "demo"
|
||||
prefs["connection"] = connection
|
||||
|
||||
window = AutoCalMainWindow(prefs=prefs)
|
||||
if transport is not None:
|
||||
window.poller.service._transport = transport # noqa: SLF001 - deliberate swap
|
||||
window.show()
|
||||
|
||||
logger.info(
|
||||
"AutoCal started (version=%s shared=%s api=%s binding=PySide6 %s "
|
||||
"theme=%s language=%s demo=%s)",
|
||||
app_version,
|
||||
shared_version,
|
||||
API_VERSION,
|
||||
_pyside_version(),
|
||||
theme,
|
||||
language,
|
||||
args.demo,
|
||||
)
|
||||
if args.demo:
|
||||
window.connect_controller(ConnectionSpec(mode="demo"), label="connect")
|
||||
|
||||
code = int(app.exec())
|
||||
|
||||
# Leave the process immediately. The ZMotion runtime keeps native threads of
|
||||
# its own, so a normal interpreter shutdown can hang after the window closes;
|
||||
# everything durable was already flushed by closeEvent and the log handlers.
|
||||
logging.shutdown()
|
||||
for stream in (sys.stdout, sys.stderr):
|
||||
try:
|
||||
stream.flush()
|
||||
except Exception: # noqa: BLE001 - a closed stream must not block exit
|
||||
pass
|
||||
os._exit(code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user