Start of simulation settings window

This commit is contained in:
2026-07-30 21:12:36 +02:00
parent 748bb08531
commit a03c6d624e
4 changed files with 384 additions and 12 deletions

View File

@@ -2,7 +2,8 @@ from collections.abc import Callable
from functools import partial from functools import partial
from typing import Protocol from typing import Protocol
from PySide6.QtCore import QObject, QPoint, QSize, Qt from PySide6.QtCore import QEvent, QObject, QPoint, QSize, Qt
from PySide6.QtGui import QMouseEvent
from PySide6.QtWidgets import QAbstractItemView, QDialog, QHeaderView, QMenu from PySide6.QtWidgets import QAbstractItemView, QDialog, QHeaderView, QMenu
from bedit_core.models import Component, ComponentID, EquationImplementation, GraphImplementation, Port, PortID, Parameter, ParameterID from bedit_core.models import Component, ComponentID, EquationImplementation, GraphImplementation, Port, PortID, Parameter, ParameterID
@@ -79,15 +80,20 @@ class DocumentTreeController(QObject):
window.ui.documentTree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) window.ui.documentTree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
window.ui.documentTree.header().setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) window.ui.documentTree.header().setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
window.ui.documentTree.setColumnWidth(1, 56) window.ui.documentTree.setColumnWidth(1, 56)
window.ui.documentTree.setContextMenuPolicy( self._tree_viewport = window.ui.documentTree.viewport()
Qt.ContextMenuPolicy.CustomContextMenu self._tree_viewport.installEventFilter(self)
)
window.ui.documentTree.customContextMenuRequested.connect(
self._show_context_menu
)
self._on_document_changed(document.model) self._on_document_changed(document.model)
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
if watched is self._tree_viewport and event.type() in (QEvent.Type.MouseButtonPress, QEvent.Type.MouseButtonRelease):
assert isinstance(event, QMouseEvent)
if event.button() == Qt.MouseButton.RightButton:
if event.type() == QEvent.Type.MouseButtonRelease:
self._show_context_menu(event.position().toPoint())
return True
return super().eventFilter(watched, event)
def _on_document_changed(self, model: CoreDocument) -> None: def _on_document_changed(self, model: CoreDocument) -> None:
"""Rebuild the tree whenever New/Open replaces the core document.""" """Rebuild the tree whenever New/Open replaces the core document."""
self._show_equation_component(None) self._show_equation_component(None)

View File

