Source code for pySimBlocks.gui.dialogs.settings.simulation
# ******************************************************************************
# pySimBlocks
# Copyright (c) 2026 Université de Lille & INRIA
# ******************************************************************************
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ******************************************************************************
# Authors: see Authors.txt
# ******************************************************************************
from PySide6.QtWidgets import (
QWidget, QFormLayout, QLabel, QLineEdit, QComboBox,
QListWidget, QListWidgetItem, QMessageBox
)
from PySide6.QtCore import Qt
from pySimBlocks.gui.models.project_state import ProjectState
from pySimBlocks.gui.project_controller import ProjectController
[docs]
class SimulationSettingsWidget(QWidget):
"""Edit simulation parameters and logged signals for the project.
Attributes:
project_state: Project state edited by the widget.
project_controller: Controller applying simulation changes.
"""
def __init__(self, project_state: ProjectState, project_controller: ProjectController):
"""Initialize the simulation settings widget.
Args:
project_state: Project state edited by the widget.
project_controller: Controller applying simulation changes.
Raises:
None.
"""
super().__init__()
self.project_state = project_state
self.project_controller = project_controller
self._logs_dirty = False
layout = QFormLayout(self)
layout.addRow(QLabel("<b>Simulation Settings</b>"))
self.dt_edit = QLineEdit(str(project_state.simulation.dt))
layout.addRow("Step time:", self.dt_edit)
self.T_edit = QLineEdit(str(project_state.simulation.T))
layout.addRow("Stop time:", self.T_edit)
self.solver_combo = QComboBox()
self.solver_combo.addItems(["fixed", "variable"])
self.solver_combo.setCurrentText(project_state.simulation.solver)
layout.addRow("Solver:", self.solver_combo)
value = self.project_state.simulation.clock
self.clock_combo = QComboBox()
self.clock_combo.addItem("internal")
self.clock_combo.addItem("external")
if value is not None:
self.clock_combo.setCurrentText(str(value))
layout.addRow("Simulation clock:", self.clock_combo)
# -------- Logs --------
self.logs_list = QListWidget()
self.logs_list.itemChanged.connect(self._on_log_changed)
self._define_log_list()
layout.addRow("Signals logged:", self.logs_list)
# --------------------------------------------------------------------------
# Public Methods
# --------------------------------------------------------------------------
[docs]
def apply(self):
"""Apply the edited simulation parameters and logging selection."""
params = {}
try:
params["dt"] = float(self.dt_edit.text())
except ValueError:
params["dt"] = self.dt_edit.text()
try:
params["T"] = float(self.T_edit.text())
except ValueError:
params["T"] = self.T_edit.text()
params["solver"] = self.solver_combo.currentText()
params["clock"] = self.clock_combo.currentText()
selected_signals = [
self.logs_list.item(i).text()
for i in range(self.logs_list.count())
if self.logs_list.item(i).checkState() == Qt.Checked
]
self.project_controller.update_simulation_params(params)
if self._logs_dirty:
self.project_controller.set_logged_signals(selected_signals)
self._logs_dirty = False
[docs]
def refresh_from_project(self):
"""Synchronize the widget from the current project state."""
self._define_log_list()
selected = set(self.project_state.logging)
self.logs_list.blockSignals(True)
for i in range(self.logs_list.count()):
item = self.logs_list.item(i)
should_be_checked = item.text() in selected
item.setCheckState(
Qt.Checked if should_be_checked else Qt.Unchecked
)
self.logs_list.blockSignals(False)
self._logs_dirty = False
# --------------------------------------------------------------------------
# Private Methods
# --------------------------------------------------------------------------
def _define_log_list(self):
"""Populate the log list from the current project outputs."""
self.logs_list.blockSignals(True)
self.logs_list.clear()
available = self.project_state.get_output_signals()
selected = set(self.project_state.logging)
for sig in available:
item = QListWidgetItem(sig)
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Checked if sig in selected else Qt.Unchecked)
self.logs_list.addItem(item)
self.logs_list.blockSignals(False)
self._logs_dirty = False
def _on_log_changed(self, item: QListWidgetItem):
"""Track logging changes and prevent removing signals used by plots."""
self._logs_dirty = True
if item.checkState() == Qt.Unchecked:
used = any(
item.text() in plot["signals"]
for plot in self.project_state.plots
)
if used:
QMessageBox.warning(
self,
"Signal in use",
f"The signal '{item.text()}' is used in a plot."
)
item.setCheckState(Qt.Checked)