eqation editor widget

This commit is contained in:
2026-07-29 18:22:38 +02:00
parent 346a963acc
commit 9df352f6b7
5 changed files with 418 additions and 2 deletions

View File

@@ -5,7 +5,7 @@ from typing import Protocol
from PySide6.QtCore import QObject, QPoint, QSize, Qt
from PySide6.QtWidgets import QAbstractItemView, QDialog, QHeaderView, QMenu
from bedit_core.models import Component, ComponentID, GraphImplementation, Port, PortID, Parameter, ParameterID
from bedit_core.models import Component, ComponentID, EquationImplementation, GraphImplementation, Port, PortID, Parameter, ParameterID
from bedit_core.models import Document as CoreDocument
from bedit_gui.documents import Document
from bedit_gui.models import Icon
@@ -57,6 +57,7 @@ class DocumentTreeController(QObject):
self._components: dict[ComponentID, Component] = {}
window.ui.documentTree.setModel(self.model)
window.ui.documentTree.selectionModel().selectionChanged.connect(self._selection_changed)
document.model_changed.connect(self._on_document_changed)
document.icon_changed.connect(self._on_icon_changed)
self.model.rename_document_requested.connect(self.document.rename)
@@ -87,6 +88,7 @@ class DocumentTreeController(QObject):
def _on_document_changed(self, model: CoreDocument) -> None:
"""Rebuild the tree whenever New/Open replaces the core document."""
self._show_equation_component(None)
self.model.set_document(model)
self._components = {}
self._collect_components(model.root)
@@ -98,6 +100,19 @@ class DocumentTreeController(QObject):
# expanded component IDs and restore only those nodes.
self.window.ui.documentTree.expandAll()
def _selection_changed(self, *_args: object) -> None:
indexes = self.window.ui.documentTree.selectionModel().selectedRows(0)
component = self.model.value(indexes[0]) if len(indexes) == 1 else None
self._show_equation_component(component if isinstance(component, Component) else None)
def _show_equation_component(self, component: Component | None) -> None:
if component is not None and isinstance(component.implementation, EquationImplementation):
self.window.equation_editor.set_component(component)
self.window.equation_editor.show()
else:
self.window.equation_editor.set_component(None)
self.window.equation_editor.hide()
def _on_icon_changed(self, component_id: ComponentID, icon: object) -> None:
component = self._components.get(component_id)
if component is None:

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>equationEditorWidget</class>
<widget class="QWidget" name="equationEditorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1079</width>
<height>730</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="declarationEditor">
<property name="text">
<string>Declarations</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="declarationsTextEdit"/>
</item>
<item>
<widget class="QLabel" name="initialEquationEditor">
<property name="text">
<string>Initial equations</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="initialEquationsTextEdit"/>
</item>
<item>
<widget class="QLabel" name="equationEditor">
<property name="text">
<string>Equations</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="equationsTextEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item alignment="Qt::AlignmentFlag::AlignLeft">
<widget class="QToolButton" name="sidebarButton">
<property name="toolTip">
<string>Hide parameter and port editors</string>
</property>
<property name="arrowType">
<enum>Qt::ArrowType::RightArrow</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="paramEditor">
<property name="text">
<string>paramEditor</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="portEditor">
<property name="text">
<string>portEditor</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,117 @@
from __future__ import annotations
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QWidget
from bedit_core.models import Component, EquationImplementation
from bedit_gui.ui.generated.ui_equation_editor_widget import Ui_equationEditorWidget
from bedit_gui.views.param_editor_widget import ParamEditorWidget
from bedit_gui.views.port_editor_widget import PortEditorWidget
class EquationEditorWidget(QWidget):
"""Editor that keeps an equation component synchronized with its fields."""
component_changed = Signal(object)
sidebar_visible_changed = Signal(bool)
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent)
self.ui = Ui_equationEditorWidget()
self.ui.setupUi(self)
self._component: Component | None = None
self._loading = False
self.param_editor = ParamEditorWidget(self)
self.port_editor = PortEditorWidget(self)
self._replace_placeholder(self.ui.paramEditor, self.param_editor)
self._replace_placeholder(self.ui.portEditor, self.port_editor)
self.ui.sidebarButton.clicked.connect(self.toggle_sidebar)
self.set_sidebar_visible(True)
self.ui.declarationsTextEdit.textChanged.connect(self._text_changed)
self.ui.initialEquationsTextEdit.textChanged.connect(self._text_changed)
self.ui.equationsTextEdit.textChanged.connect(self._text_changed)
self.param_editor.params_changed.connect(self._params_changed)
self.port_editor.ports_changed.connect(self._ports_changed)
self._set_editors_enabled(False)
def set_component(self, component: Component | None) -> None:
if component is not None and not isinstance(component.implementation, EquationImplementation):
raise TypeError("EquationEditorWidget only supports components with an equation implementation")
self._component = component
self.refresh()
def component(self) -> Component | None:
return self._component
def toggle_sidebar(self) -> None:
self.set_sidebar_visible(not self.param_editor.isVisibleTo(self))
def set_sidebar_visible(self, visible: bool) -> None:
changed = self.param_editor.isVisibleTo(self) != visible
self.param_editor.setVisible(visible)
self.port_editor.setVisible(visible)
self.ui.sidebarButton.setArrowType(Qt.ArrowType.RightArrow if visible else Qt.ArrowType.LeftArrow)
self.ui.sidebarButton.setToolTip("Hide parameter and port editors" if visible else "Show parameter and port editors")
if changed:
self.sidebar_visible_changed.emit(visible)
def refresh(self) -> None:
"""Reload the editors after the component was changed externally."""
self._loading = True
component = self._component
if component is None:
self.ui.declarationsTextEdit.clear()
self.ui.initialEquationsTextEdit.clear()
self.ui.equationsTextEdit.clear()
self.param_editor.set_params({})
self.port_editor.set_ports({})
else:
implementation = component.implementation
assert isinstance(implementation, EquationImplementation)
self.ui.declarationsTextEdit.setPlainText("\n".join(implementation.declarations))
self.ui.initialEquationsTextEdit.setPlainText("\n".join(implementation.initial_equations))
self.ui.equationsTextEdit.setPlainText("\n".join(implementation.equations))
self.param_editor.set_params(component.parameters)
self.port_editor.set_ports(component.interface.ports)
self._loading = False
self._set_editors_enabled(component is not None)
def _text_changed(self) -> None:
if self._loading or self._component is None:
return
implementation = self._component.implementation
assert isinstance(implementation, EquationImplementation)
implementation.declarations = self.ui.declarationsTextEdit.toPlainText().splitlines()
implementation.initial_equations = self.ui.initialEquationsTextEdit.toPlainText().splitlines()
implementation.equations = self.ui.equationsTextEdit.toPlainText().splitlines()
self.component_changed.emit(self._component)
def _params_changed(self) -> None:
if self._loading or self._component is None:
return
self._component.parameters = self.param_editor.params()
self.component_changed.emit(self._component)
def _ports_changed(self) -> None:
if self._loading or self._component is None:
return
self._component.interface.ports = self.port_editor.ports()
self.component_changed.emit(self._component)
def _replace_placeholder(self, placeholder: QWidget, editor: QWidget) -> None:
self.ui.verticalLayout_2.replaceWidget(placeholder, editor)
placeholder.hide()
placeholder.deleteLater()
def _set_editors_enabled(self, enabled: bool) -> None:
self.ui.declarationsTextEdit.setEnabled(enabled)
self.ui.initialEquationsTextEdit.setEnabled(enabled)
self.ui.equationsTextEdit.setEnabled(enabled)
self.param_editor.setEnabled(enabled)
self.port_editor.setEnabled(enabled)

View File

@@ -1,7 +1,8 @@
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QMainWindow, QTabWidget
from PySide6.QtWidgets import QMainWindow, QTabWidget, QVBoxLayout
from bedit_gui.ui.generated.ui_main_window import Ui_MainWindow
from bedit_gui.views.equation_editor_widget import EquationEditorWidget
class MainWindow(QMainWindow):
@@ -11,6 +12,12 @@ class MainWindow(QMainWindow):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
central_layout = QVBoxLayout(self.ui.centralwidget)
central_layout.setContentsMargins(0, 0, 0, 0)
self.equation_editor = EquationEditorWidget(self.ui.centralwidget)
self.equation_editor.hide()
central_layout.addWidget(self.equation_editor)
self.setTabPosition(Qt.AllDockWidgetAreas, QTabWidget.North)
self.setCorner(Qt.Corner.BottomLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
self.setCorner(Qt.Corner.BottomRightCorner, Qt.DockWidgetArea.RightDockWidgetArea)