from PySide6.QtCore import Signal from PySide6.QtWidgets import QMainWindow from bedit.core.simulation.openmodelica import SimulationMessage, SimulationProgress from bedit.gui.generated.ui_simulation_window import Ui_SimulationWindow class SimulationWindow(QMainWindow): """Persistent, reusable view of simulation progress and results.""" progressReceived = Signal(object) messageReceived = Signal(object) simulationFinished = Signal(object) simulationFailed = Signal(str) def __init__(self, parent=None) -> None: super().__init__(parent) self.ui = Ui_SimulationWindow() self.ui.setupUi(self) self._running = False self._run_generation = 0 self.progressReceived.connect(self._show_progress) self.messageReceived.connect(self._show_message) self.simulationFinished.connect(self._show_finished) self.simulationFailed.connect(self._show_error) def begin_run(self) -> tuple: """Reset the view and return callbacks bound to this particular run.""" self._run_generation += 1 generation = self._run_generation self._running = True self.ui.statusLabel.setText("Preparing simulation…") self.ui.progressBar.setValue(0) self.ui.timeLabel.setText("Time: 0 s") self.ui.messageList.clear() self.clear_results() return ( lambda progress: self._report_progress(generation, progress), lambda message: self._report_message(generation, message), lambda result: self._report_finished(generation, result), lambda error: self._report_error(generation, error), ) def clear_results(self) -> None: """Clear future plots and result models before a new run. Add graph widgets and their clearing logic here when simulation result loading and plotting are implemented. The Designer-owned `resultsLayout` is the intended container for those widgets. """ def _report_progress( self, generation: int, progress: SimulationProgress ) -> None: if generation == self._run_generation: self.progressReceived.emit(progress) def _report_message(self, generation: int, message: SimulationMessage) -> None: if generation == self._run_generation: self.messageReceived.emit(message) def _report_finished(self, generation: int, result) -> None: if generation == self._run_generation: self.simulationFinished.emit(result) def _report_error(self, generation: int, error: Exception) -> None: if generation == self._run_generation: self.simulationFailed.emit(str(error)) def report_start_error(self, error: Exception) -> None: """Report an error raised before asynchronous callbacks were installed.""" self.simulationFailed.emit(str(error)) def _show_progress(self, progress: SimulationProgress) -> None: self.ui.statusLabel.setText(progress.phase or "Running") self.ui.timeLabel.setText(f"Time: {progress.time:g} s") self.ui.progressBar.setValue(max(0, min(10000, progress.progress))) def _show_message(self, message: SimulationMessage) -> None: prefix = message.stream or message.type or "OpenModelica" self.ui.messageList.addItem(f"{prefix}: {message.text}") self.ui.messageList.scrollToBottom() def _show_finished(self, _result) -> None: self._running = False self.ui.progressBar.setValue(10000) self.ui.statusLabel.setText("Simulation finished") self.load_results() def _show_error(self, message: str) -> None: self._running = False self.ui.statusLabel.setText("Simulation failed") self.ui.messageList.addItem(f"Error: {message}") def load_results(self) -> None: """Populate future plots and result controls after a successful run.""" @property def is_running(self) -> bool: return self._running