Source code for magboltz_gui.util.process

"""Manage one live Magboltz subprocess connected to the GUI."""

from __future__ import annotations
from typing import TYPE_CHECKING

from PyQt6.QtCore import QProcess

if TYPE_CHECKING:
    from magboltz_gui.window.main_window import MagboltzGUI


[docs] class ProcessManager: """Own one ``QProcess`` and bridge it with the main window state.""" def __init__(self, main_window: MagboltzGUI): self.main_window = main_window self._stdout_buffer: list[str] = []
[docs] def run(self) -> None: """Create the process, wire its signals, and launch it.""" self.process = QProcess(self.main_window) # Connect QProcess signals self.process.readyReadStandardOutput.connect(self.handle_stdout) self.process.readyReadStandardError.connect(self.handle_stderr) self.process.finished.connect(self.process_finished) self.run_process()
[docs] def stop(self) -> None: """Attempt a graceful stop, then fall back to killing the process.""" try: self.process.terminate() except Exception: try: self.process.kill() except Exception: pass
[docs] def run_process(self) -> None: """Start ``magboltz`` and stream the current input card to stdin.""" if self.main_window._currentInputFile is None: self.main_window.show_error("Error", "You have to open an input file first") return cmd = str(self.main_window.magboltzPath) if self.main_window.magboltzPath is not None else "magboltz" self.process.start(cmd) self.main_window.consoleOutput.clear() self.main_window.consoleOutput.append( f"<span style='color:blue;'>{cmd} &lt; {self.main_window._currentInputFile}</span>" ) self.main_window.consoleOutput.append(f"") self.main_window.consoleOutput.append("Starting process...") with self.main_window._currentInputFile.open("r") as f: for line in f.readlines(): self.process.write(f"{line}\n".encode("utf-8"))
[docs] def handle_stdout(self) -> None: """Append standard output to the console view and capture buffer.""" data = self.process.readAllStandardOutput().data() text = data.decode("utf-8") self._stdout_buffer.append(text) self.main_window.consoleOutput.append(text)
[docs] def handle_stderr(self) -> None: """Append standard error to the console view.""" data = self.process.readAllStandardError().data() text = data.decode("utf-8") self.main_window.consoleOutput.append(f"<span style='color:red;'>{text}</span>")
[docs] def process_finished(self) -> None: """Parse the finished run and update export/plot availability.""" self.main_window.consoleOutput.append("") self.main_window.consoleOutput.append("Process finished.") stdout_text = "".join(self._stdout_buffer) try: from magboltz_gui.util.output_parser import parse_magboltz_output input_text = None input_path = str(self.main_window._currentInputFile) if self.main_window._currentInputFile else None if self.main_window._currentInputFile is not None: with self.main_window._currentInputFile.open("r", encoding="utf-8") as f: input_text = f.read() self.main_window._last_run_result = parse_magboltz_output( stdout_text=stdout_text, input_text=input_text, input_path=input_path ) self.main_window.actionResultExport.setEnabled(True) self.main_window.btnResultExport.setEnabled(True) self.main_window.actionShowPlots.setEnabled(True) self.main_window.btnResultPlots.setEnabled(True) except Exception as exc: self.main_window.show_error("Parse error", f"Failed to parse Magboltz output: {exc}") self.main_window.processes.remove(self) if not self.main_window.processes: self.main_window._set_running_state(False)