eqation editor widget
This commit is contained in:
@@ -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:
|
||||
|
||||
86
src/bedit_gui/ui/forms/equation_editor_widget.ui
Normal file
86
src/bedit_gui/ui/forms/equation_editor_widget.ui
Normal 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>
|
||||
117
src/bedit_gui/views/equation_editor_widget.py
Normal file
117
src/bedit_gui/views/equation_editor_widget.py
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user