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} < {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)