window.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. from __future__ import annotations
  2. import os
  3. import sys
  4. from typing import Any, Dict, Optional, Tuple
  5. from PyQt6 import uic
  6. from PyQt6.QtCore import QTimer
  7. from PyQt6.QtWidgets import QApplication, QMainWindow
  8. from config import ANALYSIS_BASE_URL, HARDWARE_BASE_URL, HTTP_TIMEOUT_SEC, INTERP_BASE_URL
  9. from mainui.clients import AnalysisState, LoggedApiClient
  10. from mainui.tabs.analysis_tab import AnalysisTabMixin
  11. from mainui.tabs.core import MainWindowCoreMixin
  12. from mainui.tabs.scan_tab import ScanTabMixin
  13. from mainui.tabs.services_tab import ServicesTabMixin
  14. from mainui.tabs.steam_tab import SteamTabMixin
  15. class MainWindow(
  16. MainWindowCoreMixin,
  17. ServicesTabMixin,
  18. SteamTabMixin,
  19. ScanTabMixin,
  20. AnalysisTabMixin,
  21. QMainWindow,
  22. ):
  23. """
  24. Works with the "thin" mainwindow.ui:
  25. - tabWidgetMain with containers:
  26. vlServicesContainer
  27. vlSteamSeqgenContainer
  28. vlScanControlContainer
  29. vlSpectrumToolContainer
  30. Each tab is loaded from its own *.ui into the corresponding container.
  31. """
  32. def __init__(self) -> None:
  33. super().__init__()
  34. base_dir = os.path.dirname(os.path.dirname(__file__))
  35. self.ui_dir = os.path.join(base_dir, "ui")
  36. uic.loadUi(os.path.join(self.ui_dir, "mainwindow.ui"), self)
  37. # Dedicated clients per service
  38. self.hardware_api = LoggedApiClient(HARDWARE_BASE_URL, timeout_sec=HTTP_TIMEOUT_SEC)
  39. self.interp_api = LoggedApiClient(INTERP_BASE_URL, timeout_sec=HTTP_TIMEOUT_SEC)
  40. self.analysis = AnalysisState(base_url=ANALYSIS_BASE_URL)
  41. self._scan_id: Optional[str] = None
  42. self._steam_params: Dict[str, Any] = {}
  43. self._sequence_file_path: str = ""
  44. self._steam_params_dirty: bool = False
  45. self._steam_internal_update: bool = False
  46. self._steam_default_ranges: Dict[str, Tuple[float, float]] = {}
  47. self._steam_label_bases: Dict[str, str] = {}
  48. # ---- load tab UIs
  49. self.services = None
  50. if hasattr(self, "vlServicesContainer"):
  51. self.services = self._load_tab_into_container(
  52. os.path.join(self.ui_dir, "tab_services.ui"),
  53. self.vlServicesContainer,
  54. )
  55. self.steam = None
  56. if hasattr(self, "vlSteamSeqgenContainer"):
  57. self.steam = self._load_tab_into_container(
  58. os.path.join(self.ui_dir, "tab_seqgen_steam.ui"),
  59. self.vlSteamSeqgenContainer,
  60. )
  61. self.scan = self._load_tab_into_container(
  62. os.path.join(self.ui_dir, "tab_scan_control.ui"),
  63. self.vlScanControlContainer,
  64. )
  65. self.spec = self._load_tab_into_container(
  66. os.path.join(self.ui_dir, "tab_spectrum_tool.ui"),
  67. self.vlSpectrumToolContainer,
  68. )
  69. # ---- timers
  70. self.scan_timer = QTimer(self)
  71. self.scan_timer.setInterval(500)
  72. self.scan_timer.timeout.connect(self.poll_scan_progress)
  73. self.backend_ping_timer = QTimer(self)
  74. self.backend_ping_timer.setInterval(3000)
  75. self.backend_ping_timer.timeout.connect(self._ping_backend_service)
  76. self.interp_ping_timer = QTimer(self)
  77. self.interp_ping_timer.setInterval(3000)
  78. self.interp_ping_timer.timeout.connect(self._ping_interp_service)
  79. self.analysis_ping_timer = QTimer(self)
  80. self.analysis_ping_timer.setInterval(3000)
  81. self.analysis_ping_timer.timeout.connect(self._ping_analysis_service)
  82. # ---- wire SeqGen tab
  83. if self.steam is not None:
  84. self.steam.btnSteamSet.clicked.connect(self.steam_set_limits)
  85. self.steam.btnSteamSave.clicked.connect(self.steam_save_sequence)
  86. if hasattr(self.steam, "btnSteamSaveAs"):
  87. self.steam.btnSteamSaveAs.clicked.connect(self.steam_save_sequence_as)
  88. # ---- wire tab 2
  89. system_button_map = {
  90. "gradient": ("btnGradLoad", "btnGradStart"),
  91. "rf": ("btnRFLoad", "btnRFStart"),
  92. "adc": ("btnADCLoad", "btnADCStart"),
  93. }
  94. for system, (load_btn_name, start_btn_name) in system_button_map.items():
  95. if hasattr(self.scan, load_btn_name):
  96. getattr(self.scan, load_btn_name).clicked.connect(
  97. lambda _checked=False, s=system: self.system_load(s)
  98. )
  99. if hasattr(self.scan, start_btn_name):
  100. getattr(self.scan, start_btn_name).clicked.connect(
  101. lambda _checked=False, s=system: self.system_start(s)
  102. )
  103. self.scan.btnPrepareScan.clicked.connect(self.scan_prepare)
  104. if hasattr(self.scan, "btnLoadToScanner"):
  105. self.scan.btnLoadToScanner.clicked.connect(self.scan_load_to_scanner)
  106. if hasattr(self.scan, "btnInterpreterStatus"):
  107. self.scan.btnInterpreterStatus.clicked.connect(self.scan_interpreter_status)
  108. if hasattr(self.scan, "btnFetchMeasurementData"):
  109. self.scan.btnFetchMeasurementData.clicked.connect(self.scan_fetch_measurement_data)
  110. self.scan.btnStartScan.clicked.connect(self.scan_start)
  111. self.scan.btnAbortScan.clicked.connect(self.scan_abort)
  112. # ---- init scan UI
  113. self.scan.pbScanProgress.setValue(0)
  114. if hasattr(self.scan, "lblBackendPing"):
  115. self.scan.lblBackendPing.setStyleSheet("color: #808080;")
  116. if hasattr(self.scan, "lblInterpPing"):
  117. self.scan.lblInterpPing.setStyleSheet("color: #808080;")
  118. self._set_led("gradient", None)
  119. self._set_led("rf", None)
  120. self._set_led("adc", None)
  121. self._refresh_current_sequence_ui()
  122. self._init_scan_measurement_ui()
  123. # ---- init services tab
  124. self._init_services_tab()
  125. self._init_steam_tab()
  126. # ---- analysis panel
  127. self._init_analysis_panel()
  128. self.backend_ping_timer.start()
  129. self.interp_ping_timer.start()
  130. self._ping_backend_service()
  131. self._ping_interp_service()
  132. self.statusbar.showMessage("Ready", 2000)
  133. def main() -> None:
  134. app = QApplication(sys.argv)
  135. w = MainWindow()
  136. w.show()
  137. sys.exit(app.exec())