Source code for pySimBlocks.gui.models.project_state
# ******************************************************************************
# 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 pathlib import Path
from pySimBlocks.gui.models.block_instance import BlockInstance, PortInstance
from pySimBlocks.gui.models.connection_instance import ConnectionInstance
from pySimBlocks.gui.models.project_simulation_params import ProjectSimulationParams
[docs]
class ProjectState:
"""Store the editable state of a GUI project.
Attributes:
blocks: Block instances currently present in the project.
connections: Connections currently present in the project.
simulation: Simulation parameter set for the project.
external: Optional external runtime path or identifier.
directory_path: Project directory on disk.
logging: Signals selected for logging.
logs: Last simulation logs.
plots: Plot configurations defined for the project.
"""
def __init__(self, directory_path: Path):
"""Initialize an empty project state.
Args:
directory_path: Project directory on disk.
Raises:
None.
"""
self.blocks: list[BlockInstance] = []
self.connections: list[ConnectionInstance] = []
self.simulation = ProjectSimulationParams()
self.external: str | None = None
self.directory_path = directory_path
self.logging: list[str] = []
self.logs: dict = {}
self.plots: list[dict[str, str | list[str]]] = []
# --- Public methods ---
[docs]
def clear(self):
"""Reset blocks, connections, logs, plots, and simulation settings."""
self.blocks.clear()
self.connections.clear()
self.logs.clear()
self.logging.clear()
self.plots.clear()
self.simulation.clear()
self.external = None
[docs]
def load_simulation(self, sim_data: dict, external = None):
"""Load simulation settings into the project state.
Args:
sim_data: Serialized simulation settings.
external: Optional external runtime value.
"""
self.simulation.load_from_dict(sim_data)
if external:
self.external = external
[docs]
def get_block(self, name:str):
"""Return the block with the given name if it exists.
Args:
name: Block instance name.
Returns:
Matching block instance, or None if not found.
"""
for block in self.blocks:
if name == block.name:
return block
[docs]
def add_block(self, block_instance: BlockInstance):
"""Add a block instance to the project.
Args:
block_instance: Block instance to add.
"""
self.blocks.append(block_instance)
[docs]
def remove_block(self, block_instance: BlockInstance):
"""Remove a block instance from the project if present.
Args:
block_instance: Block instance to remove.
"""
if block_instance in self.blocks:
self.blocks.remove(block_instance)
[docs]
def add_connection(self, conn: ConnectionInstance):
"""Add a connection instance to the project.
Args:
conn: Connection instance to add.
"""
self.connections.append(conn)
[docs]
def remove_connection(self, conn: ConnectionInstance):
"""Remove a connection instance from the project if present.
Args:
conn: Connection instance to remove.
"""
if conn in self.connections:
self.connections.remove(conn)
[docs]
def get_connections_of_block(self, block_instance: BlockInstance) -> list[ConnectionInstance]:
"""Return all connections touching the given block.
Args:
block_instance: Block instance to inspect.
Returns:
Connections where the block is either source or destination.
"""
return [
c for c in self.connections
if block_instance is c.src_block() or block_instance is c.dst_block()
]
[docs]
def get_connections_of_port(self, port_instance: PortInstance) -> list[ConnectionInstance]:
"""Return all connections attached to the given port.
Args:
port_instance: Port instance to inspect.
Returns:
Connections where the port is either source or destination.
"""
return [
c for c in self.connections
if port_instance is c.src_port or port_instance is c.dst_port
]
[docs]
def get_output_signals(self) -> list[str]:
"""Return all output signal paths currently available in the project.
Returns:
Output signal identifiers in ``Block.outputs.port`` format.
"""
signals = []
for block in self.blocks:
for port in block.ports:
if port.direction == "output":
signals.append(f"{block.name}.outputs.{port.name}")
return signals
[docs]
def can_plot(self) -> tuple[bool, str]:
"""Return whether plot generation is currently possible.
Returns:
Tuple containing the availability flag and the reason message.
"""
if not bool(self.logs):
return False, "Simulation has not been done.\nPlease run first."
if "time" not in self.logs:
return False, "Time is not in logs."
logged_signals = [k for k in self.logs if k != "time"]
if not logged_signals:
return (
False,
"No signals were logged.\n"
"Open Settings → Simulation, check the output signals to log, "
"click OK, then Run again.",
)
return True, "Plotting is available."