Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e8d6457
feat(plot-core): add layout modes for configured plots
LukasikMaxence May 5, 2026
0760828
feat(plot-settings): persist per-plot display mode in GUI
LukasikMaxence May 6, 2026
d694008
feat(plot-dialog): add interactive signal filtering and multi-view pr…
LukasikMaxence May 11, 2026
2286538
(examples): expand state-feedback demo for plot workflows
LukasikMaxence May 11, 2026
73b9fe2
fix(gui): subplot filter - 'All' checkbox
LukasikMaxence May 12, 2026
4d718d3
feat(gui): multi-panel manual plots with per-plot selection
LukasikMaxence May 18, 2026
6acf586
feat(gui): per-series line/marker/color style in plot dialog
LukasikMaxence May 18, 2026
48ac9cf
feat(gui): custom manual plot titles and signals legend names
LukasikMaxence May 18, 2026
636e2ff
saving manul plot preset to predefined
LukasikMaxence May 18, 2026
95e7893
fix(gui): reuse plot dialog for the session instead of reopening blank
LukasikMaxence May 19, 2026
25ab00b
fix(gui): show single plot signals as a flat row with style button
LukasikMaxence May 19, 2026
076dafb
refactor(gui): select manual plot panel by click, drop plot list sele…
LukasikMaxence May 19, 2026
efb889f
fix(gui): show minimize/maximize buttons on plot window across differ…
LukasikMaxence May 19, 2026
13ac9bb
feat(gui): save to overwrite manual plot presets and allow plot selec…
LukasikMaxence May 19, 2026
7cf08c5
fix(gui): keep plot dialog in sync after run and when showing it
LukasikMaxence May 20, 2026
a6f4b0d
feat(plots): per-panel series styles and display names in manual layout
LukasikMaxence May 20, 2026
be3c17e
feat(gui): edit and save mode-based plot presets in Plot dialog
LukasikMaxence May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions examples/basics/state_feedback/gui/parameters.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import numpy as np
import control as ct

A = np.array([[0.95, 0.1], [0, 0.98]])
B = np.array([[1], [1]])
C = np.array([[1., 0.]])
# 3-state coupled dynamics to generate richer state trajectories for plotting.
A = np.array([
[0.95, 0.10, 0.00],
[0.00, 0.97, 0.08],
[0.00, 0.00, 0.93],
])
B = np.array([[1.0], [0.7], [0.4]])
C = np.array([[1.0, 0.0, 0.0]])

K = ct.place(A, B, [0.9, 0.91])
G = np.linalg.inv(C @ np.linalg.inv(np.eye(2) - A + B @ K) @ B)
K = ct.place(A, B, [0.86, 0.88, 0.90])
G = np.linalg.inv(C @ np.linalg.inv(np.eye(3) - A + B @ K) @ B)
11 changes: 11 additions & 0 deletions examples/basics/state_feedback/gui/project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,20 @@ simulation:
- system.outputs.y
plots:
- title: Ref vs Output
mode: overlay
signals: [step.outputs.out, system.outputs.y]
- title: Command
mode: overlay
signals: [controller.outputs.u]
- title: Full closed-loop (components)
mode: split_components
signals: [step.outputs.out, system.outputs.y, controller.outputs.u, system.outputs.x]
- title: Tracking and states
mode: split_signals
signals: [step.outputs.out, system.outputs.y, system.outputs.x]
- title: States only
mode: split_components
signals: [system.outputs.x]

diagram:
blocks:
Expand Down
30 changes: 27 additions & 3 deletions pySimBlocks/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,36 @@ class PlotConfig:

def validate(self) -> None:
"""Verify that each plot descriptor has the required fields.


Classic plots require ``signals``. Manual layout presets use
``layout: manual`` with a ``panels`` list instead.

Raises:
ValueError: If a plot descriptor is missing ``"signals"`` or
if ``"signals"`` is not a list.
ValueError: If a plot descriptor is invalid.
"""
for i, plot in enumerate(self.plots):
layout = str(plot.get("layout", "")).strip().lower()
if layout == "manual":
panels = plot.get("panels")
if not isinstance(panels, list) or not panels:
raise ValueError(
f"Plot #{i} (layout: manual) must define a non-empty 'panels' list"
)
for j, panel in enumerate(panels):
if not isinstance(panel, dict):
raise ValueError(
f"Plot #{i} panel #{j} must be a mapping"
)
selection = panel.get("selection")
if isinstance(selection, dict) and selection:
continue
signals = panel.get("signals")
if isinstance(signals, list) and signals:
continue
raise ValueError(
f"Plot #{i} panel #{j} must define 'selection' or 'signals'"
)
continue
if "signals" not in plot:
raise ValueError(
f"Plot #{i} is missing required field 'signals'"
Expand Down
Loading
Loading