"""Application entry point for the PyQt GUI.""" from __future__ import annotations from contextlib import suppress import os from pathlib import Path import signal import sys import pyqtgraph as pg from PyQt6.QtCore import QTimer from PyQt6.QtWidgets import QApplication # Ensure imports are resolved when started as a script. PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from python_app.gui.app_window import AppWindow from python_app.gui.theme import apply_light_theme def _is_headless() -> bool: """Return whether the launcher requested a non-interactive deployment.""" return os.environ.get("RADAR_SYSTEM_HEADLESS", "").strip().lower() in { "1", "true", "yes", "on", } def _install_unix_signal_handlers(app: QApplication, window: AppWindow) -> None: """Route SIGINT and SIGTERM through the Qt event loop into a clean shutdown. `window.close()` runs `closeEvent`, which aborts any active capture and shuts down managed C++ processes; only then does the Qt loop exit. A short repeating timer keeps the Python interpreter pinned in the event loop just long enough to deliver pending signals. """ def _request_shutdown(*_args: object) -> None: window.close() app.quit() for sig in (signal.SIGINT, signal.SIGTERM): signal.signal(sig, _request_shutdown) keepalive_timer = QTimer(app) keepalive_timer.setInterval(200) keepalive_timer.timeout.connect(lambda: None) keepalive_timer.start() def main() -> int: """Run Qt event loop and show main radar control window.""" app = QApplication(sys.argv) apply_light_theme(app) # PyQtGraph foreground controls axis lines, tick text, labels, and titles. pg.setConfigOptions(antialias=True, background="#ffffff", foreground="#ffffff") window = AppWindow(PROJECT_ROOT) if _is_headless(): _install_unix_signal_handlers(app, window) else: window.showMaximized() exit_code = app.exec() # Release hardware, SHM readers and child processes before exiting so that a # systemd restart (after a headless fatal exit) starts from a clean slate. with suppress(Exception): window.close() return exit_code if __name__ == "__main__": raise SystemExit(main())