Source code for pySimBlocks.gui.dialogs.settings.plots

# ******************************************************************************
#                                  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, QHBoxLayout, QVBoxLayout, QListWidget, QListWidgetItem,
    QLabel, QLineEdit, QPushButton, QMessageBox
)
from PySide6.QtCore import Qt

from pySimBlocks.gui.models.project_state import ProjectState
from pySimBlocks.gui.project_controller import ProjectController


[docs] class PlotSettingsWidget(QWidget): """Edit the set of named plots stored in the project. Attributes: project_state: Project state edited by the widget. project_controller: Controller applying plot changes. edit_index: Index of the plot currently being edited, or None. """ def __init__(self, project_state: ProjectState, project_controller: ProjectController): """Initialize the plot settings widget. Args: project_state: Project state edited by the widget. project_controller: Controller applying plot changes. Raises: None. """ super().__init__() self.project_state = project_state self.project_controller = project_controller self.edit_index = None main = QHBoxLayout(self) # ================================================== # Left: plot list + actions # ================================================== left = QVBoxLayout() left.addWidget(QLabel("Plots")) self.plot_list = QListWidget() self.plot_list.currentRowChanged.connect(self.load_plot) left.addWidget(self.plot_list) self.new_btn = QPushButton("New") self.save_btn = QPushButton("Save") self.del_btn = QPushButton("Delete") self.new_btn.clicked.connect(self.new_plot) self.save_btn.clicked.connect(self.save_plot) self.del_btn.clicked.connect(self.delete_plot) left.addWidget(self.new_btn) left.addWidget(self.save_btn) left.addWidget(self.del_btn) main.addLayout(left, 1) # ================================================== # Right: editor # ================================================== right = QVBoxLayout() right.addWidget(QLabel("Title:")) self.title_edit = QLineEdit() right.addWidget(self.title_edit) right.addWidget(QLabel("Signals:")) self.signal_list = QListWidget() self.signal_list.setSelectionMode(QListWidget.NoSelection) right.addWidget(self.signal_list) main.addLayout(right, 2) self.refresh_plot_list() self.populate_signal_list() self.update_buttons_state() # -------------------------------------------------------------------------- # Public Methods # --------------------------------------------------------------------------
[docs] def refresh_from_project(self): """Synchronize the plot editor with the current project state.""" self.edit_index = None self.refresh_plot_list() self.plot_list.clearSelection() self.populate_signal_list() self.title_edit.clear() self.update_buttons_state()
[docs] def refresh_plot_list(self): """Refresh the plot titles shown in the list widget.""" self.plot_list.clear() for plot in self.project_state.plots: self.plot_list.addItem(plot["title"])
[docs] def populate_signal_list(self, checked=None): """Populate the signal checklist. Args: checked: Optional iterable of signal names to preselect. """ self.signal_list.clear() checked = set(checked or []) for sig in self.project_state.get_output_signals(): item = QListWidgetItem(sig) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked if sig in checked else Qt.Unchecked) self.signal_list.addItem(item)
[docs] def collect_selected_signals(self) -> list[str]: """Return the currently selected signal names. Returns: Selected signal names from the checklist. """ return [ self.signal_list.item(i).text() for i in range(self.signal_list.count()) if self.signal_list.item(i).checkState() == Qt.Checked ]
[docs] def reset_form(self): """Clear the editor fields and uncheck all signals.""" self.title_edit.clear() self.populate_signal_list()
[docs] def update_buttons_state(self): """Enable or disable actions based on the current selection state.""" has_selection = self.plot_list.currentRow() >= 0 self.del_btn.setEnabled(has_selection)
[docs] def load_plot(self, index): """Load the selected plot into the editor. Args: index: Index of the plot selected in the list. """ if index < 0: self.edit_index = None self.reset_form() self.update_buttons_state() return self.edit_index = index plot = self.project_state.plots[index] self.title_edit.setText(plot["title"]) self.populate_signal_list(plot["signals"]) self.update_buttons_state()
[docs] def new_plot(self): """Start editing a new plot definition.""" self.edit_index = None self.plot_list.clearSelection() self.reset_form() self.update_buttons_state()
[docs] def save_plot(self): """Create or update the currently edited plot.""" title = self.title_edit.text().strip() if not title: QMessageBox.warning(self, "Invalid plot", "Plot title cannot be empty.") return signals = self.collect_selected_signals() if not signals: QMessageBox.warning(self, "Invalid plot", "No signal selected.") return if self.edit_index is None: self.project_controller.create_plot(title, signals) self.refresh_plot_list() else: self.project_controller.update_plot(self.edit_index, title, signals) self.plot_list.item(self.edit_index).setText(title) self.update_buttons_state()
[docs] def delete_plot(self): """Delete the currently selected plot.""" if self.edit_index is None: return self.project_controller.delete_plot(self.edit_index) self.edit_index = None self.refresh_plot_list() self.plot_list.clearSelection() self.reset_form() self.update_buttons_state()