Parameter editor added
This commit is contained in:
@@ -95,7 +95,7 @@ class BondPort(Port):
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Parameter:
|
class Parameter:
|
||||||
name: str
|
name: str
|
||||||
value: Any = 1.0
|
value: Any = "1.0"
|
||||||
value_type: ValueType = ValueType.REAL
|
value_type: ValueType = ValueType.REAL
|
||||||
matrix_size: Annotated[list[int], 2] = field(default_factory=lambda: [1, 1])
|
matrix_size: Annotated[list[int], 2] = field(default_factory=lambda: [1, 1])
|
||||||
quantity: str | None = None
|
quantity: str | None = None
|
||||||
|
|||||||
@@ -25,14 +25,18 @@ def main() -> int:
|
|||||||
settings = ApplicationSettings()
|
settings = ApplicationSettings()
|
||||||
document = Document(app)
|
document = Document(app)
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
|
|
||||||
LogController(window, settings.log_level)
|
LogController(window, settings.log_level)
|
||||||
DocumentController(document, window)
|
DocumentController(document, window)
|
||||||
SettingsController(window, settings)
|
SettingsController(window, settings)
|
||||||
UndoController(document, window)
|
UndoController(document, window)
|
||||||
ViewMenuController(window)
|
ViewMenuController(window)
|
||||||
DocumentTreeController(document, window)
|
DocumentTreeController(document, window)
|
||||||
|
|
||||||
window_state_controller = WindowStateController(app, window)
|
window_state_controller = WindowStateController(app, window)
|
||||||
window_state_controller.restore()
|
window_state_controller.restore()
|
||||||
|
|
||||||
|
document.new()
|
||||||
window.showMaximized()
|
window.showMaximized()
|
||||||
|
|
||||||
return app.exec()
|
return app.exec()
|
||||||
|
|||||||
76
src/bedit_gui/commands/param_commands.py
Normal file
76
src/bedit_gui/commands/param_commands.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from PySide6.QtGui import QUndoCommand
|
||||||
|
|
||||||
|
from bedit_core.models import Component, Parameter, ParameterID
|
||||||
|
|
||||||
|
|
||||||
|
class AddParamCommand(QUndoCommand):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
document: object,
|
||||||
|
component: Component,
|
||||||
|
param_id: ParameterID,
|
||||||
|
param: Parameter,
|
||||||
|
) -> None:
|
||||||
|
super().__init__("Add port")
|
||||||
|
self.document = document
|
||||||
|
self.component = component
|
||||||
|
self.param_id = param_id
|
||||||
|
self.param = deepcopy(param)
|
||||||
|
|
||||||
|
def redo(self) -> None:
|
||||||
|
self.component.parameters[self.param_id] = deepcopy(self.param)
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
|
|
||||||
|
def undo(self) -> None:
|
||||||
|
del self.component.parameters[self.param_id]
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveParamCommand(QUndoCommand):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
document: object,
|
||||||
|
component: Component,
|
||||||
|
param_id: ParameterID,
|
||||||
|
) -> None:
|
||||||
|
super().__init__("Remove parameter")
|
||||||
|
self.document = document
|
||||||
|
self.component = component
|
||||||
|
self.param_id = param_id
|
||||||
|
self.param = deepcopy(component.parameters[param_id])
|
||||||
|
|
||||||
|
def redo(self) -> None:
|
||||||
|
del self.component.parameters[self.param_id]
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
|
|
||||||
|
def undo(self) -> None:
|
||||||
|
self.component.parameters[self.param_id] = deepcopy(self.param)
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeParamCommand(QUndoCommand):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
document: object,
|
||||||
|
component: Component,
|
||||||
|
param_id: ParameterID,
|
||||||
|
param: Parameter,
|
||||||
|
) -> None:
|
||||||
|
super().__init__("Change parameter")
|
||||||
|
self.document = document
|
||||||
|
self.component = component
|
||||||
|
self.param_id = param_id
|
||||||
|
self.old_param = deepcopy(component.parameters[param_id])
|
||||||
|
self.new_param = deepcopy(param)
|
||||||
|
|
||||||
|
def redo(self) -> None:
|
||||||
|
self.component.parameters[self.param_id] = deepcopy(self.new_param)
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
|
|
||||||
|
def undo(self) -> None:
|
||||||
|
self.component.parameters[self.param_id] = deepcopy(self.old_param)
|
||||||
|
self.document.model_changed.emit(self.document.model)
|
||||||
@@ -4,27 +4,34 @@ from typing import Protocol
|
|||||||
from PySide6.QtCore import QObject, QPoint, Qt
|
from PySide6.QtCore import QObject, QPoint, Qt
|
||||||
from PySide6.QtWidgets import QDialog, QMenu
|
from PySide6.QtWidgets import QDialog, QMenu
|
||||||
|
|
||||||
from bedit_core.models import Component, Port, PortID
|
from bedit_core.models import Component, Port, PortID, Parameter, ParameterID
|
||||||
from bedit_core.models import Document as CoreDocument
|
from bedit_core.models import Document as CoreDocument
|
||||||
from bedit_gui.documents import Document
|
from bedit_gui.documents import Document
|
||||||
from bedit_gui.views.dialogs.interface_editor_dialog import (
|
from bedit_gui.views.dialogs.interface_editor_dialog import InterfaceEditorDialog
|
||||||
InterfaceEditorDialog,
|
from bedit_gui.views.dialogs.param_editor_dialog import ParamEditorDialog
|
||||||
)
|
|
||||||
from bedit_gui.views.main_window import MainWindow
|
from bedit_gui.views.main_window import MainWindow
|
||||||
from bedit_gui.views.models.document_tree_model import DocumentTreeModel
|
from bedit_gui.views.models.document_tree_model import DocumentTreeModel
|
||||||
|
|
||||||
|
|
||||||
class InterfaceEditorLike(Protocol):
|
class InterfaceEditorLike(Protocol):
|
||||||
def exec(self) -> int: ...
|
def exec(self) -> int: ...
|
||||||
|
|
||||||
def ports(self) -> dict[PortID, Port]: ...
|
def ports(self) -> dict[PortID, Port]: ...
|
||||||
|
|
||||||
|
class ParamEditorLike(Protocol):
|
||||||
|
def exec(self) -> int: ...
|
||||||
|
def params(self) -> dict[ParameterID, Parameter]: ...
|
||||||
|
|
||||||
|
|
||||||
InterfaceEditorFactory = Callable[
|
InterfaceEditorFactory = Callable[
|
||||||
[dict[PortID, Port], MainWindow],
|
[dict[PortID, Port], MainWindow],
|
||||||
InterfaceEditorLike,
|
InterfaceEditorLike,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ParamEditorFactory = Callable[
|
||||||
|
[dict[ParameterID, Parameter], MainWindow],
|
||||||
|
ParamEditorLike
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DocumentTreeController(QObject):
|
class DocumentTreeController(QObject):
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -32,6 +39,7 @@ class DocumentTreeController(QObject):
|
|||||||
document: Document,
|
document: Document,
|
||||||
window: MainWindow,
|
window: MainWindow,
|
||||||
interface_editor_factory: InterfaceEditorFactory = InterfaceEditorDialog,
|
interface_editor_factory: InterfaceEditorFactory = InterfaceEditorDialog,
|
||||||
|
param_editor_factory: ParamEditorFactory = ParamEditorDialog,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(window)
|
super().__init__(window)
|
||||||
|
|
||||||
@@ -39,6 +47,7 @@ class DocumentTreeController(QObject):
|
|||||||
self.window = window
|
self.window = window
|
||||||
self.model = DocumentTreeModel()
|
self.model = DocumentTreeModel()
|
||||||
self.interface_editor_factory = interface_editor_factory
|
self.interface_editor_factory = interface_editor_factory
|
||||||
|
self.param_editor_factory = param_editor_factory
|
||||||
|
|
||||||
window.ui.documentTree.setModel(self.model)
|
window.ui.documentTree.setModel(self.model)
|
||||||
document.model_changed.connect(self._on_document_changed)
|
document.model_changed.connect(self._on_document_changed)
|
||||||
@@ -71,11 +80,14 @@ class DocumentTreeController(QObject):
|
|||||||
|
|
||||||
menu = QMenu(self.window.ui.documentTree)
|
menu = QMenu(self.window.ui.documentTree)
|
||||||
edit_interface = menu.addAction("Edit Interface")
|
edit_interface = menu.addAction("Edit Interface")
|
||||||
|
edit_params = menu.addAction("Edit Parameters")
|
||||||
selected = menu.exec(
|
selected = menu.exec(
|
||||||
self.window.ui.documentTree.viewport().mapToGlobal(position)
|
self.window.ui.documentTree.viewport().mapToGlobal(position)
|
||||||
)
|
)
|
||||||
if selected is edit_interface:
|
if selected is edit_interface:
|
||||||
self._edit_interface(component)
|
self._edit_interface(component)
|
||||||
|
elif selected is edit_params:
|
||||||
|
self._edit_params(component)
|
||||||
|
|
||||||
def _edit_interface(self, component: Component) -> None:
|
def _edit_interface(self, component: Component) -> None:
|
||||||
dialog = self.interface_editor_factory(
|
dialog = self.interface_editor_factory(
|
||||||
@@ -84,3 +96,12 @@ class DocumentTreeController(QObject):
|
|||||||
)
|
)
|
||||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||||
self.document.update_component_ports(component, dialog.ports())
|
self.document.update_component_ports(component, dialog.ports())
|
||||||
|
|
||||||
|
def _edit_params(self, component: Component) -> None:
|
||||||
|
dialog = self.param_editor_factory(
|
||||||
|
component.parameters,
|
||||||
|
self.window
|
||||||
|
)
|
||||||
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||||
|
self.document.update_component_params(component, dialog.params())
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ from pathlib import Path
|
|||||||
from PySide6.QtCore import QObject, Signal
|
from PySide6.QtCore import QObject, Signal
|
||||||
from PySide6.QtGui import QUndoStack
|
from PySide6.QtGui import QUndoStack
|
||||||
|
|
||||||
from bedit_core.models import ID, Component, Port, PortID
|
from bedit_core.models import ID, Component, Port, PortID, Parameter, ParameterID
|
||||||
from bedit_core.models import Document as CoreDocument
|
from bedit_core.models import Document as CoreDocument
|
||||||
from bedit_gui.commands.port_commands import AddPortCommand, ChangePortCommand, RemovePortCommand
|
from bedit_gui.commands.port_commands import AddPortCommand, ChangePortCommand, RemovePortCommand
|
||||||
|
from bedit_gui.commands.param_commands import AddParamCommand, ChangeParamCommand, RemoveParamCommand
|
||||||
from bedit_gui.commands.rename_component_command import RenameComponentCommand
|
from bedit_gui.commands.rename_component_command import RenameComponentCommand
|
||||||
from bedit_gui.commands.rename_document_command import RenameDocumentCommand
|
from bedit_gui.commands.rename_document_command import RenameDocumentCommand
|
||||||
from bedit_gui.services import document_files
|
from bedit_gui.services import document_files
|
||||||
@@ -88,11 +89,7 @@ class Document(QObject):
|
|||||||
def rename_component(self, component: Component, name: str) -> None:
|
def rename_component(self, component: Component, name: str) -> None:
|
||||||
self.undo_stack.push(RenameComponentCommand(self, component, name))
|
self.undo_stack.push(RenameComponentCommand(self, component, name))
|
||||||
|
|
||||||
def update_component_ports(
|
def update_component_ports(self, component: Component, ports: dict[PortID, Port]) -> None:
|
||||||
self,
|
|
||||||
component: Component,
|
|
||||||
ports: dict[PortID, Port],
|
|
||||||
) -> None:
|
|
||||||
current = component.interface.ports
|
current = component.interface.ports
|
||||||
removed = [
|
removed = [
|
||||||
RemovePortCommand(self, component, port_id)
|
RemovePortCommand(self, component, port_id)
|
||||||
@@ -115,3 +112,28 @@ class Document(QObject):
|
|||||||
for command in commands:
|
for command in commands:
|
||||||
self.undo_stack.push(command)
|
self.undo_stack.push(command)
|
||||||
self.undo_stack.endMacro()
|
self.undo_stack.endMacro()
|
||||||
|
|
||||||
|
def update_component_params(self, component: Component, params: dict[ParameterID, Parameter]) -> None:
|
||||||
|
current = component.parameters
|
||||||
|
removed = [
|
||||||
|
RemoveParamCommand(self, component, param_id)
|
||||||
|
for param_id in current.keys() - params.keys()
|
||||||
|
]
|
||||||
|
added = [
|
||||||
|
AddParamCommand(self, component, param_id, params[param_id])
|
||||||
|
for param_id in params.keys() - current.keys()
|
||||||
|
]
|
||||||
|
changed = [
|
||||||
|
ChangeParamCommand(self, component, param_id, params[param_id])
|
||||||
|
for param_id in current.keys() & params.keys()
|
||||||
|
if current[param_id] != params[param_id]
|
||||||
|
]
|
||||||
|
commands = [*removed, *added, *changed]
|
||||||
|
if not commands:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.undo_stack.beginMacro("Edit parameter")
|
||||||
|
for command in commands:
|
||||||
|
self.undo_stack.push(command)
|
||||||
|
self.undo_stack.endMacro()
|
||||||
|
|
||||||
|
|||||||
169
src/bedit_gui/ui/forms/param_editor_widget.ui
Normal file
169
src/bedit_gui/ui/forms/param_editor_widget.ui
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ParamEditor</class>
|
||||||
|
<widget class="QWidget" name="ParamEditor">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>541</width>
|
||||||
|
<height>241</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="leftColumn">
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="portList"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="buttonRow">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addParam">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add Parameter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeParam">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove Parameter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="rightColumn">
|
||||||
|
<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="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="nameEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="typeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Type:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="typeRow">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="typeComboBox"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="sizeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Size:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="sizeRow">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="widthSize">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> rows</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="heightSize">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> columns</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quantity:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Unit:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="valueEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="valueLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="descriptionForm">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="descriptionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Description:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="descriptionEdit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>60</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
36
src/bedit_gui/views/dialogs/param_editor_dialog.py
Normal file
36
src/bedit_gui/views/dialogs/param_editor_dialog.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QWidget
|
||||||
|
|
||||||
|
from bedit_core.models import ParameterID, Parameter
|
||||||
|
from bedit_gui.views.param_editor_widget import ParamEditorWidget
|
||||||
|
|
||||||
|
|
||||||
|
class ParamEditorDialog(QDialog):
|
||||||
|
"""Modal wrapper around the reusable param editor widget."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
params: dict[ParameterID, Parameter],
|
||||||
|
parent: QWidget | None = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Edit Parameters")
|
||||||
|
self.resize(550, 280)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.editor = ParamEditorWidget(self)
|
||||||
|
self.editor.set_params(params)
|
||||||
|
layout.addWidget(self.editor)
|
||||||
|
|
||||||
|
buttons = QDialogButtonBox(
|
||||||
|
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel,
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
buttons.accepted.connect(self.accept)
|
||||||
|
buttons.rejected.connect(self.reject)
|
||||||
|
layout.addWidget(buttons)
|
||||||
|
|
||||||
|
def params(self) -> dict[ParameterID, Parameter]:
|
||||||
|
return self.editor.params()
|
||||||
|
|
||||||
134
src/bedit_gui/views/param_editor_widget.py
Normal file
134
src/bedit_gui/views/param_editor_widget.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from PySide6.QtCore import QItemSelection, Signal
|
||||||
|
from PySide6.QtGui import QStandardItem, QStandardItemModel
|
||||||
|
from PySide6.QtWidgets import QWidget
|
||||||
|
|
||||||
|
from bedit_core.models import Parameter, ParameterID, ValueType
|
||||||
|
from bedit_gui.ui.generated.ui_param_editor_widget import Ui_ParamEditor
|
||||||
|
|
||||||
|
|
||||||
|
class ParamEditorWidget(QWidget):
|
||||||
|
"""Reusable editor for a detached draft of a component's parameters."""
|
||||||
|
|
||||||
|
params_changed = Signal()
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.ui = Ui_ParamEditor()
|
||||||
|
self.ui.setupUi(self)
|
||||||
|
self._params: dict[ParameterID, Parameter] = {}
|
||||||
|
self._param_ids: list[ParameterID] = []
|
||||||
|
self._loading = False
|
||||||
|
|
||||||
|
self.ui.typeComboBox.addItem("Real", ValueType.REAL)
|
||||||
|
self.ui.typeComboBox.addItem("Integer", ValueType.INT)
|
||||||
|
self.ui.typeComboBox.addItem("Boolean", ValueType.BOOL)
|
||||||
|
|
||||||
|
self._list_model = QStandardItemModel(self)
|
||||||
|
self.ui.portList.setModel(self._list_model)
|
||||||
|
self.ui.portList.selectionModel().selectionChanged.connect(self._selection_changed)
|
||||||
|
self.ui.addParam.clicked.connect(self._add_param)
|
||||||
|
self.ui.removeParam.clicked.connect(self._remove_param)
|
||||||
|
|
||||||
|
self.ui.nameEdit.textEdited.connect(self._form_changed)
|
||||||
|
self.ui.typeComboBox.currentIndexChanged.connect(self._form_changed)
|
||||||
|
self.ui.valueEdit.textEdited.connect(self._form_changed)
|
||||||
|
self.ui.widthSize.valueChanged.connect(self._form_changed)
|
||||||
|
self.ui.heightSize.valueChanged.connect(self._form_changed)
|
||||||
|
self.ui.descriptionEdit.textChanged.connect(self._form_changed)
|
||||||
|
|
||||||
|
self._set_editor_enabled(False)
|
||||||
|
|
||||||
|
def set_params(self, params: dict[ParameterID, Parameter]) -> None:
|
||||||
|
self._params = deepcopy(params)
|
||||||
|
self._param_ids = list(self._params)
|
||||||
|
self._rebuild_list()
|
||||||
|
|
||||||
|
def params(self) -> dict[ParameterID, Parameter]:
|
||||||
|
return deepcopy(self._params)
|
||||||
|
|
||||||
|
def _rebuild_list(self, selected_id: ParameterID | None = None) -> None:
|
||||||
|
self._list_model.clear()
|
||||||
|
for param_id in self._param_ids:
|
||||||
|
self._list_model.appendRow(QStandardItem(self._params[param_id].name))
|
||||||
|
|
||||||
|
if self._param_ids:
|
||||||
|
if selected_id not in self._params:
|
||||||
|
selected_id = self._param_ids[0]
|
||||||
|
index = self._list_model.index(self._param_ids.index(selected_id), 0)
|
||||||
|
self.ui.portList.setCurrentIndex(index)
|
||||||
|
else:
|
||||||
|
self._set_editor_enabled(False)
|
||||||
|
self.ui.removeParam.setEnabled(False)
|
||||||
|
|
||||||
|
def _selection_changed(self, _selected: QItemSelection, _deselected: QItemSelection) -> None:
|
||||||
|
param_id = self._selected_param_id()
|
||||||
|
self.ui.removeParam.setEnabled(param_id is not None)
|
||||||
|
self._set_editor_enabled(param_id is not None)
|
||||||
|
if param_id is not None:
|
||||||
|
self._load_param(self._params[param_id])
|
||||||
|
|
||||||
|
def _selected_param_id(self) -> ParameterID | None:
|
||||||
|
index = self.ui.portList.currentIndex()
|
||||||
|
if not index.isValid() or index.row() >= len(self._param_ids):
|
||||||
|
return None
|
||||||
|
return self._param_ids[index.row()]
|
||||||
|
|
||||||
|
def _load_param(self, param: Parameter) -> None:
|
||||||
|
self._loading = True
|
||||||
|
self.ui.nameEdit.setText(param.name)
|
||||||
|
self.ui.typeComboBox.setCurrentIndex(self.ui.typeComboBox.findData(param.value_type))
|
||||||
|
self.ui.valueEdit.setText(param.value)
|
||||||
|
self.ui.widthSize.setValue(param.matrix_size[0])
|
||||||
|
self.ui.heightSize.setValue(param.matrix_size[1])
|
||||||
|
self.ui.descriptionEdit.setPlainText(param.description or "")
|
||||||
|
self._loading = False
|
||||||
|
|
||||||
|
def _form_changed(self, *_args: object) -> None:
|
||||||
|
if self._loading:
|
||||||
|
return
|
||||||
|
|
||||||
|
param_id = self._selected_param_id()
|
||||||
|
if param_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
old_param = self._params[param_id]
|
||||||
|
self._params[param_id] = Parameter(
|
||||||
|
name=self.ui.nameEdit.text().strip(),
|
||||||
|
value_type=self.ui.typeComboBox.currentData(),
|
||||||
|
value=self.ui.valueEdit.text().strip(),
|
||||||
|
matrix_size=[self.ui.widthSize.value(), self.ui.heightSize.value()],
|
||||||
|
quantity=old_param.quantity,
|
||||||
|
unit=old_param.unit,
|
||||||
|
description=self.ui.descriptionEdit.toPlainText() or None,
|
||||||
|
)
|
||||||
|
self._list_model.item(self._param_ids.index(param_id)).setText(self._params[param_id].name)
|
||||||
|
self.params_changed.emit()
|
||||||
|
|
||||||
|
def _add_param(self) -> None:
|
||||||
|
param_id = ParameterID()
|
||||||
|
param = Parameter(name=f"Parameter {len(self._params) + 1}")
|
||||||
|
self._params[param_id] = param
|
||||||
|
self._param_ids.append(param_id)
|
||||||
|
self._rebuild_list(param_id)
|
||||||
|
self.params_changed.emit()
|
||||||
|
|
||||||
|
def _remove_param(self) -> None:
|
||||||
|
param_id = self._selected_param_id()
|
||||||
|
if param_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
row = self._param_ids.index(param_id)
|
||||||
|
del self._params[param_id]
|
||||||
|
self._param_ids.remove(param_id)
|
||||||
|
selected = self._param_ids[min(row, len(self._param_ids) - 1)] if self._param_ids else None
|
||||||
|
self._rebuild_list(selected)
|
||||||
|
self.params_changed.emit()
|
||||||
|
|
||||||
|
def _set_editor_enabled(self, enabled: bool) -> None:
|
||||||
|
for widget in (self.ui.nameEdit, self.ui.typeComboBox, self.ui.widthSize, self.ui.heightSize, self.ui.descriptionEdit):
|
||||||
|
widget.setEnabled(enabled)
|
||||||
Reference in New Issue
Block a user