|
@@ -19,6 +19,7 @@ from PySide6.QtWidgets import (
|
|
|
|
|
|
|
|
from src.clients.orchestrator_client import OrchestratorClient, OrchestratorError
|
|
from src.clients.orchestrator_client import OrchestratorClient, OrchestratorError
|
|
|
from src.gui.workers import OrchestratorWorker
|
|
from src.gui.workers import OrchestratorWorker
|
|
|
|
|
+from src import i18n
|
|
|
|
|
|
|
|
|
|
|
|
|
_STATUS_COLORS = {
|
|
_STATUS_COLORS = {
|
|
@@ -46,6 +47,7 @@ class ScannerTab(QWidget):
|
|
|
self._client = OrchestratorClient(orchestrator_url)
|
|
self._client = OrchestratorClient(orchestrator_url)
|
|
|
self._job_id: str | None = None
|
|
self._job_id: str | None = None
|
|
|
self._seq_info: dict | None = None
|
|
self._seq_info: dict | None = None
|
|
|
|
|
+ self._conn_state: str = "offline"
|
|
|
|
|
|
|
|
# Active workers - kept alive while running
|
|
# Active workers - kept alive while running
|
|
|
self._run_worker: OrchestratorWorker | None = None
|
|
self._run_worker: OrchestratorWorker | None = None
|
|
@@ -79,6 +81,25 @@ class ScannerTab(QWidget):
|
|
|
self._seq_info_label.setText("\n".join(summary_lines) if summary_lines else "-")
|
|
self._seq_info_label.setText("\n".join(summary_lines) if summary_lines else "-")
|
|
|
self._append_log("Sequence info received from Sequence tab.")
|
|
self._append_log("Sequence info received from Sequence tab.")
|
|
|
|
|
|
|
|
|
|
+ def retranslate_ui(self) -> None:
|
|
|
|
|
+ self._url_label.setText(i18n.tr("url_label"))
|
|
|
|
|
+ self._btn_connect.setText(i18n.tr("btn_connect"))
|
|
|
|
|
+ self._status_label.setText(i18n.tr(self._conn_state))
|
|
|
|
|
+ self._scenario_grp.setTitle(i18n.tr("grp_scenario"))
|
|
|
|
|
+ self._btn_refresh.setText(i18n.tr("btn_refresh"))
|
|
|
|
|
+ self._seq_grp.setTitle(i18n.tr("grp_seq_info"))
|
|
|
|
|
+ self._job_grp.setTitle(i18n.tr("grp_job"))
|
|
|
|
|
+ self._btn_load.setText(i18n.tr("btn_load_scenario"))
|
|
|
|
|
+ self._btn_run_all.setText(i18n.tr("btn_run_all"))
|
|
|
|
|
+ self._btn_next.setText(i18n.tr("btn_next_step"))
|
|
|
|
|
+ self._btn_abort.setText(i18n.tr("btn_abort"))
|
|
|
|
|
+ self._steps_table.setHorizontalHeaderLabels([
|
|
|
|
|
+ i18n.tr("col_step"), i18n.tr("col_status"), i18n.tr("col_result")
|
|
|
|
|
+ ])
|
|
|
|
|
+ self._bottom_tabs.setTabText(0, i18n.tr("tab_step_result"))
|
|
|
|
|
+ self._bottom_tabs.setTabText(1, i18n.tr("tab_seq_info_view"))
|
|
|
|
|
+ self._bottom_tabs.setTabText(2, i18n.tr("tab_log"))
|
|
|
|
|
+
|
|
|
# ================================================================== #
|
|
# ================================================================== #
|
|
|
# Layout builders #
|
|
# Layout builders #
|
|
|
# ================================================================== #
|
|
# ================================================================== #
|
|
@@ -101,17 +122,18 @@ class ScannerTab(QWidget):
|
|
|
lay = QHBoxLayout(bar)
|
|
lay = QHBoxLayout(bar)
|
|
|
lay.setContentsMargins(0, 0, 0, 0)
|
|
lay.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
|
|
- lay.addWidget(QLabel("Orchestrator URL:"))
|
|
|
|
|
|
|
+ self._url_label = QLabel(i18n.tr("url_label"))
|
|
|
|
|
+ lay.addWidget(self._url_label)
|
|
|
self._url_edit = QLineEdit(self._client.base_url)
|
|
self._url_edit = QLineEdit(self._client.base_url)
|
|
|
self._url_edit.setMaximumWidth(260)
|
|
self._url_edit.setMaximumWidth(260)
|
|
|
lay.addWidget(self._url_edit)
|
|
lay.addWidget(self._url_edit)
|
|
|
|
|
|
|
|
- self._btn_connect = QPushButton("Connect")
|
|
|
|
|
|
|
+ self._btn_connect = QPushButton(i18n.tr("btn_connect"))
|
|
|
self._btn_connect.setFixedWidth(80)
|
|
self._btn_connect.setFixedWidth(80)
|
|
|
self._btn_connect.clicked.connect(self._on_connect)
|
|
self._btn_connect.clicked.connect(self._on_connect)
|
|
|
lay.addWidget(self._btn_connect)
|
|
lay.addWidget(self._btn_connect)
|
|
|
|
|
|
|
|
- self._status_label = QLabel("o Offline")
|
|
|
|
|
|
|
+ self._status_label = QLabel(i18n.tr("offline"))
|
|
|
self._status_label.setStyleSheet("color: #9e9e9e; font-weight: bold;")
|
|
self._status_label.setStyleSheet("color: #9e9e9e; font-weight: bold;")
|
|
|
lay.addWidget(self._status_label)
|
|
lay.addWidget(self._status_label)
|
|
|
|
|
|
|
@@ -133,55 +155,55 @@ class ScannerTab(QWidget):
|
|
|
lay.setSpacing(8)
|
|
lay.setSpacing(8)
|
|
|
|
|
|
|
|
# Scenario selector
|
|
# Scenario selector
|
|
|
- scenario_grp = QGroupBox("Scenario")
|
|
|
|
|
- sg_lay = QVBoxLayout(scenario_grp)
|
|
|
|
|
|
|
+ self._scenario_grp = QGroupBox(i18n.tr("grp_scenario"))
|
|
|
|
|
+ sg_lay = QVBoxLayout(self._scenario_grp)
|
|
|
self._scenario_combo = QComboBox()
|
|
self._scenario_combo = QComboBox()
|
|
|
self._scenario_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
self._scenario_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
|
sg_lay.addWidget(self._scenario_combo)
|
|
sg_lay.addWidget(self._scenario_combo)
|
|
|
- self._btn_refresh = QPushButton("Refresh Refresh")
|
|
|
|
|
|
|
+ self._btn_refresh = QPushButton(i18n.tr("btn_refresh"))
|
|
|
self._btn_refresh.clicked.connect(self._on_refresh_scenarios)
|
|
self._btn_refresh.clicked.connect(self._on_refresh_scenarios)
|
|
|
sg_lay.addWidget(self._btn_refresh)
|
|
sg_lay.addWidget(self._btn_refresh)
|
|
|
- lay.addWidget(scenario_grp)
|
|
|
|
|
|
|
+ lay.addWidget(self._scenario_grp)
|
|
|
|
|
|
|
|
# Sequence summary
|
|
# Sequence summary
|
|
|
- seq_grp = QGroupBox("Sequence Info")
|
|
|
|
|
- seq_lay = QVBoxLayout(seq_grp)
|
|
|
|
|
- self._seq_info_label = QLabel("No sequence loaded")
|
|
|
|
|
|
|
+ self._seq_grp = QGroupBox(i18n.tr("grp_seq_info"))
|
|
|
|
|
+ seq_lay = QVBoxLayout(self._seq_grp)
|
|
|
|
|
+ self._seq_info_label = QLabel(i18n.tr("no_seq_loaded"))
|
|
|
self._seq_info_label.setWordWrap(True)
|
|
self._seq_info_label.setWordWrap(True)
|
|
|
self._seq_info_label.setStyleSheet("color: palette(mid); font-style: italic;")
|
|
self._seq_info_label.setStyleSheet("color: palette(mid); font-style: italic;")
|
|
|
self._seq_info_label.setFont(QFont("Courier New", 8))
|
|
self._seq_info_label.setFont(QFont("Courier New", 8))
|
|
|
seq_lay.addWidget(self._seq_info_label)
|
|
seq_lay.addWidget(self._seq_info_label)
|
|
|
- lay.addWidget(seq_grp)
|
|
|
|
|
|
|
+ lay.addWidget(self._seq_grp)
|
|
|
|
|
|
|
|
# Job info
|
|
# Job info
|
|
|
- job_grp = QGroupBox("Job")
|
|
|
|
|
- job_lay = QVBoxLayout(job_grp)
|
|
|
|
|
- self._job_label = QLabel("- no job -")
|
|
|
|
|
|
|
+ self._job_grp = QGroupBox(i18n.tr("grp_job"))
|
|
|
|
|
+ job_lay = QVBoxLayout(self._job_grp)
|
|
|
|
|
+ self._job_label = QLabel(i18n.tr("no_job"))
|
|
|
self._job_label.setFont(QFont("Courier New", 8))
|
|
self._job_label.setFont(QFont("Courier New", 8))
|
|
|
self._job_label.setWordWrap(True)
|
|
self._job_label.setWordWrap(True)
|
|
|
self._job_label.setStyleSheet("color: palette(mid);")
|
|
self._job_label.setStyleSheet("color: palette(mid);")
|
|
|
job_lay.addWidget(self._job_label)
|
|
job_lay.addWidget(self._job_label)
|
|
|
- lay.addWidget(job_grp)
|
|
|
|
|
|
|
+ lay.addWidget(self._job_grp)
|
|
|
|
|
|
|
|
# Control buttons
|
|
# Control buttons
|
|
|
- self._btn_load = QPushButton("Load Scenario")
|
|
|
|
|
|
|
+ self._btn_load = QPushButton(i18n.tr("btn_load_scenario"))
|
|
|
self._btn_load.setMinimumHeight(30)
|
|
self._btn_load.setMinimumHeight(30)
|
|
|
self._btn_load.clicked.connect(self._on_load_scenario)
|
|
self._btn_load.clicked.connect(self._on_load_scenario)
|
|
|
lay.addWidget(self._btn_load)
|
|
lay.addWidget(self._btn_load)
|
|
|
|
|
|
|
|
- self._btn_run_all = QPushButton("Run Run All")
|
|
|
|
|
|
|
+ self._btn_run_all = QPushButton(i18n.tr("btn_run_all"))
|
|
|
self._btn_run_all.setMinimumHeight(30)
|
|
self._btn_run_all.setMinimumHeight(30)
|
|
|
self._btn_run_all.setEnabled(False)
|
|
self._btn_run_all.setEnabled(False)
|
|
|
self._btn_run_all.clicked.connect(self._on_run_all)
|
|
self._btn_run_all.clicked.connect(self._on_run_all)
|
|
|
lay.addWidget(self._btn_run_all)
|
|
lay.addWidget(self._btn_run_all)
|
|
|
|
|
|
|
|
- self._btn_next = QPushButton("Next Next Step")
|
|
|
|
|
|
|
+ self._btn_next = QPushButton(i18n.tr("btn_next_step"))
|
|
|
self._btn_next.setMinimumHeight(30)
|
|
self._btn_next.setMinimumHeight(30)
|
|
|
self._btn_next.setEnabled(False)
|
|
self._btn_next.setEnabled(False)
|
|
|
self._btn_next.clicked.connect(self._on_next_step)
|
|
self._btn_next.clicked.connect(self._on_next_step)
|
|
|
lay.addWidget(self._btn_next)
|
|
lay.addWidget(self._btn_next)
|
|
|
|
|
|
|
|
- self._btn_abort = QPushButton("Stop Abort")
|
|
|
|
|
|
|
+ self._btn_abort = QPushButton(i18n.tr("btn_abort"))
|
|
|
self._btn_abort.setMinimumHeight(30)
|
|
self._btn_abort.setMinimumHeight(30)
|
|
|
self._btn_abort.setEnabled(False)
|
|
self._btn_abort.setEnabled(False)
|
|
|
self._btn_abort.clicked.connect(self._on_abort)
|
|
self._btn_abort.clicked.connect(self._on_abort)
|
|
@@ -203,7 +225,9 @@ class ScannerTab(QWidget):
|
|
|
|
|
|
|
|
# Steps table
|
|
# Steps table
|
|
|
self._steps_table = QTableWidget(0, 3)
|
|
self._steps_table = QTableWidget(0, 3)
|
|
|
- self._steps_table.setHorizontalHeaderLabels(["Step", "Status", "Result"])
|
|
|
|
|
|
|
+ self._steps_table.setHorizontalHeaderLabels([
|
|
|
|
|
+ i18n.tr("col_step"), i18n.tr("col_status"), i18n.tr("col_result")
|
|
|
|
|
+ ])
|
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
|
self._steps_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
|
@@ -222,17 +246,18 @@ class ScannerTab(QWidget):
|
|
|
self._step_result_view = QTextEdit()
|
|
self._step_result_view = QTextEdit()
|
|
|
self._step_result_view.setReadOnly(True)
|
|
self._step_result_view.setReadOnly(True)
|
|
|
self._step_result_view.setFont(QFont("Courier New", 9))
|
|
self._step_result_view.setFont(QFont("Courier New", 9))
|
|
|
- bottom_tabs.addTab(self._step_result_view, "Step Result")
|
|
|
|
|
|
|
+ self._bottom_tabs = bottom_tabs
|
|
|
|
|
+ bottom_tabs.addTab(self._step_result_view, i18n.tr("tab_step_result"))
|
|
|
|
|
|
|
|
self._seq_info_view = QTextEdit()
|
|
self._seq_info_view = QTextEdit()
|
|
|
self._seq_info_view.setReadOnly(True)
|
|
self._seq_info_view.setReadOnly(True)
|
|
|
self._seq_info_view.setFont(QFont("Courier New", 9))
|
|
self._seq_info_view.setFont(QFont("Courier New", 9))
|
|
|
- bottom_tabs.addTab(self._seq_info_view, "Sequence Info")
|
|
|
|
|
|
|
+ bottom_tabs.addTab(self._seq_info_view, i18n.tr("tab_seq_info_view"))
|
|
|
|
|
|
|
|
self._log_view = QTextEdit()
|
|
self._log_view = QTextEdit()
|
|
|
self._log_view.setReadOnly(True)
|
|
self._log_view.setReadOnly(True)
|
|
|
self._log_view.setFont(QFont("Courier New", 9))
|
|
self._log_view.setFont(QFont("Courier New", 9))
|
|
|
- bottom_tabs.addTab(self._log_view, "Log")
|
|
|
|
|
|
|
+ bottom_tabs.addTab(self._log_view, i18n.tr("tab_log"))
|
|
|
|
|
|
|
|
lay.addWidget(bottom_tabs, stretch=1)
|
|
lay.addWidget(bottom_tabs, stretch=1)
|
|
|
return panel
|
|
return panel
|
|
@@ -259,19 +284,22 @@ class ScannerTab(QWidget):
|
|
|
self._btn_connect.setEnabled(True)
|
|
self._btn_connect.setEnabled(True)
|
|
|
self._conn_progress.setVisible(False)
|
|
self._conn_progress.setVisible(False)
|
|
|
if ok:
|
|
if ok:
|
|
|
- self._status_label.setText("o Online")
|
|
|
|
|
|
|
+ self._conn_state = "online"
|
|
|
|
|
+ self._status_label.setText(i18n.tr("online"))
|
|
|
self._status_label.setStyleSheet("color: #2e7d32; font-weight: bold;")
|
|
self._status_label.setStyleSheet("color: #2e7d32; font-weight: bold;")
|
|
|
self._append_log(f"Connected to orchestrator: {self._client.base_url}")
|
|
self._append_log(f"Connected to orchestrator: {self._client.base_url}")
|
|
|
self._on_refresh_scenarios()
|
|
self._on_refresh_scenarios()
|
|
|
else:
|
|
else:
|
|
|
- self._status_label.setText("o Offline")
|
|
|
|
|
|
|
+ self._conn_state = "offline"
|
|
|
|
|
+ self._status_label.setText(i18n.tr("offline"))
|
|
|
self._status_label.setStyleSheet("color: #9e9e9e; font-weight: bold;")
|
|
self._status_label.setStyleSheet("color: #9e9e9e; font-weight: bold;")
|
|
|
self._append_log("Orchestrator not reachable.")
|
|
self._append_log("Orchestrator not reachable.")
|
|
|
|
|
|
|
|
def _on_healthcheck_error(self, msg: str) -> None:
|
|
def _on_healthcheck_error(self, msg: str) -> None:
|
|
|
self._btn_connect.setEnabled(True)
|
|
self._btn_connect.setEnabled(True)
|
|
|
self._conn_progress.setVisible(False)
|
|
self._conn_progress.setVisible(False)
|
|
|
- self._status_label.setText("o Error")
|
|
|
|
|
|
|
+ self._conn_state = "conn_error"
|
|
|
|
|
+ self._status_label.setText(i18n.tr("conn_error"))
|
|
|
self._status_label.setStyleSheet("color: #c62828; font-weight: bold;")
|
|
self._status_label.setStyleSheet("color: #c62828; font-weight: bold;")
|
|
|
self._append_log(f"Connect error: {msg}")
|
|
self._append_log(f"Connect error: {msg}")
|
|
|
|
|
|