@@ -1,4 +1,3 @@
"""GUI document data stored inside the core document metadata field."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
@@ -12,6 +11,9 @@ from bedit_core.models import ComponentID, ID, PortID
class ShapeID(ID): class ShapeID(ID):
pass pass
class SimulationID(ID):
pass
@dataclass @dataclass
class Shape: class Shape:
layer: int layer: int
@@ -32,7 +34,6 @@ class Shape:
def to_data(self) -> dict[str, Any]: def to_data(self) -> dict[str, Any]:
return {"layer": self.layer, "type": self.type, "pos": list(self.pos)} return {"layer": self.layer, "type": self.type, "pos": list(self.pos)}
class LineType(Enum): class LineType(Enum):
NONE = "none" NONE = "none"
SOLID = "solid" SOLID = "solid"
@@ -40,7 +41,6 @@ class LineType(Enum):
DOTTED = "dotted" DOTTED = "dotted"
DASH_DOT = "dash_dot" DASH_DOT = "dash_dot"
@dataclass @dataclass
class Line(Shape): class Line(Shape):
type: str = field(init=False, default="line") type: str = field(init=False, default="line")
@@ -58,7 +58,6 @@ class Line(Shape):
def to_data(self) -> dict[str, Any]: def to_data(self) -> dict[str, Any]:
return {**super().to_data(), "end": list(self.end), "line_type": self.line_type.value, "line_thickness": self.line_thickness, "line_color": self.line_color} return {**super().to_data(), "end": list(self.end), "line_type": self.line_type.value, "line_thickness": self.line_thickness, "line_color": self.line_color}
@dataclass @dataclass
class Rectangle(Shape): class Rectangle(Shape):
type: str = field(init=False, default="rectangle") type: str = field(init=False, default="rectangle")
@@ -111,7 +110,6 @@ class Icon:
def to_data(self) -> dict[str, Any]: def to_data(self) -> dict[str, Any]:
return {"shapes": {str(key): shape.to_data() for key, shape in self.shapes.items()}, "port_positions": {str(key): list(position) for key, position in self.port_positions.items()}} return {"shapes": {str(key): shape.to_data() for key, shape in self.shapes.items()}, "port_positions": {str(key): list(position) for key, position in self.port_positions.items()}}
@dataclass @dataclass
class IconDatabase: class IconDatabase:
format_version: int = 1 format_version: int = 1
@@ -124,3 +122,61 @@ class IconDatabase:
def to_data(self) -> dict[str, Any]: def to_data(self) -> dict[str, Any]:
return {"format_version": self.format_version, "icons": {str(key): icon.to_data() for key, icon in self.icons.items()}} return {"format_version": self.format_version, "icons": {str(key): icon.to_data() for key, icon in self.icons.items()}}
class SimulationMethod(Enum):
DASSL = "dassl"
@dataclass
class Simulation:
component: ComponentID
name: str
start_time: float
duration: float
use_timed_steps: bool
number_of_steps: int
step_size: float
method: SimulationMethod
dassl_tolerance: float
@classmethod
def from_data(cls, data: Mapping[str, Any]) -> Simulation:
return cls(
component=SimulationID(data.get("component"), SimulationID()),
name=data.get("name", ""),
start_time=float(data.get("start_time", "0.0")),
duration=float(data.get("duration", "1.0")),
use_timed_steps=bool(data.get("use_timed_steps", "false")),
number_of_steps=int(data.get("number_of_steps", "500")),
step_size=float(data.get("step_size", "0.001")),
method=SimulationMethod(data.get("method", "dassl")),
dassl_tolerance=float(data.get("dassl_tolerance", "1e-6")),
)
def to_data(self) -> dict[str, Any]:
return {
"component": str(self.component),
"name": self.name,
"start_time": str(self.start_time),
"duration": str(self.duration),
"use_timed_steps": str(self.use_timed_steps),
"number_of_steps": str(self.number_of_steps),
"step_size": str(self.step_size),
"method": str(self.method),
"dassl_tolerance": str(self.dassl_tolerance),
}
@dataclass
class SimulationDatabase:
format_versio: int = 1
simulations: dict[SimulationID, Simulation] = field(default_factory=dict)
@classmethod
def from_data(cls, data: Mapping[str, Any]) -> SimulationDatabase:
sims = {SimulationID(key): Simulation.from_data(value) for key, value in data.get("simulations", {}).items()}
return cls(format_version=int(data.get("format_version", 1)), simulations=sims)
def to_data(self) -> dict[str, Any]:
return {"format_version": self.format_version, "simulations": {str(key): sim.to_data() for key, sim in self.simulations.items()}}

View File

@@ -74,9 +74,20 @@
</property> </property>
<addaction name="actionAbout_QT"/> <addaction name="actionAbout_QT"/>
</widget> </widget>
<widget class="QMenu" name="menuSimulation">
<property name="title">
<string>Simulation</string>
</property>
<addaction name="actionSimulation_Settings"/>
<addaction name="actionEdit_Parameters"/>
<addaction name="separator"/>
<addaction name="actionCompile_Model"/>
<addaction name="actionOpen_Simulation_Window"/>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
<addaction name="menuView"/> <addaction name="menuView"/>
<addaction name="menuSimulation"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
@@ -153,6 +164,21 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
<widget class="QToolBar" name="simToolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionSimulation_Settings"/>
<addaction name="actionEdit_Parameters"/>
<addaction name="actionCompile_Model"/>
<addaction name="actionOpen_Simulation_Window"/>
</widget>
<action name="actionOpen_File"> <action name="actionOpen_File">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset resource="../../resources/resources.qrc">
@@ -386,6 +412,54 @@
<enum>QAction::MenuRole::NoRole</enum> <enum>QAction::MenuRole::NoRole</enum>
</property> </property>
</action> </action>
<action name="actionSimulation_Settings">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/icons/configure.png</normaloff>:/icons/icons/configure.png</iconset>
</property>
<property name="text">
<string>Simulation Settings</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionEdit_Parameters">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/icons/view-form-table.png</normaloff>:/icons/icons/view-form-table.png</iconset>
</property>
<property name="text">
<string>Edit Parameters</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionCompile_Model">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/icons/run-build.png</normaloff>:/icons/icons/run-build.png</iconset>
</property>
<property name="text">
<string>Compile Model</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionOpen_Simulation_Window">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/icons/office-chart-line.png</normaloff>:/icons/icons/office-chart-line.png</iconset>
</property>
<property name="text">
<string>Open Simulation Window</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="../../resources/resources.qrc"/> <include location="../../resources/resources.qrc"/>

View File

@@ -0,0 +1,236 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>562</width>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListWidget" name="simulationList"/>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="basicForm">
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="componentLabel">
<property name="text">
<string>Component:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="componentCompoBox"/>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="timeForm">
<item row="0" column="0">
<widget class="QLabel" name="startTimeLabel">
<property name="text">
<string>Start time:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="simLengthLabel">
<property name="text">
<string>Simulation length:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="startTimeSpinBox">
<property name="suffix">
<string> s</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="simLengthSpinBox">
<property name="suffix">
<string> s</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="stepSizeButton">
<property name="text">
<string>Step size</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="nrOfStepsButton">
<property name="text">
<string>Number of steps</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="stepSizeLabel">
<property name="text">
<string>Step size:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="stepLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="stepSizeSpinBox">
<property name="suffix">
<string> s</string>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="value">
<double>0.001000000000000</double>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="nrOfStepsSpinBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="nrOfStepsLabel">
<property name="text">
<string>Number of steps:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="simulationMethodComboBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="simMethodLabel">
<property name="text">
<string>Simulation method:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="dasslForm">
<item row="0" column="1">
<widget class="QLineEdit" name="toleranceEdit">
<property name="text">
<string>1e-06</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="toleranceLabel">
<property name="text">
<string>Tolerance:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>