x axis change
This commit is contained in:
@@ -77,3 +77,21 @@ class ChangeSimulationPlotSignalsCommand(QUndoCommand):
|
|||||||
def undo(self) -> None:
|
def undo(self) -> None:
|
||||||
self.root.plot_tabs[self.index].signals = list(self.old_signals)
|
self.root.plot_tabs[self.index].signals = list(self.old_signals)
|
||||||
self.changed(self.index)
|
self.changed(self.index)
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeSimulationPlotXAxisCommand(QUndoCommand):
|
||||||
|
def __init__(self, root: SimulationRoot, index: int, x_axis: str | None, changed: ChangedCallback) -> None:
|
||||||
|
super().__init__("Change plot x axis")
|
||||||
|
self.root = root
|
||||||
|
self.index = index
|
||||||
|
self.old_x_axis = root.plot_tabs[index].x_axis
|
||||||
|
self.x_axis = x_axis
|
||||||
|
self.changed = changed
|
||||||
|
|
||||||
|
def redo(self) -> None:
|
||||||
|
self.root.plot_tabs[self.index].x_axis = self.x_axis
|
||||||
|
self.changed(self.index)
|
||||||
|
|
||||||
|
def undo(self) -> None:
|
||||||
|
self.root.plot_tabs[self.index].x_axis = self.old_x_axis
|
||||||
|
self.changed(self.index)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from PySide6.QtCore import QObject, QPoint, Qt
|
|||||||
from PySide6.QtGui import QUndoStack
|
from PySide6.QtGui import QUndoStack
|
||||||
from PySide6.QtWidgets import QInputDialog, QMenu, QTreeWidgetItem, QWidget
|
from PySide6.QtWidgets import QInputDialog, QMenu, QTreeWidgetItem, QWidget
|
||||||
|
|
||||||
from bedit_gui.commands.simulation_plot_commands import AddSimulationPlotTabCommand, ChangeSimulationPlotSignalsCommand, RemoveSimulationPlotTabCommand, RenameSimulationPlotTabCommand
|
from bedit_gui.commands.simulation_plot_commands import AddSimulationPlotTabCommand, ChangeSimulationPlotSignalsCommand, ChangeSimulationPlotXAxisCommand, RemoveSimulationPlotTabCommand, RenameSimulationPlotTabCommand
|
||||||
from bedit_gui.controllers.simulation_file_controller import SimulationFileController
|
from bedit_gui.controllers.simulation_file_controller import SimulationFileController
|
||||||
from bedit_gui.services.application_logging import get_logger
|
from bedit_gui.services.application_logging import get_logger
|
||||||
from bedit_gui.simulation_models import SimulationPlotTab
|
from bedit_gui.simulation_models import SimulationPlotTab
|
||||||
@@ -29,6 +29,8 @@ class SimulationPlotController(QObject):
|
|||||||
tabs.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
tabs.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||||
tabs.tabBar().customContextMenuRequested.connect(self._show_tab_menu)
|
tabs.tabBar().customContextMenuRequested.connect(self._show_tab_menu)
|
||||||
tree.itemChanged.connect(self._signal_changed)
|
tree.itemChanged.connect(self._signal_changed)
|
||||||
|
tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||||
|
tree.customContextMenuRequested.connect(self._show_signal_menu)
|
||||||
files.runtime_changed.connect(self.load_root)
|
files.runtime_changed.connect(self.load_root)
|
||||||
|
|
||||||
window.ui.actionUndo.triggered.connect(self.undo)
|
window.ui.actionUndo.triggered.connect(self.undo)
|
||||||
@@ -201,6 +203,25 @@ class SimulationPlotController(QObject):
|
|||||||
elif selected is remove_action:
|
elif selected is remove_action:
|
||||||
self.remove_tab(index)
|
self.remove_tab(index)
|
||||||
|
|
||||||
|
def _show_signal_menu(self, position: QPoint) -> None:
|
||||||
|
root = self.files.root
|
||||||
|
tree = self.window.ui.simulationTree
|
||||||
|
item = tree.itemAt(position)
|
||||||
|
signal = item.data(0, Qt.ItemDataRole.UserRole) if item is not None else None
|
||||||
|
if root is None or signal is None or not 0 <= self._active_tab < len(root.plot_tabs):
|
||||||
|
return
|
||||||
|
tab = root.plot_tabs[self._active_tab]
|
||||||
|
menu = QMenu(tree)
|
||||||
|
use_signal = menu.addAction("Use as x axis")
|
||||||
|
use_signal.setEnabled(tab.x_axis != signal)
|
||||||
|
use_time = menu.addAction("Use time as x axis")
|
||||||
|
use_time.setEnabled(tab.x_axis is not None)
|
||||||
|
selected = menu.exec(tree.viewport().mapToGlobal(position))
|
||||||
|
if selected is use_signal:
|
||||||
|
self.undo_stack.push(ChangeSimulationPlotXAxisCommand(root, self._active_tab, signal, self._selection_changed))
|
||||||
|
elif selected is use_time:
|
||||||
|
self.undo_stack.push(ChangeSimulationPlotXAxisCommand(root, self._active_tab, None, self._selection_changed))
|
||||||
|
|
||||||
def _available_signals(self) -> list[str]:
|
def _available_signals(self) -> list[str]:
|
||||||
root = self.files.root
|
root = self.files.root
|
||||||
if root is None:
|
if root is None:
|
||||||
@@ -220,7 +241,8 @@ class SimulationPlotController(QObject):
|
|||||||
return
|
return
|
||||||
widget = self.window.ui.resultsTabWidget.widget(index)
|
widget = self.window.ui.resultsTabWidget.widget(index)
|
||||||
if isinstance(widget, SimulationPlotWidget):
|
if isinstance(widget, SimulationPlotWidget):
|
||||||
widget.set_plot(root.results, root.plot_tabs[index].signals)
|
tab = root.plot_tabs[index]
|
||||||
|
widget.set_plot(root.results, tab.signals, tab.x_axis)
|
||||||
|
|
||||||
def _set_undo_text(self, command: str) -> None:
|
def _set_undo_text(self, command: str) -> None:
|
||||||
self.window.ui.actionUndo.setText(f"Undo {command}" if command else "Undo")
|
self.window.ui.actionUndo.setText(f"Undo {command}" if command else "Undo")
|
||||||
|
|||||||
@@ -29,16 +29,17 @@ class CompiledModel:
|
|||||||
class SimulationPlotTab:
|
class SimulationPlotTab:
|
||||||
name: str
|
name: str
|
||||||
signals: list[str] = field(default_factory=list)
|
signals: list[str] = field(default_factory=list)
|
||||||
|
x_axis: str | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_data(cls, data: Mapping[str, Any]) -> SimulationPlotTab:
|
def from_data(cls, data: Mapping[str, Any]) -> SimulationPlotTab:
|
||||||
raw_signals = data.get("signals", [])
|
raw_signals = data.get("signals", [])
|
||||||
if not isinstance(raw_signals, list):
|
if not isinstance(raw_signals, list):
|
||||||
raise TypeError("plot tab signals must be a list")
|
raise TypeError("plot tab signals must be a list")
|
||||||
return cls(name=str(data.get("name", "Plot")), signals=[str(signal) for signal in raw_signals])
|
return cls(name=str(data.get("name", "Plot")), signals=[str(signal) for signal in raw_signals], x_axis=str(data["x_axis"]) if data.get("x_axis") is not None else None)
|
||||||
|
|
||||||
def to_data(self) -> dict[str, Any]:
|
def to_data(self) -> dict[str, Any]:
|
||||||
return {"name": self.name, "signals": self.signals}
|
return {"name": self.name, "signals": self.signals, "x_axis": self.x_axis}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg, NavigationToolbar2QT
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from PySide6.QtWidgets import QVBoxLayout, QWidget
|
from PySide6.QtWidgets import QVBoxLayout, QWidget
|
||||||
|
|
||||||
@@ -12,31 +12,35 @@ class SimulationPlotWidget(QWidget):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.figure = Figure(layout="constrained")
|
self.figure = Figure(layout="constrained")
|
||||||
self.canvas = FigureCanvasQTAgg(self.figure)
|
self.canvas = FigureCanvasQTAgg(self.figure)
|
||||||
|
self.toolbar = NavigationToolbar2QT(self.canvas, self)
|
||||||
self.axes = self.figure.add_subplot()
|
self.axes = self.figure.add_subplot()
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
layout.addWidget(self.toolbar)
|
||||||
layout.addWidget(self.canvas)
|
layout.addWidget(self.canvas)
|
||||||
|
|
||||||
def set_plot(self, results: list[SimulationResult], signals: list[str]) -> None:
|
def set_plot(self, results: list[SimulationResult], signals: list[str], x_axis: str | None = None) -> None:
|
||||||
self.axes.clear()
|
self.axes.clear()
|
||||||
for signal in signals:
|
for signal in signals:
|
||||||
times: list[float] = []
|
x_values: list[float] = []
|
||||||
values: list[float] = []
|
values: list[float] = []
|
||||||
sample_offset = 0
|
sample_offset = 0
|
||||||
for result in results:
|
for result in results:
|
||||||
result_values = result.data.get(signal)
|
result_values = result.data.get(signal)
|
||||||
if result_values is None:
|
if result_values is None:
|
||||||
continue
|
continue
|
||||||
result_times = result.data.get("time")
|
result_x = result.data.get(x_axis or "time")
|
||||||
if result_times is not None and len(result_times) == len(result_values):
|
if result_x is not None and len(result_x) == len(result_values):
|
||||||
times.extend(result_times)
|
x_values.extend(result_x)
|
||||||
|
elif x_axis is not None:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
times.extend(range(sample_offset, sample_offset + len(result_values)))
|
x_values.extend(range(sample_offset, sample_offset + len(result_values)))
|
||||||
values.extend(result_values)
|
values.extend(result_values)
|
||||||
sample_offset += len(result_values)
|
sample_offset += len(result_values)
|
||||||
if values:
|
if values:
|
||||||
self.axes.plot(times, values, label=signal)
|
self.axes.plot(x_values, values, label=signal)
|
||||||
self.axes.set_xlabel("Time")
|
self.axes.set_xlabel(x_axis or "Time")
|
||||||
self.axes.grid(True)
|
self.axes.grid(True)
|
||||||
if self.axes.lines:
|
if self.axes.lines:
|
||||||
self.axes.legend()
|
self.axes.legend()
|
||||||
|
|||||||
@@ -7908,13 +7908,22 @@
|
|||||||
"signals": [
|
"signals": [
|
||||||
"j0.e",
|
"j0.e",
|
||||||
"j1.f"
|
"j1.f"
|
||||||
]
|
],
|
||||||
|
"x_axis": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "R power",
|
"name": "R power",
|
||||||
"signals": [
|
"signals": [
|
||||||
"R.power"
|
"R.power"
|
||||||
]
|
],
|
||||||
|
"x_axis": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Eh idk?",
|
||||||
|
"signals": [
|
||||||
|
"j1.f"
|
||||||
|
],
|
||||||
|
"x_axis": "j0.e"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user